111 Commits
Author SHA1 Message Date
Domen KožarandGitHub 57a3171f94 programs/devenv: init module (#1834) 2026-07-19 22:34:20 +00:00
Anish Pallati c1403be257 programs/devenv: init module
Signed-off-by: Anish Pallati <i@anish.land>
2026-07-19 00:17:39 -04:00
SamandGitHub b4cccbd4bc darwin-rebuild: add flake options to zsh completions (#1669) 2026-07-18 08:19:57 +00:00
Angel J 1543ae0852 darwin-rebuild: add flake options to zsh completions
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
2026-07-17 22:20:27 -07:00
Michael HoangandGitHub a4cf1d1085 mas: add module for Mac App Store management (#1668) 2026-07-15 13:58:56 +00:00
SamandGitHub d5bd9cd77a bump nixpkgs to fix manual/website builds (#1822) 2026-07-07 03:45:56 +00:00
zowoq b2217f8051 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/8c3cede7ddc26bd659d2d383b5610efbd2c7a16e?narHash=sha256-rppURzHviaQN131F%2BnLiLdGfcb0uCd9gGP0E5%2Biw9MI%3D' (2026-06-08)
  → 'github:NixOS/nixpkgs/f205b5574fd0cb7da5b702a2da51507b7f4fdd1b?narHash=sha256-/NAkDSsve%2BGNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s%3D' (2026-07-05)
2026-07-07 13:03:24 +10:00
SamandGitHub 08920bcfd1 manualHTML: adopt to nixos/nixpkgs#537810 (#1818) 2026-07-07 01:06:54 +00:00
Yifei Sun 320cbf535b manualHTML: adopt to nixos/nixpkgs#537810 2026-07-04 12:51:44 +02:00
Angel J deadc7204c mas: add module for Mac App Store management
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
2026-06-25 18:43:32 -07:00
SamandGitHub a1fa429e94 homebrew: pass extraEnv to cleanup checks (#1804) 2026-06-18 05:49:52 +00:00
Bryan Lai d8a6661f78 homebrew: use brewBundleCmd also for system.checks 2026-06-18 11:00:29 +08:00
Bryan Lai 5138eaf896 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.
2026-06-18 09:35:18 +08:00
Bryan Lai e636bf1664 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.
2026-06-18 09:35:18 +08:00
SamandGitHub ee9c7b96c9 homebrew: address new CLI flag requirements (#1789) 2026-06-17 16:57:21 +00:00
Yifei Sun cd7cf09aa3 homebrew: set trusted flag in Brewfile directly
Suggested-by: Austin Horstman <khaneliman12@gmail.com>
Reviewed-by: Sam <30577766+Samasaur1@users.noreply.github.com>
2026-06-17 18:23:19 +02:00
SamandGitHub ee20edc445 users: realpath home dirs before comparison (#1803) 2026-06-17 16:21:08 +00:00
Yifei Sun bb9c29c193 homebrew: address bundle command new CLI flag requirements 2026-06-17 18:15:07 +02:00
Ihar Hrachyshka 08278afff6 users: realpath home dirs before comparison
When we create users with homedir under /var, they end up with
NFSHomeDirectory under /private/var because /var is a symlink. This
breaks consequent activations and requires that user overrides their
home dir attribute with /private/var/... to pass the activation
precheck.

`realpath` should normalize the discrepancy.
2026-06-14 20:54:47 -10:00
SamandGitHub aabb2037ed flake.lock: update nixpkgs (#1799) 2026-06-12 05:33:53 +00:00
SamandGitHub 0bddd7a5ed time: remove double condition (#1800) 2026-06-12 05:32:59 +00:00
SandroandGitHub 9f8122dd2b time: remove double condition 2026-06-11 20:52:34 +02:00
ben 818b88b75f flake.lock: update nixpkgs 2026-06-11 10:06:13 -07:00
SamandGitHub d0d0978f34 Fix typo (#1798) 2026-06-11 16:43:09 +00:00
SandroandGitHub ace757665e Fix typo 2026-06-11 17:58:55 +02:00
SamandGitHub d9ffa378a6 github-runner: remove node20 runtime from defaults (#1796) 2026-06-11 15:00:05 +00:00
Yuriy Taraday b317d77bbf github-runner: remove node20 runtime from defaults
It's marked as insecure starting with 26.05, and now github-runner package defaults to supporting only node24
See https://github.com/NixOS/nixpkgs/pull/524856
2026-06-11 14:02:39 +02:00
SamandGitHub f73cbf1f65 (feature) Add swapEscapeAndCapsLock and swapRightCommandAndRightOption (#1706) 2026-06-11 06:46:22 +00:00
SamandGitHub 958c16ba9a spacebar: add default package (#1786) 2026-06-11 05:59:41 +00:00
Ethan Carter Edwards 4bcdd687ca spacebar: add default package
Upstream should be the default.

Signed-off-by: Ethan Carter Edwards <ethan@ethancedwards.com>
2026-06-08 11:41:05 -07:00
Gaetan Lepage 3f0f0ece39 feat(keyboard): add swapRightCommandAndRightOption 2026-06-08 10:42:29 +02:00
Gaetan Lepage 57ff7cbd20 feat(keyboard): add swapCapsLockAndEscape 2026-06-08 10:42:29 +02:00
SamandGitHub 6a771120d6 version: bump to 26.11 (#1782) 2026-06-07 01:23:23 +00:00
Sam f8531f95fe tests/github-runners: limit to supported node runtimes 2026-05-26 08:50:22 -07:00
Sam 7c3d11b160 modules/terminfo: update list of packages with terminfo 2026-05-26 08:29:42 -07:00
Sam 8073b91aa3 scripts/release: init
This script has not yet been tested in its entirety, but the file
editing operations were used for #1781 and #1782
2026-05-26 08:29:18 -07:00
Sam ae3e2b18d3 flake.lock: update
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/af84f9d270d404c17699522fab95bbf928a2d92f' (2025-12-17)
  → 'github:NixOS/nixpkgs/d849bb215dcdf71bce3e686839ccdb4219e84b2f' (2026-05-24)
2026-05-25 13:16:30 -07:00
Sam 82325d90a1 version: bump to 26.11 2026-05-25 13:08:31 -07:00
SamandGitHub 56c666e108 nixpkgs.config: Add allowUnfreePackages option (#1742) 2026-05-17 16:55:09 +00:00
SamandGitHub 3fe4b4fb3c darwin-rebuild: support --log-format (#1675) 2026-05-17 15:23:44 +00:00
SamandGitHub 426b6e8f02 tmux: update key binding (#1681) 2026-05-17 15:23:31 +00:00
Bryan 905fe60936 add reverseSplitBindings option 2026-05-17 22:44:37 +08:00
BryanandBryan 4ec530e8b5 tmux: update key binding 2026-05-17 22:44:37 +08:00
Michael HoangandGitHub 1e107a7b92 Revert "tests/aerospace: adapt to new toml generator" (#1773) 2026-05-17 07:31:14 +00:00
SamandGitHub 09b3575c2d Revert "tests/aerospace: adapt to new toml generator" 2026-05-16 20:11:00 -07:00
SamandGitHub 8c62fba085 homebrew: add onActivation.extraEnv option (#1745) 2026-05-03 03:57:46 +00:00
Michael HoangandGitHub d0e2383e3f tests/aerospace: adapt to new toml generator (#1756) 2026-05-03 03:07:42 +00:00
Sam b1737791a9 tests/aerospace: adapt to new toml generator
See https://github.com/NixOS/nixpkgs/commit/e23ad3bd2739af8ac41d5338fb389029f551cde9
2026-04-27 21:34:25 -07:00
isabel 1e2d8fbb37 darwin-rebuild: support --log-format 2026-04-21 01:41:39 +01:00
mmenanno 3510d049d3 homebrew: replace envHints/analytics/updateReportNew with onActivation.extraEnv
Per review feedback on #1745: rather than adding one boolean option per
HOMEBREW_NO_* env var, expose a generic onActivation.extraEnv attrset that
is prepended to brewBundleCmd alongside HOMEBREW_NO_AUTO_UPDATE=1. This
covers the same use case and the long tail of other HOMEBREW_NO_* vars
without tying the module to Homebrew's naming choices.

The global-scope booleans are dropped with no replacement — users who want
these variables set globally can write them directly into
environment.variables, so a global.extraEnv would be pure sugar.
2026-04-14 12:31:00 -05:00
mmenanno 8f6396c0dc homebrew: add envHints, analytics, and updateReportNew options
Add options to control Homebrew environment variable hints, analytics
notices, and new formulae/casks reports during system activation and
manual brew commands.

These follow the existing autoUpdate pattern: boolean options that
default to true (preserving current behavior) and set the corresponding
HOMEBREW_NO_* environment variable when disabled.

onActivation options affect brew bundle during darwin-rebuild switch.
global options affect interactive brew commands via environment.variables.
2026-04-06 19:20:06 -04:00
Martin Häcker a0a51c56f0 nixpkgs.config: Add allowUnfreePackages option
Also make sure it merges correctly and verify with a test.

This was added in nixpkgs some time ago and is quite a nice addition as
it merges additively (in contrast to allowUnfreePredicate) thus allowing
to localize allowUnfreePackage specifications to all the locations in
your local config where unfree packages are added.
2026-04-01 21:03:30 +02:00
SamandGitHub 06648f4902 etc: support Lix 2.95.1 (#1731) 2026-04-01 09:53:30 +00:00
SamandGitHub ec2e2c72e7 prometheus-node-exporter: separate extra flags with a space (#1739) 2026-04-01 09:26:30 +00:00
SamandGitHub 5176e2f4b4 etc: support lix-installer 3.95.0 (#1730) 2026-04-01 06:12:18 +00:00
Ihar Hrachyshka df49e56fd6 prometheus-node-exporter: separate extra flags with a space 2026-03-31 22:07:04 -04:00
Martin Woo 2fec379474 Add Lix 2.95.1 nix.custom.conf. 2026-03-23 05:52:19 +01:00
Martin Woo c1d952849a etc: support Lix 2.95.1 2026-03-22 13:53:32 +01:00
Sam 2fa8900609 etc: support lix-installer 3.95.0 2026-03-21 11:23:28 -07:00
Michael HoangandGitHub da529ac9e4 services/openssh: manage host keys (#1701) 2026-03-08 20:03:47 +00:00
Michael HoangandGitHub 52d0615161 nix: add nix 2.33 to auto-optimise-store assertion (#1551) 2026-03-01 15:40:24 +00:00
Michael Hoang e6efc7c131 services/openssh: manage host keys 2026-02-27 15:42:59 +01:00
SamandGitHub 3bfa436c19 README: Update instructions for 25.11 (#1712) 2026-02-25 04:16:36 +00:00
Sean Gilligan 72a7e97c84 README: Update instructions for 25.11
Replace all references of `25.05` with `25.11`
2026-02-24 14:04:09 -08:00
SamandGitHub 6a7fdcd583 readme: Use bullet-list for documentation location (#1703) 2026-02-19 17:08:02 +00:00
Sean Gilligan ebe39ab3fa readme: Use bullet-list for documentation location
This will make it much easier to find the online documentation at
a quick glance, as well as making it easier to see the commands
for viewing locally.
2026-02-17 18:03:42 -08:00
SamandGitHub aff4c008ce etc: support experimental official Nix installer (#1702) 2026-02-17 23:45:16 +00:00
Michael Hoang 6d789c5a41 etc: support experimental official Nix installer 2026-02-17 23:53:32 +01:00
SamandGitHub 6c5a56295d modules/homebrew: comprehensive module refresh (#1692) 2026-02-12 19:01:55 +00:00
Malo Bourgon a43b4091db modules/homebrew: add CHANGELOG entry for module refresh 2026-02-12 10:24:39 -08:00
Malo Bourgon e0ffd55e7a modules/homebrew: fix typos and improve option descriptions 2026-02-12 10:24:39 -08:00
Malo Bourgon c68f5d1387 modules/homebrew: add onActivation.cleanup "check" mode
Closes #1032

Add `"check"` to the `onActivation.cleanup` enum. When set, nix-darwin runs
`brew bundle cleanup` during system checks to detect Homebrew packages that
are installed but not present in the generated Brewfile. If extra packages
are found, activation fails with a list of them and remediation steps.

Unlike `"uninstall"` and `"zap"`, the `"check"` mode never removes packages
-- it only reports. This runs during both `darwin-rebuild check` and
`darwin-rebuild switch`, matching the behavior of all other system checks.
2026-02-12 10:24:39 -08:00
Malo Bourgon ca6f8609c3 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.
2026-02-12 10:24:39 -08:00
Malo Bourgon 8c29e146dd modules/homebrew: replace brewPrefix with prefix
Closes #596

`homebrew.brewPrefix` defaulted to the bin directory (`/opt/homebrew/bin`),
not the actual Homebrew prefix (`/opt/homebrew`). This misled users into
writing `${config.homebrew.brewPrefix}/bin`, producing the broken path
`/opt/homebrew/bin/bin`.

Replace it with `homebrew.prefix`, which has correct semantics matching
`brew --prefix`. The old `brewPrefix` option is removed using
`mkRemovedOptionModule`, which catches both users who set the option and
users who read it in custom code. A warning also fires if the new `prefix`
value ends with `/bin`, catching users who copy the old value verbatim.
2026-02-10 09:26:54 -08:00
Malo Bourgon 24531016d8 modules/homebrew: deprecate homebrew.global.lockfiles
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
2026-02-10 09:26:54 -08:00
Malo Bourgon 65cfcebaa2 modules/homebrew: remove homebrew.whalebrews option
Whalebrew support was fully removed from Homebrew Bundle in
Homebrew 4.7.0 (Nov 2025). A `whalebrew` entry in a Brewfile now
raises `RuntimeError: Invalid Brewfile: undefined method 'whalebrew'`,
breaking the entire `brew bundle` invocation.

Use `mkRemovedOptionModule` so that existing configs get a clear
warning instead of an undefined-option error. Also removes the
auto-addition of `"whalebrew"` to `homebrew.brews` and the Brewfile
generation for Docker containers.
2026-02-10 09:26:54 -08:00
Malo Bourgon 3479b795aa modules/homebrew: add homebrew.cargoPackages option
Add support for `cargo "pkg"` entries in the generated Brewfile. Homebrew
Bundle supports installing Rust crates via `cargo install`; the `rust`
formula is automatically installed if not already present.
2026-02-10 09:24:45 -08:00
Malo Bourgon cbe4a600d4 modules/homebrew: add homebrew.goPackages option
Add support for `go "pkg"` entries in the generated Brewfile. Homebrew
Bundle supports installing Go packages via `go install`; the `go`
formula is automatically installed if not already present.
2026-02-10 09:24:17 -08:00
Malo Bourgon c65c24c87c modules/homebrew: add postinstall option for brews and casks
Both `brew bundle` formula and cask installers support a `postinstall`
option -- a shell command to run after the package is installed or
upgraded. The command only executes when the package actually changed,
not on every `brew bundle` run.

Examples from the Homebrew docs added to the `homebrew.brews` and
`homebrew.casks` option examples and tests.
2026-02-10 09:22:16 -08:00
Malo Bourgon a3fd89f1bb 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.
2026-02-10 09:22:15 -08:00
Malo Bourgon 36815b4852 modules/homebrew: add restart_service "always" support
Homebrew supports restart_service: :always which restarts the service
on every brew bundle run, even if the formula wasn't changed.
2026-02-10 09:22:15 -08:00
Michael HoangandGitHub 7c952d9a52 Add support for installing vscode extensions via brew (#1222) 2026-02-10 15:13:34 +00:00
Frank Chiarulli Jr. fdbfb1dc1b add support for installing vscode extensions via brew 2026-02-09 21:59:25 -05:00
SamandGitHub 0d7874ef7e modules/homebrew: remove duplicates from brewfile (#1689) 2026-02-04 05:49:06 +00:00
Josh GibbsandGitHub 53dd29f381 remove duplicates from brewfile 2026-02-03 21:22:21 -08:00
zowoq 131e68e07e nix: add nix 2.33 to auto-optimise-store assertion 2026-01-23 19:28:06 +10:00
SamandGitHub 0fc4e7ac67 networking: add dhcpClientId option to set DHCP Client ID (#1562) 2026-01-18 19:31:43 +00:00
Ihar Hrachyshka 05a5979906 networking: add dhcpClientId option to set DHCP Client ID 2026-01-18 14:10:56 -05:00
Michael HoangandGitHub 8b720b9662 programs/direnv: add enable[shell]Integration options (#1680) 2026-01-16 11:11:07 +00:00
Radek Pietruszewski 6d323f4ffd programs/direnv: add enable*Integration options
Ability to disable shell hook is needed to use alternative shell hooks - e.g. direnv-instant. These options mirror NixOS's exactly.
2026-01-16 10:14:43 +01:00
Michael HoangandGitHub 7b1d394e7d feat(screencapture): add save-selections (#1677) 2026-01-12 12:21:49 +00:00
vaaski 48124872b6 fix(screencapture): move test location 2026-01-10 23:37:15 +01:00
Michael HoangandGitHub 9f48ffaca1 Add flake-parts module (#1670) 2026-01-06 16:55:03 +00:00
Manuel Mendez 3ab1648f50 Add flake-parts module 2026-01-06 10:20:38 -05:00
vaaski e5e7b6e878 feat(screencapture): add save-selections 2026-01-06 15:41:11 +01:00
SamandGitHub c31afa6e76 fix(modules): fixed typos in various modules (#1672) 2025-12-29 17:10:40 +00:00
Nikita Lenyk 8cecf9c5c5 fix(modules): fixed typos in various modules 2025-12-29 18:31:13 +02:00
Michael HoangandGitHub f0c8e1f6fe feat(gnupg): add configurable package option and install it (#1667) 2025-12-26 21:26:36 +00:00
Angel J d70b24c2a8 test(gnupg): add tests for gnupg agent configuration
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
2025-12-26 07:44:43 -08:00
angel 0b53d57d3a feat(gnupg): add configurable package option and install it
Signed-off-by: Angel J <78835633+Iamanaws@users.noreply.github.com>
2025-12-26 07:42:37 -08:00
SamandGitHub c2b36207f2 readme: recommend only the Lix installer (#1659) 2025-12-23 21:20:13 +00:00
Sam eb05ef6d65 readme: recommend only the Lix installer 2025-12-23 15:57:16 -05:00
Michael HoangandGitHub adc6bed3ad modules/examples/lnl: drop qes (#1666) 2025-12-23 20:15:52 +00:00
Sam c496b15409 modules/examples/lnl: drop qes
See:
- https://github.com/NixOS/nixpkgs/issues/371202
- https://github.com/NixOS/nixpkgs/pull/473109
- https://github.com/astratagem/dotfield/issues/1
- https://github.com/asmvik/skhd/issues/44

Looks like it was merged into skhd pre-2020 and was only in nixpkgs in a
cached state. Based on that last link the cached version may not even
work on newer versions of macOS?

Regardless, this is causing tests to fail.
2025-12-23 14:09:41 -05:00
Michael HoangandGitHub 5fb45ece61 Update nixpkgs (#1658) 2025-12-18 06:13:12 +00:00
Hleb Shauchenka bfe00257dc Update nixpkgs 2025-12-17 15:48:28 +01:00
Michael HoangandGitHub 9b628e171b github-runner: fix authentication with actions/checkout@v6 (#1655) 2025-12-14 03:47:29 +00:00
Louis Opter a58dd30d2b github-runner: canonicalizes workDir to fix actions/checkout@v6
We ran into this issue wherein GitHub's `actions/checkout` would fail
because `git-config` would fail to include additional configuration.

The symptom was:

```
Error: fatal: could not read Username for 'https://github.com': terminal prompts disabled
```

And is caused by `git config --local includeIf.gitdir:/var/lib/github-runners/_work/…`.

Wherein `git` apparently resolves (canonicalizes) the current repository
path which then does not match the argument for `includeIf.gitdir` which
then means the configuration that `actions/checkout` is trying to apply
does not get pulled in, which then prevents git from authenticating with
GitHub and fails the build.

Ngl, gemini 3 found that out for me.

We could prefix `/private` everywhere, but changing the user's home
directory is going to [be tricky], not sure what do to about that.

[be tricky]: https://github.com/nix-darwin/nix-darwin/blob/7e22bf538aa3e0937effcb1cee73d5f1bcc26f79/modules/users/default.nix#L208
2025-12-12 20:52:37 +00:00
Louis Opter 8393ede275 github-runner: fix warnings from nixd" 2025-12-12 20:52:33 +00:00
SamandGitHub 7e22bf538a version: bump to 26.05 (#1649) 2025-12-06 23:50:51 +00:00
Sam 000ec99b5a version: bump to 26.05 2025-11-30 21:11:37 -07:00
60 changed files with 1465 additions and 208 deletions
+41 -1
View File
@@ -1,3 +1,43 @@
2026-05-16
- The reversed split-window key bindings previously generated by
`programs.tmux.enableSensible` are no longer emitted when
`system.stateVersion` is 7 or later, so tmux uses its conventional
behavior: `%` splits the current pane horizontally and `"` splits it
vertically.
Existing configurations with `system.stateVersion` 6 or earlier keep
the previous reversed bindings. You can explicitly choose either
behavior with `programs.tmux.reverseSplitBindings`.
2026-02-10
- Major changes to `homebrew` module
`homebrew.brewPrefix` was renamed to `homebrew.prefix`, and its semantics
changed: the old option pointed to the bin directory (e.g.,
`/opt/homebrew/bin`), while the new option points to the Homebrew prefix
(e.g., `/opt/homebrew`), matching `brew --prefix`.
`homebrew.whalebrews` was removed. Whalebrew support was removed from
Homebrew Bundle in Homebrew 4.7.0 (Nov 2025), so `whalebrew` entries in a
Brewfile now cause `brew bundle` to fail.
`homebrew.global.lockfiles` and `homebrew.global.noLock` no longer have any
effect. Homebrew Bundle removed lockfile support in Homebrew 4.4.0 (Oct 2024).
`homebrew.onActivation.cleanup` now supports a `"check"` mode, which checks
for unlisted packages and aborts activation if any are found, without
removing them.
Shell integration options were added: `homebrew.enableBashIntegration`,
`homebrew.enableFishIntegration`, and `homebrew.enableZshIntegration`.
New Brewfile entry types were added: `homebrew.goPackages`,
`homebrew.cargoPackages`, and `homebrew.vscode`.
New options were added for brews: `postinstall`, `link = "overwrite"`, and
`restart_service = "always"`. The `postinstall` option was also added for
casks.
2025-01-30
- Previously, some nix-darwin options applied to the user running
`darwin-rebuild`. As part of a longterm migration to make
@@ -116,7 +156,7 @@
`nix.daemonIONice` was renamed to `nix.daemonIOLowPriority`, and
`nix.daemonNiceLevel` was removed in favor a new option
`nix.nix.daemonProcessType`.
`nix.daemonProcessType`.
2021-01-16
- Added `homebrew` module, to manage formulas installed by Homebrew via `brew bundle`.
+14 -23
View File
@@ -13,20 +13,9 @@ nix-darwin is built up around [Nixpkgs](https://github.com/NixOS/nixpkgs), quite
The only prerequisite is a Nix implementation; both Nix and Lix are supported.
As the official Nix installer does not include an automated uninstaller, and manual uninstallation on macOS is a complex process, we recommend using one of the following installers instead:
* The [Nix installer from Determinate Systems](https://github.com/DeterminateSystems/nix-installer?tab=readme-ov-file#determinate-nix-installer) is only recommended for use with flake-based setups.
It can install one of two distributions of Nix:
* To install the **recommended** vanilla upstream [Nix](https://nixos.org), you will need to explicitly pass the `--prefer-upstream-nix` flag to the installer.
* When run without the `--prefer-upstream-nix` flag, it will install the [Determinate](https://docs.determinate.systems/) distribution.
As Determinate manages the Nix installation itself, you will need to set `nix.enable = false;` in your configuration to disable nix-darwins own Nix management.
Some nix-darwin functionality that relies on managing the Nix installation, like the `nix.*` options to adjust Nix settings or configure a Linux builder, will be unavailable.
* The [Lix installer](https://lix.systems/install/#on-any-other-linuxmacos-system) supports both flake-based and channel-based setups.
As the official Nix installer does not include an automated uninstaller, and manual uninstallation on macOS is a complex process, we recommend using the [Lix installer](https://lix.systems/install/#on-any-other-linuxmacos-system), which supports both flake-based and channel-based setups.
The installer you use doesn't affect which Nix interpreter your system will use later on. nix-darwin manages the Nix installation by default and will default to upstream Nix. If you wish to use Lix instead of Nix, set `nix.package = pkgs.lix` in your configuration.
## Getting started
@@ -50,8 +39,8 @@ cd /etc/nix-darwin
# To use Nixpkgs unstable:
nix flake init -t nix-darwin/master
# To use Nixpkgs 25.05:
nix flake init -t nix-darwin/nix-darwin-25.05
# To use Nixpkgs 26.05:
nix flake init -t nix-darwin/nix-darwin-26.05
sed -i '' "s/simple/$(scutil --get LocalHostName)/" flake.nix
```
@@ -71,9 +60,9 @@ Add the following to `flake.nix` in the same folder as `configuration.nix`:
description = "John's darwin system";
inputs = {
# Use `github:NixOS/nixpkgs/nixpkgs-25.05-darwin` to use Nixpkgs 25.05.
# Use `github:NixOS/nixpkgs/nixpkgs-26.05-darwin` to use Nixpkgs 26.05.
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# Use `github:nix-darwin/nix-darwin/nix-darwin-25.05` to use Nixpkgs 25.05.
# Use `github:nix-darwin/nix-darwin/nix-darwin-26.05` to use Nixpkgs 26.05.
nix-darwin.url = "github:nix-darwin/nix-darwin/master";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
};
@@ -99,8 +88,8 @@ Unlike NixOS, `nix-darwin` does not have an installer, you can just run `darwin-
```bash
# To use Nixpkgs unstable:
sudo nix run nix-darwin/master#darwin-rebuild -- switch
# To use Nixpkgs 25.05:
sudo nix run nix-darwin/nix-darwin-25.05#darwin-rebuild -- switch
# To use Nixpkgs 26.05:
sudo nix run nix-darwin/nix-darwin-26.05#darwin-rebuild -- switch
```
### Step 3. Using `nix-darwin`
@@ -143,8 +132,8 @@ Copy the [simple](./modules/examples/simple.nix) example to `/etc/nix-darwin/con
```bash
# If you use Nixpkgs unstable (the default):
sudo nix-channel --add https://github.com/nix-darwin/nix-darwin/archive/master.tar.gz darwin
# If you use Nixpkgs 25.05:
sudo nix-channel --add https://github.com/nix-darwin/nix-darwin/archive/nix-darwin-25.05.tar.gz darwin
# If you use Nixpkgs 26.05:
sudo nix-channel --add https://github.com/nix-darwin/nix-darwin/archive/nix-darwin-26.05.tar.gz darwin
sudo nix-channel --update
```
@@ -177,9 +166,11 @@ sudo nix-channel --update
## Documentation
`darwin-help` will open up a local copy of the reference documentation, it can also be found online [here](https://nix-darwin.github.io/nix-darwin/manual/index.html).
The reference documentation is available:
The documentation is also available as manpages by running `man 5 configuration.nix`.
* Online: [nix-darwin reference](https://nix-darwin.github.io/nix-darwin/manual/index.html)
* Locally in your browser via the `darwin-help` command
* As a manual page via `man 5 configuration.nix`
## Uninstalling
@@ -0,0 +1,7 @@
# Set up Nix only on SSH connections
# See: https://github.com/DeterminateSystems/nix-installer/pull/714
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ] && [ -n "${SSH_CONNECTION:-}" ] && [ "${SHLVL:-0}" -eq 1 ]; then
. '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi
# End Nix
@@ -0,0 +1,3 @@
# Written by https://github.com/NixOS/nix-installer
# The contents below are based on options specified at installation time.
@@ -0,0 +1,10 @@
# Generated by https://github.com/NixOS/nix-installer
# See `/nix/nix-installer --version` for the version details.
extra-experimental-features = nix-command flakes
always-allow-substitutes = true
bash-prompt-prefix = (nix:$name)\040
max-jobs = auto
extra-nix-path = nixpkgs=flake:nixpkgs
!include nix.custom.conf
@@ -0,0 +1,11 @@
# Generated by https://install.lix.systems/.
# See `/nix/lix-installer --version` for the version details.
extra-experimental-features = nix-command
always-allow-substitutes = true
extra-trusted-substituters = https://cache.lix.systems
extra-trusted-public-keys = cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
bash-prompt-prefix = (nix:$name)\040
max-jobs = auto
!include nix.custom.conf
@@ -0,0 +1,3 @@
# Written by https://install.lix.systems/.
# The contents below are based on options specified at installation time.
@@ -0,0 +1,12 @@
# Generated by https://install.lix.systems/.
# See `/nix/lix-installer --version` for the version details.
extra-experimental-features = nix-command flakes
always-allow-substitutes = true
extra-trusted-substituters = https://cache.lix.systems
extra-trusted-public-keys = cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
bash-prompt-prefix = (nix:$name)\040
max-jobs = auto
extra-nix-path = nixpkgs=flake:nixpkgs
!include nix.custom.conf
+1 -2
View File
@@ -94,8 +94,7 @@ in rec {
--stylesheet highlightjs/mono-blue.css \
--script ./highlightjs/highlight.pack.js \
--script ./highlightjs/loader.js \
--toc-depth 1 \
--chunk-toc-depth 1 \
--sidebar-depth 1 \
./manual.md \
$dst/index.html
+11
View File
@@ -0,0 +1,11 @@
{
lib,
...
}:
{
options.flake.darwinConfigurations = lib.mkOption {
type = lib.types.lazyAttrsOf lib.types.raw;
default = { };
description = "Darwin system configurations";
};
}
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1747728033,
"narHash": "sha256-NnXFQu7g4LnvPIPfJmBuZF7LFy/fey2g2+LCzjQhTUk=",
"lastModified": 1783279667,
"narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2f9173bde1d3fbf1ad26ff6d52f952f9e9da52ea",
"rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b",
"type": "github"
},
"original": {
+2
View File
@@ -56,6 +56,8 @@
darwin-uninstaller = prev.callPackage ./pkgs/darwin-uninstaller { };
};
flakeModules.default = ./flake-module.nix;
darwinModules.hydra = ./modules/examples/hydra.nix;
darwinModules.lnl = ./modules/examples/lnl.nix;
darwinModules.simple = ./modules/examples/simple.nix;
+3 -2
View File
@@ -42,16 +42,17 @@
with pkgs.pkgsBuildBuild;
[
alacritty
alacritty-graphics
ghostty-bin
kitty
mtm
rio
rxvt-unicode-unwrapped
rxvt-unicode-unwrapped-emoji
st
termite
tmux
wezterm
] ++ lib.optional (pkgs ? ghostty-bin) ghostty-bin
]
)
);
+1 -1
View File
@@ -88,7 +88,7 @@ in
description = ''
Shell script code called during global environment initialisation
after all variables and profileVariables have been set.
This code is asumed to be shell-independent, which means you should
This code is assumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
};
-2
View File
@@ -50,8 +50,6 @@
pkgs.jq
pkgs.ripgrep
pkgs.shellcheck
pkgs.qes
];
services.yabai.enable = true;
+334 -115
View File
@@ -12,7 +12,7 @@ let
mkBrewfileSectionString = heading: entries: optionalString (entries != [ ]) ''
# ${heading}
${concatMapStringsSep "\n" (v: v.brewfileLine or v) entries}
${concatStringsSep "\n" (unique (map (v: v.brewfileLine or v) entries))}
'';
@@ -28,9 +28,23 @@ 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 ----------------------------------------------------------
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;
@@ -62,29 +76,36 @@ let
onActivationOptions = { config, ... }: {
options = {
cleanup = mkOption {
type = types.enum [ "none" "uninstall" "zap" ];
type = types.enum [ "none" "check" "uninstall" "zap" ];
default = "none";
example = "uninstall";
description = ''
This option manages what happens to formulae installed by Homebrew, that aren't present in
This option manages what happens to packages 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
When set to `"none"` (the default), packages not present in the generated
Brewfile are left installed.
When set to `"check"`, {command}`nix-darwin` verifies during system activation that no
Homebrew packages (taps, formulae, casks, etc.) are installed that aren't present in the
generated Brewfile. If extra packages are found, activation fails with a list of them.
Note that when this check fails during {command}`darwin-rebuild switch`, the entire
system activation is aborted and no other configuration changes will be applied until
the issue is resolved.
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.
uninstalls all packages not listed in the generated Brewfile, i.e.,
{command}`brew uninstall` is run for those packages.
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.
flags. This uninstalls all packages not listed in the generated Brewfile, and if the
package is a cask, removes all files associated with that cask. In other words,
{command}`brew uninstall --zap` is run for all those packages.
If you plan on exclusively using {command}`nix-darwin` to manage formulae
If you plan on exclusively using {command}`nix-darwin` to manage packages
installed by Homebrew, you probably want to set this option to
`"uninstall"` or `"zap"`.
'';
@@ -97,7 +118,7 @@ let
{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.
Note that Homebrew auto-updates when it's been more than 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
@@ -121,6 +142,26 @@ let
activation.
'';
};
extraEnv = mkOption {
type = types.attrsOf types.str;
default = { };
example = {
HOMEBREW_NO_ENV_HINTS = "1";
HOMEBREW_NO_ANALYTICS = "1";
};
description = ''
Extra environment variables to set when {command}`nix-darwin` invokes
{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`)
that aren't inherited from the user's shell environment because activation runs
under sudo.
Each entry is prepended to the {command}`brew bundle` invocation in the form
`KEY=VALUE`, alongside `HOMEBREW_NO_AUTO_UPDATE=1` when applicable.
'';
};
extraFlags = mkOption {
type = types.listOf types.str;
default = [ ];
@@ -131,17 +172,31 @@ let
'';
};
brewBundleCmd = mkInternalOption { type = types.str; };
brewBundleCmd = mkInternalOption { type = types.functionTo types.str; };
};
config = {
brewBundleCmd = concatStringsSep " " (
optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1"
brewBundleCmd = { onlyCheck }: concatStringsSep " " (
[
''PATH="${cfg.prefix}/bin:${lib.makeBinPath [ pkgs.mas ]}:$PATH"''
"sudo"
"--preserve-env=PATH"
"--user=${escapeShellArg cfg.user}"
"--set-home"
"env"
]
++ 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") "--cleanup"
++ optional (config.cleanup == "zap") "--cleanup --zap"
++ 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
)
);
};
};
@@ -155,14 +210,6 @@ let
Whether to enable Homebrew to automatically use the Brewfile that this module generates in
the Nix store, when you manually invoke {command}`brew bundle`.
Enabling this option will change the default value of
[](#opt-homebrew.global.lockfiles) to `false` since, with
this option enabled, {command}`brew bundle [install]` will default to using the
Brewfile that this module generates in the Nix store, unless you explicitly point it at
another Brewfile using the `--file` flag. As a result, it will try to
write the lockfile in the Nix store, and complain that it can't (though the command will
run successfully regardless).
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
@@ -178,7 +225,7 @@ let
{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.
above if it's been more than 5 minutes since it last updated.
You may want to consider disabling this option if you have
[](#opt-homebrew.onActivation.upgrade) enabled, and
@@ -191,31 +238,13 @@ let
[](#opt-environment.variables).
'';
};
lockfiles = mkOption {
type = types.bool;
default = !config.brewfile;
defaultText = literalExpression "!config.homebrew.global.brewfile";
description = ''
Whether to enable Homebrew to generate lockfiles when you manually invoke
{command}`brew bundle [install]`.
This option will default to `false` if
[](#opt-homebrew.global.brewfile) is enabled since, with that option enabled,
{command}`brew bundle [install]` will default to using the Brewfile that this
module generates in the Nix store, unless you explicitly point it at another Brewfile
using the `--file` flag. As a result, it will try to write the
lockfile in the Nix store, and complain that it can't (though the command will run
successfully regardless).
Implementation note: when disabled, this option sets the
`HOMEBREW_BUNDLE_NO_LOCK` environment variable, by adding it to
[](#opt-environment.variables).
'';
};
# The `noLock` option was replaced by `lockfiles`. Due to `homebrew.global` being a submodule,
# we can't use `mkRemovedOptionModule`, so we leave this option definition here, and trigger
# and error message with an assertion below if it's set by the user.
# `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; };
@@ -225,7 +254,6 @@ let
homebrewEnvironmentVariables = {
HOMEBREW_BUNDLE_FILE = mkIf config.brewfile "${brewfileFile}";
HOMEBREW_NO_AUTO_UPDATE = mkIf (!config.autoUpdate) "1";
HOMEBREW_BUNDLE_NO_LOCK = mkIf (!config.lockfiles) "1";
};
};
};
@@ -253,6 +281,25 @@ let
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).
Note: Homebrew Bundle accepts this option in Brewfile syntax but may silently ignore it
during installation. See [the Homebrew Bundle source](https://github.com/Homebrew/brew/tree/master/Library/Homebrew/bundle)
for current behavior.
'';
};
trusted = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether to trust this tap during {command}`nix-darwin` system activation, by adding the
`trusted: true` option to its {command}`brew bundle` Brewfile entry.
Homebrew 6.0.0 enabled `HOMEBREW_REQUIRE_TAP_TRUST` by default, which refuses to
load formulae/casks from non-official taps that haven't been trusted, aborting activation.
Set this to `true` for non-official taps you control so their formulae and casks are
installed during activation. Official taps are always trusted, so this has no effect only
them.
'';
};
@@ -268,7 +315,8 @@ let
"tap ${sCfg.name}"
+ optionalString (sCfg ? clone_target) ", ${sCfg.clone_target}"
+ optionalString (sCfg ? force_auto_update)
", force_auto_update: ${sCfg.force_auto_update}";
", force_auto_update: ${sCfg.force_auto_update}"
+ optionalString config.trusted ", trusted: true";
};
};
@@ -385,19 +433,34 @@ let
};
require_sha = mkNullOrBoolOption {
description = ''
Whether to require cask(s) to have a checksum.
Whether to require casks to have a checksum.
Homebrew's default is `false`.
'';
};
no_quarantine = mkNullOrBoolOption {
description = "Whether to disable quarantining of downloads.";
description = ''
Whether to disable quarantining of downloads.
Note: this option is deprecated in Homebrew and may be removed in a
future release. See [Homebrew/brew#20755](https://github.com/Homebrew/brew/issues/20755).
Homebrew's default is `false`.
'';
};
no_binaries = mkNullOrBoolOption {
description = "Whether to disable linking of helper executables.";
description = ''
Whether to disable linking of helper executables.
Homebrew's default is `false`.
'';
};
ignore_dependencies = mkNullOrBoolOption {
description = "Ignore casks dependencies in case you manage them extrenally";
description = ''
Whether to ignore cask dependencies, e.g., when you manage them externally.
Homebrew's default is `false`.
'';
};
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
@@ -424,7 +487,7 @@ let
type = with types; nullOr (listOf str);
default = null;
description = ''
Arguments flags to pass to {command}`brew install`. Values should not include the
Argument flags to pass to {command}`brew install`. Values should not include the
leading `"--"`.
'';
};
@@ -437,12 +500,14 @@ let
'';
};
restart_service = mkOption {
type = with types; nullOr (either bool (enum [ "changed" ]));
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.
be restarted when the formula is newly installed or upgraded. If set to
`"always"`, the service will be restarted on every {command}`brew bundle`
run, even if nothing changed.
Homebrew's default is `false`.
'';
@@ -450,17 +515,53 @@ let
start_service = mkNullOrBoolOption {
description = ''
Whether to run {command}`brew services start` for the formula and register it to
launch at login (or boot).
launch at login (or boot). Unlike {option}`restart_service`, this only starts
the service if it is not currently running, without restarting an already-running
service.
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.
'';
};
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.
trusted = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to trust this formula during {command}`nix-darwin` system activation, by adding the
`trusted: true` option to its {command}`brew bundle` Brewfile entry.
Homebrew 6.0.0 enabled `HOMEBREW_REQUIRE_TAP_TRUST` by default, which refuses to
load formulae from non-official taps that haven't been trusted, aborting activation. Set
this to `true` for a formula from a non-official tap you control so it is installed during
activation.
This only takes effect when {option}`name` is a fully-qualified name (`user/repo/formula`),
since only fully-qualified names map to a tap and can be trusted (and installed) on their
own. When {option}`name` is a plain formula name, it is resolved through your tapped
repositories, so trust must instead come from the containing tap being marked as trusted
(see [](#opt-homebrew.taps)). Official taps are always trusted, so this has no effect on
them.
'';
};
@@ -470,20 +571,15 @@ let
config =
let
sCfg = mkProcessedSubmodConfig config;
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" ];
sCfgSubset = removeAttrs sCfg [ "name" "restart_service" "link" "trusted" ];
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
+ optionalString config.trusted ", trusted: true";
};
};
@@ -510,6 +606,34 @@ let
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.
'';
};
trusted = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to trust this cask during {command}`nix-darwin` system activation, by adding the
`trusted: true` option to its {command}`brew bundle` Brewfile entry.
Homebrew 6.0.0 enabled `HOMEBREW_REQUIRE_TAP_TRUST` by default, which refuses to
load casks from non-official taps that haven't been trusted, silently skipping them. Set
this to `true` for a cask from a non-official tap you control so it is installed during
activation.
This only takes effect when {option}`name` is a fully-qualified name (`user/repo/cask`),
since only fully-qualified names map to a tap and can be trusted (and installed) on their
own. When {option}`name` is a plain cask name, it is resolved through your tapped
repositories, so trust must instead come from the containing tap being marked as trusted
(see [](#opt-homebrew.taps)). Official taps are always trusted, so this has no effect on
them.
'';
};
brewfileLine = mkInternalOption { type = types.nullOr types.str; };
};
@@ -517,12 +641,13 @@ let
config =
let
sCfg = mkProcessedSubmodConfig config;
sCfgSubset = removeAttrs sCfg [ "name" ];
sCfgSubset = removeAttrs sCfg [ "name" "trusted" ];
in
{
brewfileLine =
"cask ${sCfg.name}"
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}";
+ optionalString (sCfgSubset != { }) ", ${mkBrewfileLineOptionsListString sCfgSubset}"
+ optionalString config.trusted ", trusted: true";
};
};
in
@@ -533,21 +658,26 @@ in
imports = [
(mkRenamedOptionModule [ "homebrew" "autoUpdate" ] [ "homebrew" "onActivation" "autoUpdate" ])
(mkRenamedOptionModule [ "homebrew" "cleanup" ] [ "homebrew" "onActivation" "cleanup" ])
(mkRemovedOptionModule [ "homebrew" "brewPrefix" ] "`homebrew.brewPrefix` has been renamed to `homebrew.prefix` and its semantics changed: the old option pointed to the bin directory (e.g., `/opt/homebrew/bin`), while the new option points to the Homebrew prefix (e.g., `/opt/homebrew`), matching `brew --prefix`. Please replace `homebrew.brewPrefix` with `homebrew.prefix`, removing the trailing `/bin` if present.")
(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,
and casks, as well as Mac App Store apps and Docker containers, using Homebrew Bundle.
casks, Mac App Store apps, Visual Studio Code extensions, 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), and [](#opt-homebrew.whalebrews) options
to list the Homebrew formulae, casks, Mac App Store apps, and Docker containers 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.masApps), [](#opt-homebrew.vscode),
[](#opt-homebrew.goPackages), and [](#opt-homebrew.cargoPackages) options to list
the Homebrew formulae, casks, Mac App Store apps, Visual Studio Code extensions,
Go packages, and Cargo crates 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.
@@ -572,20 +702,27 @@ in
'';
};
brewPrefix = mkOption {
prefix = mkOption {
type = types.str;
default = if pkgs.stdenv.hostPlatform.isAarch64 then "/opt/homebrew/bin" else "/usr/local/bin";
default = if pkgs.stdenv.hostPlatform.isAarch64 then "/opt/homebrew" else "/usr/local";
defaultText = literalExpression ''
if pkgs.stdenv.hostPlatform.isAarch64 then "/opt/homebrew/bin"
else "/usr/local/bin"
if pkgs.stdenv.hostPlatform.isAarch64 then "/opt/homebrew"
else "/usr/local"
'';
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.
The Homebrew prefix directory, i.e., the value that {command}`brew --prefix` returns.
The default is automatically set based on your system's platform, and should only need
to be changed if you manually installed Homebrew in a non-standard location.
'';
};
# 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 = { };
@@ -648,6 +785,8 @@ in
description = ''
Whether to always upgrade casks listed in [](#opt-homebrew.casks) regardless
of whether it's unversioned or it updates itself.
Homebrew's default is `false`.
'';
};
@@ -674,6 +813,12 @@ in
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 = ''
@@ -705,6 +850,12 @@ in
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 = ''
@@ -739,21 +890,47 @@ in
'';
};
whalebrews = mkOption {
vscode = mkOption {
type = with types; listOf str;
default = [ ];
example = [ "whalebrew/wget" ];
example = [ "golang.go" ];
description = ''
List of Docker images to install using {command}`whalebrew`.
List of Visual Studio Code extensions to install using Homebrew Bundle.
When this option is used, `"whalebrew"` is automatically added to
[](#opt-homebrew.brews).
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}`whalebrew` see:
[github.com/whalebrew/whalebrew](https://github.com/whalebrew/whalebrew).
For more information on {command}`code` see:
[VSCode Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace).
'';
};
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.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
@@ -766,7 +943,7 @@ in
brewfile = mkInternalOption {
type = types.str;
description = "String reprensentation of the generated Brewfile useful for debugging.";
description = "String representation of the generated Brewfile useful for debugging.";
};
};
@@ -775,22 +952,16 @@ in
config = {
assertions = [
# See comment above `homebrew.global.noLock` option declaration for why this is required.
{ assertion = cfg.global.noLock == null; message = "The option `homebrew.global.noLock' was removed, use `homebrew.global.lockfiles' in it's place."; }
];
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.")
(mkIf (hasSuffix "/bin" cfg.prefix) "`homebrew.prefix` should be the Homebrew prefix directory (e.g., `/opt/homebrew`), not the bin directory. The value should match what `brew --prefix` returns. Did you mean to remove the trailing `/bin`?")
];
system.requiresPrimaryUser = mkIf (cfg.enable && options.homebrew.user.highestPrio == (mkOptionDefault {}).priority) [
"homebrew.enable"
];
homebrew.brews =
optional (cfg.whalebrews != [ ]) "whalebrew";
homebrew.brewfile =
"# Created by `nix-darwin`'s `homebrew` module\n\n"
+ mkBrewfileSectionString "Taps" cfg.taps
@@ -800,25 +971,73 @@ in
+ mkBrewfileSectionString "Casks" cfg.casks
+ mkBrewfileSectionString "Mac App Store apps"
(mapAttrsToList (n: id: ''mas "${n}", id: ${toString id}'') cfg.masApps)
+ mkBrewfileSectionString "Docker containers" (map (v: ''whalebrew "${v}"'') cfg.whalebrews)
+ mkBrewfileSectionString "Visual Studio Code extensions" (map (v: ''vscode "${v}"'') cfg.vscode)
+ mkBrewfileSectionString "Go packages" (map (v: ''go "${v}"'') cfg.goPackages)
+ mkBrewfileSectionString "Cargo packages" (map (v: ''cargo "${v}"'') cfg.cargoPackages)
+ optionalString (cfg.extraConfig != "") ("# Extra config\n" + cfg.extraConfig);
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.checks.text = mkIf (cfg.enable && cfg.onActivation.cleanup == "check") ''
if [ -f "${cfg.prefix}/bin/brew" ]; then
homebrewCleanupExitCode=0
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"
printf >&2 '\n'
printf >&2 'To fix this, either:\n'
printf >&2 ' - Add the listed packages to your nix-darwin Homebrew configuration\n'
printf >&2 ' - Remove them by running: brew bundle cleanup --force\n'
printf >&2 ' - Set homebrew.onActivation.cleanup to "uninstall" or "zap"\n'
exit 2
elif [ "$homebrewCleanupExitCode" -ne 0 ]; then
printf >&2 '\e[1;31merror: brew bundle cleanup failed, aborting activation\e[0m\n'
printf >&2 '%s\n' "$homebrewCleanupResult"
exit 2
fi
fi
'';
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}
if [ -f "${cfg.prefix}/bin/brew" ]; then
${cfg.onActivation.brewBundleCmd { onlyCheck = false; }}
else
echo -e "\e[1;31merror: Homebrew is not installed, skipping...\e[0m" >&2
fi
'';
};
meta.maintainers = [
lib.maintainers.malo or "malo"
];
}
+2
View File
@@ -108,10 +108,12 @@
./programs/_1password-gui.nix
./programs/arqbackup.nix
./programs/bash
./programs/devenv.nix
./programs/direnv.nix
./programs/fish.nix
./programs/gnupg.nix
./programs/man.nix
./programs/mas.nix
./programs/info
./programs/nix-index
./programs/ssh.nix
+19
View File
@@ -18,6 +18,9 @@ let
*${lib.escapeShellArg srv}*)
networksetup -setdnsservers ${lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.dns))}
networksetup -setsearchdomains ${lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.search))}
${optionalString (cfg.dhcpClientId != null) ''
networksetup -setdhcp ${lib.escapeShellArgs [ srv cfg.dhcpClientId ]}
''}
;;
esac
'') cfg.knownNetworkServices}
@@ -133,6 +136,21 @@ in
'';
};
networking.dhcpClientId = mkOption {
type = types.nullOr types.str;
default = null;
example = "my-client-id";
description = ''
The DHCP client identifier to use when requesting an IP address from a DHCP server.
If this option is set, it will be used by the system when requesting an IP address.
If not set, no changes will be made.
Set to the string "empty" to clear any previously configured client ID
and restore the system default behavior.
'';
};
networking.dns = mkOption {
type = types.listOf types.str;
default = [];
@@ -162,6 +180,7 @@ in
warnings = [
(mkIf (cfg.knownNetworkServices == [] && cfg.dns != []) "networking.knownNetworkServices is empty, dns servers will not be configured.")
(mkIf (cfg.knownNetworkServices == [] && cfg.search != []) "networking.knownNetworkServices is empty, dns searchdomains will not be configured.")
(mkIf (cfg.knownNetworkServices == [] && cfg.dhcpClientId != null) "networking.knownNetworkServices is empty, dhcp client ID will not be configured.")
];
system.activationScripts.networking.text = ''
+14 -4
View File
@@ -757,6 +757,9 @@ in
"6bb8d6b0dd16b44ee793a9b8382dac76c926e4c16ffb8ddd2bb4884d1ca3f811" # DeterminateSystems Nix installer 0.34.0
"24797ac05542ff8b52910efc77870faa5f9e3275097227ea4e50c430a5f72916" # lix-installer 0.17.1 with flakes
"b027b5cad320b5b8123d9d0db9f815c3f3921596c26dc3c471457098e4d3cc40" # lix-installer 0.17.1 without flakes
"74ee0ae5ad21a1b101617685fd3d001f74a9466d9d763d92eb75b99cc740db91" # experimental official Nix installer 2.33.3
"daa9d824601c088f52de3da176e14328ba41e7be3eb450fdf8b93c86f236f722" # lix-installer 3.95.0 (47bc300) with flakes
"7659f39ef9ab9cdf50f8e6ae3476cbf3f27be6485500d1f225d45574d943a9f7" # lix-installer 3.95.0 (47bc300) without flakes
];
environment.etc."nix/registry.json".text = builtins.toJSON {
@@ -814,9 +817,12 @@ in
{ assertion = config.users.groups ? "nixbld" -> config.users.groups.nixbld.members != []; message = "refusing to remove all members from nixbld group, this would break nix"; }
{
# Should be fixed in Lix by https://gerrit.lix.systems/c/lix/+/2100
# Should be fixed in Lix by https://gerrit.lix.systems/c/lix/+/2100, Nix by https://github.com/NixOS/nix/commit/d888846b68dd5fad998b84c5cb6246b1b63398cd
# Lix 2.92.0 will set `VERSION_SUFFIX` to `""`; `lib.versionAtLeast "" "pre20241107"` will return `true`.
assertion = cfg.settings.auto-optimise-store -> (cfg.package.pname == "lix" && (isNixAtLeast "2.92.0" && versionAtLeast (strings.removePrefix "-" cfg.package.VERSION_SUFFIX) "pre20241107"));
assertion = cfg.settings.auto-optimise-store -> (
(cfg.package.pname == "lix" && (isNixAtLeast "2.92.0" && versionAtLeast (strings.removePrefix "-" cfg.package.VERSION_SUFFIX) "pre20241107"))
|| (cfg.package.pname == "nix" && ((isNixAtLeast "2.31.3" && !isNixAtLeast "2.32") || isNixAtLeast "2.32.5" || isNixAtLeast "2.33"))
);
message = "`nix.settings.auto-optimise-store` is known to corrupt the Nix Store, please use `nix.optimise.automatic` instead.";
}
];
@@ -881,10 +887,14 @@ in
# to express that we want it deleted and know only one hash?
system.activationScripts.checks.text = mkAfter ''
nixCustomConfKnownSha256Hashes=(
# v0.33.0
# DetSys v0.33.0
6787fade1cf934f82db554e78e1fc788705c2c5257fddf9b59bdd963ca6fec63
# v0.34.0
# DetSys v0.34.0
3bd68ef979a42070a44f8d82c205cfd8e8cca425d91253ec2c10a88179bb34aa
# Nix 2.33.3
71f7fdc9f6c9e55ca0f2e6f85137037d660b3224a34d59305e8530ca292bc734
# Lix 2.95.1
a6dee4985bf207d3bec6a3cee28aefb33e60f5d0a91d8c20bbd71b9dadb2e601
)
if [[ -e /etc/nix/nix.custom.conf ]]; then
nixCustomConfSha256Output=$(shasum -a 256 /etc/nix/nix.custom.conf)
+5 -2
View File
@@ -17,10 +17,13 @@ let
mergeConfig =
lhs_: rhs_:
let
lhs = optCall lhs_ { inherit pkgs; };
rhs = optCall rhs_ { inherit pkgs; };
lhs = optCall lhs_ { inherit lib pkgs; };
rhs = optCall rhs_ { inherit lib pkgs; };
in
lib.recursiveUpdate lhs rhs
// lib.optionalAttrs (lhs ? allowUnfreePackages) {
allowUnfreePackages = lhs.allowUnfreePackages ++ (lib.attrByPath [ "allowUnfreePackages" ] [ ] rhs);
}
// lib.optionalAttrs (lhs ? packageOverrides) {
packageOverrides =
pkgs:
+53
View File
@@ -0,0 +1,53 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.devenv;
hook = shell: "${lib.getExe cfg.package} hook ${shell}";
in
{
meta.maintainers = [
lib.maintainers.domenkozar or "domenkozar"
lib.maintainers.sandydoo or "sandydoo"
lib.maintainers.anish or "anish"
];
options.programs.devenv = {
enable = lib.mkEnableOption "devenv, fast, declarative, reproducible and composable developer environments";
package = lib.mkPackageOption pkgs "devenv" { };
enableBashIntegration = lib.mkEnableOption "auto-activation of devenv environments in Bash" // {
default = true;
};
enableFishIntegration = lib.mkEnableOption "auto-activation of devenv environments in Fish" // {
default = true;
};
enableZshIntegration = lib.mkEnableOption "auto-activation of devenv environments in Zsh" // {
default = true;
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
programs.bash.interactiveShellInit = lib.mkIf cfg.enableBashIntegration ''
eval "$(${hook "bash"})"
'';
programs.fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
${hook "fish"} | source
'';
programs.zsh.interactiveShellInit = lib.mkIf cfg.enableZshIntegration ''
eval "$(${hook "zsh"})"
'';
};
}
+20 -3
View File
@@ -5,6 +5,13 @@
...
}: let
cfg = config.programs.direnv;
enabledOption =
x:
lib.mkEnableOption x
// {
default = true;
example = false;
};
format = pkgs.formats.toml {};
in {
meta.maintainers = [
@@ -25,6 +32,16 @@ in {
description = "The wrapped direnv package.";
};
enableBashIntegration = enabledOption ''
Bash integration
'';
enableZshIntegration = enabledOption ''
Zsh integration
'';
enableFishIntegration = enabledOption ''
Fish integration
'';
direnvrcExtra = lib.mkOption {
type = lib.types.lines;
default = "";
@@ -97,7 +114,7 @@ in {
};
};
};
zsh.interactiveShellInit = ''
zsh.interactiveShellInit = lib.mkIf cfg.enableZshIntegration ''
if ${lib.boolToString cfg.loadInNixShell} || printenv PATH | grep -vqc '/nix/store'; then
eval "$(${lib.getExe cfg.finalPackage} hook zsh)"
fi
@@ -105,13 +122,13 @@ in {
#$NIX_GCROOT for "nix develop" https://github.com/NixOS/nix/blob/6db66ebfc55769edd0c6bc70fcbd76246d4d26e0/src/nix/develop.cc#L530
#$IN_NIX_SHELL for "nix-shell"
bash.interactiveShellInit = ''
bash.interactiveShellInit = lib.mkIf cfg.enableBashIntegration ''
if ${lib.boolToString cfg.loadInNixShell} || [ -z "$IN_NIX_SHELL$NIX_GCROOT$(printenv PATH | grep '/nix/store')" ] ; then
eval "$(${lib.getExe cfg.finalPackage} hook bash)"
fi
'';
fish.interactiveShellInit = ''
fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
if ${lib.boolToString cfg.loadInNixShell};
or printenv PATH | grep -vqc '/nix/store';
${lib.getExe cfg.finalPackage} hook fish | source
+24 -7
View File
@@ -1,8 +1,19 @@
{ config, lib, pkgs, ... }:
with lib;
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
getExe'
mkIf
mkOption
mkPackageOption
optionalString
types
;
cfg = config.programs.gnupg;
@@ -10,6 +21,8 @@ in
{
options.programs.gnupg = {
package = mkPackageOption pkgs "gnupg" { };
agent.enable = mkOption {
type = types.bool;
default = false;
@@ -29,9 +42,12 @@ in
};
config = mkIf cfg.agent.enable {
environment.systemPackages = [ cfg.package ];
launchd.user.agents.gnupg-agent.serviceConfig = {
ProgramArguments = [
"${pkgs.gnupg}/bin/gpg-connect-agent" "/bye"
(getExe' cfg.package "gpg-connect-agent")
"/bye"
];
RunAtLoad = cfg.agent.enableSSHSupport;
KeepAlive.SuccessfulExit = false;
@@ -40,12 +56,13 @@ in
environment.extraInit = ''
# Bind gpg-agent to this TTY if gpg commands are used.
export GPG_TTY=$(tty)
'' + (optionalString cfg.agent.enableSSHSupport ''
''
+ (optionalString cfg.agent.enableSSHSupport ''
# SSH agent protocol doesn't support changing TTYs, so bind the agent
# to every new TTY.
${pkgs.gnupg}/bin/gpg-connect-agent --quiet updatestartuptty /bye > /dev/null 2>&1
${getExe' cfg.package "gpg-connect-agent"} --quiet updatestartuptty /bye > /dev/null 2>&1
export SSH_AUTH_SOCK=$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket)
export SSH_AUTH_SOCK=$(${getExe' cfg.package "gpgconf"} --list-dirs agent-ssh-socket)
'');
};
}
+209
View File
@@ -0,0 +1,209 @@
{
config,
lib,
options,
pkgs,
...
}:
let
inherit (lib)
attrValues
concatStringsSep
escapeShellArg
getExe
literalExpression
mapAttrsToList
mkEnableOption
mkIf
mkOption
mkOptionDefault
mkPackageOption
optionalString
types
;
cfg = config.programs.mas;
apps = mapAttrsToList (name: id: { inherit name id; }) cfg.packages;
desiredIds = map (app: toString app.id) apps;
homebrewIds = map toString (attrValues config.homebrew.masApps);
hasWork = cfg.update || cfg.packages != { } || cfg.cleanup || homebrewIds != [ ];
activationScript =
if hasWork then
''
echo >&2 "setting up App Store apps (mas)..."
runAsUser() {
sudo \
--preserve-env=PATH \
--set-home \
--user=${escapeShellArg cfg.user} \
"$@"
}
listStatus=0
listOutput=$(
runAsUser ${getExe cfg.package} list 2>&1
) || listStatus=$?
if (( listStatus != 0 )); then
echo >&2 "warning: mas list failed (exit ''${listStatus}):"
echo >&2 "''${listOutput}"
if echo "''${listOutput}" | grep -qi "not signed in"; then
echo >&2 "login required; skipping App Store installs/updates/cleanup"
exit 0
fi
fi
# Only emit cleanup-only shell variables when cleanup is enabled; otherwise shellcheck
# treats them as unused and fails the activation script build.
installedIds=()
${if cfg.cleanup then
''
# Parse mas list output: "ID AppName (version)"
declare -A installedApps
while IFS= read -r line; do
[[ -z "$line" ]] && continue
line="''${line#"''${line%%[![:space:]]*}"}"
id="''${line%% *}"
rest="''${line#"$id"}"
rest="''${rest#"''${rest%%[![:space:]]*}"}"
name="''${rest% (*}"
name="''${name%"''${name##*[![:space:]]}"}"
[[ -n "$id" ]] && {
installedIds+=( "$id" )
installedApps["$id"]="$name"
}
done <<<"$listOutput"
''
else
''
while IFS= read -r line; do
[[ -z "$line" ]] && continue
line="''${line#"''${line%%[![:space:]]*}"}"
id="''${line%% *}"
[[ -n "$id" ]] && installedIds+=( "$id" )
done <<<"$listOutput"
''}
${optionalString cfg.update ''
runAsUser ${getExe cfg.package} update || true
''}
desiredIds=(
${concatStringsSep "\n " desiredIds}
)
is_installed() {
local needle=$1
for id in "''${installedIds[@]}"; do
if [[ "$id" == "$needle" ]]; then
return 0
fi
done
return 1
}
${optionalString (cfg.packages != { }) ''
for appId in "''${desiredIds[@]}"; do
if is_installed "$appId"; then
continue
fi
runAsUser ${getExe cfg.package} install "$appId" || true
done
''}
${optionalString cfg.cleanup ''
homebrewIds=(
${concatStringsSep "\n " homebrewIds}
)
keepIds=( "''${desiredIds[@]}" "''${homebrewIds[@]}" )
for installedId in "''${installedIds[@]}"; do
keep=false
for keepId in "''${keepIds[@]}"; do
if [[ "$installedId" == "$keepId" ]]; then
keep=true
break
fi
done
if ! $keep; then
appName="''${installedApps[$installedId]:-$installedId}"
echo >&2 "removing $appName from App Store"
runAsUser ${getExe cfg.package} uninstall "$installedId" || true
fi
done
''}
''
else
"";
in
{
options.programs.mas = {
enable = mkEnableOption "managing Mac App Store apps with mas";
user = mkOption {
type = types.str;
default = config.system.primaryUser;
defaultText = literalExpression "config.system.primaryUser";
description = ''
The user account that runs {command}`mas`. This user must be signed into the Mac App Store
for installs or updates to succeed.
'';
};
package = mkPackageOption pkgs "mas" { };
packages = mkOption {
type = types.attrsOf types.ints.positive;
default = { };
example = literalExpression ''
{
Xcode = 497799835;
"1Password for Safari" = 1569813296;
}
'';
description = ''
Applications to install from the Mac App Store. Attribute names are only for readability;
values must be the numeric identifiers used by {command}`mas`.
'';
};
update = mkOption {
type = types.bool;
default = true;
description = ''
Whether to run {command}`mas update` during system activation in addition to installing the
configured apps.
'';
};
cleanup = mkOption {
type = types.bool;
default = false;
description = ''
Whether to uninstall Mac App Store apps that are currently installed but not listed in
{option}`programs.mas.packages`. Apps listed in {option}`homebrew.masApps` are also preserved.
This runs before install/update; any app id not in either set will be removed.
'';
};
};
config = {
system.requiresPrimaryUser =
mkIf (cfg.enable && options.programs.mas.user.highestPrio == (mkOptionDefault { }).priority)
[
"programs.mas.enable"
];
environment.systemPackages = mkIf cfg.enable [ cfg.package ];
system.activationScripts.mas.text = mkIf cfg.enable activationScript;
};
}
+16 -2
View File
@@ -57,6 +57,17 @@ in
description = "Enable sensible configuration options for tmux.";
};
programs.tmux.reverseSplitBindings = mkOption {
type = types.bool;
default = config.system.stateVersion <= 6 && cfg.enableSensible;
defaultText = literalExpression "config.system.stateVersion <= 6 && config.programs.tmux.enableSensible";
example = true;
description = ''
Whether to reverse the `%` and `"` split-window key bindings
emitted by the tmux module.
'';
};
programs.tmux.enableMouse = mkOption {
type = types.bool;
default = false;
@@ -127,8 +138,6 @@ in
set -s escape-time 0
bind c new-window -c '#{pane_current_path}'
bind % split-window -v -c '#{pane_current_path}'
bind '"' split-window -h -c '#{pane_current_path}'
# TODO: make these interactive
bind C new-session
@@ -138,6 +147,11 @@ in
# set -g utf8 on
'';
programs.tmux.tmuxOptions.splitBindings.text = mkIf cfg.reverseSplitBindings ''
bind % split-window -v -c '#{pane_current_path}'
bind '"' split-window -h -c '#{pane_current_path}'
'';
programs.tmux.tmuxOptions.mouse.text = mkIf cfg.enableMouse ''
set -g mouse on
setw -g mouse on
+1
View File
@@ -260,6 +260,7 @@ in
environment.etc."zshenv".knownSha256Hashes = [
"d07015be6875f134976fce84c6c7a77b512079c1c5f9594dfa65c70b7968b65f" # DeterminateSystems installer
"4e8f7cb9b699511f4ba5f9d5f8de1c9f5efb5c607de88faf5f58b8b9cb38edbf" # experimental official Nix installer 2.33.3
];
};
+1 -1
View File
@@ -263,7 +263,7 @@ in
nodeRuntimes = mkOption {
type = with types; nonEmptyListOf (enum [ "node20" "node24" ]);
default = [ "node20" "node24" ];
default = [ "node24" ];
description = ''
List of Node.js runtimes the runner should support.
'';
+5 -5
View File
@@ -2,13 +2,13 @@
let
inherit (lib) any attrValues boolToString concatStringsSep escapeShellArg
flatten flip getExe getExe' hasAttr hasPrefix mapAttrsToList mapAttrs' mkBefore
mkDefault mkIf mkMerge nameValuePair optionalAttrs optionalString replaceStrings;
flatten flip getExe getExe' hasPrefix mapAttrsToList mapAttrs' mkBefore
mkDefault mkIf mkMerge nameValuePair optionalString replaceStrings;
mkSvcName = name: "github-runner-${name}";
mkStateDir = cfg: "/var/lib/github-runners/${cfg.name}";
mkLogDir = cfg: "/var/log/github-runners/${cfg.name}";
mkWorkDir = cfg: if (cfg.workDir != null) then cfg.workDir else "/var/lib/github-runners/_work/${cfg.name}";
mkWorkDir = cfg: if (cfg.workDir != null) then cfg.workDir else "/private/var/lib/github-runners/_work/${cfg.name}";
in
{
config.assertions = flatten (
@@ -54,7 +54,7 @@ in
in
{
launchd = mkIf cfg.enable {
text = mkBefore (''
text = mkBefore ''
echo >&2 "setting up GitHub Runner '${cfg.name}'..."
# shellcheck disable=SC2174
@@ -70,7 +70,7 @@ in
${getExe' pkgs.coreutils "mkdir"} -p -m u=rwx,g=rx,o= ${escapeShellArg (mkWorkDir cfg)}
${getExe' pkgs.coreutils "chown"} ${user}:${group} ${escapeShellArg (mkWorkDir cfg)}
''}
'');
'';
};
}));
+1 -1
View File
@@ -22,7 +22,7 @@ in {
];
options.services.jankyborders = {
enable = mkEnableOption "Enable the jankyborders service.";
enable = mkEnableOption "the jankyborders service.";
package = mkPackageOption pkgs "jankyborders" {};
+1 -1
View File
@@ -33,7 +33,7 @@ in
services.khd.i3Keybindings = mkOption {
type = types.bool;
default = false;
description = "Wether to configure i3 style keybindings for kwm.";
description = "Whether to configure i3 style keybindings for kwm.";
};
};
@@ -7,8 +7,8 @@
let
inherit (lib)
escapeShellArg
concatStringsSep
escapeShellArgs
getExe
mkEnableOption
mkIf
@@ -104,7 +104,8 @@ in {
]
++ (map (collector: "--collector.${collector}") cfg.enabledCollectors)
++ (map (collector: "--no-collector.${collector}") cfg.disabledCollectors)
) + escapeShellArgs cfg.extraFlags;
++ (map escapeShellArg cfg.extraFlags)
);
serviceConfig = let
logPath = config.users.users._prometheus-node-exporter.home
+ "/prometheus-node-exporter.log";
+88 -1
View File
@@ -1,7 +1,72 @@
{ config, lib, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.services.openssh;
hostKeyOpts = {
options = {
type = lib.mkOption {
type = lib.types.enum [ "dsa" "ecdsa" "ed25519" "rsa" ];
description = ''
Key type passed to `ssh-keygen -t`.
'';
};
path = lib.mkOption {
type = lib.types.str;
description = ''
Path to the private key file.
'';
};
bits = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = ''
Key size in bits. If `null`, `ssh-keygen` uses the default
for the given key type (RSA=3072, ECDSA=256, ED25519=fixed).
'';
};
comment = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Comment for the key, passed to `ssh-keygen -C`.
Defaults to an empty string to match Apple's built-in host key
generation and avoid leaking the hostname.
'';
};
};
};
hostKeysConfig = lib.concatMapStringsSep "\n"
(k: "HostKey ${k.path}")
cfg.hostKeys;
keygenScript = lib.concatMapStrings (k:
let
escapedPath = lib.escapeShellArg k.path;
in ''
if ! [[ -s ${escapedPath} ]]; then
if ! [[ -L ${escapedPath} ]]; then
rm -f ${escapedPath}
fi
keygenArgs=(
-t ${lib.escapeShellArg k.type}
${lib.optionalString (k.bits != null) "-b ${toString k.bits}"}
-C ${lib.escapeShellArg k.comment}
-f ${escapedPath}
-N ""
)
mkdir -p "$(dirname ${escapedPath})"
chmod 0755 "$(dirname ${escapedPath})"
${lib.getExe' pkgs.openssh "ssh-keygen"} "''${keygenArgs[@]}"
fi
'') cfg.hostKeys;
in
{
options = {
@@ -24,6 +89,22 @@ in
See {manpage}`sshd_config(5)` for help.
'';
};
hostKeys = lib.mkOption {
type = lib.types.listOf (lib.types.submodule hostKeyOpts);
default = [
{ type = "rsa"; path = "/etc/ssh/ssh_host_rsa_key"; }
{ type = "ecdsa"; path = "/etc/ssh/ssh_host_ecdsa_key"; }
{ type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; }
];
description = ''
SSH host key declarations. Each entry specifies a key type and path.
`HostKey` directives are written to the sshd configuration for each
entry.
The default matches the keys that macOS automatically generates.
'';
};
};
};
@@ -41,6 +122,12 @@ in
fi
'');
environment.etc."ssh/sshd_config.d/099-host-keys.conf" = lib.mkIf (cfg.hostKeys != []) {
text = hostKeysConfig;
};
environment.etc."ssh/sshd_config.d/100-nix-darwin.conf".text = cfg.extraConfig;
system.activationScripts.openssh.text = lib.mkIf (cfg.hostKeys != []) keygenScript;
};
}
+2 -1
View File
@@ -22,11 +22,12 @@ in
services.spacebar.enable = mkOption {
type = bool;
default = false;
description = "Whether to enable the spacebar spacebar.";
description = "Whether to enable the spacebar.";
};
services.spacebar.package = mkOption {
type = path;
default = pkgs.spacebar;
description = "The spacebar package to use.";
};
+1 -1
View File
@@ -83,7 +83,7 @@ let
preDown = mkOption {
type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines;
default = "";
description = "List of commadns to run before interface shutdown.";
description = "List of commands to run before interface shutdown.";
};
preUp = mkOption {
+2
View File
@@ -121,6 +121,7 @@ in
${cfg.activationScripts.applications.text}
${cfg.activationScripts.pam.text}
${cfg.activationScripts.patches.text}
${cfg.activationScripts.openssh.text}
${cfg.activationScripts.etc.text}
${cfg.activationScripts.defaults.text}
${cfg.activationScripts.userDefaults.text}
@@ -133,6 +134,7 @@ in
${cfg.activationScripts.keyboard.text}
${cfg.activationScripts.fonts.text}
${cfg.activationScripts.nvram.text}
${cfg.activationScripts.mas.text}
${cfg.activationScripts.homebrew.text}
${cfg.activationScripts.postActivation.text}
+1 -1
View File
@@ -252,7 +252,7 @@ let
'';
homebrewInstalled = ''
if [[ ! -f ${escapeShellArg config.homebrew.brewPrefix}/brew && -z "''${INSTALLING_HOMEBREW:-}" ]]; then
if [[ ! -f ${escapeShellArg config.homebrew.prefix}/bin/brew && -z "''${INSTALLING_HOMEBREW:-}" ]]; then
echo "error: Using the homebrew module requires homebrew installed, aborting activation" >&2
echo "Homebrew doesn't seem to be installed. Please install homebrew separately." >&2
echo "You can install homebrew using the following command:" >&2
@@ -41,6 +41,14 @@ with lib;
'';
};
system.defaults.screencapture.save-selections = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Remember the selection window of the last screencapture. The default is true.
'';
};
system.defaults.screencapture.show-thumbnail = mkOption {
type = types.nullOr types.bool;
default = null;
+28
View File
@@ -38,6 +38,18 @@ in
description = "Whether to swap the left Command key and left Alt key.";
};
system.keyboard.swapRightCommandAndRightOption = mkOption {
type = types.bool;
default = false;
description = "Whether to swap the right Command key and right Option key.";
};
system.keyboard.swapCapsLockAndEscape = mkOption {
type = types.bool;
default = false;
description = "Whether to swap the Caps Lock key and Escape key.";
};
system.keyboard.swapLeftCtrlAndFn = mkOption {
type = types.bool;
default = false;
@@ -64,6 +76,14 @@ in
(mkIf cfg.remapCapsLockToControl { HIDKeyboardModifierMappingSrc = 30064771129; HIDKeyboardModifierMappingDst = 30064771296; })
(mkIf cfg.remapCapsLockToEscape { HIDKeyboardModifierMappingSrc = 30064771129; HIDKeyboardModifierMappingDst = 30064771113; })
(mkIf cfg.nonUS.remapTilde { HIDKeyboardModifierMappingSrc = 30064771172; HIDKeyboardModifierMappingDst = 30064771125; })
(mkIf cfg.swapCapsLockAndEscape {
HIDKeyboardModifierMappingSrc = 30064771129;
HIDKeyboardModifierMappingDst = 30064771113;
})
(mkIf cfg.swapCapsLockAndEscape {
HIDKeyboardModifierMappingSrc = 30064771113;
HIDKeyboardModifierMappingDst = 30064771129;
})
(mkIf cfg.swapLeftCommandAndLeftAlt {
HIDKeyboardModifierMappingSrc = 30064771299;
HIDKeyboardModifierMappingDst = 30064771298;
@@ -72,6 +92,14 @@ in
HIDKeyboardModifierMappingSrc = 30064771298;
HIDKeyboardModifierMappingDst = 30064771299;
})
(mkIf cfg.swapRightCommandAndRightOption {
HIDKeyboardModifierMappingSrc = 30064771303;
HIDKeyboardModifierMappingDst = 30064771302;
})
(mkIf cfg.swapRightCommandAndRightOption {
HIDKeyboardModifierMappingSrc = 30064771302;
HIDKeyboardModifierMappingDst = 30064771303;
})
(mkIf cfg.swapLeftCtrlAndFn {
HIDKeyboardModifierMappingSrc = 30064771296;
HIDKeyboardModifierMappingDst = 1095216660483;
+1 -1
View File
@@ -51,7 +51,7 @@ in
system.maxStateVersion = mkOption {
internal = true;
type = types.int;
default = 6;
default = 7;
};
system.darwinLabel = mkOption {
+6 -13
View File
@@ -3,19 +3,8 @@
with lib;
let
cfg = config.time;
timeZone = optionalString (cfg.timeZone != null) ''
if ! systemsetup -listtimezones | grep -q "^ ${cfg.timeZone}$"; then
echo "${cfg.timeZone} is not a valid timezone. The command 'listtimezones' will show a list of valid time zones." >&2
false
fi
systemsetup -settimezone "${cfg.timeZone}" 2>/dev/null 1>&2
'';
in
{
options = {
@@ -37,8 +26,12 @@ in
system.activationScripts.time.text = mkIf (cfg.timeZone != null) ''
# Set defaults
echo "configuring time..." >&2
${timeZone}
if ! systemsetup -listtimezones | grep -q "^ ${cfg.timeZone}$"; then
echo "${cfg.timeZone} is not a valid timezone. The command 'listtimezones' will show a list of valid time zones." >&2
false
fi
systemsetup -settimezone "${cfg.timeZone}" 2>/dev/null 1>&2
'';
};
+3 -1
View File
@@ -203,7 +203,9 @@ in
else
homeDirectory=$(dscl . -read ${dsclUser} NFSHomeDirectory)
homeDirectory=''${homeDirectory#NFSHomeDirectory: }
if [[ ${escapeShellArg v.home} != "$homeDirectory" ]]; then
configuredHomeDirectory=$(realpath ${escapeShellArg v.home})
homeDirectory=$(realpath "$homeDirectory")
if [[ "$configuredHomeDirectory" != "$homeDirectory" ]]; then
printf >&2 '\e[1;31merror: config contains the wrong home directory for %s, aborting activation\e[0m\n' ${name}
printf >&2 'nix-darwin does not support changing the home directory of existing users.\n'
printf >&2 '\n'
+2 -2
View File
@@ -28,7 +28,7 @@ showSyntax() {
echo " [--no-update-lock-file] [--no-write-lock-file]" >&2
echo " [--override-input input flake] [--update-input input]" >&2
echo " [--no-registries] [--offline] [--refresh]]" >&2
echo " [--substituters substituters-list] ..." >&2
echo " [--substituters substituters-list] [--log-format log-format] ..." >&2
exit 1
}
@@ -128,7 +128,7 @@ while [ $# -gt 0 ]; do
fi
shift 1
;;
--substituters)
--substituters|--log-format)
if [ -z "$1" ]; then
echo "$0: '$i' requires an argument"
exit 1
@@ -21,6 +21,17 @@ _arguments \
'(--keep-going -k)'{--keep-going,-k}"[keep going until all builds are finished]"\
'(--keep-failed -K)'{--keep-failed,-K}'[keep failed builds (usually in /tmp)]'\
'--fallback[If binary download fails, fall back on building from source]'\
'--flake[Build from the given flake URI]:flake uri:'\
'--no-flake[Disable implicit flake detection]'\
'(-L --print-build-logs)'{-L,--print-build-logs}'[Print build logs during the build]'\
'--refresh[Refresh flake inputs]'\
'--impure[Allow impure evaluation for flakes]'\
'--recreate-lock-file[Recreate flake.lock from scratch]'\
'*--update-input[Update a specific flake input]:input name:'\
'*--override-input[Override a flake input]:input name: :flake url: '\
'--offline[Do not access the network]'\
'--dry-run[Show what would be done without making changes]'\
'-Q[Only print the resulting store path]'\
'--show-trace[Print stack trace of evaluation errors]'\
'*--option[set Nix configuration option]:options:_nix_options:value:_nix_options_value'\
'*--arg[argument to pass to the Nix function]:Name:_nix_complete_function_arg:Value: '\
+8
View File
@@ -83,13 +83,21 @@ in {
tests.environment-path = makeTest ./tests/environment-path.nix;
tests.environment-terminfo = makeTest ./tests/environment-terminfo.nix;
tests.homebrew = makeTest ./tests/homebrew.nix;
tests.homebrew-cleanup-check = makeTest ./tests/homebrew-cleanup-check.nix;
tests.homebrew-shell-integration = makeTest ./tests/homebrew-shell-integration.nix;
tests.launchd-daemons = makeTest ./tests/launchd-daemons.nix;
tests.launchd-setenv = makeTest ./tests/launchd-setenv.nix;
tests.networking-firewall = makeTest ./tests/networking-firewall.nix;
tests.networking-hostname = makeTest ./tests/networking-hostname.nix;
tests.networking-networkservices = makeTest ./tests/networking-networkservices.nix;
tests.networking-networkservices-no-dhcp-client-id = makeTest ./tests/networking-networkservices-no-dhcp-client-id.nix;
tests.nix-enable = makeTest ./tests/nix-enable.nix;
tests.nixpkgs-overlays = makeTest ./tests/nixpkgs-overlays.nix;
tests.nixpkgs-config-allow-unfree = makeTest ./tests/nixpkgs-config-allow-unfree.nix;
tests.programs-devenv = makeTest ./tests/programs-devenv.nix;
tests.programs-gnupg = makeTest ./tests/programs-gnupg.nix;
tests.programs-mas = makeTest ./tests/programs-mas.nix;
tests.programs-mas-no-cleanup = makeTest ./tests/programs-mas-no-cleanup.nix;
tests.programs-ssh = makeTest ./tests/programs-ssh.nix;
tests.programs-tmux = makeTest ./tests/programs-tmux.nix;
tests.programs-zsh = makeTest ./tests/programs-zsh.nix;
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
git checkout master
git pull
version=$(jq -r .release version.json)
# branch off ${version}
echo "Cutting nix-darwin-${version} release branch"
git checkout -b "nix-darwin-${version}"
sed -i -e "s!- master!- nix-darwin-${version}!" .github/workflows/test.yml
sed -i -e "s!NIXPKGS_BRANCH: nixpkgs-unstable!NIXPKGS_BRANCH: nixpkgs-${version}-darwin!" .github/workflows/test.yml
sed -i -e "s!nixpkgs-unstable!nixpkgs-${version}-darwin!" modules/examples/flake/flake.nix
sed -i -e "s!github:nix-darwin/nix-darwin/master!github:nix-darwin/nix-darwin/nix-darwin-${version}!" modules/examples/flake/flake.nix
sed -i -e "s!nixpkgs/unstable!nixpkgs/stable!g" modules/nix/nixpkgs.nix
sed -i -e "s!nixpkgs-unstable!nixpkgs-${version}-darwin!" flake.nix
nix flake lock
cat <<EOF > README.md
# nix-darwin
This is the ${version} release branch of nix-darwin. See [the main readme](https://github.com/nix-darwin/nix-darwin#readme) for documentation
EOF
cat <<EOF > version.json
{
"release": "${version}",
"isReleaseBranch": true
}
EOF
git add .
git commit -m "version: branch off ${version}"
# update master
echo "Updating master to point to next version"
git checkout master
## update version.json to point to the next version
IFS='.' read -r major minor <<< "$version"
if [[ "$minor" = "11" ]]; then
major=$(( major + 1 ))
minor="05"
else
minor="11"
fi
cat <<EOF > version.json
{
"release": "${major}.${minor}",
"isReleaseBranch": false
}
EOF
## update readme so that instructions refer to the version we're cutting (our supported stable version)
sed -i -e "s![0-9][0-9]\.[0-9][0-9]!${version}!g" README.md
git add .
git commit -m "version: bump to ${major}.${minor}"
nix flake update
git add .
git commit -m "flake.lock: update"
+5
View File
@@ -550,6 +550,11 @@ launchctl asuser "$(id -u -- test-defaults-user)" sudo --user=test-defaults-user
<plist version="1.0">
<string>/tmp</string>
</plist>'
launchctl asuser "$(id -u -- test-defaults-user)" sudo --user=test-defaults-user -- defaults write com.apple.screencapture save-selections '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<true/>
</plist>'
launchctl asuser "$(id -u -- test-defaults-user)" sudo --user=test-defaults-user -- defaults write com.apple.screencapture target '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
+23
View File
@@ -0,0 +1,23 @@
{ config, ... }:
let
brewBundleInstallCmd = config.homebrew.onActivation.brewBundleCmd { onlyCheck = false; };
in
{
homebrew.enable = true;
homebrew.user = "test-homebrew-user";
homebrew.onActivation.cleanup = "check";
test = ''
echo "checking that cleanup check is present in system checks" >&2
grep "brew bundle --file='.*-Brewfile' cleanup" ${config.out}/activate
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
'';
}
+29
View File
@@ -0,0 +1,29 @@
{ config, ... }:
{
homebrew.enable = true;
programs.bash.enable = true;
programs.zsh.enable = true;
programs.fish.enable = true;
homebrew.user = "test-homebrew-user";
homebrew.enableBashIntegration = true;
homebrew.enableFishIntegration = true;
homebrew.enableZshIntegration = true;
test = ''
echo >&2 "checking bash shell integration in /etc/bashrc"
grep 'brew shellenv bash' ${config.out}/etc/bashrc
echo >&2 "checking bash completions in /etc/bashrc"
grep 'bash_completion' ${config.out}/etc/bashrc
echo >&2 "checking zsh shell integration in /etc/zshrc"
grep 'brew shellenv zsh' ${config.out}/etc/zshrc
echo >&2 "checking fish shell integration in /etc/fish/config.fish"
grep 'brew shellenv fish' ${config.out}/etc/fish/config.fish
echo >&2 "checking fish completions in /etc/fish/config.fish"
grep 'fish_complete_path' ${config.out}/etc/fish/config.fish
'';
}
+39 -7
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";
@@ -49,6 +50,10 @@ in
link = true;
conflicts_with = [ "mysql" ];
}
{
name = "postgresql@16";
postinstall = "\${HOMEBREW_PREFIX}/opt/postgresql@16/bin/postgres -D \${HOMEBREW_PREFIX}/var/postgresql@16";
}
];
homebrew.casks = [
@@ -61,6 +66,10 @@ in
name = "opera";
greedy = true;
}
{
name = "google-cloud-sdk";
postinstall = "\${HOMEBREW_PREFIX}/bin/gcloud components update";
}
];
homebrew.masApps = {
@@ -68,10 +77,19 @@ in
Xcode = 497799835;
};
homebrew.whalebrews = [
"whalebrew/wget"
homebrew.vscode = [
"golang.go"
];
homebrew.goPackages = [
"github.com/charmbracelet/crush"
];
homebrew.cargoPackages = [
"ripgrep"
];
test = ''
bf=${lib.escapeShellArg config.homebrew.brewfile}
@@ -85,19 +103,33 @@ 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''}
${mkTest "postgresql@16" ''brew "postgresql@16", postinstall: "''${HOMEBREW_PREFIX}/opt/postgresql@16/bin/postgres -D ''${HOMEBREW_PREFIX}/var/postgresql@16"''}
echo "checking cask entries in Brewfile" >&2
${mkTest "google-chrome" ''cask "google-chrome"''}
${mkTest "firefox" ''cask "firefox", args: { appdir: "~/my-apps/Applications" }''}
${mkTest "opera" ''cask "opera", greedy: true''}
${mkTest "google-cloud-sdk" ''cask "google-cloud-sdk", postinstall: "''${HOMEBREW_PREFIX}/bin/gcloud components update"''}
echo "checking mas entries in Brewfile" >&2
${mkTest "1Password for Safari" ''mas "1Password for Safari", id: 1569813296''}
${mkTest "Xcode" ''mas "Xcode", id: 497799835''}
echo "checking whalebrew entries in Brewfile" >&2
${mkTest "whalebrew/wget" ''whalebrew "whalebrew/wget"''}
echo "checking vscode entries in Brewfile" >&2
${mkTest "golang.go" ''vscode "golang.go"''}
echo "checking go entries in Brewfile" >&2
${mkTest "github.com/charmbracelet/crush" ''go "github.com/charmbracelet/crush"''}
echo "checking cargo entries in Brewfile" >&2
${mkTest "ripgrep" ''cargo "ripgrep"''}
echo "checking that shell integration is absent by default" >&2
(! grep 'brew shellenv' ${config.out}/etc/zshrc)
echo "checking that cleanup check is absent by default" >&2
(! grep 'brew bundle cleanup --file=' ${config.out}/activate)
'';
}
@@ -0,0 +1,14 @@
{ config, lib, ... }:
{
networking.knownNetworkServices = [ "Wi-Fi" "Thunderbolt Ethernet" ];
networking.dns = [ "8.8.8.8" "8.8.4.4" ];
test = ''
echo checking dhcp client ID is not configured in /activate >&2
if grep -q "networksetup -setdhcp" ${config.out}/activate; then
echo "unexpected dhcp client ID configuration in /activate" >&2
exit 1
fi
'';
}
+3
View File
@@ -3,11 +3,14 @@
{
networking.knownNetworkServices = [ "Wi-Fi" "Thunderbolt Ethernet" ];
networking.dns = [ "8.8.8.8" "8.8.4.4" ];
networking.dhcpClientId = "test-client-id";
test = ''
echo checking dns settings in /activate >&2
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Wi-Fi" "8.8.8.8" "8.8.4.4" ]}" ${config.out}/activate
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "8.8.8.8" "8.8.4.4" ]}" ${config.out}/activate
grep "networksetup -setdhcp ${lib.escapeShellArgs [ "Wi-Fi" "test-client-id" ]}" ${config.out}/activate
grep "networksetup -setdhcp ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "test-client-id" ]}" ${config.out}/activate
echo checking empty searchdomain settings in /activate >&2
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Wi-Fi" "empty" ]}" ${config.out}/activate
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "empty" ]}" ${config.out}/activate
+45
View File
@@ -0,0 +1,45 @@
# Check that nixpkgs.config.allowUnfreePackages is merged correctly
# run with: nix-build release.nix -A tests.nixpkgs-config-allow-unfree
{
config,
lib,
pkgs,
...
}:
{
# Module 1: Define some unfree packages
nixpkgs.config.allowUnfreePackages = [
"vscode"
"slack"
];
# Module 2: Define more unfree packages (simulating multiple modules)
# In a real scenario, this would be in a separate file
imports = [
(
{ config, ... }:
{
nixpkgs.config.allowUnfreePackages = [
"zoom"
"discord"
];
}
)
];
test = ''
echo checking allowUnfreePackages merging >&2
# Verify that all packages from both modules are present
expected_packages=("discord" "slack" "vscode" "zoom")
actual_packages=(${builtins.toString (builtins.sort builtins.lessThan config.nixpkgs.config.allowUnfreePackages)})
for pkg in "''${expected_packages[@]}"; do
if [[ ! " ''${actual_packages[@]} " =~ " $pkg " ]]; then
echo "ERROR: Expected package '$pkg' not found in allowUnfreePackages" >&2
exit 1
fi
done
'';
}
+42
View File
@@ -0,0 +1,42 @@
{
config,
lib,
pkgs,
...
}:
let
devenv =
pkgs.runCommand "devenv-0.0.0" { } ''
mkdir -p $out/bin
cat > $out/bin/devenv <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x $out/bin/devenv
''
// {
meta.mainProgram = "devenv";
};
in
{
programs.bash.enable = true;
programs.zsh.enable = true;
programs.fish.enable = true;
programs.devenv = {
enable = true;
package = devenv;
};
test = ''
echo >&2 "checking devenv bash integration in /etc/bashrc"
grep 'devenv hook bash' ${config.out}/etc/bashrc
echo >&2 "checking devenv zsh integration in /etc/zshrc"
grep 'devenv hook zsh' ${config.out}/etc/zshrc
echo >&2 "checking devenv fish integration in /etc/fish/config.fish"
grep 'devenv hook fish' ${config.out}/etc/fish/config.fish
'';
}
+31
View File
@@ -0,0 +1,31 @@
{
config,
lib,
pkgs,
...
}:
let
gnupg = pkgs.runCommand "gnupg-0.0.0" { } "mkdir -p $out/bin";
in
{
system.primaryUser = "test-gnupg-user";
programs.gnupg.package = gnupg;
programs.gnupg.agent.enable = true;
programs.gnupg.agent.enableSSHSupport = true;
test = ''
echo >&2 "checking gnupg-agent service in ~/Library/LaunchAgents"
grep "org.nixos.gnupg-agent" ${config.out}/user/Library/LaunchAgents/org.nixos.gnupg-agent.plist
grep "${gnupg}/bin/gpg-connect-agent" ${config.out}/user/Library/LaunchAgents/org.nixos.gnupg-agent.plist
echo >&2 "checking GPG_TTY in set-environment"
grep 'export GPG_TTY=\$(tty)' ${config.system.build.setEnvironment}
echo >&2 "checking SSH support in set-environment"
grep "${gnupg}/bin/gpg-connect-agent --quiet updatestartuptty /bye" ${config.system.build.setEnvironment}
grep "${gnupg}/bin/gpgconf --list-dirs agent-ssh-socket" ${config.system.build.setEnvironment}
'';
}
+60
View File
@@ -0,0 +1,60 @@
{
config,
pkgs,
...
}:
let
mas =
pkgs.runCommand "mas-0.0.0" { } ''
mkdir -p $out/bin
cat > $out/bin/mas <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x $out/bin/mas
''
// {
meta.mainProgram = "mas";
};
in
{
system.primaryUser = "primary-mas-user";
programs.mas = {
enable = true;
user = "test-mas-user";
package = mas;
update = true;
cleanup = false;
packages = {
Xcode = 497799835;
};
};
homebrew.masApps = {
"KeepFromHomebrew" = 424242;
};
test = ''
echo "checking mas present in systemPackages" >&2
test -x ${config.out}/sw/bin/mas
echo "checking mas activation script still installs and updates apps" >&2
grep 'desiredIds=(' ${config.out}/activate
grep '497799835' ${config.out}/activate
grep 'mas install \"$appId\"' ${config.out}/activate
grep 'mas update' ${config.out}/activate
echo "checking cleanup-only variables are omitted" >&2
if grep 'declare -A installedApps' ${config.out}/activate; then
echo "unexpected installedApps declaration when cleanup is disabled" >&2
exit 1
fi
if grep 'keepIds=(' ${config.out}/activate; then
echo "unexpected keepIds declaration when cleanup is disabled" >&2
exit 1
fi
'';
}
+71
View File
@@ -0,0 +1,71 @@
{
config,
lib,
pkgs,
...
}:
let
mas =
pkgs.runCommand "mas-0.0.0" { } ''
mkdir -p $out/bin
cat > $out/bin/mas <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x $out/bin/mas
''
// {
meta.mainProgram = "mas";
};
in
{
system.primaryUser = "primary-mas-user";
programs.mas = {
enable = true;
user = "test-mas-user";
package = mas;
update = true;
cleanup = true;
packages = {
"1Password for Safari" = 1569813296;
Xcode = 497799835;
};
};
homebrew.masApps = {
"KeepFromHomebrew" = 424242;
};
test = ''
echo "checking mas present in systemPackages" >&2
test -x ${config.out}/sw/bin/mas
echo "checking mas activation script uses requested user" >&2
grep -- '--user=test-mas-user' ${config.out}/activate
echo "checking mas desired ids are present" >&2
grep 'desiredIds=(' ${config.out}/activate
grep '1569813296' ${config.out}/activate
grep '497799835' ${config.out}/activate
echo "checking mas install loop exists" >&2
grep 'mas install \"$appId\"' ${config.out}/activate
echo "checking mas update is triggered" >&2
grep 'mas update' ${config.out}/activate
echo "checking mas parses installedApps from mas list" >&2
grep 'declare -A installedApps' ${config.out}/activate
grep 'installedApps\[' ${config.out}/activate
echo "checking mas cleanup log and uninstall" >&2
grep 'removing .* from App Store' ${config.out}/activate
grep 'runAsUser .*/mas uninstall' ${config.out}/activate
echo "checking homebrew.masApps ids are kept during cleanup" >&2
grep 'homebrewIds=(' ${config.out}/activate
grep '424242' ${config.out}/activate
'';
}
+27 -2
View File
@@ -1,12 +1,37 @@
{ config, pkgs, ... }:
{ config, ... }:
{
services.openssh.enable = true;
services.openssh.extraConfig = ''
StreamLocalBindUnlink yes
'';
services.openssh.hostKeys = [
{ type = "ed25519"; path = "/etc/ssh/custom_host_ed25519_key"; comment = "my-host"; }
{ type = "rsa"; path = "/etc/ssh/custom_host_rsa_key"; bits = 4096; }
];
test = ''
echo >&2 "checking for StreamLocalBindUnlink in /etc/ssh/ssh_known_hosts"
echo >&2 "checking for StreamLocalBindUnlink in /etc/ssh/sshd_config.d/100-nix-darwin.conf"
grep 'StreamLocalBindUnlink yes' ${config.out}/etc/ssh/sshd_config.d/100-nix-darwin.conf
echo >&2 "checking for HostKey directives in /etc/ssh/sshd_config.d/099-host-keys.conf"
grep 'HostKey /etc/ssh/custom_host_ed25519_key' ${config.out}/etc/ssh/sshd_config.d/099-host-keys.conf
grep 'HostKey /etc/ssh/custom_host_rsa_key' ${config.out}/etc/ssh/sshd_config.d/099-host-keys.conf
echo >&2 "checking that default keys are absent from config"
(! grep 'HostKey /etc/ssh/ssh_host_rsa_key' ${config.out}/etc/ssh/sshd_config.d/099-host-keys.conf)
(! grep 'HostKey /etc/ssh/ssh_host_ecdsa_key' ${config.out}/etc/ssh/sshd_config.d/099-host-keys.conf)
(! grep 'HostKey /etc/ssh/ssh_host_ed25519_key' ${config.out}/etc/ssh/sshd_config.d/099-host-keys.conf)
echo >&2 "checking for ssh-keygen commands in activation script"
grep 'ssh-keygen.*keygenArgs' ${config.out}/activate
echo >&2 "checking for keygenArgs in activation script"
grep '\-t ed25519' ${config.out}/activate
grep '\-C my-host' ${config.out}/activate
grep '\-f /etc/ssh/custom_host_ed25519_key' ${config.out}/activate
grep '\-t rsa' ${config.out}/activate
grep '\-b 4096' ${config.out}/activate
grep '\-f /etc/ssh/custom_host_rsa_key' ${config.out}/activate
'';
}
+1
View File
@@ -115,6 +115,7 @@
system.defaults.screencapture.location = "/tmp";
system.defaults.screencapture.target = "file";
system.defaults.screencapture.include-date = true;
system.defaults.screencapture.save-selections = true;
system.defaults.screensaver.askForPassword = true;
system.defaults.screensaver.askForPasswordDelay = 5;
system.defaults.smb.NetBIOSName = "IMAC-000000";
+8
View File
@@ -5,20 +5,28 @@
system.keyboard.remapCapsLockToControl = true;
system.keyboard.remapCapsLockToEscape = true;
system.keyboard.nonUS.remapTilde = true;
system.keyboard.swapCapsLockAndEscape = true;
system.keyboard.swapLeftCommandAndLeftAlt = true;
system.keyboard.swapRightCommandAndRightOption = true;
system.keyboard.swapLeftCtrlAndFn = true;
test = ''
echo checking keyboard mappings in /activate >&2
grep "hidutil property --set '{\"UserKeyMapping\":.*}'" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771129" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771113" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771172" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771113" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771125" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771129" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771296" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771298" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771299" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771296" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771303" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingSrc\":30064771302" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771302" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":30064771303" ${config.out}/activate
grep "\"HIDKeyboardModifierMappingDst\":1095216660483" ${config.out}/activate
'';
}
+1 -1
View File
@@ -1,4 +1,4 @@
{
"release": "25.11",
"release": "26.11",
"isReleaseBranch": false
}