Skip to content

v1.1.0 — Linux bootstrap, cross-platform parity

Choose a tag to compare

@Jobikinobi Jobikinobi released this 24 May 05:12
· 80 commits to main since this release
9f4d03a

v1.1.0 — Linux bootstrap, cross-platform parity

The Linux dotfiles flow finally works end-to-end. Before this release, chezmoi init --apply Jobikinobi on a fresh Linux box would install Homebrew, then silently fail to install any of the actual core tools. This release fixes that and ships the missing pieces so the same chezmoi init --apply produces a fully bootstrapped Linux machine — zsh as the login shell, all of Brewfile.core (powerlevel10k, fzf, bat, eza, helix, ripgrep, direnv, etc.), the Rust + Node toolchains, npm globals, Supabase CLI, and the tailnet-joined Tailscale daemon. Field-tested end-to-end on a fresh Ubuntu 26.04 cloud-init VM (tst-02): cloud-init reaches status: done on first try, all 30 expected tools present.

Bug fixes

  • brew bundle was a no-op. The script called brew bundle --file=… without a subcommand; current Homebrew prints help and installs nothing. Fixed to brew bundle install --file=…. (The one-character bug with the biggest blast radius.)
  • No apt prereqs on Linux. Linuxbrew needs build-essential procps curl file git ca-certificates already present, and the .zshrc needs zsh installed and to be the login shell. New run_once_before_00-apt-bootstrap.sh.tmpl installs the apt set and runs chsh for the current user.
  • Toolchain init ran in the wrong phase order. rustup-init / fnm / npm globals / supabase setup was in a BEFORE-stage script, but the brew formulas they depend on come from Brewfile.core which only installs in the AFTER stage. Split into run_once_before_install-tailscale.sh.tmpl (needs network identity early) and run_once_after_install-toolchains.sh.tmpl.
  • brew install rustup doesn't give you cargo / rustc on Linux. Linuxbrew's rustup formula ships only the rustup binary, with no ~/.cargo/bin proxies. Dropped rustup from Brewfile.core; install via the official https://sh.rustup.rs script in the toolchain script, which provisions the proper ~/.cargo/env + ~/.cargo/bin/{cargo,rustc,…}.
  • chezmoi apply died on age-encrypted files when ~/.config/chezmoi/key.txt wasn't bootstrapped, killing the AFTER-script stage before the brewfile install ran. Templated .chezmoiignore block now skips the encrypted target paths when the recipient key is absent.
  • dot_zshrc.tmpl aborted apply when Doppler wasn't authenticated. doppler secrets get was called at template-render time and exited non-zero on fresh machines. Wrapped in sh -c "… 2>/dev/null || true" so empty strings are exported and the apply succeeds.
  • Tailscale auto-auth read the wrong Doppler path (backend/prd/TAILSCALE_AUTH_KEY instead of backend/dev/TAILSCALE_AUTHKEY_SERVER). Corrected and added --advertise-tags=tag:server for parity with the rest of the tailnet.
  • CI tested main regardless of which branch was under review. Dockerfile.test's chezmoi init --apply Jobikinobi always clones the default branch. Added a CHEZMOI_BRANCH build arg defaulting to main; the CI workflow passes github.head_ref for pull_request events so PRs test their own fix.
  • $USER was unset in Docker RUN context. The Dockerfile's USER jth sets the uid but not the env var; the apt-bootstrap script's chsh "$USER" therefore failed with "user does not exist" in CI. Switched to $(id -un), which resolves the identity the same way in all execution contexts.

New: Proxmox cloud-init template

scripts/cloud-init/proxmox-tst.yaml.tmpl — render-and-upload-as-snippet user-data for booting a fresh Ubuntu/Debian VM that joins the tailnet, drops chezmoi to ~/.local/bin, and runs chezmoi init --apply --branch <X> Jobikinobi. The Tailscale auth key is written to /run/ts-authkey (tmpfs, mode 0600) and consumed via tailscale up --auth-key=file:…, then shredded — so the key never appears in ps arguments at any point.

Field-test rig

Tested on a fresh Proxmox VM (tst-02, VMID 251) cloned from an Ubuntu 26.04 generic cloud image template with --cpu x86-64-v2-AES (needed for the SSSE3 instruction set Homebrew bottles require — the default kvm64 model fails this check).

Final state after first cloud-init:

  • cloud-init status → done
  • getent passwd jth | cut -d: -f7 → /usr/bin/zsh
  • tailscale status → enrolled as tag:server, Tailscale SSH on
  • /run/ts-authkey → does not exist (shredded after enrollment, never in ps)
  • 30 tools verified present: Brewfile.core (19), Rust toolchain (3), Node + npm (2), npm globals (4), Supabase, chezmoi, tailscale

Closes

Closes #19.