From e636bf16640beba5b9cb9ac1ae4e773966a80a78 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Wed, 17 Jun 2026 12:42:01 +0800 Subject: [PATCH 1/3] homebrew: pass onActivation.extraEnv to checks When onActivation.cleanup == "check", a separate `brew bundle cleanup` command is run in the system.checks phase before the actual activation. This `brew bundle cleanup` invocation should use the same `extraEnv` as the main `brew bundle [install]` command, for consistency. For example, when $XDG_CONFIG_HOME is set in `extraEnv`, it should also be picked up by `brew bundle cleanup` in the system.checks phase. --- modules/homebrew.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/homebrew.nix b/modules/homebrew.nix index 67d079c..586c147 100644 --- a/modules/homebrew.nix +++ b/modules/homebrew.nix @@ -151,7 +151,7 @@ let }; description = '' Extra environment variables to set when {command}`nix-darwin` invokes - {command}`brew bundle [install]` during system activation. + {command}`brew bundle [install]` during system checks and activation. Useful for setting Homebrew's `HOMEBREW_NO_*` variables (e.g., `HOMEBREW_NO_ENV_HINTS`, `HOMEBREW_NO_ANALYTICS`, `HOMEBREW_NO_UPDATE_REPORT_NEW`) @@ -1001,6 +1001,7 @@ in --user=${escapeShellArg cfg.user} \ --set-home \ env HOMEBREW_NO_AUTO_UPDATE=1 \ + ${concatStringsSep " " (mapAttrsToList (k: v: "${k}=${escapeShellArg v}") cfg.onActivation.extraEnv)} \ brew bundle cleanup --file='${brewfileFile}' 2>&1) || homebrewCleanupExitCode=$? if [ "$homebrewCleanupExitCode" -eq 1 ]; then printf >&2 '\e[1;31merror: found Homebrew packages not listed in the Brewfile, aborting activation\e[0m\n' From 5138eaf896e1b800f6eb3a7fba9af45f0ed85e42 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Wed, 17 Jun 2026 11:13:10 +0800 Subject: [PATCH 2/3] homebrew: mv PATH & sudo setup to brewBundleCmd This should be a trivial refactor, which enables us to reuse `brewBundleCmd` in the future for `brew bundle cleanup` as well. --- modules/homebrew.nix | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/homebrew.nix b/modules/homebrew.nix index 586c147..2ecea45 100644 --- a/modules/homebrew.nix +++ b/modules/homebrew.nix @@ -177,7 +177,15 @@ let config = { brewBundleCmd = concatStringsSep " " ( - optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1" + [ + ''PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH"'' + "sudo" + "--preserve-env=PATH" + "--user=${escapeShellArg cfg.user}" + "--set-home" + "env" + ] + ++ optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1" ++ mapAttrsToList (k: v: "${k}=${escapeShellArg v}") config.extraEnv ++ [ "brew bundle --file='${brewfileFile}'" ] ++ optional (!config.upgrade) "--no-upgrade" @@ -1024,13 +1032,7 @@ in # Homebrew Bundle echo >&2 "Homebrew bundle..." if [ -f "${cfg.prefix}/bin/brew" ]; then - PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH" \ - sudo \ - --preserve-env=PATH \ - --user=${escapeShellArg cfg.user} \ - --set-home \ - env \ - ${cfg.onActivation.brewBundleCmd} + ${cfg.onActivation.brewBundleCmd} else echo -e "\e[1;31merror: Homebrew is not installed, skipping...\e[0m" >&2 fi From d8a6661f78281431f182f222c7df96e2c9480fc5 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Wed, 17 Jun 2026 12:09:51 +0800 Subject: [PATCH 3/3] homebrew: use brewBundleCmd also for system.checks --- modules/homebrew.nix | 30 ++++++++++++++---------------- tests/homebrew-cleanup-check.nix | 14 +++++++++----- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/modules/homebrew.nix b/modules/homebrew.nix index 2ecea45..4288246 100644 --- a/modules/homebrew.nix +++ b/modules/homebrew.nix @@ -172,11 +172,11 @@ let ''; }; - brewBundleCmd = mkInternalOption { type = types.str; }; + brewBundleCmd = mkInternalOption { type = types.functionTo types.str; }; }; config = { - brewBundleCmd = concatStringsSep " " ( + brewBundleCmd = { onlyCheck }: concatStringsSep " " ( [ ''PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH"'' "sudo" @@ -185,13 +185,18 @@ let "--set-home" "env" ] - ++ optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1" + ++ optional (onlyCheck || !config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1" ++ mapAttrsToList (k: v: "${k}=${escapeShellArg v}") config.extraEnv ++ [ "brew bundle --file='${brewfileFile}'" ] - ++ optional (!config.upgrade) "--no-upgrade" - ++ optional (config.cleanup == "uninstall") "--force-cleanup" - ++ optional (config.cleanup == "zap") "--zap --force-cleanup" - ++ config.extraFlags + ++ ( + if onlyCheck then + [ "cleanup 2>&1" ] + else + optional (!config.upgrade) "--no-upgrade" + ++ optional (config.cleanup == "uninstall") "--force-cleanup" + ++ optional (config.cleanup == "zap") "--zap --force-cleanup" + ++ config.extraFlags + ) ); }; }; @@ -1003,14 +1008,7 @@ in system.checks.text = mkIf (cfg.enable && cfg.onActivation.cleanup == "check") '' if [ -f "${cfg.prefix}/bin/brew" ]; then homebrewCleanupExitCode=0 - homebrewCleanupResult=$(PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH" \ - sudo \ - --preserve-env=PATH \ - --user=${escapeShellArg cfg.user} \ - --set-home \ - env HOMEBREW_NO_AUTO_UPDATE=1 \ - ${concatStringsSep " " (mapAttrsToList (k: v: "${k}=${escapeShellArg v}") cfg.onActivation.extraEnv)} \ - brew bundle cleanup --file='${brewfileFile}' 2>&1) || homebrewCleanupExitCode=$? + homebrewCleanupResult=$(${cfg.onActivation.brewBundleCmd { onlyCheck = true; }}) || homebrewCleanupExitCode=$? if [ "$homebrewCleanupExitCode" -eq 1 ]; then printf >&2 '\e[1;31merror: found Homebrew packages not listed in the Brewfile, aborting activation\e[0m\n' printf >&2 '%s\n' "$homebrewCleanupResult" @@ -1032,7 +1030,7 @@ in # Homebrew Bundle echo >&2 "Homebrew bundle..." if [ -f "${cfg.prefix}/bin/brew" ]; then - ${cfg.onActivation.brewBundleCmd} + ${cfg.onActivation.brewBundleCmd { onlyCheck = false; }} else echo -e "\e[1;31merror: Homebrew is not installed, skipping...\e[0m" >&2 fi diff --git a/tests/homebrew-cleanup-check.nix b/tests/homebrew-cleanup-check.nix index 2a63481..6ccdff0 100644 --- a/tests/homebrew-cleanup-check.nix +++ b/tests/homebrew-cleanup-check.nix @@ -1,5 +1,9 @@ { config, ... }: +let + brewBundleInstallCmd = config.homebrew.onActivation.brewBundleCmd { onlyCheck = false; }; +in + { homebrew.enable = true; homebrew.user = "test-homebrew-user"; @@ -7,12 +11,12 @@ test = '' echo "checking that cleanup check is present in system checks" >&2 - grep 'brew bundle cleanup --file=' ${config.out}/activate + grep "brew bundle --file='.*-Brewfile' cleanup" ${config.out}/activate - echo "checking that brew bundle command does not have --cleanup flag" >&2 - if echo "${config.homebrew.onActivation.brewBundleCmd}" | grep -F -- '--cleanup' > /dev/null; then - echo "Expected no --cleanup flag in brewBundleCmd" - echo "Actual: ${config.homebrew.onActivation.brewBundleCmd}" + echo "checking that brew bundle [install] command does not have --cleanup flag" >&2 + if echo "${brewBundleInstallCmd}" | grep -F -- '--cleanup' > /dev/null; then + echo "Expected no --cleanup flag in brewBundleInstallCmd" + echo "Actual: ${brewBundleInstallCmd}" exit 1 fi '';