Homebrew Bundle removed lockfile support in Homebrew 4.4.0 (Oct 2024): the `--no-lock` CLI flag, the `HOMEBREW_BUNDLE_NO_LOCK` env var, and the `no_lock` parameter in `installer.rb` are all dead code. Setting `homebrew.global.lockfiles` has had no effect on current Homebrew versions. - Replace the `lockfiles` option with a hidden stub (matching `noLock`) - Replace the `noLock` hard assertion with a shared deprecation warning for both options - Stop setting `HOMEBREW_BUNDLE_NO_LOCK` in `environment.variables` - Remove the lockfiles paragraph from the `brewfile` option description
857 lines
31 KiB
Nix
857 lines
31 KiB
Nix
# Created by: https://github.com/malob
|
||
{ config, lib, options, pkgs, ... }:
|
||
|
||
with lib;
|
||
|
||
let
|
||
cfg = config.homebrew;
|
||
|
||
brewfileFile = pkgs.writeText "Brewfile" cfg.brewfile;
|
||
|
||
# Brewfile creation helper functions -------------------------------------------------------------
|
||
|
||
mkBrewfileSectionString = heading: entries: optionalString (entries != [ ]) ''
|
||
# ${heading}
|
||
${concatStringsSep "\n" (unique (map (v: v.brewfileLine or v) entries))}
|
||
|
||
'';
|
||
|
||
mkBrewfileLineValueString = v:
|
||
if isInt v then toString v
|
||
else if isFloat v then strings.floatToString v
|
||
else if isBool v then boolToString v
|
||
else if isString v then ''"${v}"''
|
||
else if isAttrs v then "{ ${concatStringsSep ", " (mapAttrsToList (n: v': "${n}: ${mkBrewfileLineValueString v'}") v)} }"
|
||
else if isList v then "[${concatMapStringsSep ", " mkBrewfileLineValueString v}]"
|
||
else abort "The value: ${generators.toPretty v} is not a valid Brewfile value.";
|
||
|
||
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 ----------------------------------------------------------
|
||
|
||
mkNullOrBoolOption = args: mkOption (args // {
|
||
type = types.nullOr types.bool;
|
||
default = null;
|
||
});
|
||
|
||
mkNullOrStrOption = args: mkOption (args // {
|
||
type = types.nullOr types.str;
|
||
default = null;
|
||
});
|
||
|
||
mkInternalOption = args: mkOption (args // {
|
||
visible = false;
|
||
internal = true;
|
||
readOnly = true;
|
||
});
|
||
|
||
mkProcessedSubmodConfig = attrs: mapAttrs (_: mkBrewfileLineValueString)
|
||
(filterAttrsRecursive (n: v: n != "_module" && n != "brewfileLine" && v != null) attrs);
|
||
|
||
|
||
# Submodules -------------------------------------------------------------------------------------
|
||
# Option values and descriptions of Brewfile entries are sourced/derived from:
|
||
# * `brew` manpage: https://docs.brew.sh/Manpage
|
||
# * `brew bundle` source files (at https://github.com/Homebrew/brew/tree/master/Library/Homebrew/bundle):
|
||
# * dsl.rb
|
||
# * {brew,cask,tap}_installer.rb
|
||
# * ../test/bundle/{brew,cask,tap}_installer_spec.rb
|
||
|
||
onActivationOptions = { config, ... }: {
|
||
options = {
|
||
cleanup = mkOption {
|
||
type = types.enum [ "none" "uninstall" "zap" ];
|
||
default = "none";
|
||
example = "uninstall";
|
||
description = ''
|
||
This option manages what happens to formulae installed by Homebrew, that aren't present in
|
||
the Brewfile generated by this module, during {command}`nix-darwin` system
|
||
activation.
|
||
|
||
When set to `"none"` (the default), formulae not present in the generated
|
||
Brewfile are left installed.
|
||
|
||
When set to `"uninstall"`, {command}`nix-darwin` invokes
|
||
{command}`brew bundle [install]` with the {command}`--cleanup` flag. This
|
||
uninstalls all formulae not listed in generated Brewfile, i.e.,
|
||
{command}`brew uninstall` is run for those formulae.
|
||
|
||
When set to `"zap"`, {command}`nix-darwin` invokes
|
||
{command}`brew bundle [install]` with the {command}`--cleanup --zap`
|
||
flags. This uninstalls all formulae not listed in the generated Brewfile, and if the
|
||
formula is a cask, removes all files associated with that cask. In other words,
|
||
{command}`brew uninstall --zap` is run for all those formulae.
|
||
|
||
If you plan on exclusively using {command}`nix-darwin` to manage formulae
|
||
installed by Homebrew, you probably want to set this option to
|
||
`"uninstall"` or `"zap"`.
|
||
'';
|
||
};
|
||
autoUpdate = mkOption {
|
||
type = types.bool;
|
||
default = false;
|
||
description = ''
|
||
Whether to enable Homebrew to auto-update itself and all formulae during
|
||
{command}`nix-darwin` system activation. The default is `false`
|
||
so that repeated invocations of {command}`darwin-rebuild switch` are idempotent.
|
||
|
||
Note that Homebrew auto-updates when it's been more then 5 minutes since it last updated.
|
||
|
||
Although auto-updating is disabled by default during system activation, note that Homebrew
|
||
will auto-update when you manually invoke certain Homebrew commands. To modify this
|
||
behavior see [](#opt-homebrew.global.autoUpdate).
|
||
|
||
Implementation note: when disabled, this option sets the `HOMEBREW_NO_AUTO_UPDATE`
|
||
environment variable when {command}`nix-darwin` invokes {command}`brew bundle [install]`
|
||
during system activation.
|
||
'';
|
||
};
|
||
upgrade = mkOption {
|
||
type = types.bool;
|
||
default = false;
|
||
description = ''
|
||
Whether to enable Homebrew to upgrade outdated formulae and Mac App Store apps during
|
||
{command}`nix-darwin` system activation. The default is `false`
|
||
so that repeated invocations of {command}`darwin-rebuild switch` are idempotent.
|
||
|
||
Implementation note: when disabled, {command}`nix-darwin` invokes
|
||
{command}`brew bundle [install]` with the {command}`--no-upgrade` flag during system
|
||
activation.
|
||
'';
|
||
};
|
||
extraFlags = mkOption {
|
||
type = types.listOf types.str;
|
||
default = [ ];
|
||
example = [ "--verbose" ];
|
||
description = ''
|
||
Extra flags to pass to {command}`brew bundle [install]` during {command}`nix-darwin`
|
||
system activation.
|
||
'';
|
||
};
|
||
|
||
brewBundleCmd = mkInternalOption { type = types.str; };
|
||
};
|
||
|
||
config = {
|
||
brewBundleCmd = concatStringsSep " " (
|
||
optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1"
|
||
++ [ "brew bundle --file='${brewfileFile}'" ]
|
||
++ optional (!config.upgrade) "--no-upgrade"
|
||
++ optional (config.cleanup == "uninstall") "--cleanup"
|
||
++ optional (config.cleanup == "zap") "--cleanup --zap"
|
||
++ config.extraFlags
|
||
);
|
||
};
|
||
};
|
||
|
||
globalOptions = { config, ... }: {
|
||
options = {
|
||
brewfile = mkOption {
|
||
type = types.bool;
|
||
default = false;
|
||
description = ''
|
||
Whether to enable Homebrew to automatically use the Brewfile that this module generates in
|
||
the Nix store, when you manually invoke {command}`brew bundle`.
|
||
|
||
Implementation note: when enabled, this option sets the
|
||
`HOMEBREW_BUNDLE_FILE` environment variable to the path of the Brewfile
|
||
that this module generates in the Nix store, by adding it to
|
||
[](#opt-environment.variables).
|
||
'';
|
||
};
|
||
autoUpdate = mkOption {
|
||
type = types.bool;
|
||
default = true;
|
||
description = ''
|
||
Whether to enable Homebrew to auto-update itself and all formulae when you manually invoke
|
||
commands like {command}`brew install`, {command}`brew upgrade`,
|
||
{command}`brew tap`, and {command}`brew bundle [install]`.
|
||
|
||
Note that Homebrew auto-updates when you manually invoke commands like the ones mentioned
|
||
above if it's been more then 5 minutes since it last updated.
|
||
|
||
You may want to consider disabling this option if you have
|
||
[](#opt-homebrew.onActivation.upgrade) enabled, and
|
||
[](#opt-homebrew.onActivation.autoUpdate) disabled, if you want to ensure that
|
||
your installed formulae will only be upgraded during {command}`nix-darwin` system
|
||
activation, after you've explicitly run {command}`brew update`.
|
||
|
||
Implementation note: when disabled, this option sets the
|
||
`HOMEBREW_NO_AUTO_UPDATE` environment variable, by adding it to
|
||
[](#opt-environment.variables).
|
||
'';
|
||
};
|
||
# `noLock` was the original option; `lockfiles` replaced it (with inverted semantics).
|
||
# Both are now dead: Homebrew Bundle removed lockfile support in Homebrew 4.4.0
|
||
# (Oct 2024), so the `HOMEBREW_BUNDLE_NO_LOCK` env var and `--no-lock` CLI flag are
|
||
# ignored. We keep both definitions with null defaults to detect explicit user
|
||
# configuration and emit a warning below. We can't use `mkRemovedOptionModule` because
|
||
# `homebrew.global` is a submodule.
|
||
lockfiles = mkOption { visible = false; default = null; };
|
||
noLock = mkOption { visible = false; default = null; };
|
||
|
||
homebrewEnvironmentVariables = mkInternalOption { type = types.attrs; };
|
||
};
|
||
|
||
config = {
|
||
homebrewEnvironmentVariables = {
|
||
HOMEBREW_BUNDLE_FILE = mkIf config.brewfile "${brewfileFile}";
|
||
HOMEBREW_NO_AUTO_UPDATE = mkIf (!config.autoUpdate) "1";
|
||
};
|
||
};
|
||
};
|
||
|
||
tapOptions = { config, ... }: {
|
||
options = {
|
||
name = mkOption {
|
||
type = types.str;
|
||
example = "apple/apple";
|
||
description = ''
|
||
When {option}`clone_target` is unspecified, this is the name of a formula
|
||
repository to tap from GitHub using HTTPS. For example, `"user/repo"`
|
||
will tap https://github.com/user/homebrew-repo.
|
||
'';
|
||
};
|
||
clone_target = mkNullOrStrOption {
|
||
description = ''
|
||
Use this option to tap a formula repository from anywhere, using any transport protocol
|
||
that {command}`git` handles. When {option}`clone_target` is specified, taps
|
||
can be cloned from places other than GitHub and using protocols other than HTTPS, e.g.,
|
||
SSH, git, HTTP, FTP(S), rsync.
|
||
'';
|
||
};
|
||
force_auto_update = mkNullOrBoolOption {
|
||
description = ''
|
||
Whether to auto-update the tap even if it is not hosted on GitHub. By default, only taps
|
||
hosted on GitHub are auto-updated (for performance reasons).
|
||
'';
|
||
};
|
||
|
||
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
|
||
};
|
||
|
||
config =
|
||
let
|
||
sCfg = mkProcessedSubmodConfig config;
|
||
in
|
||
{
|
||
brewfileLine =
|
||
"tap ${sCfg.name}"
|
||
+ optionalString (sCfg ? clone_target) ", ${sCfg.clone_target}"
|
||
+ optionalString (sCfg ? force_auto_update)
|
||
", force_auto_update: ${sCfg.force_auto_update}";
|
||
};
|
||
};
|
||
|
||
# Sourced from https://docs.brew.sh/Manpage#global-cask-options
|
||
# and valid values for `HOMEBREW_CASK_OPTS`.
|
||
caskArgsOptions = { config, ... }: {
|
||
options = {
|
||
appdir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Applications.
|
||
|
||
Homebrew's default is {file}`/Applications`.
|
||
'';
|
||
};
|
||
colorpickerdir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Color Pickers.
|
||
|
||
Homebrew's default is {file}`~/Library/ColorPickers`.
|
||
'';
|
||
};
|
||
prefpanedir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Preference Panes.
|
||
|
||
Homebrew's default is {file}`~/Library/PreferencePanes`.
|
||
'';
|
||
};
|
||
qlplugindir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for QuickLook Plugins.
|
||
|
||
Homebrew's default is {file}`~/Library/QuickLook`.
|
||
'';
|
||
};
|
||
mdimporterdir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Spotlight Plugins.
|
||
|
||
Homebrew's default is {file}`~/Library/Spotlight`.
|
||
'';
|
||
};
|
||
dictionarydir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Dictionaries.
|
||
|
||
Homebrew's default is {file}`~/Library/Dictionaries`.
|
||
'';
|
||
};
|
||
fontdir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Fonts.
|
||
|
||
Homebrew's default is {file}`~/Library/Fonts`.
|
||
'';
|
||
};
|
||
servicedir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Services.
|
||
|
||
Homebrew's default is {file}`~/Library/Services`.
|
||
'';
|
||
};
|
||
input_methoddir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Input Methods.
|
||
|
||
Homebrew's default is {file}`~/Library/Input Methods`.
|
||
'';
|
||
};
|
||
internet_plugindir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Internet Plugins.
|
||
|
||
Homebrew's default is {file}`~/Library/Internet Plug-Ins`.
|
||
'';
|
||
};
|
||
audio_unit_plugindir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Audio Unit Plugins.
|
||
|
||
Homebrew's default is
|
||
{file}`~/Library/Audio/Plug-Ins/Components`.
|
||
'';
|
||
};
|
||
vst_plugindir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for VST Plugins.
|
||
|
||
Homebrew's default is {file}`~/Library/Audio/Plug-Ins/VST`.
|
||
'';
|
||
};
|
||
vst3_plugindir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for VST3 Plugins.
|
||
|
||
Homebrew's default is {file}`~/Library/Audio/Plug-Ins/VST3`.
|
||
'';
|
||
};
|
||
screen_saverdir = mkNullOrStrOption {
|
||
description = ''
|
||
Target location for Screen Savers.
|
||
|
||
Homebrew's default is {file}`~/Library/Screen Savers`.
|
||
'';
|
||
};
|
||
language = mkNullOrStrOption {
|
||
description = ''
|
||
Comma-separated list of language codes to prefer for cask installation. The first matching
|
||
language is used, otherwise it reverts to the cask’s default language. The default value
|
||
is the language of your system.
|
||
'';
|
||
example = "zh-TW";
|
||
};
|
||
require_sha = mkNullOrBoolOption {
|
||
description = ''
|
||
Whether to require cask(s) to have a checksum.
|
||
|
||
Homebrew's default is `false`.
|
||
'';
|
||
};
|
||
no_quarantine = mkNullOrBoolOption {
|
||
description = "Whether to disable quarantining of downloads.";
|
||
};
|
||
no_binaries = mkNullOrBoolOption {
|
||
description = "Whether to disable linking of helper executables.";
|
||
};
|
||
ignore_dependencies = mkNullOrBoolOption {
|
||
description = "Ignore casks dependencies in case you manage them extrenally";
|
||
};
|
||
|
||
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
|
||
};
|
||
|
||
config =
|
||
let
|
||
sCfg = mkProcessedSubmodConfig config;
|
||
in
|
||
{
|
||
brewfileLine =
|
||
if sCfg == { } then null
|
||
else "cask_args ${mkBrewfileLineOptionsListString sCfg}";
|
||
};
|
||
};
|
||
|
||
brewOptions = { config, ... }: {
|
||
options = {
|
||
name = mkOption {
|
||
type = types.str;
|
||
description = "The name of the formula to install.";
|
||
};
|
||
args = mkOption {
|
||
type = with types; nullOr (listOf str);
|
||
default = null;
|
||
description = ''
|
||
Arguments flags to pass to {command}`brew install`. Values should not include the
|
||
leading `"--"`.
|
||
'';
|
||
};
|
||
conflicts_with = mkOption {
|
||
type = with types; nullOr (listOf str);
|
||
default = null;
|
||
description = ''
|
||
List of formulae that should be unlinked and their services stopped (if they are
|
||
installed).
|
||
'';
|
||
};
|
||
restart_service = mkOption {
|
||
type = with types; nullOr (either bool (enum [ "changed" "always" ]));
|
||
default = null;
|
||
description = ''
|
||
Whether to run {command}`brew services restart` for the formula and register it to
|
||
launch at login (or boot). If set to `"changed"`, the service will only
|
||
be restarted on version changes. If set to `"always"`, the service will
|
||
be restarted on every {command}`brew bundle` run, even if nothing changed.
|
||
|
||
Homebrew's default is `false`.
|
||
'';
|
||
};
|
||
start_service = mkNullOrBoolOption {
|
||
description = ''
|
||
Whether to run {command}`brew services start` for the formula and register it to
|
||
launch at login (or boot).
|
||
|
||
Homebrew's default is `false`.
|
||
'';
|
||
};
|
||
link = mkOption {
|
||
type = with types; nullOr (either bool (enum [ "overwrite" ]));
|
||
default = null;
|
||
description = ''
|
||
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.
|
||
'';
|
||
};
|
||
postinstall = mkNullOrStrOption {
|
||
description = ''
|
||
A shell command to run after the formula is installed or upgraded. The command is passed
|
||
to the system shell and only executes when the formula actually changed (was freshly
|
||
installed or upgraded), not on every {command}`brew bundle` run.
|
||
'';
|
||
};
|
||
# `version_file` is intentionally not exposed: it writes the installed version to a file
|
||
# path relative to the `brew bundle` working directory, which is not meaningful during
|
||
# nix-darwin system activation.
|
||
|
||
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
|
||
};
|
||
|
||
config =
|
||
let
|
||
sCfg = mkProcessedSubmodConfig config;
|
||
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" "link" ];
|
||
in
|
||
{
|
||
brewfileLine =
|
||
"brew ${sCfg.name}"
|
||
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}"
|
||
+ mkBrewfileLineBoolOrSymbolString "link" config sCfg
|
||
+ mkBrewfileLineBoolOrSymbolString "restart_service" config sCfg;
|
||
};
|
||
};
|
||
|
||
caskOptions = { config, ... }: {
|
||
options = {
|
||
name = mkOption {
|
||
type = types.str;
|
||
description = "The name of the cask to install.";
|
||
};
|
||
args = mkOption {
|
||
type = types.nullOr (types.submodule caskArgsOptions);
|
||
default = null;
|
||
visible = "shallow"; # so that options from `homebrew.caskArgs` aren't repeated.
|
||
description = ''
|
||
Arguments passed to {command}`brew install --cask` when installing this cask. See
|
||
[](#opt-homebrew.caskArgs) for the available options.
|
||
'';
|
||
};
|
||
greedy = mkOption {
|
||
type = types.nullOr types.bool;
|
||
default = cfg.greedyCasks;
|
||
description = ''
|
||
Whether to always upgrade this cask regardless of whether it's unversioned or it updates
|
||
itself.
|
||
'';
|
||
};
|
||
postinstall = mkNullOrStrOption {
|
||
description = ''
|
||
A shell command to run after the cask is installed or upgraded. The command is passed to
|
||
the system shell and only executes when the cask was actually installed or upgraded, not
|
||
on every {command}`brew bundle` run.
|
||
'';
|
||
};
|
||
|
||
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
|
||
};
|
||
|
||
config =
|
||
let
|
||
sCfg = mkProcessedSubmodConfig config;
|
||
sCfgSubset = removeAttrs sCfg [ "name" ];
|
||
in
|
||
{
|
||
brewfileLine =
|
||
"cask ${sCfg.name}"
|
||
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}";
|
||
};
|
||
};
|
||
in
|
||
|
||
{
|
||
# Interface --------------------------------------------------------------------------------------
|
||
|
||
imports = [
|
||
(mkRenamedOptionModule [ "homebrew" "autoUpdate" ] [ "homebrew" "onActivation" "autoUpdate" ])
|
||
(mkRenamedOptionModule [ "homebrew" "cleanup" ] [ "homebrew" "onActivation" "cleanup" ])
|
||
(mkRemovedOptionModule [ "homebrew" "whalebrews" ] "Whalebrew support was removed from Homebrew Bundle in Homebrew 4.7.0 (Nov 2025). `whalebrew` entries in a Brewfile now cause `brew bundle` to fail. Please manage Whalebrew images directly using the `whalebrew` CLI.")
|
||
];
|
||
|
||
options.homebrew = {
|
||
enable = mkEnableOption ''
|
||
{command}`nix-darwin` to manage installing/updating/upgrading Homebrew taps, formulae,
|
||
casks, Mac App Store apps, Go packages, and Cargo crates using Homebrew Bundle.
|
||
|
||
Note that enabling this option does not install Homebrew, see the Homebrew
|
||
[website](https://brew.sh) for installation instructions.
|
||
|
||
Use the [](#opt-homebrew.brews), [](#opt-homebrew.casks),
|
||
[](#opt-homebrew.masApps), [](#opt-homebrew.goPackages),
|
||
[](#opt-homebrew.cargoPackages), and [](#opt-homebrew.vscode) options to list the
|
||
Homebrew formulae, casks, Mac App Store apps, Go packages, Cargo crates, and
|
||
Visual Studio Code extensions you'd like to install. Use the
|
||
[](#opt-homebrew.taps) option, to make additional formula repositories available to
|
||
Homebrew. This module uses those options (along with the
|
||
[](#opt-homebrew.caskArgs) options) to generate a Brewfile that
|
||
{command}`nix-darwin` passes to the {command}`brew bundle` command during
|
||
system activation.
|
||
|
||
The default configuration of this module prevents Homebrew Bundle from auto-updating Homebrew
|
||
and all formulae, as well as upgrading anything that's already installed, so that repeated
|
||
invocations of {command}`darwin-rebuild switch` (without any change to the
|
||
configuration) are idempotent. You can modify this behavior using the options under
|
||
[](#opt-homebrew.onActivation).
|
||
|
||
This module also provides a few options for modifying how Homebrew commands behave when
|
||
you manually invoke them, under [](#opt-homebrew.global)'';
|
||
|
||
user = mkOption {
|
||
type = types.str;
|
||
default = config.system.primaryUser;
|
||
defaultText = literalExpression "config.system.primaryUser";
|
||
description = ''
|
||
The user that owns the Homebrew installation. In most cases
|
||
this should be the normal user account that you installed
|
||
Homebrew as.
|
||
'';
|
||
};
|
||
|
||
brewPrefix = mkOption {
|
||
type = types.str;
|
||
default = if pkgs.stdenv.hostPlatform.isAarch64 then "/opt/homebrew/bin" else "/usr/local/bin";
|
||
defaultText = literalExpression ''
|
||
if pkgs.stdenv.hostPlatform.isAarch64 then "/opt/homebrew/bin"
|
||
else "/usr/local/bin"
|
||
'';
|
||
description = ''
|
||
The path prefix where the {command}`brew` executable is located. This will be set to
|
||
the correct value based on your system's platform, and should only need to be changed if you
|
||
manually installed Homebrew in a non-standard location.
|
||
'';
|
||
};
|
||
|
||
onActivation = mkOption {
|
||
type = types.submodule onActivationOptions;
|
||
default = { };
|
||
description = ''
|
||
Options for configuring the behavior of the {command}`brew bundle` command that
|
||
{command}`nix-darwin` runs during system activation.
|
||
'';
|
||
};
|
||
|
||
global = mkOption {
|
||
type = types.submodule globalOptions;
|
||
default = { };
|
||
description = ''
|
||
Options for configuring the behavior of Homebrew commands when you manually invoke them.
|
||
'';
|
||
};
|
||
|
||
taps = mkOption {
|
||
type = with types; listOf (coercedTo str (name: { inherit name; }) (submodule tapOptions));
|
||
default = [ ];
|
||
example = literalExpression ''
|
||
# Adapted from https://docs.brew.sh/Brew-Bundle-and-Brewfile
|
||
[
|
||
# `brew tap`
|
||
"apple/apple"
|
||
|
||
# `brew tap` with custom Git URL and arguments
|
||
{
|
||
name = "user/tap-repo";
|
||
clone_target = "https://user@bitbucket.org/user/homebrew-tap-repo.git";
|
||
force_auto_update = true;
|
||
}
|
||
]
|
||
'';
|
||
description = ''
|
||
List of Homebrew formula repositories to tap.
|
||
|
||
Taps defined as strings, e.g., `"user/repo"`, are a shorthand for:
|
||
|
||
`{ name = "user/repo"; }`
|
||
'';
|
||
};
|
||
|
||
caskArgs = mkOption {
|
||
type = types.submodule caskArgsOptions;
|
||
default = { };
|
||
example = literalExpression ''
|
||
{
|
||
appdir = "~/Applications";
|
||
require_sha = true;
|
||
}
|
||
'';
|
||
description = ''
|
||
Arguments passed to {command}`brew install --cask` for all casks listed in
|
||
[](#opt-homebrew.casks).
|
||
'';
|
||
};
|
||
|
||
greedyCasks = mkNullOrBoolOption {
|
||
description = ''
|
||
Whether to always upgrade casks listed in [](#opt-homebrew.casks) regardless
|
||
of whether it's unversioned or it updates itself.
|
||
'';
|
||
};
|
||
|
||
brews = mkOption {
|
||
type = with types; listOf (coercedTo str (name: { inherit name; }) (submodule brewOptions));
|
||
default = [ ];
|
||
example = literalExpression ''
|
||
# Adapted from https://docs.brew.sh/Brew-Bundle-and-Brewfile
|
||
[
|
||
# `brew install`
|
||
"imagemagick"
|
||
|
||
# `brew install --with-rmtp`, `brew services restart` on version changes
|
||
{
|
||
name = "denji/nginx/nginx-full";
|
||
args = [ "with-rmtp" ];
|
||
restart_service = "changed";
|
||
}
|
||
|
||
# `brew install`, always `brew services restart`, `brew link`, `brew unlink mysql` (if it is installed)
|
||
{
|
||
name = "mysql@5.6";
|
||
restart_service = true;
|
||
link = true;
|
||
conflicts_with = [ "mysql" ];
|
||
}
|
||
|
||
# `brew install`, run a post-install command on version changes
|
||
{
|
||
name = "postgresql@16";
|
||
postinstall = "\''${HOMEBREW_PREFIX}/opt/postgresql@16/bin/postgres -D \''${HOMEBREW_PREFIX}/var/postgresql@16";
|
||
}
|
||
]
|
||
'';
|
||
description = ''
|
||
List of Homebrew formulae to install.
|
||
|
||
Formulae defined as strings, e.g., `"imagemagick"`, are a shorthand for:
|
||
|
||
`{ name = "imagemagick"; }`
|
||
'';
|
||
};
|
||
|
||
casks = mkOption {
|
||
type = with types; listOf (coercedTo str (name: { inherit name; }) (submodule caskOptions));
|
||
default = [ ];
|
||
example = literalExpression ''
|
||
# Adapted from https://docs.brew.sh/Brew-Bundle-and-Brewfile
|
||
[
|
||
# `brew install --cask`
|
||
"google-chrome"
|
||
|
||
# `brew install --cask --appdir=~/my-apps/Applications`
|
||
{
|
||
name = "firefox";
|
||
args = { appdir = "~/my-apps/Applications"; };
|
||
}
|
||
|
||
# always upgrade auto-updated or unversioned cask to latest version even if already installed
|
||
{
|
||
name = "opera";
|
||
greedy = true;
|
||
}
|
||
|
||
# `brew install --cask`, run a post-install command on install or upgrade
|
||
{
|
||
name = "google-cloud-sdk";
|
||
postinstall = "\''${HOMEBREW_PREFIX}/bin/gcloud components update";
|
||
}
|
||
]
|
||
'';
|
||
description = ''
|
||
List of Homebrew casks to install.
|
||
|
||
Casks defined as strings, e.g., `"google-chrome"`, are a shorthand for:
|
||
|
||
`{ name = "google-chrome"; }`
|
||
'';
|
||
};
|
||
|
||
masApps = mkOption {
|
||
type = types.attrsOf types.ints.positive;
|
||
default = { };
|
||
example = literalExpression ''
|
||
{
|
||
"1Password for Safari" = 1569813296;
|
||
Xcode = 497799835;
|
||
}
|
||
'';
|
||
description = ''
|
||
Applications to install from Mac App Store using {command}`mas`.
|
||
|
||
Note that you need to be signed into the Mac App Store for {command}`mas` to
|
||
successfully install and upgrade applications, and that unfortunately apps removed from this
|
||
option will not be uninstalled automatically even if
|
||
[](#opt-homebrew.onActivation.cleanup) is set to `"uninstall"`
|
||
or `"zap"` (this is currently a limitation of Homebrew Bundle).
|
||
|
||
For more information on {command}`mas` see:
|
||
[github.com/mas-cli/mas](https://github.com/mas-cli/mas).
|
||
'';
|
||
};
|
||
|
||
goPackages = mkOption {
|
||
type = with types; listOf str;
|
||
default = [ ];
|
||
example = [ "github.com/charmbracelet/crush" ];
|
||
description = ''
|
||
List of Go packages to install using {command}`go install`.
|
||
|
||
Homebrew will automatically install the {command}`go` formula if it is not already
|
||
installed.
|
||
'';
|
||
};
|
||
|
||
cargoPackages = mkOption {
|
||
type = with types; listOf str;
|
||
default = [ ];
|
||
example = [ "ripgrep" ];
|
||
description = ''
|
||
List of Rust packages to install using {command}`cargo install`.
|
||
|
||
Homebrew will automatically install the {command}`rust` formula if it is not already
|
||
installed.
|
||
'';
|
||
};
|
||
|
||
vscode = mkOption {
|
||
type = with types; listOf str;
|
||
default = [ ];
|
||
example = [ "golang.go" ];
|
||
description = ''
|
||
List of Visual Studio Code extensions to install using Homebrew Bundle.
|
||
|
||
A compatible editor (Visual Studio Code, VSCodium, Cursor, or VS Code Insiders)
|
||
must be available. If none is found, Homebrew will attempt to install
|
||
`visual-studio-code` automatically.
|
||
|
||
For more information on {command}`code` see:
|
||
[VSCode Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace).
|
||
'';
|
||
};
|
||
|
||
|
||
extraConfig = mkOption {
|
||
type = types.lines;
|
||
default = "";
|
||
example = ''
|
||
# 'brew cask install' only if '/usr/libexec/java_home --failfast' fails
|
||
cask "java" unless system "/usr/libexec/java_home --failfast"
|
||
'';
|
||
description = "Extra lines to be added verbatim to the bottom of the generated Brewfile.";
|
||
};
|
||
|
||
brewfile = mkInternalOption {
|
||
type = types.str;
|
||
description = "String reprensentation of the generated Brewfile useful for debugging.";
|
||
};
|
||
};
|
||
|
||
|
||
# Implementation ---------------------------------------------------------------------------------
|
||
|
||
config = {
|
||
|
||
warnings = [
|
||
(mkIf (options.homebrew.autoUpdate.isDefined || options.homebrew.cleanup.isDefined) "The `homebrew' module no longer upgrades outdated formulae and apps by default during `nix-darwin' system activation. To enable upgrading, set `homebrew.onActivation.upgrade = true'.")
|
||
(mkIf (cfg.global.noLock != null || cfg.global.lockfiles != null) "The options `homebrew.global.noLock' and `homebrew.global.lockfiles' have been deprecated. Homebrew Bundle removed lockfile support in Homebrew 4.4.0 (Oct 2024), so these options no longer have any effect. Please remove them from your configuration.")
|
||
];
|
||
|
||
system.requiresPrimaryUser = mkIf (cfg.enable && options.homebrew.user.highestPrio == (mkOptionDefault {}).priority) [
|
||
"homebrew.enable"
|
||
];
|
||
|
||
homebrew.brewfile =
|
||
"# Created by `nix-darwin`'s `homebrew` module\n\n"
|
||
+ mkBrewfileSectionString "Taps" cfg.taps
|
||
+ mkBrewfileSectionString "Arguments for all casks"
|
||
(optional (cfg.caskArgs.brewfileLine != null) cfg.caskArgs)
|
||
+ mkBrewfileSectionString "Brews" cfg.brews
|
||
+ mkBrewfileSectionString "Casks" cfg.casks
|
||
+ mkBrewfileSectionString "Mac App Store apps"
|
||
(mapAttrsToList (n: id: ''mas "${n}", id: ${toString id}'') cfg.masApps)
|
||
+ mkBrewfileSectionString "Go packages" (map (v: ''go "${v}"'') cfg.goPackages)
|
||
+ mkBrewfileSectionString "Cargo packages" (map (v: ''cargo "${v}"'') cfg.cargoPackages)
|
||
+ mkBrewfileSectionString "Visual Studio Code extensions" (map (v: ''vscode "${v}"'') cfg.vscode)
|
||
+ optionalString (cfg.extraConfig != "") ("# Extra config\n" + cfg.extraConfig);
|
||
|
||
environment.variables = mkIf cfg.enable cfg.global.homebrewEnvironmentVariables;
|
||
|
||
system.activationScripts.homebrew.text = mkIf cfg.enable ''
|
||
# Homebrew Bundle
|
||
echo >&2 "Homebrew bundle..."
|
||
if [ -f "${cfg.brewPrefix}/brew" ]; then
|
||
PATH="${cfg.brewPrefix}:${lib.makeBinPath [ pkgs.mas ]}:$PATH" \
|
||
sudo \
|
||
--preserve-env=PATH \
|
||
--user=${escapeShellArg cfg.user} \
|
||
--set-home \
|
||
env \
|
||
${cfg.onActivation.brewBundleCmd}
|
||
else
|
||
echo -e "\e[1;31merror: Homebrew is not installed, skipping...\e[0m" >&2
|
||
fi
|
||
'';
|
||
};
|
||
}
|