# Created by: https://github.com/malob # Inspired by: https://github.com/lccambiaghi/nixpkgs/blob/main/modules/homebrew.nix { config, lib, pkgs, ... }: with lib; let cfg = config.programs.brew-bundle; brewfileSection = heading: type: entries: if entries != [] then "# ${heading}\n" + (concatMapStrings (name: "${type} \"${name}\"\n") entries) + "\n" else ""; masBrewfileSection = entries: if entries != {} then "# Mac App Store apps\n" + concatStringsSep "\n" (mapAttrsToList (name: id: ''mas "${name}", id: ${toString id}'') entries) + "\n" else ""; brewfile = pkgs.writeText "Brewfile" ( (brewfileSection "Taps" "tap" cfg.taps) + (brewfileSection "Brews" "brew" cfg.brews) + (brewfileSection "Casks" "cask" cfg.casks) + (masBrewfileSection cfg.masApps) + (brewfileSection "Docker contrainers" "whalebrew" cfg.whalebrews) + (if cfg.extraConfig != "" then "# Extra config\n" + cfg.extraConfig else "") ); brew-bundle-command = "HOMEBREW_NO_AUTO_UPDATE=" + (if cfg.noAutoUpdate then "1" else "0") + " brew bundle --file='${brewfile}' --no-lock" + (if cfg.cleanup == "uninstall" || cfg.cleanup == "zap" then " --cleanup" else "") + (if cfg.cleanup == "zap" then " --zap" else ""); in { options.programs.brew-bundle = { enable = mkEnableOption '' configuring your Brewfile, and installing/updating the formulas therein via the brew bundle command, using nix-darwin. Note that enabling this option does not install Homebrew. See the Homebrew website for installation instructions: https://brew.sh ''; noAutoUpdate = mkOption { type = types.bool; default = true; example = false; description = '' Sets the HOMEBREW_NO_AUTO_UPDATE environment variable when running the brew bundle command. The default is true so that repeated invocations of darwin-rebuild switch are idempotent. ''; }; cleanup = mkOption { type = types.enum [ "none" "uninstall" "zap" ]; default = "none"; example = "uninstall"; description = '' This option manages what happens to formulas installed by Homebrew, that aren't present in the Brewfile generated by this module. When set to "none" (the default), formulas not present in the generated Brewfile are left installed. When set to "uninstall", nix-darwin invokes brew bundle install with the --cleanup flag. This uninstalls all formulas not listed in generate Brewfile, i.e., brew uninstall is run for those formulas. When set to "zap", nix-darwin invokes brew bundle install with the --cleanup --zap flags. This uninstalls all forumalas not listed in the generated Brewfile, and if the formula is a cask, removes all files associated with the cask. In other words, brew uninstall --zap is run for all those formulas. If you plan on exclusively using nix-darwin to manage formulas installed by Homebrew, you probably want to set this option to "uninstall" or "zap". ''; }; userConfig.brewfile = mkOption { type = types.bool; default = true; description = '' When enabled, when you manually invoke brew bundle, it will automatically use the Brewfile in the Nix store that this module generates. Sets the HOMEBREW_BUNDLE_FILE enviroment variable to the path of the Brewfile in the Nix store that this module generates, by adding it to . ''; }; userConfig.noLock = mkOption { type = types.bool; default = true; description = '' When enabled, lock files aren't generated when you manually invoke brew bundle. Sets the HOMEBREW_BUNDLE_NO_LOCK enviroment variable, by adding it to . ''; }; taps = mkOption { type = with types; listOf str; default = []; example = [ "homebrew/cask-fonts" ]; description = "Homebrew formula repositories to tap"; }; brews = mkOption { type = with types; listOf str; default = []; example = [ "mas" ]; description = "Homebrew brews to install"; }; casks = mkOption { type = with types; listOf str; default = []; example = [ "hammerspoon" "virtualbox" ]; description = "Homebrew casks to install"; }; masApps = mkOption { type = with types; attrsOf int; default = {}; example = { "1Password" = 1107421413; Xcode = 497799835; }; description = '' Applications to install from Mac App Store using mas. When this option is used, "mas" is automatically added to . Note that you need to be signed into the Mac App Store for mas to successfully install and upgrade applications, and that unfortunately apps removed from this option will not be uninstalled automatically even if is set to "uninstall" or "zap" (this is currently a limitation of Homebrew Bundle). For more information on mas see: https://github.com/mas-cli/mas ''; }; whalebrews = mkOption { type = with types; listOf str; default = []; example = [ "whalebrew/wget" ]; description = '' Docker images to install using whalebrew. When this option is used, "whalebrew" is automatically added to . For more information on whalebrew see: https://github.com/whalebrew/whalebrew ''; }; extraConfig = mkOption { type = types.lines; default = ""; example = '' # 'brew tap' with custom Git URL tap "user/tap-repo", "https://user@bitbucket.org/user/homebrew-tap-repo.git" # set arguments for all 'brew cask install' commands cask_args appdir: "~/Applications", require_sha: true # 'brew install --with-rmtp', 'brew services restart' on version changes brew "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) brew "mysql@5.6", restart_service: true, link: true, conflicts_with: ["mysql"] # 'brew cask install --appdir=~/my-apps/Applications' cask "firefox", args: { appdir: "~/my-apps/Applications" } # '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 generated Brewfile."; }; }; config = { assertions = mkIf cfg.enable [ { assertion = builtins.pathExists /usr/local/bin/brew; message = '' Homebrew not installed. Please install Homebrew yourself before using the programs.brew-bundle module. See installation instructions at: https://brew.sh ''; } ]; programs.brew-bundle.brews = optional (cfg.masApps != {}) "mas" ++ optional (cfg.whalebrews != []) "whalebrew"; environment.variables = mkIf cfg.enable ( (if cfg.userConfig.brewfile then { HOMEBREW_BUNDLE_FILE = "${brewfile}"; } else {}) // (if cfg.userConfig.noLock then { HOMEBREW_BUNDLE_NO_LOCK = "1"; } else {}) ); system.activationScripts.brew-bundle.text = mkIf cfg.enable '' # Homebrew Bundle echo >&2 "Homebrew bundle..." PATH=/usr/local/bin:$PATH ${brew-bundle-command} ''; }; }