modules/homebrew: add link: :overwrite support

Homebrew supports `link: :overwrite` which runs `brew link --overwrite`,
force-overwriting existing symlinks. Extract the existing
`restart_service` special-case logic into a reusable helper
(`mkBrewfileLineBoolOrSymbolString`) for options that can be either a
bool or a Ruby symbol in the Brewfile.
This commit is contained in:
Malo Bourgon 2026-02-09 19:36:07 -08:00
parent 36815b4852
commit a3fd89f1bb
No known key found for this signature in database
2 changed files with 24 additions and 17 deletions

View file

@ -28,6 +28,15 @@ let
mkBrewfileLineOptionsListString = attrs:
concatStringsSep ", " (mapAttrsToList (n: v: "${n}: ${v}") attrs);
# Renders a Brewfile option that can be either a bool or a Ruby symbol (e.g. `:overwrite`).
mkBrewfileLineBoolOrSymbolString = name: config: sCfg:
optionalString (hasAttr name sCfg) (
", ${name}: " + (
if isBool config.${name} then sCfg.${name}
else ":${config.${name}}"
)
);
# Option and submodule helper functions ----------------------------------------------------------
@ -456,12 +465,15 @@ let
Homebrew's default is `false`.
'';
};
link = mkNullOrBoolOption {
link = mkOption {
type = with types; nullOr (either bool (enum [ "overwrite" ]));
default = null;
description = ''
Whether to link the formula to the Homebrew prefix. When this option is
`null`, Homebrew will use it's default behavior which is to link the
formula if it's currently unlinked and not keg-only, and to unlink the formula if it's
currently linked and keg-only.
Whether to link the formula to the Homebrew prefix. When set to `"overwrite"`,
existing symlinks will be overwritten ({command}`brew link --overwrite`). When this
option is `null`, Homebrew will use its default behavior which is to link the formula
if it's currently unlinked and not keg-only, and to unlink the formula if it's currently
linked and keg-only.
'';
};
@ -471,20 +483,14 @@ let
config =
let
sCfg = mkProcessedSubmodConfig config;
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" ];
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" "link" ];
in
{
brewfileLine =
"brew ${sCfg.name}"
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}"
# We need to handle the `restart_service` option seperately since it can be either a bool
# or `:changed` in the Brewfile.
+ optionalString (sCfg ? restart_service) (
", restart_service: " + (
if isBool config.restart_service then sCfg.restart_service
else ":${config.restart_service}"
)
);
+ mkBrewfileLineBoolOrSymbolString "link" config sCfg
+ mkBrewfileLineBoolOrSymbolString "restart_service" config sCfg;
};
};

View file

@ -17,7 +17,7 @@ in
homebrew.user = "test-homebrew-user";
# Examples taken from https://github.com/Homebrew/homebrew-bundle
# Examples adapted from https://docs.brew.sh/Brew-Bundle-and-Brewfile
homebrew.taps = [
"homebrew/cask"
{
@ -41,7 +41,8 @@ in
{
name = "denji/nginx/nginx-full";
args = [ "with-rmtp" ];
restart_service = "changed";
link = "overwrite";
restart_service = "always";
}
{
name = "mysql@5.6";
@ -89,7 +90,7 @@ in
echo "checking brew entries in Brewfile" >&2
${mkTest "imagemagick" ''brew "imagemagick"''}
${mkTest "denji/nginx/nginx-full" ''brew "denji/nginx/nginx-full", args: ["with-rmtp"], restart_service: :changed''}
${mkTest "denji/nginx/nginx-full" ''brew "denji/nginx/nginx-full", args: ["with-rmtp"], link: :overwrite, restart_service: :always''}
${mkTest "mysql@5.6" ''brew "mysql@5.6", conflicts_with: ["mysql"], link: true, restart_service: true''}
echo "checking cask entries in Brewfile" >&2