modules/homebrew: add shell integration options

Add `enableBashIntegration`, `enableFishIntegration`, and
`enableZshIntegration` options that evaluate `brew shellenv` to set up
Homebrew's environment and shell completions. This automates the
boilerplate that every nix-darwin Homebrew user currently writes manually.

All three shells use `interactiveShellInit`, consistent with direnv and
home-manager conventions. Fish additionally sets up completions paths in
the same hook.
This commit is contained in:
Malo Bourgon 2026-02-10 00:06:24 -08:00
parent 8c29e146dd
commit ca6f8609c3
No known key found for this signature in database
4 changed files with 72 additions and 0 deletions

View file

@ -40,6 +40,11 @@ let
# Option and submodule helper functions ----------------------------------------------------------
mkShellIntegrationOption = shell: mkEnableOption ''
Homebrew ${shell} shell integration, which sets up Homebrew's environment
and shell completions
'';
mkNullOrBoolOption = args: mkOption (args // {
type = types.nullOr types.bool;
default = null;
@ -587,6 +592,13 @@ in
'';
};
# These default to `false` (unlike direnv, which defaults to `true`) because existing users
# likely already have `brew shellenv` in their dotfiles, and enabling by default would cause
# duplicate evaluation.
enableBashIntegration = mkShellIntegrationOption "Bash";
enableFishIntegration = mkShellIntegrationOption "Fish";
enableZshIntegration = mkShellIntegrationOption "Zsh";
onActivation = mkOption {
type = types.submodule onActivationOptions;
default = { };
@ -840,6 +852,33 @@ in
environment.variables = mkIf cfg.enable cfg.global.homebrewEnvironmentVariables;
programs = mkIf cfg.enable {
bash.interactiveShellInit = mkIf cfg.enableBashIntegration ''
eval "$(${cfg.prefix}/bin/brew shellenv bash)"
if [[ -r "${cfg.prefix}/etc/profile.d/bash_completion.sh" ]]; then
source "${cfg.prefix}/etc/profile.d/bash_completion.sh"
else
for COMPLETION in "${cfg.prefix}/etc/bash_completion.d/"*; do
[[ -r "$COMPLETION" ]] && source "$COMPLETION"
done
fi
'';
zsh.interactiveShellInit = mkIf cfg.enableZshIntegration ''
eval "$(${cfg.prefix}/bin/brew shellenv zsh)"
'';
fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
eval (${cfg.prefix}/bin/brew shellenv fish)
if test -d "${cfg.prefix}/share/fish/completions"
set -p fish_complete_path "${cfg.prefix}/share/fish/completions"
end
if test -d "${cfg.prefix}/share/fish/vendor_completions.d"
set -p fish_complete_path "${cfg.prefix}/share/fish/vendor_completions.d"
end
'';
};
system.activationScripts.homebrew.text = mkIf cfg.enable ''
# Homebrew Bundle
echo >&2 "Homebrew bundle..."