Replace chezmoi with Makefile + stow setup#1
Merged
Conversation
- Rename dot_* files to home/.* for direct stow management; drop the four run_once_*.sh.tmpl chezmoi scripts in favour of always-idempotent modules in macos/. - Split system config into macos/defaults.sh (system-wide), macos/dock.sh (dock layout), and macos/apps.sh (per-app prefs). All re-runnable. - Move helper functions to lib/dock_operations.sh, reference via $DOTFILES_DIR so the dock script works regardless of CWD. - New top-level Makefile is the single entry point: make install / update / brew / stow / fonts / macos / apps / doctor / brew-dump / adopt. - scripts/bootstrap.sh installs Xcode CLT, Rosetta (Apple Silicon) and Homebrew if missing. scripts/doctor.sh sanity-checks the environment. - Brewfile audit: drop chezmoi, nvm, pipx (unused or replaced); add bash, stow, gnu-sed (gsed), gnupg (gpg-connect-agent), node, and bun (via the oven-sh/bun tap). Reorganise into commented sections. - bin/ holds personal scripts on $PATH. First inhabitant: eod, an end-of-day report listing uncommitted/unpushed work across every git repo under ~/Developer. - .zshrc: drop chezmoi completion, expose DOTFILES_DIR, prepend bin/ to PATH, add a `dotfiles` alias that proxies to make from anywhere. - Add .gitignore for .DS_Store and Brewfile.lock.json; remove the committed copies. Drop the dead iTerm2 chezmoi symlink (Ghostty now). - Rewrite README with the new layout, target table and conventions.
defaults -app errors out (and with set -e aborts the whole script) when the target app doesn't exist. Guard the iTerm line with a directory check so a Ghostty-only setup doesn't bail out mid-defaults.
…/dotfiles into claude/audit-mac-setup-R7zZb
The 'tell application "Terminal" to set font ...' AppleScript calls launch Terminal.app to apply the change, which surfaces as a random Terminal window every time 'dotfiles update' runs. Drop them; Ghostty is the primary terminal and Terminal.app's defaults still get the non-osascript writes.
- Compact table: one row per repo with status (●N dirty, ↑N ahead, ↓N behind, no-upstream). Only repos that need attention are shown. - --fetch parallelises 'git fetch' (capped at 10 concurrent) so behind counts reflect the actual remote state. Default mode stays fast and uses last-known state. - --apply walks repos needing attention; the action menu is built from the repo's state -- pull when behind only, push when ahead only, pull --rebase when diverged, stash+rebase+pop when behind+dirty, plus always-available 'shell here', 'view diff', 'view log', skip, quit. Implies --fetch so decisions are based on current state. - Use 'git symbolic-ref --short HEAD' instead of rev-parse --abbrev-ref so unborn-HEAD repos report their branch correctly instead of injecting a '?' into the row. - Make the worktree-skipping explicit in the header comment. Sort the repo list after collecting (drops the GNU-only 'sort -z' dependency). - DIR can be passed as a positional arg; defaults to $DEVELOPER_DIR or ~/Developer.
- Prune common build/dependency directories so their checked-out git
repos (e.g. SwiftPM's .build/checkouts/*, node_modules/*) don't
pollute the report. Defaults cover .build, node_modules, vendor,
Pods, DerivedData, .terraform, .gradle, target, .venv, venv,
__pycache__, .next, .nuxt, .svelte-kit, .cache. Override via the
EOD_SKIP_DIRS env var.
- Fetch by default; add --no-fetch to opt out. --apply no longer needs
to imply --fetch since fetch is on.
- Background-parallel fetch with a live spinner ("⠋ Fetching… N/M")
so the previously-silent hang has feedback. Status line is cleared
before normal output.
- Real table layout: dimmed REPO/BRANCH/STATUS headers with ─ rules,
computed column widths, middle-truncation for over-wide paths and
branches. ⊘ instead of literal "no upstream" for compactness.
- Prefer en_US.UTF-8 if the locale is available so ${#string} and ─
rendering both work in characters rather than bytes.
- Fix a set-e exit: clear_status returned 1 when stdout wasn't a TTY,
which aborted the script under `set -e`. Both status helpers now
explicitly return 0.
Runs on push to main, on PRs that touch Brewfile/Makefile/scripts, and
on manual dispatch. Uses macos-15 runners (Apple Silicon).
What it covers:
- brew bundle against a generated Brewfile.ci containing only tap and
brew lines -- mas needs an Apple ID, casks are slow and irrelevant
for verifying the CLI surface.
- make stow with conflicting hosted-runner dotfiles removed first, so
the symlink layout actually gets created.
- Symlink existence check for the dotfiles we expect to manage.
- brew --version + brew list smoke.
- node + pnpm round-trip (init, install ms, require it from node).
- eod against three fake repos covering clean/dirty/ahead states.
- make doctor.
What it deliberately skips: cask installs, mas, the macOS defaults and
dock/apps modules (no apply target on a CI runner).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rebuilds the dotfiles around a
Makefile+stowworkflow so everything is idempotent and re-runnable on existing Macs. Brew is now the single source of truth for every CLI, GUI app, and plugin. Addsbin/for personal scripts, witheodas the first inhabitant.What changed
Layout
home/— files stowed into$HOME(make stow).dot_*renames done; iTerm2 chezmoi symlink dropped since the dock and Brewfile have moved to Ghostty.macos/— three always-idempotent modules:defaults.sh(system-wide),dock.sh(dock layout),apps.sh(per-app prefs). Replaces the four chezmoirun_once_*.sh.tmplscripts.lib/dock_operations.sh— sourced via$DOTFILES_DIRso it works regardless of CWD (the old script broke with a relative./source).scripts/bootstrap.sh— Xcode CLT, Rosetta on Apple Silicon, Homebrew.scripts/doctor.shsanity-checks the environment.bin/— personal scripts, added to$PATHby.zshrc. New:bin/eod.Brewfile audit (lines up with what scripts and shell actually use)
chezmoi(replaced),nvm(conflicted with the volta usage that previously got removed in 8e54533),pipx(unused).bash(scripts need bash 4+),stow(new workflow),gnu-sed(forgsedindefaults.sh),gnupg(forgpg-connect-agentinapps.sh),node,bunvia theoven-sh/buntap.bin/eod— end-of-day reportWalks every git repo under
~/Developerand lists anything with uncommitted changes, unpushed commits, or no upstream branch. Run before shutting down for the day.DEVELOPER_DIR=…or a positional arg overrides the path..zshrcDOTFILES_DIR(defaults to~/.dotfiles) and prepends$DOTFILES_DIR/binto$PATH.dotfilesalias =make -C $DOTFILES_DIR, sodotfiles update,dotfiles brew,dotfiles doctorwork from anywhere.Other
README.mdwith the layout, target table, and conventions..gitignorefor.DS_StoreandBrewfile.lock.json(removes the committed copies).Makefile targets
make installmake updatemake brewbrew bundlemake brew-dumpmake stowhome/into$HOME(--no-folding)make adopt$HOMEinto the repomake macosdefaults.sh+dock.shmake appsapps.shmake fonts~/Library/Fonts(skips existing)make doctorTest plan
Container-side checks done here:
bash -non every scriptmake helplists every targetbin/eodexercised against a fakeDEVELOPER_DIRcontaining a clean, dirty, and unpushed repo — output is correct and uses TTY coloursNeeds verifying on an actual Mac:
make installon a fresh VM (ormake adoptthenmake updateon your existing machine)sudopath: the newdefaults.shprefers/etc/pam.d/sudo_local(Sonoma+) and falls back to editing/etc/pam.d/sudomake fontson a Mac with existing fonts (it skips ones already present)eodagainst your real~/DeveloperMigration notes (existing Macs)
Or, if you want to overwrite local copies with what's in the repo: back them up, delete them, then
make stow.Generated by Claude Code