Managing Packages¶
haven manages Homebrew packages and mise language runtimes alongside your dotfiles, keeping everything in sync in a single repo.
Homebrew¶
Installing packages¶
Use haven pkg install instead of bare brew install — it runs the install and updates your Brewfile so the package is tracked (brew backend, the default):
haven pkg install ripgrep
haven pkg install bat
haven pkg install iterm2 --cask # GUI apps and fonts
haven pkg install ripgrep --module shell # add to a specific module's Brewfile
Uninstalling packages¶
haven pkg uninstall ripgrep
haven pkg uninstall iterm2 --cask
Removes the formula from all Brewfiles in the repo, then runs brew uninstall.
Brewfile layout¶
| Path | Used when |
|---|---|
brew/Brewfile |
haven pkg install with no --module |
brew/Brewfile.<name> |
haven pkg install --module <name> |
You can also write Brewfiles directly:
# brew/Brewfile.shell
brew "fish"
brew "starship"
brew "ripgrep"
brew "fd"
brew "bat"
cask "iterm2"
Applying Brewfiles¶
haven apply --brews # install packages from all active Brewfiles
haven apply --brews --module shell # only the shell module's Brewfile
Removing unreferenced packages¶
haven apply --brews --remove-unreferenced-brews # auto-remove anything not in a Brewfile
haven apply --brews --interactive # prompt before removing each package
haven apply --brews --zap # remove + zap cask app data
Checking Homebrew drift¶
haven status --brews # which packages are present / missing
haven diff --brews # detailed diff
Checking for outdated packages¶
haven pkg outdated
Lists upgradable packages across all configured backends, grouped by backend. Output:
==> brew outdated
bat 0.24.0 → 0.25.0
==> mise outdated
node 20.11.0 → 22.14.0
Upgrading packages¶
haven pkg upgrade # upgrade all packages across all backends
haven pkg upgrade ripgrep # upgrade a specific package
haven pkg upgrade node # upgrade a specific mise tool
For mise tools, the pinned version in mise/mise*.toml is rewritten after the upgrade
so the change is tracked in VCS. For brew, brew upgrade runs without modifying Brewfiles.
Note:
haven upgrade(nopkg) upgrades the haven binary itself. Usehaven pkg upgradefor managed packages.
Searching for packages¶
haven pkg search bat # search across all configured backends
Results are grouped by backend with copy-pasteable install hints:
==> brew
bat haven pkg install bat
bat-extras haven pkg install bat-extras
==> mise
bun haven pkg install bun --mise
mise (language runtimes)¶
mise manages language runtimes (Node.js, Python, Ruby, Go, etc.). haven integrates with it via module configuration.
Configuring mise in a module¶
# modules/shell.toml
[mise]
config = "source/mise.toml" # path relative to repo root
On apply, haven runs mise install using the specified config file. If mise is not installed, the section is skipped with a hint.
Example mise.toml¶
# source/mise.toml
[tools]
node = "lts"
python = "3.12"
go = "latest"
Track this file with haven:
haven add ~/.mise.toml
# or reference source/mise.toml directly in your module
Modules¶
Modules group Homebrew packages and mise configs under a name. They control packages only — dotfiles are tracked via magic-name encoding in source/, not modules.
Creating a module¶
# modules/shell.toml
[homebrew]
brewfile = "brew/Brewfile.shell"
[mise]
config = "source/mise.toml"
# modules/work.toml
[homebrew]
brewfile = "brew/Brewfile.work"
requires_op = true # skip if 1Password isn't available
1Password guard¶
Adding requires_op = true to a module causes haven to skip that module's brew and mise steps if the op CLI is not installed or the user is not signed in — instead of failing hard:
# modules/secrets.toml
requires_op = true
[homebrew]
brewfile = "brew/Brewfile.secrets"
Profiles¶
Profiles control which modules are active on each machine. Declared in haven.toml:
[profile.default]
modules = ["shell", "git", "packages"]
[profile.work]
extends = "default" # inherits all modules from default
modules = ["work", "secrets"]
[profile.personal]
extends = "default"
modules = ["personal"]
[profile.minimal]
modules = ["shell"]
extends gives you single-level inheritance — the parent's modules are applied first, then the child's are appended (duplicates removed).
Applying a profile¶
haven apply --profile work
haven apply --profile minimal
haven status --profile work
The last-used profile is saved in ~/.haven/state.json and reused automatically unless overridden.
New machine tip¶
Commit haven.toml with all your profiles. On a new machine:
haven init gh:yourname/haven --apply --profile work
One command clones the repo and applies the right profile.
Custom template variables¶
Add machine-specific variables to haven.toml for use in .tmpl files:
[data]
work_email = "alice@corp.example"
kanata_path = "/usr/local/bin/kanata"
homebrew_path = "/opt/homebrew"
Access them in any .tmpl file as {{ data.<key> }}. Run haven data to see all variables in scope.