cmd / laptop
I maintain a laptop repo that configures a macOS machine as a software development environment.
Install
export LAPTOP="$HOME/laptop"
git clone https://github.com/croaky/laptop.git $LAPTOP
cd $LAPTOP
./laptop.sh
What it sets up
The script is idempotent. I run it on macOS on Apple Silicon. It:
- installs system packages with Homebrew
- sets the shell to zsh
- symlinks dotfiles to
$HOME - installs Go, Rust, and Go tools: deadcode, goimports, gopls
- installs tsgo and dprint as single binaries
- installs CLIs: awscli, bat, cb, fd, fzf, gh, git, ripgrep, tree, ubi, pgfmt
- manages local apps and blocks trackers in
/etc/hostswith hostctl - sets up Neovim
- starts Postgres dev and test clusters
No JavaScript runtime
The machine has no node, npm, or bun. tsgo, dprint, esbuild's Go API, and native language servers cover what those ran. See web / no js runtime for the native toolchain.
Hosts file
hostctl manages /etc/hosts for local development and DNS-level ad blocking.
laptop.sh runs it.
Local apps
Browsers treat http://localhost and http://*.localhost as
potentially trustworthy origins,
so Secure Context features work without TLS.
I run apps on different subdomains and ports
(site.localhost:2000, htmz.localhost:2002) to test
cross-origin security:
SameSite=StrictcookiesSec-Fetch-Site: same-originandOriginheadersReferrer-Policy: strict-origin-when-cross-originheader
Ad blocking
A hosts file blocks ads and trackers across every app on the laptop.
hostctl enables blocking by default:
hostctl
To disable blocking while keeping local app entries:
hostctl --unblock
Core script logic:
#!/bin/bash
set -euo pipefail
if [[ "${1:-}" == "--unblock" ]]; then
cat /etc/hosts.local | sudo tee /etc/hosts >/dev/null
else
curl -s https://winhelp2002.mvps.org/hosts.txt | tr -d '\r' | sudo tee /etc/hosts >/dev/null
cat /etc/hosts.local | sudo tee -a /etc/hosts >/dev/null
fi
sudo killall -HUP mDNSResponder
Server configuration
A Go server binds to a port, and the hosts file resolves the subdomain:
fmt.Println("Serving at http://site.localhost:2000")
http.ListenAndServe(":2000", handler)
I use the 2000 port range for Go servers and 3000 for Ruby servers.