Dotfiles and Brewfile maintenance

What to update and when, and how to rebuild a new Mac from the backup. chezmoi holds the config files; ~/.Brewfile holds the list of installed packages.

When to update

You did this Do this
brew upgrade Nothing. The Brewfile lists names, not versions
Installed or removed a package Refresh the Brewfile
Edited a tracked config file chezmoi re-add the file and push
Created a new config file worth keeping chezmoi add the file and push
A tool broke after a major upgrade Fix the config, then chezmoi re-add and push

Check for drift any time with chezmoi status. Empty output means everything matches the repo.

How to update

Packages changed

brew bundle dump --file ~/.Brewfile --force && chezmoi re-add ~/.Brewfile && chezmoi git push

A config file changed

Edited directly:

chezmoi re-add ~/.config/ghostty/config
chezmoi git push

Or edit through chezmoi so it's tracked automatically:

chezmoi edit ~/.config/ghostty/config
chezmoi apply
chezmoi git push

A new file to track

chezmoi add ~/.config/some-tool/config
chezmoi git push

Use chezmoi add --encrypt for anything sensitive. re-add only works on files chezmoi already tracks.

autoCommit is on, so every add or re-add is committed automatically. Pushing is manual so you can run gitleaks first if a change might contain a secret.

One-time: make the config restorable

chezmoi never commits its own ~/.config/chezmoi/chezmoi.toml, so a new Mac wouldn't know about age encryption or Bitwarden. Commit a template of it instead; chezmoi init turns it back into the real config. The age recipient is a public key, so it's safe in the repo.

  1. Check the recipient matches your key (it must start with age1):
    grep 'public key' ~/.config/chezmoi/key.txt
    grep recipient ~/.config/chezmoi/chezmoi.toml
  2. Test encryption round-trips:
    echo "test" > /tmp/enc-test
    chezmoi encrypt /tmp/enc-test | chezmoi decrypt
    It should print test.
  3. Copy the config into the repo as a template and commit it. It's a new file, so it needs git add; commit -a alone won't pick it up:
    cp ~/.config/chezmoi/chezmoi.toml "$(chezmoi source-path)/.chezmoi.toml.tmpl"
    chezmoi git -- add .chezmoi.toml.tmpl
    chezmoi git -- commit -m "Add chezmoi config template"
    chezmoi git push
  4. Confirm it's tracked and pushed:
    chezmoi git -- log --oneline -3
    chezmoi git -- ls-files | grep chezmoi.toml
    The log should show origin/main on the latest commit, and the second command should print .chezmoi.toml.tmpl.

If you change chezmoi.toml later, copy it over the template again and commit:

cp ~/.config/chezmoi/chezmoi.toml "$(chezmoi source-path)/.chezmoi.toml.tmpl"
chezmoi git -- commit -am "Update chezmoi config template" && chezmoi git push

Rebuild a new Mac

The order matters: chezmoi puts ~/.Brewfile back first, then Homebrew installs from it.

  1. Install the Xcode command line tools and Homebrew:
    xcode-select --install
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    eval "$(/opt/homebrew/bin/brew shellenv)"
  2. Install the bootstrap tools:
    brew install chezmoi age bitwarden-cli gh
  3. Restore the age key from Bitwarden (secure note) to ~/.config/chezmoi/key.txt:
    mkdir -p ~/.config/chezmoi
    vim ~/.config/chezmoi/key.txt
    chmod 600 ~/.config/chezmoi/key.txt
  4. Unlock Bitwarden, so templates that pull secrets can render:
    bw login
    export BW_SESSION=$(bw unlock --raw)
  5. Sign in to GitHub and pull everything down:
    gh auth login
    chezmoi init --apply https://github.com/<you>/dotfiles.git
    init creates ~/.config/chezmoi/chezmoi.toml from the committed template, then decrypts and applies everything.
  6. Install every package and app from the Brewfile:
    brew bundle --file ~/.Brewfile
  7. Check it all landed:
    chezmoi status
    chezmoi doctor
    pwsh -c "Get-Command Get-GuestStatus"

Then open a new Ghostty window to pick up the shell and prompt config.

Troubleshooting

Formulae dependency graph sorting found a circular dependency

Stale dependency data from when packages were installed. Harmless for the Brewfile. To clear it:

brew update
brew reinstall libtiff webp

chezmoi: ... not managed

The file isn't tracked yet. Use chezmoi add instead of re-add.

chezmoi init --apply fails on a Bitwarden template

BW_SESSION isn't set or has expired. Rerun step 4, then chezmoi apply.

age: error: unknown recipient type

The recipient in chezmoi.toml is mistyped. age public keys always start with age1; anything else (for example age2...) is a copy error. Write the correct key in automatically:

PUB=$(grep 'public key' ~/.config/chezmoi/key.txt | awk '{print $4}')
sed -i '' "s|recipient = \".*\"|recipient = \"$PUB\"|" ~/.config/chezmoi/chezmoi.toml
grep recipient ~/.config/chezmoi/chezmoi.toml

Then rerun the encryption test and update the template (one-time section, step 3).

age: error: failed to read header: parsing age header: file is empty

A knock-on error: encryption failed earlier in the pipe, so decrypt received nothing. Fix the first error shown.

nothing added to commit but untracked files present

The file is new to git, and commit -a only commits files git already tracks. Add it first:

chezmoi git -- add <file>
chezmoi git -- commit -m "Add <file>"
chezmoi git push

gitleaks output when committing

Lines like INF no leaks found come from the pre-commit hook and mean the commit was scanned and is clean. If it reports a leak, the commit is blocked: remove the secret, move it to Bitwarden or chezmoi add --encrypt, and commit again.

Encrypted files fail to decrypt

key.txt is missing or wrong, or chezmoi.toml wasn't generated (the template step was skipped). Check the key matches the recipient in ~/.config/chezmoi/chezmoi.toml:

grep 'public key' ~/.config/chezmoi/key.txt
grep recipient ~/.config/chezmoi/chezmoi.toml

brew bundle stops on one package

Usually a cask that needs a manual approval or has been renamed. Fix or remove that line in ~/.Brewfile, rerun brew bundle, then chezmoi re-add ~/.Brewfile.