diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..92331afe --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# nixpkgs: format with `nixfmt` +dc1c716ded39758062ed7e6bc410ad274119de9f diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25211d30..d87b7636 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,67 +6,73 @@ on: - master env: - CURRENT_STABLE_CHANNEL: nixpkgs-24.05-darwin + NIXPKGS_BRANCH: nixpkgs-unstable + NIX_VERSION: 2.24.11 jobs: + # The `test-stable` and `install-against-stable` job names are + # load‐bearing, despite their inaccuracy on the unstable branch, as + # they are set as required checks in the repository configuration, + # which only repository admins can change. + # + # TODO: Change them once the repository configuration is updated. + test-stable: runs-on: macos-13 steps: - uses: actions/checkout@v4 - - name: Install nix corresponding to latest stable channel + - name: Install Nix uses: cachix/install-nix-action@v30 with: - install_url: https://releases.nixos.org/nix/nix-2.18.8/install - - run: nix flake check --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} - - test-unstable: - runs-on: macos-13 - steps: - - uses: actions/checkout@v4 - - name: Install nix from current unstable channel - uses: cachix/install-nix-action@v30 - with: - install_url: https://releases.nixos.org/nix/nix-2.24.9/install - - run: nix flake check --override-input nixpkgs nixpkgs/nixpkgs-unstable + install_url: https://releases.nixos.org/nix/nix-${{ env.NIX_VERSION }}/install + - run: nix flake check --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} install-against-stable: runs-on: macos-13 + timeout-minutes: 30 steps: - uses: actions/checkout@v4 - - name: Install nix corresponding to latest stable channel + - name: Install Nix uses: cachix/install-nix-action@v30 with: - install_url: https://releases.nixos.org/nix/nix-2.18.8/install - nix_path: nixpkgs=channel:${{ env.CURRENT_STABLE_CHANNEL }} + install_url: https://releases.nixos.org/nix/nix-${{ env.NIX_VERSION }}/install + nix_path: nixpkgs=channel:${{ env.NIXPKGS_BRANCH }} - name: Install channels run: | - nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin - nix-channel --add https://nixos.org/channels/${{ env.CURRENT_STABLE_CHANNEL }} nixpkgs + nix-channel --add https://nixos.org/channels/${{ env.NIXPKGS_BRANCH }} nixpkgs nix-channel --update - name: Install nix-darwin run: | - export NIX_PATH=$HOME/.nix-defexpr/channels - - mkdir -p ~/.config/nix-darwin - cp modules/examples/simple.nix ~/.config/nix-darwin/configuration.nix + sudo mkdir -p /etc/nix-darwin + sudo cp modules/examples/simple.nix /etc/nix-darwin/configuration.nix nixConfHash=$(shasum -a 256 /etc/nix/nix.conf | cut -d ' ' -f 1) - /usr/bin/sed -i.bak \ - "s/# programs.fish.enable = true;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \ - ~/.config/nix-darwin/configuration.nix + sudo /usr/bin/sed -i.bak \ + "s/# programs.fish.enable = true;/ \ + imports = [ \ + ({ options, ... }: { \ + nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; \ + environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ]; \ + nix.nixPath = \ + [ { darwin = \"${PWD////\/}\"; } ] \ + ++ options.nix.nixPath.default; \ + }) \ + ]; \ + /" \ + /etc/nix-darwin/configuration.nix - nix run .#darwin-rebuild \ - -- switch \ - -I darwin-config=$HOME/.config/nix-darwin/configuration.nix + nix run .#darwin-rebuild -- switch \ + -I darwin=. \ + -I darwin-config=/etc/nix-darwin/configuration.nix - name: Switch to new configuration run: | . /etc/bashrc - /usr/bin/sed -i.bak \ + sudo /usr/bin/sed -i.bak \ "s/pkgs.vim/pkgs.hello/" \ - ~/.config/nix-darwin/configuration.nix + /etc/nix-darwin/configuration.nix - darwin-rebuild switch -I darwin=. + darwin-rebuild switch hello - name: Test uninstallation of nix-darwin @@ -75,148 +81,54 @@ jobs: # `cachix/install-nix-action` but not by our default config above nix run .#darwin-uninstaller \ --extra-experimental-features "nix-command flakes" \ - --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} + --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} nix run .#darwin-uninstaller.tests.uninstaller \ --extra-experimental-features "nix-command flakes" \ - --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} + --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} - install-against-unstable: + install-flake: runs-on: macos-13 timeout-minutes: 30 steps: - uses: actions/checkout@v4 - - name: Install nix from current unstable channel + - name: Install Nix uses: cachix/install-nix-action@v30 with: - install_url: https://releases.nixos.org/nix/nix-2.24.9/install - nix_path: nixpkgs=channel:nixpkgs-unstable - - name: Install channels - run: | - nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin - nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs - nix-channel --update + install_url: https://releases.nixos.org/nix/nix-${{ env.NIX_VERSION }}/install - name: Install nix-darwin run: | - export NIX_PATH=$HOME/.nix-defexpr/channels - - mkdir -p ~/.config/nix-darwin - cp modules/examples/simple.nix ~/.config/nix-darwin/configuration.nix - - nixConfHash=$(shasum -a 256 /etc/nix/nix.conf | cut -d ' ' -f 1) - /usr/bin/sed -i.bak \ - "s/# programs.fish.enable = true;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \ - ~/.config/nix-darwin/configuration.nix - - nix run .#darwin-rebuild \ - -- switch \ - -I darwin-config=$HOME/.config/nix-darwin/configuration.nix - - name: Switch to new configuration - run: | - . /etc/bashrc - - /usr/bin/sed -i.bak \ - "s/pkgs.vim/pkgs.hello/" \ - ~/.config/nix-darwin/configuration.nix - - darwin-rebuild switch -I darwin=. - - hello - - name: Test uninstallation of nix-darwin - run: | - # We need to specify `--extra-experimental-features` because `experimental-features` is set by - # `cachix/install-nix-action` but not by our default config above - nix run .#darwin-uninstaller \ - --extra-experimental-features "nix-command flakes" \ - --override-input nixpkgs nixpkgs/nixpkgs-unstable - nix run .#darwin-uninstaller.tests.uninstaller \ - --extra-experimental-features "nix-command flakes" \ - --override-input nixpkgs nixpkgs/nixpkgs-unstable - - install-flake-against-stable: - runs-on: macos-13 - steps: - - uses: actions/checkout@v4 - - name: Install nix version corresponding to latest stable channel - uses: cachix/install-nix-action@v30 - with: - install_url: https://releases.nixos.org/nix/nix-2.18.8/install - - name: Install nix-darwin - run: | - mkdir -p ~/.config/nix-darwin + sudo mkdir -p /etc/nix-darwin darwin=$(pwd) - pushd ~/.config/nix-darwin - nix flake init -t $darwin + pushd /etc/nix-darwin + sudo nix flake init -t $darwin nixConfHash=$(shasum -a 256 /etc/nix/nix.conf | cut -d ' ' -f 1) - /usr/bin/sed -i.bak \ + sudo /usr/bin/sed -i.bak \ "s/# programs.fish.enable = true;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \ flake.nix - /usr/bin/sed -i.bak \ + sudo /usr/bin/sed -i.bak \ + 's/darwinConfigurations."simple"/darwinConfigurations."'$(scutil --get LocalHostName)'"/g' \ + flake.nix + sudo /usr/bin/sed -i.bak \ 's/nixpkgs.hostPlatform = "aarch64-darwin";/nixpkgs.hostPlatform = "'$(nix eval --expr builtins.currentSystem --impure --raw)'";/' \ flake.nix popd - nix run .#darwin-rebuild -- \ - switch --flake ~/.config/nix-darwin#simple \ + nix run .#darwin-rebuild -- switch \ --override-input nix-darwin . \ - --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} + --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} - name: Switch to new configuration run: | . /etc/bashrc - /usr/bin/sed -i.bak \ + sudo /usr/bin/sed -i.bak \ "s/pkgs.vim/pkgs.hello/" \ - ~/.config/nix-darwin/flake.nix + /etc/nix-darwin/flake.nix - darwin-rebuild switch --flake ~/.config/nix-darwin#simple \ + darwin-rebuild switch \ --override-input nix-darwin . \ - --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} + --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} hello - name: Test uninstallation of nix-darwin run: | - nix run .#darwin-uninstaller --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} - nix run .#darwin-uninstaller.tests.uninstaller --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} - - install-flake-against-unstable: - runs-on: macos-13 - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - name: Install nix from current unstable channel - uses: cachix/install-nix-action@v30 - with: - install_url: https://releases.nixos.org/nix/nix-2.24.9/install - - name: Install nix-darwin - run: | - mkdir -p ~/.config/nix-darwin - darwin=$(pwd) - pushd ~/.config/nix-darwin - nix flake init -t $darwin - nixConfHash=$(shasum -a 256 /etc/nix/nix.conf | cut -d ' ' -f 1) - /usr/bin/sed -i.bak \ - "s/# programs.fish.enable = true;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \ - flake.nix - /usr/bin/sed -i.bak \ - 's/nixpkgs.hostPlatform = "aarch64-darwin";/nixpkgs.hostPlatform = "'$(nix eval --expr builtins.currentSystem --impure --raw)'";/' \ - flake.nix - popd - nix run .#darwin-rebuild -- \ - switch --flake ~/.config/nix-darwin#simple \ - --override-input nix-darwin . \ - --override-input nixpkgs nixpkgs/nixpkgs-unstable - - name: Switch to new configuration - run: | - . /etc/bashrc - - /usr/bin/sed -i.bak \ - "s/pkgs.vim/pkgs.hello/" \ - ~/.config/nix-darwin/flake.nix - - darwin-rebuild switch --flake ~/.config/nix-darwin#simple \ - --override-input nix-darwin . \ - --override-input nixpkgs nixpkgs/nixpkgs-unstable - - hello - - name: Test uninstallation of nix-darwin - run: | - nix run .#darwin-uninstaller --override-input nixpkgs nixpkgs/nixpkgs-unstable - nix run .#darwin-uninstaller.tests.uninstaller --override-input nixpkgs nixpkgs/nixpkgs-unstable + nix run .#darwin-uninstaller --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} + nix run .#darwin-uninstaller.tests.uninstaller --override-input nixpkgs nixpkgs/${{ env.NIXPKGS_BRANCH }} diff --git a/CHANGELOG b/CHANGELOG index b9a9adcc..74591eff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,23 @@ +2025-01-29 +- There is now a `nix.enable` toggle to disable management of the Nix + installation. Nix installation management has been made more + opinionated as a consequence; nix-darwin now only supports managing a + multi‐user daemon installation of Nix, and unconditionally takes + ownership of the nix-daemon launchd daemon and the `_nixbld*` build + users when Nix installation management is enabled. + + If the new constraints do not work with your setup, you can disable + the `nix.enable` option to opt out of Nix installation management + entirely; see the option documentation for caveats. + +2025-01-18 +- The default configuration path for all new installations + is `/etc/nix-darwin`. This was already the undocumented + default for `darwin-rebuild switch` when using flakes. This + is implemented by setting `environment.darwinConfig` to + `"/etc/nix-darwin/configuration.nix"` by default when + `system.stateVersion` ≥ 6. + 2024-09-10 - The default Nix build user group ID is now set to 350 when `system.stateVersion` ≥ 5, to reflect the default for new Nix diff --git a/README.md b/README.md index d5373273..20447dec 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,19 @@ nix-darwin is built up around [Nixpkgs](https://github.com/NixOS/nixpkgs), quite ## Prerequisites -The only prerequisite is a Nix implementation, both Nix and Lix are supported. +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. **Make sure you use it without the `--determinate` flag**. The `--determinate` flag installs the Determinate Nix distribution which does not work out of the box with nix-darwin. +* 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 say `no` when prompted to install `Determinate Nix`. + + * When run with the `--determinate` 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-darwin’s 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. @@ -33,12 +41,18 @@ Despite being an experimental feature in Nix currently, nix-darwin recommends th Getting started from scratch

-If you don't have an existing `configuration.nix`, you can run the following commands to generate a basic `flake.nix` inside `~/.config/nix-darwin`: +If you don't have an existing `configuration.nix`, you can run the following commands to generate a basic `flake.nix` inside `/etc/nix-darwin`: ```bash -mkdir -p ~/.config/nix-darwin -cd ~/.config/nix-darwin -nix flake init -t nix-darwin +sudo mkdir -p /etc/nix-darwin +sudo chown $(id -nu):$(id -ng) /etc/nix-darwin +cd /etc/nix-darwin + +# To use Nixpkgs unstable: +nix flake init -t nix-darwin/master +# To use Nixpkgs 24.11: +nix flake init -t nix-darwin/nix-darwin-24.11 + sed -i '' "s/simple/$(scutil --get LocalHostName)/" flake.nix ``` @@ -57,8 +71,10 @@ Add the following to `flake.nix` in the same folder as `configuration.nix`: description = "John's darwin system"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-24.05-darwin"; - nix-darwin.url = "github:LnL7/nix-darwin"; + # Use `github:NixOS/nixpkgs/nixpkgs-24.11-darwin` to use Nixpkgs 24.11. + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + # Use `github:LnL7/nix-darwin/nix-darwin-24.11` to use Nixpkgs 24.11. + nix-darwin.url = "github:LnL7/nix-darwin/master"; nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; }; @@ -81,7 +97,10 @@ Make sure to set `nixpkgs.hostPlatform` in your `configuration.nix` to either `x Unlike NixOS, `nix-darwin` does not have an installer, you can just run `darwin-rebuild switch` to install nix-darwin. As `darwin-rebuild` won't be installed in your `PATH` yet, you can use the following command: ```bash -nix run nix-darwin -- switch --flake ~/.config/nix-darwin +# To use Nixpkgs unstable: +nix run nix-darwin/master#darwin-rebuild -- switch +# To use Nixpkgs 24.11: +nix run nix-darwin/nix-darwin-24.11#darwin-rebuild -- switch ``` ### Step 3. Using `nix-darwin` @@ -89,7 +108,7 @@ nix run nix-darwin -- switch --flake ~/.config/nix-darwin After installing, you can run `darwin-rebuild` to apply changes to your system: ```bash -darwin-rebuild switch --flake ~/.config/nix-darwin +darwin-rebuild switch ``` #### Using flake inputs @@ -117,13 +136,17 @@ nix-darwin.lib.darwinSystem { ### Step 1. Creating `configuration.nix` -Copy the [simple](./modules/examples/simple.nix) example to `~/.config/nix-darwin/configuration.nix`. +Copy the [simple](./modules/examples/simple.nix) example to `/etc/nix-darwin/configuration.nix`. ### Step 2. Adding `nix-darwin` channel ```bash -nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin -nix-channel --update +# If you use Nixpkgs unstable (the default): +sudo nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin +# If you use Nixpkgs 24.11: +sudo nix-channel --add https://github.com/LnL7/nix-darwin/archive/nix-darwin-24.11.tar.gz darwin + +sudo nix-channel --update ``` ### Step 3. Installing `nix-darwin` @@ -131,8 +154,8 @@ nix-channel --update To install `nix-darwin`, you can just run `darwin-rebuild switch` to install nix-darwin. As `darwin-rebuild` won't be installed in your `PATH` yet, you can use the following command: ```bash -nix-build https://github.com/LnL7/nix-darwin/archive/master.tar.gz -A darwin-rebuild -./result/bin/darwin-rebuild switch -I darwin-config=$HOME/.config/nix-darwin/configuration.nix +nix-build '' -A darwin-rebuild +./result/bin/darwin-rebuild switch -I darwin-config=/etc/nix-darwin/configuration.nix ``` ### Step 4. Using `nix-darwin` @@ -145,10 +168,10 @@ darwin-rebuild switch ### Step 5. Updating `nix-darwin` -You can update `nix-darwin` using the following command: +You can update Nixpkgs and `nix-darwin` using the following command: ```bash -nix-channel --update darwin +sudo nix-channel --update ``` diff --git a/doc/known-files/3bd68ef979a42070a44f8d82c205cfd8e8cca425d91253ec2c10a88179bb34aa b/doc/known-files/3bd68ef979a42070a44f8d82c205cfd8e8cca425d91253ec2c10a88179bb34aa new file mode 100644 index 00000000..dacf1689 --- /dev/null +++ b/doc/known-files/3bd68ef979a42070a44f8d82c205cfd8e8cca425d91253ec2c10a88179bb34aa @@ -0,0 +1,3 @@ +# Written by https://github.com/DeterminateSystems/nix-installer. +# The contents below are based on options specified at installation time. + diff --git a/doc/known-files/53712b4335030e2dbfb46bb235f8cffcac83fea404bd32dc99417ac89e2dd7c5 b/doc/known-files/53712b4335030e2dbfb46bb235f8cffcac83fea404bd32dc99417ac89e2dd7c5 new file mode 100644 index 00000000..44b308a0 --- /dev/null +++ b/doc/known-files/53712b4335030e2dbfb46bb235f8cffcac83fea404bd32dc99417ac89e2dd7c5 @@ -0,0 +1,14 @@ + +# Generated by https://github.com/DeterminateSystems/nix-installer. +# See `/nix/nix-installer --version` for the version details. + +!include nix.custom.conf + +experimental-features = nix-command flakes +always-allow-substitutes = true +extra-trusted-substituters = https://cache.flakehub.com +extra-trusted-public-keys = cache.flakehub.com-3:hJuILl5sVK4iKm86JzgdXW12Y2Hwd5G07qKtHTOcDCM= cache.flakehub.com-4:Asi8qIv291s0aYLyH6IOnr5Kf6+OF14WVjkE6t3xMio= cache.flakehub.com-5:zB96CRlL7tiPtzA9/WKyPkp3A2vqxqgdgyTVNGShPDU= cache.flakehub.com-6:W4EGFwAGgBj3he7c5fNh9NkOXw0PUVaxygCVKeuvaqU= cache.flakehub.com-7:mvxJ2DZVHn/kRxlIaxYNMuDG1OvMckZu32um1TadOR8= cache.flakehub.com-8:moO+OVS0mnTjBTcOUh2kYLQEd59ExzyoW1QgQ8XAARQ= cache.flakehub.com-9:wChaSeTI6TeCuV/Sg2513ZIM9i0qJaYsF+lZCXg0J6o= cache.flakehub.com-10:2GqeNlIp6AKp4EF2MVbE1kBOp9iBSyo0UPR9KoR0o1Y= +bash-prompt-prefix = (nix:$name)\040 +max-jobs = auto +extra-nix-path = nixpkgs=flake:nixpkgs +upgrade-nix-store-path-url = https://install.determinate.systems/nix-upgrade/stable/universal diff --git a/doc/known-files/6787fade1cf934f82db554e78e1fc788705c2c5257fddf9b59bdd963ca6fec63 b/doc/known-files/6787fade1cf934f82db554e78e1fc788705c2c5257fddf9b59bdd963ca6fec63 new file mode 100644 index 00000000..74a863d8 --- /dev/null +++ b/doc/known-files/6787fade1cf934f82db554e78e1fc788705c2c5257fddf9b59bdd963ca6fec63 @@ -0,0 +1,4 @@ + +# Written by https://github.com/DeterminateSystems/nix-installer. +# The contents below are based on options specified at installation time. + diff --git a/doc/known-files/6bb8d6b0dd16b44ee793a9b8382dac76c926e4c16ffb8ddd2bb4884d1ca3f811 b/doc/known-files/6bb8d6b0dd16b44ee793a9b8382dac76c926e4c16ffb8ddd2bb4884d1ca3f811 new file mode 100644 index 00000000..0973d2c6 --- /dev/null +++ b/doc/known-files/6bb8d6b0dd16b44ee793a9b8382dac76c926e4c16ffb8ddd2bb4884d1ca3f811 @@ -0,0 +1,13 @@ +# Generated by https://github.com/DeterminateSystems/nix-installer. +# See `/nix/nix-installer --version` for the version details. + +extra-experimental-features = nix-command flakes +always-allow-substitutes = true +extra-trusted-substituters = https://cache.flakehub.com +extra-trusted-public-keys = cache.flakehub.com-3:hJuILl5sVK4iKm86JzgdXW12Y2Hwd5G07qKtHTOcDCM= cache.flakehub.com-4:Asi8qIv291s0aYLyH6IOnr5Kf6+OF14WVjkE6t3xMio= cache.flakehub.com-5:zB96CRlL7tiPtzA9/WKyPkp3A2vqxqgdgyTVNGShPDU= cache.flakehub.com-6:W4EGFwAGgBj3he7c5fNh9NkOXw0PUVaxygCVKeuvaqU= cache.flakehub.com-7:mvxJ2DZVHn/kRxlIaxYNMuDG1OvMckZu32um1TadOR8= cache.flakehub.com-8:moO+OVS0mnTjBTcOUh2kYLQEd59ExzyoW1QgQ8XAARQ= cache.flakehub.com-9:wChaSeTI6TeCuV/Sg2513ZIM9i0qJaYsF+lZCXg0J6o= cache.flakehub.com-10:2GqeNlIp6AKp4EF2MVbE1kBOp9iBSyo0UPR9KoR0o1Y= +bash-prompt-prefix = (nix:$name)\040 +max-jobs = auto +extra-nix-path = nixpkgs=flake:nixpkgs +upgrade-nix-store-path-url = https://install.determinate.systems/nix-upgrade/stable/universal + +!include nix.custom.conf diff --git a/doc/manual/default.nix b/doc/manual/default.nix index d8e12f29..ef4b64d5 100644 --- a/doc/manual/default.nix +++ b/doc/manual/default.nix @@ -53,7 +53,7 @@ in rec { substitute \ ${optionsDoc.optionsJSON}/nix-support/hydra-build-products \ $out/nix-support/hydra-build-products \ - --replace \ + --replace-fail \ '${optionsDoc.optionsJSON}/share/doc/nixos' \ "$out/share/doc/darwin" ''; @@ -74,8 +74,8 @@ in rec { cp -r ${pkgs.documentation-highlighter} $dst/highlightjs substitute ${./manual.md} manual.md \ - --replace '@DARWIN_VERSION@' "${version}" \ - --replace \ + --replace-fail '@DARWIN_VERSION@' "${version}" \ + --replace-fail \ '@DARWIN_OPTIONS_JSON@' \ ${optionsJSON}/share/doc/darwin/options.json diff --git a/eval-config.nix b/eval-config.nix index cd14493d..7c6d48ff 100644 --- a/eval-config.nix +++ b/eval-config.nix @@ -1,10 +1,72 @@ +let + nixDarwinVersion = builtins.fromJSON (builtins.readFile ./version.json); + + checkRelease = lib: + # Avoid breaking configurations when the unstable Nixpkgs version + # rolls over. + # + # TODO: Something more refined than this would be ideal, as this + # still means you could be using unstable nix-darwin 25.05 with + # Nixpkgs 26.05, which would be unfortunate. + if nixDarwinVersion.isReleaseBranch then + lib.trivial.release == nixDarwinVersion.release + else + lib.versionAtLeast lib.trivial.release nixDarwinVersion.release; +in + { lib , modules , baseModules ? import ./modules/module-list.nix , specialArgs ? { } , check ? true +, enableNixpkgsReleaseCheck ? true }@args: +assert enableNixpkgsReleaseCheck -> checkRelease lib || throw '' + + nix-darwin now uses release branches that correspond to Nixpkgs releases. + The nix-darwin and Nixpkgs branches in use must match, but you are currently + using nix-darwin ${nixDarwinVersion.release} with Nixpkgs ${lib.trivial.release}. + + On macOS, you should use either the `nixpkgs-unstable` or + `nixpkgs-YY.MM-darwin` branches of Nixpkgs. These correspond to the + `master` and `nix-darwin-YY.MM` branches of nix-darwin, respectively. Check + for the currently supported Nixpkgs releases. + + If you’re using flakes, make sure your inputs look like this: + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/NIXPKGS-BRANCH"; + nix-darwin.url = "github:LnL7/nix-darwin/NIX-DARWIN-BRANCH"; + nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; + # … + }; + + If you’re using channels, you can check your current channels with: + + $ sudo nix-channel --list + nixpkgs https://nixos.org/channels/NIXPKGS-BRANCH + darwin https://github.com/LnL7/nix-darwin/archive/NIX-DARWIN-BRANCH.tar.gz + … + $ nix-channel --list + … + + If `darwin` or `nixpkgs` are present in `nix-channel --list` (without + `sudo`), you should delete them with `nix-channel --remove NAME`. These can + contribute to version mismatch problems. + + You can then fix your channels like this: + + $ sudo nix-channel --add https://nixos.org/channels/NIXPKGS-BRANCH nixpkgs + $ sudo nix-channel --add https://github.com/LnL7/nix-darwin/archive/NIX-DARWIN-BRANCH.tar.gz darwin + $ sudo nix-channel --update + + After that, activating your system again should work correctly. If it + doesn’t, please open an issue at + and include as much + information as possible. +''; + let argsModule = { _file = ./eval-config.nix; @@ -15,7 +77,7 @@ let }; }; - eval = lib.evalModules (builtins.removeAttrs args [ "lib" ] // { + eval = lib.evalModules (builtins.removeAttrs args [ "lib" "enableNixpkgsReleaseCheck" ] // { class = "darwin"; modules = modules ++ [ argsModule ] ++ baseModules; specialArgs = { modulesPath = builtins.toString ./modules; } // specialArgs; diff --git a/flake.lock b/flake.lock index 3ce8eac6..0f05e93e 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,18 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1718149104, - "narHash": "sha256-Ds1QpobBX2yoUDx9ZruqVGJ/uQPgcXoYuobBguyKEh8=", + "lastModified": 1736241350, + "narHash": "sha256-CHd7yhaDigUuJyDeX0SADbTM9FXfiWaeNyY34FL1wQU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e913ae340076bbb73d9f4d3d065c2bca7caafb16", + "rev": "8c9fd3e564728e90829ee7dbac6edc972971cd0f", "type": "github" }, "original": { - "id": "nixpkgs", - "type": "indirect" + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" } }, "root": { diff --git a/flake.nix b/flake.nix index 2c1ae1b2..92569fa9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,10 @@ { description = "A collection of darwin modules"; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + outputs = { self, nixpkgs }: let forAllSystems = nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ]; forDarwinSystems = nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" ]; diff --git a/modules/environment/default.nix b/modules/environment/default.nix index fc4db2ab..377a9594 100644 --- a/modules/environment/default.nix +++ b/modules/environment/default.nix @@ -67,8 +67,24 @@ in }; environment.darwinConfig = mkOption { - type = types.either types.path types.str; - default = "$HOME/.nixpkgs/darwin-configuration.nix"; + type = types.nullOr (types.either types.path types.str); + default = + if config.nixpkgs.flake.setNixPath then + # Don’t set this for flake‐based systems. + null + else if config.system.stateVersion >= 6 then + "/etc/nix-darwin/configuration.nix" + else + "$HOME/.nixpkgs/darwin-configuration.nix"; + defaultText = literalExpression '' + if config.nixpkgs.flake.setNixPath then + # Don’t set this for flake‐based systems. + null + else if config.system.stateVersion >= 6 then + "/etc/nix-darwin/configuration.nix" + else + "$HOME/.nixpkgs/darwin-configuration.nix" + ''; description = '' The path of the darwin configuration.nix used to configure the system, this updates the default darwin-config entry in NIX_PATH. Since this @@ -161,7 +177,7 @@ in environment.systemPath = mkMerge [ [ (makeBinPath cfg.profiles) ] - (mkOrder 1200 [ "/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin" ]) + (mkOrder 1200 [ "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" ]) ]; # Use user, default and system profiles. diff --git a/modules/examples/flake/flake.nix b/modules/examples/flake/flake.nix index 4520b8ff..447c468a 100644 --- a/modules/examples/flake/flake.nix +++ b/modules/examples/flake/flake.nix @@ -3,7 +3,7 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - nix-darwin.url = "github:LnL7/nix-darwin"; + nix-darwin.url = "github:LnL7/nix-darwin/master"; nix-darwin.inputs.nixpkgs.follows = "nixpkgs"; }; @@ -27,7 +27,7 @@ # Used for backwards compatibility, please read the changelog before changing. # $ darwin-rebuild changelog - system.stateVersion = 5; + system.stateVersion = 6; # The platform the configuration will be used on. nixpkgs.hostPlatform = "aarch64-darwin"; diff --git a/modules/examples/hydra.nix b/modules/examples/hydra.nix index f87ed5d0..d30f5c00 100644 --- a/modules/examples/hydra.nix +++ b/modules/examples/hydra.nix @@ -43,5 +43,5 @@ in echo "ok" ''; - system.stateVersion = 5; + system.stateVersion = 6; } diff --git a/modules/examples/lnl.nix b/modules/examples/lnl.nix index 2204c2fa..8dff10cc 100644 --- a/modules/examples/lnl.nix +++ b/modules/examples/lnl.nix @@ -199,7 +199,7 @@ programs.zsh.enableFzfGit = true; programs.zsh.enableFzfHistory = true; - programs.zsh.variables.cfg = "$HOME/.config/nixpkgs/darwin/configuration.nix"; + programs.zsh.variables.cfg = "/etc/nix-darwin/configuration.nix"; programs.zsh.variables.darwin = "$HOME/.nix-defexpr/darwin"; programs.zsh.variables.nixpkgs = "$HOME/.nix-defexpr/nixpkgs"; @@ -319,8 +319,7 @@ # path = /etc/per-user/lnl/gitconfig # environment.etc."per-user/lnl/gitconfig".text = builtins.readFile "${inputs.dotfiles}/git/gitconfig"; - nix.configureBuildUsers = true; nix.nrBuildUsers = 32; - system.stateVersion = 5; + system.stateVersion = 6; } diff --git a/modules/examples/simple.nix b/modules/examples/simple.nix index 5771ec60..7751fb29 100644 --- a/modules/examples/simple.nix +++ b/modules/examples/simple.nix @@ -7,13 +7,10 @@ [ pkgs.vim ]; - # Use custom location for configuration.nix. - environment.darwinConfig = "$HOME/.config/nix-darwin/configuration.nix"; - # Enable alternative shell support in nix-darwin. # programs.fish.enable = true; # Used for backwards compatibility, please read the changelog before changing. # $ darwin-rebuild changelog - system.stateVersion = 5; + system.stateVersion = 6; } diff --git a/modules/homebrew.nix b/modules/homebrew.nix index 7aee9e13..10764fbb 100644 --- a/modules/homebrew.nix +++ b/modules/homebrew.nix @@ -137,7 +137,7 @@ let config = { brewBundleCmd = concatStringsSep " " ( optional (!config.autoUpdate) "HOMEBREW_NO_AUTO_UPDATE=1" - ++ [ "brew bundle --file='${brewfileFile}' --no-lock" ] + ++ [ "brew bundle --file='${brewfileFile}'" ] ++ optional (!config.upgrade) "--no-upgrade" ++ optional (config.cleanup == "uninstall") "--cleanup" ++ optional (config.cleanup == "zap") "--cleanup --zap" @@ -396,6 +396,9 @@ let no_binaries = mkNullOrBoolOption { description = "Whether to disable linking of helper executables."; }; + ignore_dependencies = mkNullOrBoolOption { + description = "Ignore casks dependencies in case you manage them extrenally"; + }; brewfileLine = mkInternalOption { type = types.nullOr types.str; }; }; @@ -705,9 +708,6 @@ in description = '' Applications to install from Mac App Store using {command}`mas`. - When this option is used, `"mas"` is automatically added to - [](#opt-homebrew.brews). - Note that you need to be signed into the Mac App Store for {command}`mas` to successfully install and upgrade applications, and that unfortunately apps removed from this option will not be uninstalled automatically even if @@ -765,8 +765,7 @@ in ]; homebrew.brews = - optional (cfg.masApps != { }) "mas" - ++ optional (cfg.whalebrews != [ ]) "whalebrew"; + optional (cfg.whalebrews != [ ]) "whalebrew"; homebrew.brewfile = "# Created by `nix-darwin`'s `homebrew` module\n\n" @@ -786,7 +785,8 @@ in # Homebrew Bundle echo >&2 "Homebrew bundle..." if [ -f "${cfg.brewPrefix}/brew" ]; then - PATH="${cfg.brewPrefix}":$PATH ${cfg.onActivation.brewBundleCmd} + PATH="${cfg.brewPrefix}:${lib.makeBinPath [ pkgs.mas ]}:$PATH" \ + ${cfg.onActivation.brewBundleCmd} else echo -e "\e[1;31merror: Homebrew is not installed, skipping...\e[0m" >&2 fi diff --git a/modules/misc/ids.nix b/modules/misc/ids.nix index 34b36859..ddab290a 100644 --- a/modules/misc/ids.nix +++ b/modules/misc/ids.nix @@ -39,11 +39,13 @@ in ids.uids = { nixbld = lib.mkDefault 350; _prometheus-node-exporter = 534; + _dnscrypt-proxy = 535; }; ids.gids = { nixbld = lib.mkDefault (if config.system.stateVersion < 5 then 30000 else 350); _prometheus-node-exporter = 534; + _dnscrypt-proxy = 535; }; }; diff --git a/modules/module-list.nix b/modules/module-list.nix index aa190c7d..d01bbdb9 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -62,6 +62,7 @@ ./services/chunkwm.nix ./services/cachix-agent.nix ./services/dnsmasq.nix + ./services/dnscrypt-proxy.nix ./services/emacs.nix ./services/eternal-terminal.nix ./services/github-runner @@ -82,6 +83,7 @@ ./services/nix-gc ./services/nix-optimise ./services/ofborg + ./services/openssh.nix ./services/postgresql ./services/privoxy ./services/redis diff --git a/modules/networking/default.nix b/modules/networking/default.nix index 7a81ca1c..8097e276 100644 --- a/modules/networking/default.nix +++ b/modules/networking/default.nix @@ -118,7 +118,6 @@ in echo "configuring networking..." >&2 ${optionalString (cfg.computerName != null) '' - # shellcheck disable=SC1112 scutil --set ComputerName ${escapeShellArg cfg.computerName} ''} ${optionalString (cfg.hostName != null) '' @@ -133,6 +132,11 @@ in ${optionalString (cfg.wakeOnLan.enable != null) '' systemsetup -setWakeOnNetworkAccess '${onOff cfg.wakeOnLan.enable}' &> /dev/null ''} + + if [ -e /etc/hosts.before-nix-darwin ]; then + echo "restoring /etc/hosts..." >&2 + sudo mv /etc/hosts{.before-nix-darwin,} + fi ''; }; diff --git a/modules/nix/default.nix b/modules/nix/default.nix index 09e6e50d..e5d0801d 100644 --- a/modules/nix/default.nix +++ b/modules/nix/default.nix @@ -12,6 +12,8 @@ let isNixAtLeast = versionAtLeast (getVersion nixPackage); + configureBuildUsers = !(config.nix.settings.auto-allocate-uids or false); + makeNixBuildUser = nr: { name = "_nixbld${toString nr}"; value = { @@ -49,13 +51,16 @@ let mkKeyValuePairs = attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs); + isExtra = key: hasPrefix "extra-" key; + in pkgs.writeTextFile { name = "nix.conf"; text = '' # WARNING: this file is generated from the nix.* options in # your nix-darwin configuration. Do not edit it! - ${mkKeyValuePairs cfg.settings} + ${mkKeyValuePairs (filterAttrs (key: value: !(isExtra key)) cfg.settings)} + ${mkKeyValuePairs (filterAttrs (key: value: isExtra key) cfg.settings)} ${cfg.extraOptions} ''; checkPhase = @@ -134,6 +139,34 @@ let namedPaths ++ searchPaths; }; + handleUnmanaged = managedConfig: mkMerge [ + (mkIf cfg.enable managedConfig) + (mkIf (!cfg.enable) { + system.activationScripts.nix-daemon.text = '' + # Restore unmanaged Nix daemon if present + unmanagedNixProfile=/nix/var/nix/profiles/default + if [[ + -e /run/current-system/Library/LaunchDaemons/org.nixos.nix-daemon.plist + && -e $unmanagedNixProfile/Library/LaunchDaemons/org.nixos.nix-daemon.plist + ]]; then + printf >&2 'restoring unmanaged Nix daemon...\n' + cp \ + "$unmanagedNixProfile/Library/LaunchDaemons/org.nixos.nix-daemon.plist" \ + /Library/LaunchDaemons + launchctl load -w /Library/LaunchDaemons/org.nixos.nix-daemon.plist + fi + ''; + }) + ]; + + managedDefault = name: default: { + default = if cfg.enable then default else throw '' + ${name}: accessed when `nix.enable` is off; this is a bug in + nix-darwin or a third‐party module + ''; + defaultText = default; + }; + in { @@ -144,7 +177,6 @@ in in [ # Only ever in NixOS - (mkRemovedOptionModule [ "nix" "enable" ] "No `nix-darwin` equivalent to this NixOS option.") (mkRemovedOptionModule [ "nix" "daemonCPUSchedPolicy" ] (altOption "nix.daemonProcessType")) (mkRemovedOptionModule [ "nix" "daemonIOSchedClass" ] (altOption "nix.daemonProcessType")) (mkRemovedOptionModule [ "nix" "daemonIOSchedPriority" ] (altOption "nix.daemonIOLowPriority")) @@ -157,6 +189,14 @@ in (mkRenamedOptionModule [ "users" "nix" "nrBuildUsers" ] [ "nix" "nrBuildUsers" ]) (mkRenamedOptionModule [ "nix" "daemonIONice" ] [ "nix" "daemonIOLowPriority" ]) (mkRemovedOptionModule [ "nix" "daemonNiceLevel" ] (consider "nix.daemonProcessType")) + (mkRemovedOptionModule [ "nix" "useDaemon" ] '' + nix-darwin now only supports managing multi‐user daemon + installations of Nix. + '') + (mkRemovedOptionModule [ "nix" "configureBuildUsers" ] '' + nix-darwin now manages build users unconditionally when + `nix.enable` is on. + '') ] ++ mapAttrsToList (oldConf: newConf: mkRenamedOptionModule [ "nix" oldConf ] [ "nix" "settings" newConf ]) legacyConfMappings; ###### interface @@ -165,29 +205,43 @@ in nix = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to enable Nix. + + Disabling this will stop nix-darwin from managing the + installed version of Nix, the nix-daemon launchd daemon, and + the settings in {file}`/etc/nix/nix.conf`. + + This allows you to use nix-darwin without it taking over your + system installation of Nix. 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. You will also have to upgrade + Nix yourself, as nix-darwin will no longer do so. + + ::: {.warning} + If you have already removed your global system installation + of Nix, this will break nix-darwin and you will have to + reinstall Nix to fix it. + ::: + ''; + }; + package = mkOption { type = types.package; - default = pkgs.nix; + inherit (managedDefault "nix.package" pkgs.nix) default; defaultText = literalExpression "pkgs.nix"; description = '' This option specifies the Nix package instance to use throughout the system. ''; }; - # Not in NixOS module - useDaemon = mkOption { - type = types.bool; - default = false; - description = '' - If set, Nix will use the daemon to perform operations. - Use this instead of services.nix-daemon.enable if you don't want the - daemon service to be managed for you. - ''; - }; - distributedBuilds = mkOption { type = types.bool; - default = false; + inherit (managedDefault "nix.distributedBuilds" false) default defaultText; description = '' Whether to distribute builds to the machines listed in {option}`nix.buildMachines`. @@ -197,7 +251,7 @@ in # Not in NixOS module daemonProcessType = mkOption { type = types.enum [ "Background" "Standard" "Adaptive" "Interactive" ]; - default = "Standard"; + inherit (managedDefault "nix.daemonProcessType" "Standard") default defaultText; description = '' Nix daemon process resource limits class. These limits propagate to build processes. `Standard` is the default process type @@ -212,7 +266,7 @@ in # Not in NixOS module daemonIOLowPriority = mkOption { type = types.bool; - default = false; + inherit (managedDefault "nix.daemonIOLowPriority" false) default defaultText; description = '' Whether the Nix daemon process should considered to be low priority when doing file system I/O. @@ -340,7 +394,7 @@ in }; }; }); - default = [ ]; + inherit (managedDefault "nix.buildMachines" [ ]) default defaultText; description = '' This option lists the machines to be used if distributed builds are enabled (see {option}`nix.distributedBuilds`). @@ -354,21 +408,13 @@ in envVars = mkOption { type = types.attrs; internal = true; - default = { }; + inherit (managedDefault "nix.envVars" { }) default defaultText; description = "Environment variables used by Nix."; }; - # Not in NixOS module - configureBuildUsers = mkOption { - type = types.bool; - default = false; - description = '' - Enable configuration for nixbld group and users. - ''; - }; - nrBuildUsers = mkOption { type = types.int; + inherit (managedDefault "nix.nrBuildUsers" 0) default defaultText; description = '' Number of `nixbld` user accounts created to perform secure concurrent builds. If you receive an error @@ -396,11 +442,13 @@ in # Definition differs substantially from NixOS module nixPath = mkOption { type = nixPathType; - default = lib.optionals cfg.channel.enable [ - # Include default path . - { darwin-config = "${config.environment.darwinConfig}"; } - "/nix/var/nix/profiles/per-user/root/channels" - ]; + inherit (managedDefault "nix.nixPath" ( + lib.optionals cfg.channel.enable [ + # Include default path . + { darwin-config = "${config.environment.darwinConfig}"; } + "/nix/var/nix/profiles/per-user/root/channels" + ] + )) default; defaultText = lib.literalExpression '' lib.optionals cfg.channel.enable [ @@ -422,7 +470,7 @@ in checkConfig = mkOption { type = types.bool; - default = true; + inherit (managedDefault "nix.checkConfig" true) default defaultText; description = '' If enabled (the default), checks for data type mismatches and that Nix can parse the generated nix.conf. @@ -483,7 +531,7 @@ in }; } )); - default = { }; + inherit (managedDefault "nix.registry" { }) default defaultText; description = '' A system-wide flake registry. ''; @@ -491,7 +539,7 @@ in extraOptions = mkOption { type = types.lines; - default = ""; + inherit (managedDefault "nix.extraOptions" "") default defaultText; example = '' keep-outputs = true keep-derivations = true @@ -617,7 +665,6 @@ in trusted-users = mkOption { type = types.listOf types.str; - default = [ "root" ]; example = [ "root" "alice" "@admin" ]; description = '' A list of names of users that have additional rights when @@ -661,7 +708,7 @@ in }; }; }; - default = { }; + inherit (managedDefault "nix.settings" { }) default defaultText; description = '' Configuration for Nix, see @@ -679,7 +726,7 @@ in ###### implementation - config = { + config = handleUnmanaged { environment.systemPackages = [ nixPackage @@ -703,6 +750,8 @@ in "5d23e6d7015756c6f300f8cd558ec4d9234ca61deefd4f2478e91a49760b0747" # DeterminateSystems Nix installer 0.16.0 "e4974acb79c56148cb8e92137fa4f2de9b7356e897b332fc4e6769e8c0b83e18" # DeterminateSystems Nix installer 0.20.0 "966d22ef5bb9b56d481e8e0d5f7ca2deaf4d24c0f0fc969b2eeaa7ae0aa42907" # DeterminateSystems Nix installer 0.22.0 + "53712b4335030e2dbfb46bb235f8cffcac83fea404bd32dc99417ac89e2dd7c5" # DeterminateSystems Nix installer 0.33.0 + "6bb8d6b0dd16b44ee793a9b8382dac76c926e4c16ffb8ddd2bb4884d1ca3f811" # DeterminateSystems Nix installer 0.34.0 "24797ac05542ff8b52910efc77870faa5f9e3275097227ea4e50c430a5f72916" # lix-installer 0.17.1 with flakes "b027b5cad320b5b8123d9d0db9f815c3f3921596c26dc3c471457098e4d3cc40" # lix-installer 0.17.1 without flakes ]; @@ -758,13 +807,13 @@ in # Not in NixOS module { assertion = elem "nixbld" config.users.knownGroups -> elem "nixbld" createdGroups; message = "refusing to delete group nixbld in users.knownGroups, this would break nix"; } - { assertion = elem "_nixbld1" config.users.knownGroups -> elem "_nixbld1" createdUsers; message = "refusing to delete user _nixbld1 in users.knownUsers, this would break nix"; } + { assertion = elem "_nixbld1" config.users.knownUsers -> elem "_nixbld1" createdUsers; message = "refusing to delete user _nixbld1 in users.knownUsers, this would break nix"; } { 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 - # As `isNixAtLeast "2.92.0" "2.92.0-devpre20241107" == false`, we need to explicitly check if the user is running Lix 2.92.0 - assertion = cfg.settings.auto-optimise-store -> (cfg.package.pname == "lix" && (isNixAtLeast "2.92.0-devpre20241107" || cfg.package.version == "2.92.0")); + # 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")); message = "`nix.settings.auto-optimise-store` is known to corrupt the Nix Store, please use `nix.optimise.automatic` instead."; } ]; @@ -784,21 +833,11 @@ in # Set up the environment variables for running Nix. environment.variables = cfg.envVars // { NIX_PATH = cfg.nixPath; }; - environment.extraInit = mkMerge [ - (mkIf cfg.channel.enable '' - if [ -e "$HOME/.nix-defexpr/channels" ]; then - export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}" - fi - '') - # Not in NixOS module - '' - # Set up secure multi-user builds: non-root users build through the - # Nix daemon. - if [ ! -w /nix/var/nix/db ]; then - export NIX_REMOTE=daemon - fi - '' - ]; + environment.extraInit = mkIf cfg.channel.enable '' + if [ -e "$HOME/.nix-defexpr/channels" ]; then + export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}" + fi + ''; environment.extraSetup = mkIf (!cfg.channel.enable) '' rm --force $out/bin/nix-channel @@ -806,10 +845,10 @@ in nix.nrBuildUsers = mkDefault (max 32 (if cfg.settings.max-jobs == "auto" then 0 else cfg.settings.max-jobs)); - users.users = mkIf cfg.configureBuildUsers nixbldUsers; + users.users = mkIf configureBuildUsers nixbldUsers; # Not in NixOS module - users.groups.nixbld = mkIf cfg.configureBuildUsers { + users.groups.nixbld = mkIf configureBuildUsers { description = "Nix build group for nix-daemon"; gid = config.ids.gids.nixbld; members = attrNames nixbldUsers; @@ -817,14 +856,62 @@ in users.knownUsers = let nixbldUserNames = attrNames nixbldUsers; in - mkIf cfg.configureBuildUsers (mkMerge [ + mkMerge [ nixbldUserNames (map (removePrefix "_") nixbldUserNames) # delete old style nixbld users - ]); - users.knownGroups = mkIf cfg.configureBuildUsers [ "nixbld" ]; + ]; + users.knownGroups = [ "nixbld" ]; + + # The Determinate Systems installer puts user‐specified settings in + # `/etc/nix/nix.custom.conf` since v0.33.0. Supplement the + # `/etc/nix/nix.conf` hash check so that we don’t accidentally + # clobber user configuration. + # + # TODO: Maybe this could use a more general file placement mechanism + # to express that we want it deleted and know only one hash? + system.activationScripts.etcChecks.text = mkAfter '' + nixCustomConfKnownSha256Hashes=( + # v0.33.0 + 6787fade1cf934f82db554e78e1fc788705c2c5257fddf9b59bdd963ca6fec63 + # v0.34.0 + 3bd68ef979a42070a44f8d82c205cfd8e8cca425d91253ec2c10a88179bb34aa + ) + if [[ -e /etc/nix/nix.custom.conf ]]; then + nixCustomConfSha256Output=$(shasum -a 256 /etc/nix/nix.custom.conf) + nixCustomConfSha256Hash=''${nixCustomConfSha256Output%% *} + nixCustomConfIsKnown= + for nixCustomConfKnownSha256Hash + in "''${nixCustomConfKnownSha256Hashes[@]}" + do + if + [[ $nixCustomConfSha256Hash == "$nixCustomConfKnownSha256Hash" ]] + then + nixCustomConfIsKnown=1 + break + fi + done + if [[ ! $nixCustomConfIsKnown ]]; then + printf >&2 '\e[1;31merror: custom settings in `/etc/nix/nix.custom.conf`, aborting activation\e[0m\n' + printf >&2 'You will need to migrate these to nix-darwin `nix.*` settings if you\n' + printf >&2 'wish to keep them. Check the manual for the appropriate settings and\n' + printf >&2 'add them to your system configuration, then run:\n' + printf >&2 '\n' + printf >&2 ' $ sudo mv /etc/nix/nix.custom.conf{,.before-nix-darwin}\n' + printf >&2 '\n' + printf >&2 'and activate your system again.\n' + exit 2 + fi + fi + ''; # Unrelated to use in NixOS module - system.activationScripts.nix-daemon.text = mkIf cfg.useDaemon '' + system.activationScripts.nix-daemon.text = '' + # Follow up on the `/etc/nix/nix.custom.conf` check. + # TODO: Use a more generalized file placement mechanism for this. + if [[ -e /etc/nix/nix.custom.conf ]]; then + mv /etc/nix/nix.custom.conf{,.before-nix-darwin} + fi + if ! diff /etc/nix/nix.conf /run/current-system/etc/nix/nix.conf &> /dev/null || ! diff /etc/nix/machines /run/current-system/etc/nix/machines &> /dev/null; then echo "reloading nix-daemon..." >&2 launchctl kill HUP system/org.nixos.nix-daemon @@ -835,12 +922,15 @@ in done ''; - # Legacy configuration conversion. nix.settings = mkMerge [ { trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ]; + trusted-users = [ "root" ]; substituters = mkAfter [ "https://cache.nixos.org/" ]; + # Not in NixOS module + build-users-group = "nixbld"; + # Not implemented yet # system-features = mkDefault ( # [ "nixos-test" "benchmark" "big-parallel" "kvm" ] ++ @@ -856,8 +946,6 @@ in (mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; }) - # Not in NixOS module - (mkIf cfg.useDaemon { build-users-group = "nixbld"; }) ]; }; diff --git a/modules/nix/linux-builder.nix b/modules/nix/linux-builder.nix index 2bcb62ea..36a41310 100644 --- a/modules/nix/linux-builder.nix +++ b/modules/nix/linux-builder.nix @@ -61,9 +61,16 @@ in maxJobs = mkOption { type = types.ints.positive; - default = 1; - example = 4; + default = cfg.package.nixosConfig.virtualisation.cores; + defaultText = '' + The `virtualisation.cores` of the build machine's final NixOS configuration. + ''; + example = 2; description = '' + Instead of setting this directly, you should set + {option}`nix.linux-builder.config.virtualisation.cores` to configure + the amount of cores the Linux builder should have. + The number of concurrent jobs the Linux builder machine supports. The build machine will enforce its own limits, but this allows hydra to schedule better since there is no work-stealing between build @@ -153,6 +160,13 @@ in }; config = mkIf cfg.enable { + assertions = [ + { + assertion = config.nix.enable; + message = ''`nix.linux-builder.enable` requires `nix.enable`''; + } + ]; + system.activationScripts.preActivation.text = '' mkdir -p ${cfg.workingDirectory} ''; diff --git a/modules/nix/nix-darwin.nix b/modules/nix/nix-darwin.nix index 4a989d79..9c78f3a6 100644 --- a/modules/nix/nix-darwin.nix +++ b/modules/nix/nix-darwin.nix @@ -4,7 +4,7 @@ let nix-tools = pkgs.callPackage ../../pkgs/nix-tools { inherit (config.system) profile; inherit (config.environment) systemPath; - nixPackage = config.nix.package; + nixPath = lib.optionalString config.nix.enable (lib.concatStringsSep ":" config.nix.nixPath); }; darwin-uninstaller = pkgs.callPackage ../../pkgs/darwin-uninstaller { }; diff --git a/modules/nix/nixpkgs-flake.nix b/modules/nix/nixpkgs-flake.nix index bb7c1b0a..bc00c78d 100644 --- a/modules/nix/nixpkgs-flake.nix +++ b/modules/nix/nixpkgs-flake.nix @@ -37,8 +37,8 @@ in setNixPath = mkOption { type = types.bool; - default = cfg.source != null; - defaultText = "config.nixpkgs.flake.source != null"; + default = config.nix.enable && cfg.source != null; + defaultText = literalExpression ''config.nix.enable && nixpkgs.flake.source != null''; description = '' Whether to set {env}`NIX_PATH` to include `nixpkgs=flake:nixpkgs` such that `` @@ -57,8 +57,8 @@ in setFlakeRegistry = mkOption { type = types.bool; - default = cfg.source != null; - defaultText = "config.nixpkgs.flake.source != null"; + default = config.nix.enable && cfg.source != null; + defaultText = literalExpression ''config.nix.enable && config.nixpkgs.flake.source != null''; description = '' Whether to pin nixpkgs in the system-wide flake registry (`/etc/nix/registry.json`) to the @@ -85,6 +85,18 @@ in be set, since it is implemented in terms of indirection through the flake registry. ''; } + + # TODO: Upstream these to NixOS. + + { + assertion = cfg.setNixPath -> config.nix.enable; + message = ''`nixpkgs.flake.setNixPath` requires `nix.enable`''; + } + + { + assertion = cfg.setFlakeRegistry -> config.nix.enable; + message = ''`nixpkgs.flake.setFlakeRegistry` requires `nix.enable`''; + } ]; } (mkIf cfg.setFlakeRegistry { diff --git a/modules/nix/nixpkgs.nix b/modules/nix/nixpkgs.nix index ee999971..51bb1718 100644 --- a/modules/nix/nixpkgs.nix +++ b/modules/nix/nixpkgs.nix @@ -1,89 +1,96 @@ -{ config, options, lib, pkgs, ... }: - -with lib; +{ + config, + options, + lib, + pkgs, + ... +}: let cfg = config.nixpkgs; opt = options.nixpkgs; - isConfig = x: - builtins.isAttrs x || lib.isFunction x; + isConfig = x: builtins.isAttrs x || lib.isFunction x; - optCall = f: x: - if lib.isFunction f - then f x - else f; + optCall = f: x: if lib.isFunction f then f x else f; - mergeConfig = lhs_: rhs_: + mergeConfig = + lhs_: rhs_: let lhs = optCall lhs_ { inherit pkgs; }; rhs = optCall rhs_ { inherit pkgs; }; in - recursiveUpdate lhs rhs // - optionalAttrs (lhs ? packageOverrides) { - packageOverrides = pkgs: - optCall lhs.packageOverrides pkgs // - optCall (attrByPath [ "packageOverrides" ] { } rhs) pkgs; - } // - optionalAttrs (lhs ? perlPackageOverrides) { - perlPackageOverrides = pkgs: - optCall lhs.perlPackageOverrides pkgs // - optCall (attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs; + lib.recursiveUpdate lhs rhs + // lib.optionalAttrs (lhs ? packageOverrides) { + packageOverrides = + pkgs: + optCall lhs.packageOverrides pkgs // optCall (lib.attrByPath [ "packageOverrides" ] { } rhs) pkgs; + } + // lib.optionalAttrs (lhs ? perlPackageOverrides) { + perlPackageOverrides = + pkgs: + optCall lhs.perlPackageOverrides pkgs + // optCall (lib.attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs; }; - configType = mkOptionType { + configType = lib.mkOptionType { name = "nixpkgs-config"; description = "nixpkgs config"; - check = x: - let traceXIfNot = c: - if c x then true - else lib.traceSeqN 1 x false; - in traceXIfNot isConfig; - merge = args: foldr (def: mergeConfig def.value) {}; + check = + x: + let + traceXIfNot = c: if c x then true else lib.traceSeqN 1 x false; + in + traceXIfNot isConfig; + merge = args: lib.foldr (def: mergeConfig def.value) { }; }; - overlayType = mkOptionType { + overlayType = lib.mkOptionType { name = "nixpkgs-overlay"; description = "nixpkgs overlay"; check = lib.isFunction; merge = lib.mergeOneOption; }; - pkgsType = types.pkgs // { + pkgsType = lib.types.pkgs // { # This type is only used by itself, so let's elaborate the description a bit # for the purpose of documentation. description = "An evaluation of Nixpkgs; the top level attribute set of packages"; }; - hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority; + hasBuildPlatform = opt.buildPlatform.highestPrio < (lib.mkOptionDefault { }).priority; hasHostPlatform = opt.hostPlatform.isDefined; hasPlatform = hasHostPlatform || hasBuildPlatform; # Context for messages - hostPlatformLine = optionalString hasHostPlatform "${showOptionWithDefLocs opt.hostPlatform}"; - buildPlatformLine = optionalString hasBuildPlatform "${showOptionWithDefLocs opt.buildPlatform}"; + hostPlatformLine = lib.optionalString hasHostPlatform "${lib.showOptionWithDefLocs opt.hostPlatform}"; + buildPlatformLine = lib.optionalString hasBuildPlatform "${lib.showOptionWithDefLocs opt.buildPlatform}"; - legacyOptionsDefined = - optional (opt.system.highestPrio < (mkDefault {}).priority) opt.system - ; + legacyOptionsDefined = lib.optional ( + opt.system.highestPrio < (lib.mkDefault { }).priority + ) opt.system; defaultPkgs = - if opt.hostPlatform.isDefined - then - let isCross = cfg.buildPlatform != cfg.hostPlatform; - systemArgs = - if isCross - then { + if opt.hostPlatform.isDefined then + let + isCross = cfg.buildPlatform != cfg.hostPlatform; + systemArgs = + if isCross then + { localSystem = cfg.buildPlatform; crossSystem = cfg.hostPlatform; } - else { + else + { localSystem = cfg.hostPlatform; }; in - import cfg.source ({ - inherit (cfg) config overlays; - } // systemArgs) + import cfg.source ( + { + inherit (cfg) config overlays; + } + // systemArgs + ) else import cfg.source { inherit (cfg) config overlays; @@ -96,9 +103,9 @@ in { options.nixpkgs = { - pkgs = mkOption { + pkgs = lib.mkOption { type = pkgsType; - example = literalExpression "import {}"; + example = lib.literalExpression "import {}"; description = '' If set, the pkgs argument to all nix-darwin modules is the value of this option, extended with `nixpkgs.overlays`, if @@ -120,53 +127,48 @@ in ''; }; - config = mkOption { - default = {}; - example = literalExpression - '' - { allowBroken = true; allowUnfree = true; } - ''; + config = lib.mkOption { + default = { }; + example = lib.literalExpression '' + { allowBroken = true; allowUnfree = true; } + ''; type = configType; description = '' - The configuration of the Nix Packages collection. (For - details, see the Nixpkgs documentation.) It allows you to set - package configuration options. + Global configuration for Nixpkgs. + The complete list of [Nixpkgs configuration options](https://nixos.org/manual/nixpkgs/unstable/#sec-config-options-reference) is in the [Nixpkgs manual section on global configuration](https://nixos.org/manual/nixpkgs/unstable/#chap-packageconfig). - Ignored when `nixpkgs.pkgs` is set. + Ignored when {option}`nixpkgs.pkgs` is set. ''; }; - overlays = mkOption { - default = []; - example = literalExpression - '' - [ - (self: super: { - openssh = super.openssh.override { - hpnSupport = true; - kerberos = self.libkrb5; - }; - }) - ] - ''; - type = types.listOf overlayType; + overlays = lib.mkOption { + default = [ ]; + example = lib.literalExpression '' + [ + (self: super: { + openssh = super.openssh.override { + hpnSupport = true; + kerberos = self.libkrb5; + }; + }) + ] + ''; + type = lib.types.listOf overlayType; description = '' - List of overlays to use with the Nix Packages collection. - (For details, see the Nixpkgs documentation.) It allows - you to override packages globally. Each function in the list - takes as an argument the *original* Nixpkgs. - The first argument should be used for finding dependencies, and - the second should be used for overriding recipes. + List of overlays to apply to Nixpkgs. + This option allows modifying the Nixpkgs package set accessed through the `pkgs` module argument. - If `nixpkgs.pkgs` is set, overlays specified here - will be applied after the overlays that were already present - in `nixpkgs.pkgs`. + For details, see the [Overlays chapter in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-overlays). + + If the {option}`nixpkgs.pkgs` option is set, overlays specified using `nixpkgs.overlays` will be applied after the overlays that were already included in `nixpkgs.pkgs`. ''; }; - hostPlatform = mkOption { - type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform - example = { system = "aarch64-darwin"; config = "aarch64-apple-darwin"; }; + hostPlatform = lib.mkOption { + type = lib.types.either lib.types.str lib.types.attrs; # TODO utilize lib.systems.parsedPlatform + example = { + system = "aarch64-darwin"; + }; # Make sure that the final value has all fields for sake of other modules # referring to this. TODO make `lib.systems` itself use the module system. apply = lib.systems.elaborate; @@ -179,15 +181,24 @@ in ''; }; - buildPlatform = mkOption { - type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform + buildPlatform = lib.mkOption { + type = lib.types.either lib.types.str lib.types.attrs; # TODO utilize lib.systems.parsedPlatform default = cfg.hostPlatform; - example = { system = "x86_64-darwin"; config = "x86_64-apple-darwin"; }; + example = { + system = "x86_64-darwin"; + }; # Make sure that the final value has all fields for sake of other modules # referring to this. - apply = lib.systems.elaborate; - defaultText = literalExpression - ''config.nixpkgs.hostPlatform''; + apply = + inputBuildPlatform: + let + elaborated = lib.systems.elaborate inputBuildPlatform; + in + if lib.systems.equals elaborated cfg.hostPlatform then + cfg.hostPlatform # make identical, so that `==` equality works; see https://github.com/NixOS/nixpkgs/issues/278001 + else + elaborated; + defaultText = lib.literalExpression ''config.nixpkgs.hostPlatform''; description = '' Specifies the platform on which nix-darwin should be built. By default, nix-darwin is built on the system where it runs, but you can @@ -202,12 +213,11 @@ in ''; }; - system = mkOption { - type = types.str; + system = lib.mkOption { + type = lib.types.str; example = "x86_64-darwin"; default = - if opt.hostPlatform.isDefined - then + if opt.hostPlatform.isDefined then throw '' Neither ${opt.system} nor any other option in nixpkgs.* is meant to be read by modules and configurations. @@ -232,9 +242,9 @@ in # nix-darwin only - source = mkOption { - type = types.path; - defaultText = literalMD '' + source = lib.mkOption { + type = lib.types.path; + defaultText = lib.literalMD '' `` or nix-darwin's `nixpkgs` flake input ''; description = '' @@ -247,8 +257,8 @@ in ''; }; - constructedByUs = mkOption { - type = types.bool; + constructedByUs = lib.mkOption { + type = lib.types.bool; internal = true; description = '' Whether `pkgs` was constructed by this module. This is false when any of @@ -266,40 +276,51 @@ in # which is somewhat costly for Nixpkgs. With an explicit priority, we only # evaluate the wrapper to find out that the priority is lower, and then we # don't need to evaluate `finalPkgs`. - lib.mkOverride lib.modules.defaultOverridePriority - finalPkgs.__splicedPackages; + lib.mkOverride lib.modules.defaultOverridePriority finalPkgs.__splicedPackages; }; nixpkgs.constructedByUs = # We set it with default priority and it can not be merged, so if the # pkgs module argument has that priority, it's from us. (lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio - == lib.modules.defaultOverridePriority + == lib.modules.defaultOverridePriority # Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it. - && !opt.pkgs.isDefined; + && !opt.pkgs.isDefined; assertions = [ ( let pkgsSystem = finalPkgs.stdenv.targetPlatform.system; - in { + in + { assertion = cfg.constructedByUs -> !hasPlatform -> cfg.system == pkgsSystem; - message = "The nix-darwin nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but nix-darwin was configured for system ${darwinExpectedSystem} via nix-darwin option nixpkgs.system. The nix-darwin system settings must match the Nixpkgs target system."; + message = "The nix-darwin nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but nix-darwin was configured for system ${config.nixpkgs.system} via nix-darwin option nixpkgs.system. The nix-darwin system settings must match the Nixpkgs target system."; } ) { - assertion = cfg.constructedByUs -> hasPlatform -> legacyOptionsDefined == []; + assertion = cfg.constructedByUs -> hasPlatform -> legacyOptionsDefined == [ ]; message = '' - Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}: - ${hostPlatformLine - }${buildPlatformLine - } + Your system configures nixpkgs with the platform parameter${lib.optionalString hasBuildPlatform "s"}: + ${hostPlatformLine}${buildPlatformLine} However, it also defines the legacy options: - ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined} + ${lib.concatMapStrings lib.showOptionWithDefLocs legacyOptionsDefined} For a future proof system configuration, we recommend to remove the legacy definitions. ''; } + { + assertion = opt.pkgs.isDefined -> cfg.config == { }; + message = '' + Your system configures nixpkgs with an externally created instance. + `nixpkgs.config` options should be passed when creating the instance instead. + + Current value: + ${lib.generators.toPretty { multiline = true; } cfg.config} + + Defined in: + ${lib.concatMapStringsSep "\n" (file: " - ${file}") opt.config.files} + ''; + } ]; }; } diff --git a/modules/power/default.nix b/modules/power/default.nix index a99905ff..c3e4974d 100644 --- a/modules/power/default.nix +++ b/modules/power/default.nix @@ -15,6 +15,8 @@ in default = null; description = '' Whether to restart the computer after a power failure. + + Option is not supported on all devices. ''; }; diff --git a/modules/programs/fish.nix b/modules/programs/fish.nix index 4b76e02e..65797ccd 100644 --- a/modules/programs/fish.nix +++ b/modules/programs/fish.nix @@ -57,6 +57,8 @@ in type = types.bool; }; + package = lib.mkPackageOption pkgs "fish" { }; + useBabelfish = mkOption { type = types.bool; default = false; @@ -238,7 +240,7 @@ in ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d"; } - { systemPackages = [ pkgs.fish ]; } + { systemPackages = [ cfg.package ]; } ]; }; diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh/default.nix index 51c7796d..1e87732a 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh/default.nix @@ -114,6 +114,15 @@ in type = with types; attrsOf (submodule userOptions); }; + programs.ssh.extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + Extra configuration text loaded in {file}`ssh_config`. + See {manpage}`ssh_config(5)` for help. + ''; + }; + programs.ssh.knownHosts = mkOption { default = {}; type = types.attrsOf (types.submodule host); @@ -151,6 +160,7 @@ in + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile) )) + "\n"; }; + "ssh/ssh_config.d/100-nix-darwin.conf".text = config.programs.ssh.extraConfig; "ssh/sshd_config.d/101-authorized-keys.conf" = { text = '' # sshd doesn't like reading from symbolic links, so we cat diff --git a/modules/programs/zsh/default.nix b/modules/programs/zsh/default.nix index c6ec9175..65689bad 100644 --- a/modules/programs/zsh/default.nix +++ b/modules/programs/zsh/default.nix @@ -204,7 +204,7 @@ in } ${optionalString cfg.enableFastSyntaxHighlighting - "source ${pkgs.zsh-fast-syntax-highlighting}/share/zsh-fast-syntax-highlighting/zsh-fast-syntax-highlighting.zsh" + "source ${pkgs.zsh-fast-syntax-highlighting}/share/zsh/site-functions/fast-syntax-highlighting.plugin.zsh" } ${optionalString cfg.enableFzfCompletion "source ${fzfCompletion}"} diff --git a/modules/security/pam.nix b/modules/security/pam.nix index 69b4c37e..e671e23f 100644 --- a/modules/security/pam.nix +++ b/modules/security/pam.nix @@ -1,69 +1,106 @@ { config, lib, pkgs, ... }: -with lib; - let - cfg = config.security.pam; - - # Implementation Notes - # - # We don't use `environment.etc` because this would require that the user manually delete - # `/etc/pam.d/sudo` which seems unwise given that applying the nix-darwin configuration requires - # sudo. We also can't use `system.patchs` since it only runs once, and so won't patch in the - # changes again after OS updates (which remove modifications to this file). - # - # As such, we resort to line addition/deletion in place using `sed`. We add a comment to the - # added line that includes the name of the option, to make it easier to identify the line that - # should be deleted when the option is disabled. - mkSudoTouchIdAuthScript = isEnabled: - let - file = "/etc/pam.d/sudo"; - option = "security.pam.enableSudoTouchIdAuth"; - sed = "${pkgs.gnused}/bin/sed"; - in '' - ${if isEnabled then '' - # Enable sudo Touch ID authentication, if not already enabled - if ! grep 'pam_tid.so' ${file} > /dev/null; then - ${sed} -i '2i\ - auth sufficient pam_tid.so # nix-darwin: ${option} - ' ${file} - fi - '' else '' - # Disable sudo Touch ID authentication, if added by nix-darwin - if grep '${option}' ${file} > /dev/null; then - ${sed} -i '/${option}/d' ${file} - fi - ''} - ''; + cfg = config.security.pam.services.sudo_local; in - { + imports = [ + (lib.mkRemovedOptionModule [ "security" "pam" "enableSudoTouchIdAuth" ] '' + This option has been renamed to `security.pam.services.sudo_local.touchIdAuth` for consistency with NixOS. + '') + ]; + options = { - security.pam.enableSudoTouchIdAuth = mkEnableOption "" // { - description = '' - Enable sudo authentication with Touch ID. + security.pam.services.sudo_local = { + enable = lib.mkEnableOption "managing {file}`/etc/pam.d/sudo_local` with nix-darwin" // { + default = true; + example = false; + }; - When enabled, this option adds the following line to - {file}`/etc/pam.d/sudo`: + text = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + Contents of {file}`/etc/pam.d/sudo_local` + ''; + }; - ``` - auth sufficient pam_tid.so - ``` + touchIdAuth = lib.mkEnableOption "" // { + description = '' + Whether to enable Touch ID with sudo. - ::: {.note} - macOS resets this file when doing a system update. As such, sudo - authentication with Touch ID won't work after a system update - until the nix-darwin configuration is reapplied. - ::: - ''; + This will also allow your Apple Watch to be used for sudo. If this doesn't work, + you can go into `System Settings > Touch ID & Password` and toggle the switch for + your Apple Watch. + ''; + }; + + watchIdAuth = lib.mkEnableOption "" // { + description = '' + Use Apple Watch for sudo authentication, for devices without Touch ID or + laptops with lids closed, consider using this. + + When enabled, you can use your Apple Watch to authenticate sudo commands. + If this doesn't work, you can go into `System Settings > Touch ID & Password` + and toggle the switch for your Apple Watch. + ''; + }; + + reattach = lib.mkEnableOption "" // { + description = '' + Whether to enable reattaching a program to the user's bootstrap session. + + This fixes Touch ID for sudo not working inside tmux and screen. + + This allows programs like tmux and screen that run in the background to + survive across user sessions to work with PAM services that are tied to the + bootstrap session. + ''; + }; }; }; config = { - system.activationScripts.pam.text = '' + security.pam.services.sudo_local.text = lib.concatLines ( + (lib.optional cfg.reattach "auth optional ${pkgs.pam-reattach}/lib/pam/pam_reattach.so") + ++ (lib.optional cfg.touchIdAuth "auth sufficient pam_tid.so") + ++ (lib.optional cfg.watchIdAuth "auth sufficient ${pkgs.pam-watchid}/lib/pam_watchid.so") + ); + + environment.etc."pam.d/sudo_local" = { + inherit (cfg) enable text; + }; + + system.activationScripts.pam.text = + let + file = "/etc/pam.d/sudo"; + marker = "security.pam.services.sudo_local"; + deprecatedOption = "security.pam.enableSudoTouchIdAuth"; + sed = lib.getExe pkgs.gnused; + in + '' # PAM settings echo >&2 "setting up pam..." - ${mkSudoTouchIdAuthScript cfg.enableSudoTouchIdAuth} + + # REMOVEME when macOS 13 no longer supported as macOS automatically + # nukes this file on system upgrade + # Always clear out older implementation if it is present + if grep '${deprecatedOption}' ${file} > /dev/null; then + ${sed} -i '/${deprecatedOption}/d' ${file} + fi + + ${if cfg.enable then '' + # REMOVEME when macOS 13 no longer supported + # `sudo_local` is automatically included after macOS 14 + if ! grep 'sudo_local' ${file} > /dev/null; then + ${sed} -i '2iauth include sudo_local # nix-darwin: ${marker}' ${file} + fi + '' else '' + # Remove include line if we added it + if grep '${marker}' ${file} > /dev/null; then + ${sed} -i '/${marker}/d' ${file} + fi + ''} ''; }; } diff --git a/modules/services/activate-system/default.nix b/modules/services/activate-system/default.nix index 6a982fe8..df0b48e4 100644 --- a/modules/services/activate-system/default.nix +++ b/modules/services/activate-system/default.nix @@ -21,14 +21,15 @@ ln -sfn $(cat ${config.system.profile}/systemConfig) /run/current-system # Prevent the current configuration from being garbage-collected. - ln -sfn /run/current-system /nix/var/nix/gcroots/current-system + if [[ -d /nix/var/nix/gcroots ]]; then + ln -sfn /run/current-system /nix/var/nix/gcroots/current-system + fi ${config.system.activationScripts.etcChecks.text} ${config.system.activationScripts.etc.text} ${config.system.activationScripts.keyboard.text} ''; serviceConfig.RunAtLoad = true; - serviceConfig.KeepAlive.SuccessfulExit = false; }; }; } diff --git a/modules/services/aerospace/default.nix b/modules/services/aerospace/default.nix index 50d47b3b..3080579d 100644 --- a/modules/services/aerospace/default.nix +++ b/modules/services/aerospace/default.nix @@ -9,7 +9,31 @@ let cfg = config.services.aerospace; format = pkgs.formats.toml { }; - configFile = format.generate "aerospace.toml" cfg.settings; + filterAttrsRecursive = pred: set: + lib.listToAttrs ( + lib.concatMap ( + name: let + v = set.${name}; + in + if pred v + then [ + (lib.nameValuePair name ( + if lib.isAttrs v + then filterAttrsRecursive pred v + else if lib.isList v + then + (map (i: + if lib.isAttrs i + then filterAttrsRecursive pred i + else i) (lib.filter pred v)) + else v + )) + ] + else [] + ) (lib.attrNames set) + ); + filterNulls = filterAttrsRecursive (v: v != null); + configFile = format.generate "aerospace.toml" (filterNulls cfg.settings); in { @@ -36,7 +60,8 @@ in after-startup-command = lib.mkOption { type = listOf str; default = [ ]; - description = "Do not use AeroSpace to run commands after startup. (Managed by launchd instead)"; + description = "Add commands that run after AeroSpace startup"; + example = [ "layout tiles" ]; }; enable-normalization-flatten-containers = lib.mkOption { type = bool; @@ -71,9 +96,84 @@ in description = "Default orientation for the root container."; }; on-window-detected = lib.mkOption { - type = listOf str; + type = listOf (submodule { + options = { + "if" = lib.mkOption { + type = submodule { + options = { + app-id = lib.mkOption { + type = nullOr str; + default = null; + description = "The application ID to match (optional)."; + }; + workspace = lib.mkOption { + type = nullOr str; + default = null; + description = "The workspace name to match (optional)."; + }; + window-title-regex-substring = lib.mkOption { + type = nullOr str; + default = null; + description = "Substring to match in the window title (optional)."; + }; + app-name-regex-substring = lib.mkOption { + type = nullOr str; + default = null; + description = "Regex substring to match the app name (optional)."; + }; + during-aerospace-startup = lib.mkOption { + type = nullOr bool; + default = null; + description = "Whether to match during aerospace startup (optional)."; + }; + }; + }; + default = { }; + description = "Conditions for detecting a window."; + }; + check-further-callbacks = lib.mkOption { + type = nullOr bool; + default = null; + description = "Whether to check further callbacks after this rule (optional)."; + }; + run = lib.mkOption { + type = oneOf [str (listOf str)]; + example = ["move-node-to-workspace m" "resize-node"]; + description = "Commands to execute when the conditions match (required)."; + }; + }; + }); default = [ ]; - description = "Commands to run every time a new window is detected."; + example = [ + { + "if" = { + app-id = "Another.Cool.App"; + workspace = "cool-workspace"; + window-title-regex-substring = "Title"; + app-name-regex-substring = "CoolApp"; + during-aerospace-startup = false; + }; + check-further-callbacks = false; + run = ["move-node-to-workspace m" "resize-node"]; + } + ]; + description = "Commands to run every time a new window is detected with optional conditions."; + }; + workspace-to-monitor-force-assignment = lib.mkOption { + type = attrsOf (oneOf [int str (listOf str)]); + default = { }; + description = '' + Map workspaces to specific monitors. + Left-hand side is the workspace name, and right-hand side is the monitor pattern. + ''; + example = { + "1" = 1; # First monitor from left to right. + "2" = "main"; # Main monitor. + "3" = "secondary"; # Secondary monitor (non-main). + "4" = "built-in"; # Built-in display. + "5" = "^built-in retina display$"; # Regex for the built-in retina display. + "6" = ["secondary" "dell"]; # Match first pattern in the list. + }; }; on-focus-changed = lib.mkOption { type = listOf str; @@ -142,10 +242,6 @@ in assertion = cfg.settings.after-login-command == [ ]; message = "AeroSpace will not run these commands as it does not start itself."; } - { - assertion = cfg.settings.after-startup-command == [ ]; - message = "AeroSpace will not run these commands as it does not start itself."; - } ]; environment.systemPackages = [ cfg.package ]; diff --git a/modules/services/buildkite-agents.nix b/modules/services/buildkite-agents.nix index 69bc1f65..3c04d904 100644 --- a/modules/services/buildkite-agents.nix +++ b/modules/services/buildkite-agents.nix @@ -227,8 +227,9 @@ in { path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils pkgs.darwin.DarwinTools ]; environment = { HOME = cfg.dataDir; + NIX_REMOTE = "daemon"; inherit (config.environment.variables) NIX_SSL_CERT_FILE; - } // (if config.nix.useDaemon then { NIX_REMOTE = "daemon"; } else {}); + }; ## NB: maximum care is taken so that secrets (ssh keys and the CI token) ## don't end up in the Nix store. diff --git a/modules/services/cachix-agent.nix b/modules/services/cachix-agent.nix index d9e4a909..508261af 100644 --- a/modules/services/cachix-agent.nix +++ b/modules/services/cachix-agent.nix @@ -51,6 +51,14 @@ in { }; config = mkIf cfg.enable { + # TODO: Upstream this to NixOS. + assertions = [ + { + assertion = config.nix.enable; + message = ''`services.cachix-agent.enable` requires `nix.enable`''; + } + ]; + launchd.daemons.cachix-agent = { script = '' . ${cfg.credentialsFile} diff --git a/modules/services/dnscrypt-proxy.nix b/modules/services/dnscrypt-proxy.nix new file mode 100644 index 00000000..f0ba1d52 --- /dev/null +++ b/modules/services/dnscrypt-proxy.nix @@ -0,0 +1,81 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + + cfg = config.services.dnscrypt-proxy; + + format = pkgs.formats.toml { }; + + configFile = format.generate "dnscrypt-proxy.toml" cfg.settings; + +in + +{ + options.services.dnscrypt-proxy = { + + enable = lib.mkEnableOption "the dnscrypt-proxy service."; + + package = lib.mkPackageOption pkgs "dnscrypt-proxy" { }; + + settings = lib.mkOption { + description = '' + Attrset that is converted and passed as TOML config file. + For available params, see: + ''; + example = lib.literalExpression '' + { + sources.public-resolvers = { + urls = [ "https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md" ]; + cache_file = "public-resolvers.md"; + minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"; + refresh_delay = 72; + }; + } + ''; + type = format.type; + default = { }; + }; + }; + + config = lib.mkIf cfg.enable { + users.users._dnscrypt-proxy = { + uid = config.ids.uids._dnscrypt-proxy; + gid = config.ids.gids._dnscrypt-proxy; + home = "/var/lib/dnscrypt-proxy"; + createHome = true; + shell = "/usr/bin/false"; + description = "System user for dnscrypt-proxy"; + }; + + users.groups._dnscrypt-proxy = { + gid = config.ids.gids._dnscrypt-proxy; + description = "System group for dnscrypt-proxy"; + }; + + users.knownUsers = [ "_dnscrypt-proxy" ]; + users.knownGroups = [ "_dnscrypt-proxy" ]; + + launchd.daemons.dnscrypt-proxy = { + script = '' + ${lib.getExe' cfg.package "dnscrypt-proxy"} -config ${configFile} + ''; + serviceConfig = + let + logPath = config.users.users._dnscrypt-proxy.home + "/dnscrypt-proxy.log"; + in + { + RunAtLoad = true; + KeepAlive = true; + StandardOutPath = logPath; + StandardErrorPath = logPath; + GroupName = "_dnscrypt-proxy"; + UserName = "_dnscrypt-proxy"; + }; + }; + }; +} diff --git a/modules/services/github-runner/options.nix b/modules/services/github-runner/options.nix index 8f98aa07..5152cc43 100644 --- a/modules/services/github-runner/options.nix +++ b/modules/services/github-runner/options.nix @@ -3,7 +3,9 @@ , ... }: -with lib; +let + inherit (lib) literalExpression mkOption mkPackageOption types; +in { options.services.github-runners = mkOption { description = '' @@ -88,6 +90,9 @@ with lib; Changing this option or the `tokenFile`’s content triggers a new runner registration. + You can also manually trigger a new runner registration by deleting + {file}`/var/lib/github-runners//.runner` and restarting the service. + We suggest using the fine-grained PATs. A runner registration token is valid only for 1 hour after creation, so the next time the runner configuration changes this will give you hard-to-debug HTTP 404 errors in the configure step. diff --git a/modules/services/github-runner/service.nix b/modules/services/github-runner/service.nix index 21d908e0..3668a721 100644 --- a/modules/services/github-runner/service.nix +++ b/modules/services/github-runner/service.nix @@ -1,6 +1,10 @@ { config, lib, pkgs, ... }: -with lib; + let + inherit (lib) any attrValues boolToString concatStringsSep escapeShellArg + flatten flip getExe getExe' hasAttr hasPrefix mapAttrsToList mapAttrs' mkBefore + mkDefault mkIf mkMerge nameValuePair optionalAttrs optionalString replaceStrings; + mkSvcName = name: "github-runner-${name}"; mkStateDir = cfg: "/var/lib/github-runners/${cfg.name}"; mkLogDir = cfg: "/var/log/github-runners/${cfg.name}"; @@ -9,6 +13,11 @@ in { config.assertions = flatten ( flip mapAttrsToList config.services.github-runners (name: cfg: map (mkIf cfg.enable) [ + # TODO: Upstream this to NixOS. + { + assertion = config.nix.enable; + message = ''`services.github-runners.${name}.enable` requires `nix.enable`''; + } { assertion = (cfg.user == null && cfg.group == null) || (cfg.user != null); message = "`services.github-runners.${name}`: Either set `user` and `group` to `null` to have nix-darwin manage them or set at least `user` explicitly"; @@ -51,15 +60,17 @@ in ( umask -S u=rwx,g=rx,o= > /dev/null - ${pkgs.coreutils}/bin/mkdir -p ${escapeShellArg (mkStateDir cfg)} - ${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkStateDir cfg)} + ${getExe' pkgs.coreutils "mkdir"} -p ${escapeShellArg (mkStateDir cfg)} + ${getExe' pkgs.coreutils "chown"} ${user}:${group} ${escapeShellArg (mkStateDir cfg)} - ${pkgs.coreutils}/bin/mkdir -p ${escapeShellArg (mkLogDir cfg)} - ${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkLogDir cfg)} + ${getExe' pkgs.coreutils "mkdir"} -p ${escapeShellArg (mkLogDir cfg)} + # launchd will fail to start the service if the outer direction doesn't have sufficient permissions + ${getExe' pkgs.coreutils "chmod"} o+rx ${escapeShellArg (mkLogDir { name = ""; })} + ${getExe' pkgs.coreutils "chown"} ${user}:${group} ${escapeShellArg (mkLogDir cfg)} ${optionalString (cfg.workDir == null) '' - ${pkgs.coreutils}/bin/mkdir -p ${escapeShellArg (mkWorkDir cfg)} - ${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkWorkDir cfg)} + ${getExe' pkgs.coreutils "mkdir"} -p ${escapeShellArg (mkWorkDir cfg)} + ${getExe' pkgs.coreutils "chown"} ${user}:${group} ${escapeShellArg (mkWorkDir cfg)} ''} ) ''); @@ -123,7 +134,7 @@ in else args+=(--token "$token") fi - ${package}/bin/config.sh "''${args[@]}" + ${getExe' package "config.sh"} "''${args[@]}" ''; }; in @@ -131,12 +142,12 @@ in echo "Configuring GitHub Actions Runner" # Always clean the working directory - ${pkgs.findutils}/bin/find ${escapeShellArg workDir} -mindepth 1 -delete + ${getExe pkgs.findutils} ${escapeShellArg workDir} -mindepth 1 -delete # Clean the $RUNNER_ROOT if we are in ephemeral mode if ${boolToString cfg.ephemeral}; then echo "Cleaning $RUNNER_ROOT" - ${pkgs.findutils}/bin/find "$RUNNER_ROOT" -mindepth 1 -delete + ${getExe pkgs.findutils} "$RUNNER_ROOT" -mindepth 1 -delete fi # If the `.runner` file does not exist, we assume the runner is not configured @@ -145,7 +156,7 @@ in fi # Start the service - ${package}/bin/Runner.Listener run --startuptype service + ${getExe' package "Runner.Listener"} run --startuptype service ''; serviceConfig = mkMerge [ diff --git a/modules/services/gitlab-runner.nix b/modules/services/gitlab-runner.nix index 94c291ef..329fd727 100644 --- a/modules/services/gitlab-runner.nix +++ b/modules/services/gitlab-runner.nix @@ -551,8 +551,9 @@ in launchd.daemons.gitlab-runner = { environment = { #config.networking.proxy.envVars // { HOME = "${config.users.users.gitlab-runner.home}"; + NIX_REMOTE = "daemon"; NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; - } // (if config.nix.useDaemon then { NIX_REMOTE = "daemon"; } else {}); + }; path = with pkgs; [ bash gawk diff --git a/modules/services/hercules-ci-agent/default.nix b/modules/services/hercules-ci-agent/default.nix index fc3d9520..47368294 100644 --- a/modules/services/hercules-ci-agent/default.nix +++ b/modules/services/hercules-ci-agent/default.nix @@ -22,6 +22,14 @@ in }; config = mkIf cfg.enable { + # TODO: Upstream this to NixOS. + assertions = [ + { + assertion = config.nix.enable; + message = ''`services.hercules-ci-agent.enable` requires `nix.enable`''; + } + ]; + launchd.daemons.hercules-ci-agent = { script = "exec ${cfg.package}/bin/hercules-ci-agent --config ${cfg.tomlFile}"; @@ -74,7 +82,7 @@ in darwin.label = config.system.darwinLabel; darwin.revision = config.system.darwinRevision; darwin.version = config.system.darwinVersion; - darwin.nix.daemon = config.nix.useDaemon; + darwin.nix.daemon = true; darwin.nix.sandbox = config.nix.settings.sandbox; }; }; diff --git a/modules/services/lorri.nix b/modules/services/lorri.nix index 0c123004..c4e1acee 100644 --- a/modules/services/lorri.nix +++ b/modules/services/lorri.nix @@ -29,6 +29,14 @@ in }; config = mkIf cfg.enable { + # TODO: Upstream this to NixOS. + assertions = [ + { + assertion = config.nix.enable; + message = ''`services.lorri.enable` requires `nix.enable`''; + } + ]; + environment.systemPackages = [ pkgs.lorri ]; launchd.user.agents.lorri = { command = with pkgs; "${lorri}/bin/lorri daemon"; @@ -43,4 +51,4 @@ in }; }; }; -} \ No newline at end of file +} diff --git a/modules/services/nix-daemon.nix b/modules/services/nix-daemon.nix index ffc7e651..404ca057 100644 --- a/modules/services/nix-daemon.nix +++ b/modules/services/nix-daemon.nix @@ -3,17 +3,18 @@ let cfg = config.services.nix-daemon; - inherit (lib) mkDefault mkIf mkMerge mkOption types; + inherit (lib) mkRemovedOptionModule mkDefault mkIf mkMerge mkOption types; in { - options = { - services.nix-daemon.enable = mkOption { - type = types.bool; - default = true; - description = "Whether to enable the nix-daemon service."; - }; + imports = [ + (mkRemovedOptionModule [ "services" "nix-daemon" "enable" ] '' + nix-darwin now manages nix-daemon unconditionally when + `nix.enable` is on. + '') + ]; + options = { services.nix-daemon.enableSocketListener = mkOption { type = types.bool; default = false; @@ -39,9 +40,7 @@ in }; }; - config = mkIf cfg.enable { - - nix.useDaemon = true; + config = mkIf config.nix.enable { launchd.daemons.nix-daemon = { command = lib.getExe' config.nix.package "nix-daemon"; diff --git a/modules/services/nix-gc/default.nix b/modules/services/nix-gc/default.nix index 9fe8e86d..b8d5c4d9 100644 --- a/modules/services/nix-gc/default.nix +++ b/modules/services/nix-gc/default.nix @@ -14,6 +14,7 @@ in (mkRemovedOptionModule [ "nix" "gc" "dates" ] "Use `nix.gc.interval` instead.") (mkRemovedOptionModule [ "nix" "gc" "randomizedDelaySec" ] "No `nix-darwin` equivalent to this NixOS option.") (mkRemovedOptionModule [ "nix" "gc" "persistent" ] "No `nix-darwin` equivalent to this NixOS option.") + (mkRemovedOptionModule [ "nix" "gc" "user" ] "The garbage collection service now always runs as `root`.") ]; ###### interface @@ -28,13 +29,6 @@ in description = "Automatically run the garbage collector at a specific time."; }; - # Not in NixOS module - user = mkOption { - type = types.nullOr types.str; - default = null; - description = "User that runs the garbage collector."; - }; - interval = mkOption { type = launchdTypes.StartCalendarInterval; default = [{ Weekday = 7; Hour = 3; Minute = 15; }]; @@ -62,15 +56,18 @@ in ###### implementation - config = mkIf cfg.automatic { + config = { + assertions = [ + { + assertion = cfg.automatic -> config.nix.enable; + message = ''nix.gc.automatic requires nix.enable''; + } + ]; - launchd.daemons.nix-gc = { + launchd.daemons.nix-gc = mkIf cfg.automatic { command = "${config.nix.package}/bin/nix-collect-garbage ${cfg.options}"; - environment.NIX_REMOTE = optionalString config.nix.useDaemon "daemon"; serviceConfig.RunAtLoad = false; serviceConfig.StartCalendarInterval = cfg.interval; - serviceConfig.UserName = cfg.user; }; - }; } diff --git a/modules/services/nix-optimise/default.nix b/modules/services/nix-optimise/default.nix index c0ee0a38..addf0467 100644 --- a/modules/services/nix-optimise/default.nix +++ b/modules/services/nix-optimise/default.nix @@ -20,6 +20,7 @@ in { imports = [ (mkRemovedOptionModule [ "nix" "optimise" "dates" ] "Use `nix.optimise.interval` instead.") + (mkRemovedOptionModule [ "nix" "optimise" "user" ] "The store optimisation service now always runs as `root`.") ]; ###### interface @@ -34,13 +35,6 @@ in description = "Automatically run the nix store optimiser at a specific time."; }; - # Not in NixOS module - user = mkOption { - type = types.nullOr types.str; - default = null; - description = "User that runs the store optimisation."; - }; - interval = mkOption { type = launchdTypes.StartCalendarInterval; default = [{ Weekday = 7; Hour = 4; Minute = 15; }]; @@ -58,17 +52,20 @@ in ###### implementation - config = mkIf cfg.automatic { + config = { + assertions = [ + { + assertion = cfg.automatic -> config.nix.enable; + message = ''nix.optimise.automatic requires nix.enable''; + } + ]; - launchd.daemons.nix-optimise = { - environment.NIX_REMOTE = optionalString config.nix.useDaemon "daemon"; + launchd.daemons.nix-optimise = mkIf cfg.automatic { command = "${lib.getExe' config.nix.package "nix-store"} --optimise"; serviceConfig = { RunAtLoad = false; StartCalendarInterval = cfg.interval; - UserName = cfg.user; }; }; - }; } diff --git a/modules/services/ofborg/default.nix b/modules/services/ofborg/default.nix index 8959cc84..acd6974f 100644 --- a/modules/services/ofborg/default.nix +++ b/modules/services/ofborg/default.nix @@ -46,6 +46,13 @@ in }; config = mkIf cfg.enable { + assertions = [ + { + assertion = config.nix.enable; + message = ''`services.ofborg.enable` requires `nix.enable`''; + } + ]; + warnings = mkIf (isDerivation cfg.configFile) [ "services.ofborg.configFile is a derivation, credentials will be world readable" ]; diff --git a/modules/services/openssh.nix b/modules/services/openssh.nix new file mode 100644 index 00000000..859f79d8 --- /dev/null +++ b/modules/services/openssh.nix @@ -0,0 +1,33 @@ +{ config, lib, ... }: + +let + cfg = config.services.openssh; +in +{ + options = { + services.openssh.enable = lib.mkOption { + type = lib.types.nullOr lib.types.bool; + default = null; + description = '' + Whether to enable Apple's built-in OpenSSH server. + + The default is null which means let macOS manage the OpenSSH server. + ''; + }; + }; + + config = { + # We don't use `systemsetup -setremotelogin` as it requires Full Disk Access + system.activationScripts.launchd.text = lib.mkIf (cfg.enable != null) (if cfg.enable then '' + if [[ "$(systemsetup -getremotelogin | sed 's/Remote Login: //')" == "Off" ]]; then + launchctl enable system/com.openssh.sshd + launchctl bootstrap system /System/Library/LaunchDaemons/ssh.plist + fi + '' else '' + if [[ "$(systemsetup -getremotelogin | sed 's/Remote Login: //')" == "On" ]]; then + launchctl bootout system/com.openssh.sshd + launchctl disable system/com.openssh.sshd + fi + ''); + }; +} diff --git a/modules/system/activation-scripts.nix b/modules/system/activation-scripts.nix index 5f8916cc..c8ad20ad 100644 --- a/modules/system/activation-scripts.nix +++ b/modules/system/activation-scripts.nix @@ -13,6 +13,32 @@ let mkTextDerivation = name: text: pkgs.writeScript "activate-${name}" text; }; + activationPath = + lib.makeBinPath [ + pkgs.gnugrep + pkgs.coreutils + ] + + lib.optionalString (!config.nix.enable) '' + $( + # If `nix.enable` is off, there might be an unmanaged Nix + # installation (say in `/nix/var/nix/profiles/default`) that + # activation scripts (such as Home Manager) want to find on the + # `$PATH`. Search for it directly to avoid polluting the + # activation script environment with everything on the + # `environment.systemPath`. + if nixEnvPath=$( + PATH="${config.environment.systemPath}" command -v nix-env + ); then + printf ':' + ${lib.getExe' pkgs.coreutils "dirname"} -- "$( + ${lib.getExe' pkgs.coreutils "readlink"} \ + --canonicalize-missing \ + -- "$nixEnvPath" + )" + fi + )'' + + ":@out@/sw/bin:/usr/bin:/bin:/usr/sbin:/sbin"; + in { @@ -40,13 +66,12 @@ in #! ${stdenv.shell} set -e set -o pipefail - export PATH="${pkgs.gnugrep}/bin:${pkgs.coreutils}/bin:@out@/sw/bin:/usr/bin:/bin:/usr/sbin:/sbin" + + PATH="${activationPath}" + export PATH systemConfig=@out@ - _status=0 - trap "_status=1" ERR - # Ensure a consistent umask. umask 0022 @@ -81,9 +106,9 @@ in ln -sfn "$(readlink -f "$systemConfig")" /run/current-system # Prevent the current configuration from being garbage-collected. - ln -sfn /run/current-system /nix/var/nix/gcroots/current-system - - exit $_status + if [[ -d /nix/var/nix/gcroots ]]; then + ln -sfn /run/current-system /nix/var/nix/gcroots/current-system + fi ''; # FIXME: activationScripts.checks should be system level @@ -91,7 +116,9 @@ in #! ${stdenv.shell} set -e set -o pipefail - export PATH="${pkgs.gnugrep}/bin:${pkgs.coreutils}/bin:@out@/sw/bin:/usr/bin:/bin" + + PATH="${activationPath}" + export PATH systemConfig=@out@ diff --git a/modules/system/base.nix b/modules/system/base.nix index f20e2b64..40c3699b 100644 --- a/modules/system/base.nix +++ b/modules/system/base.nix @@ -2,50 +2,35 @@ { system.activationScripts.createRun.text = '' - IFS="." read -r -a macOSVersion <<< "$(sw_vers -productVersion)" + if [[ $(stat -c '%a' /etc/synthetic.conf) != "644" ]]; then + echo "fixing permissions on /etc/synthetic.conf..." + sudo chmod 644 /etc/synthetic.conf + fi - if [[ ''${macOSVersion[0]} -gt 10 || ( ''${macOSVersion[0]} -eq 10 && ''${macOSVersion[1]} -ge 15 ) ]]; then - if ! grep -q '^run\b' /etc/synthetic.conf 2>/dev/null; then - echo "setting up /run via /etc/synthetic.conf..." - printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf >/dev/null - fi + if [[ $(grep -c '^run\b' /etc/synthetic.conf) -gt 1 ]]; then + echo "found duplicate run entries in /etc/synthetic.conf, removing..." + sudo sed -i "" -e '/^run\tprivate\/var\/run$/d' /etc/synthetic.conf + fi - if [[ ''${macOSVersion[0]} -gt 10 ]]; then - sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true - else - sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B || true - fi + if ! grep -q '^run\b' /etc/synthetic.conf 2>/dev/null; then + echo "setting up /run via /etc/synthetic.conf..." + printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf >/dev/null + fi - if [[ ! -L /run ]]; then - printf >&2 'error: apfs.util failed to symlink /run, aborting activation\n' - printf >&2 'To create a symlink from /run to /var/run, please run:\n' - printf >&2 '\n' - printf >&2 "$ printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf" + sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || true - if [[ ''${macOSVersion[0]} -gt 10 ]]; then - printf >&2 '$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t\n' - else - printf >&2 '$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B\n' - fi - - printf >&2 '\n' - printf >&2 'The current contents of /etc/synthetic.conf is:\n' - printf >&2 '\n' - sudo sed 's/^/ /' /etc/synthetic.conf >&2 - printf >&2 '\n' - exit 1 - fi - else - echo "setting up /run..." - sudo ln -sfn private/var/run /run - - if [[ ! -L /run ]]; then - printf >&2 'error: failed to symlink /run, aborting activation\n' - printf >&2 'To create a symlink from /run to /var/run, please run:\n' - printf >&2 '\n' - printf >&2 '$ sudo ln -sfn private/var/link /run\n' - exit 1 - fi + if [[ ! -L /run ]]; then + printf >&2 'error: apfs.util failed to symlink /run, aborting activation\n' + printf >&2 'To create a symlink from /run to /var/run, please run:\n' + printf >&2 '\n' + printf >&2 "$ printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf\n" + printf >&2 '$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t\n' + printf >&2 '\n' + printf >&2 'The current contents of /etc/synthetic.conf is:\n' + printf >&2 '\n' + sed 's/^/ /' /etc/synthetic.conf >&2 + printf >&2 '\n' + exit 1 fi ''; } diff --git a/modules/system/checks.nix b/modules/system/checks.nix index 796e7e4e..6afe796c 100644 --- a/modules/system/checks.nix +++ b/modules/system/checks.nix @@ -8,53 +8,54 @@ let cfg = config.system.checks; - darwinChanges = '' - darwinChanges=/dev/null - if test -e /run/current-system/darwin-changes; then - darwinChanges=/run/current-system/darwin-changes - fi - - darwinChanges=$(diff --changed-group-format='%>' --unchanged-group-format= /run/current-system/darwin-changes $systemConfig/darwin-changes 2> /dev/null) || true - if test -n "$darwinChanges"; then - echo >&2 - echo "CHANGELOG" >&2 - echo >&2 - echo "$darwinChanges" >&2 - echo >&2 + macOSVersion = '' + IFS=. read -ra osVersion <<<"$(sw_vers -productVersion)" + if (( osVersion[0] < 11 || (osVersion[0] == 11 && osVersion[1] < 3) )); then + printf >&2 '\e[1;31merror: macOS version is less than 11.3, aborting activation\e[0m\n' + printf >&2 'Nixpkgs 25.05 requires macOS Big Sur 11.3 or newer, and 25.11 will\n' + printf >&2 'require macOS Sonoma 14.\n' + printf >&2 '\n' + printf >&2 'For more information on your options going forward, see the 25.05\n' + printf >&2 'release notes:\n' + printf >&2 '\n' + printf >&2 '\n' + printf >&2 'Nixpkgs 24.11 and nix-darwin 24.11 continue to support down to macOS\n' + printf >&2 'Sierra 10.12, and will be supported through June 2025.\n' + printf >&2 '\n' + printf >&2 'You can override this check by setting:\n' + printf >&2 '\n' + printf >&2 ' system.checks.verifyMacOSVersion = false;\n' + printf >&2 '\n' + printf >&2 'However, we are unable to provide support if you do so.\n' + exit 2 fi ''; - runLink = '' - if [[ ! -e /run ]]; then - printf >&2 'error: directory /run does not exist, aborting activation\n' - exit 1 + determinate = '' + if [[ -e /usr/local/bin/determinate-nixd ]]; then + printf >&2 '\e[1;31merror: Determinate detected, aborting activation\e[0m\n' + printf >&2 'Determinate uses its own daemon to manage the Nix installation that\n' + printf >&2 'conflicts with nix-darwin’s native Nix management.\n' + printf >&2 '\n' + printf >&2 'To turn off nix-darwin’s management of the Nix installation, set:\n' + printf >&2 '\n' + printf >&2 ' nix.enable = false;\n' + printf >&2 '\n' + printf >&2 'This will allow you to use nix-darwin with Determinate. Some nix-darwin\n' + printf >&2 'functionality that relies on managing the Nix installation, like the\n' + printf >&2 '`nix.*` options to adjust Nix settings or configure a Linux builder,\n' + printf >&2 'will be unavailable.\n' + exit 2 fi ''; - - oldBuildUsers = '' - if dscl . -list /Users | grep -q '^nixbld'; then - echo "error: Detected old style nixbld users, aborting activation" >&2 - echo "These can cause migration problems when upgrading to certain macOS versions" >&2 - echo "You can enable the following option to migrate to new style nixbld users" >&2 - echo >&2 - echo " nix.configureBuildUsers = true;" >&2 - echo >&2 - echo "or disable this check with" >&2 - echo >&2 - echo " system.checks.verifyBuildUsers = false;" >&2 - echo >&2 - exit 2 - fi - ''; - preSequoiaBuildUsers = '' - ${lib.optionalString config.nix.configureBuildUsers '' - # Don’t complain when we’re about to migrate old‐style build users… - if ! dscl . -list /Users | grep -q '^nixbld'; then - ''} firstBuildUserID=$(dscl . -read /Users/_nixbld1 UniqueID | awk '{print $2}') - if [[ $firstBuildUserID != ${toString (config.ids.uids.nixbld + 1)} ]]; then + if + # Don’t complain when we’re about to migrate old‐style build users… + [[ $firstBuildUserID != ${toString (config.ids.uids.nixbld + 1)} ]] \ + && ! dscl . -list /Users | grep -q '^nixbld' + then printf >&2 '\e[1;31merror: Build users have unexpected UIDs, aborting activation\e[0m\n' printf >&2 'The default Nix build user ID range has been adjusted for\n' printf >&2 'compatibility with macOS Sequoia 15. Your _nixbld1 user currently has\n' @@ -87,22 +88,6 @@ let printf >&2 '\n' exit 2 fi - ${lib.optionalString config.nix.configureBuildUsers "fi"} - ''; - - buildUsers = '' - buildUser=$(dscl . -read /Groups/nixbld GroupMembership 2>&1 | awk '/^GroupMembership: / {print $2}') || true - if [[ -z "$buildUser" ]]; then - echo "error: Using the nix-daemon requires build users, aborting activation" >&2 - echo "Create the build users or disable the daemon:" >&2 - echo "$ darwin-install" >&2 - echo >&2 - echo "or set (this requires some manual intervention to restore permissions)" >&2 - echo >&2 - echo " services.nix-daemon.enable = false;" >&2 - echo >&2 - exit 2 - fi ''; buildGroupID = '' @@ -118,7 +103,6 @@ let printf >&2 'Possible causes include setting up a new Nix installation with an\n' printf >&2 'existing nix-darwin configuration, setting up a new nix-darwin\n' printf >&2 'installation with an existing Nix installation, or manually increasing\n' - # shellcheck disable=SC2016 printf >&2 'your `system.stateVersion` setting.\n' printf >&2 '\n' printf >&2 'You can set the configured group ID to match the actual value:\n' @@ -132,46 +116,21 @@ let fi ''; - nixDaemon = if config.nix.useDaemon then '' - if ! dscl . -read /Groups/nixbld PrimaryGroupID &> /dev/null; then - printf >&2 'error: The daemon should not be enabled for single-user installs, aborting activation\n' - printf >&2 'Disable the nix-daemon service:\n' + nixDaemon = '' + if [[ "$(stat --format='%u' /nix)" != 0 ]]; then + printf >&2 'error: single‐user install detected, aborting activation\n' + printf >&2 'nix-darwin now only supports managing multi‐user daemon installations\n' + printf >&2 'of Nix. You can uninstall nix-darwin and Nix and then reinstall both to\n' + printf >&2 'fix this.\n' printf >&2 '\n' - printf >&2 ' services.nix-daemon.enable = false;\n' + printf >&2 'If you don’t want to do that, you can disable management of the Nix\n' + printf >&2 'installation with:\n' printf >&2 '\n' - # shellcheck disable=SC2016 - printf >&2 'and remove `nix.useDaemon` from your configuration if it is present.\n' + printf >&2 ' nix.enable = false;\n' printf >&2 '\n' + printf >&2 'See the `nix.enable` option documentation for caveats.\n' exit 2 fi - '' else '' - if dscl . -read /Groups/nixbld PrimaryGroupID &> /dev/null; then - printf >&2 'error: The daemon should be enabled for multi-user installs, aborting activation\n' - printf >&2 'Enable the nix-daemon service:\n' - printf >&2 '\n' - printf >&2 ' services.nix-daemon.enable = true;\n' - printf >&2 '\n' - exit 2 - fi - ''; - - nixChannels = '' - channelsLink=$(readlink "$HOME/.nix-defexpr/channels") || true - case "$channelsLink" in - *"$USER"*) - ;; - "") - ;; - *) - echo "error: The ~/.nix-defexpr/channels symlink does not point your users channels, aborting activation" >&2 - echo "Running nix-channel will regenerate it" >&2 - echo >&2 - echo " rm ~/.nix-defexpr/channels" >&2 - echo " nix-channel --update" >&2 - echo >&2 - exit 2 - ;; - esac ''; nixInstaller = '' @@ -196,7 +155,7 @@ let darwinConfig=$(NIX_PATH=$nixPath nix-instantiate --find-file darwin-config) || true if ! test -e "$darwinConfig"; then echo "error: Changed but target does not exist, aborting activation" >&2 - echo "Create ''${darwinConfig:-~/.nixpkgs/darwin-configuration.nix} or set environment.darwinConfig:" >&2 + echo "Create ''${darwinConfig:-/etc/nix-darwin/configuration.nix} or set environment.darwinConfig:" >&2 echo >&2 echo " environment.darwinConfig = \"$(nix-instantiate --find-file darwin-config 2> /dev/null || echo '***')\";" >&2 echo >&2 @@ -211,8 +170,8 @@ let if ! test -e "$darwinPath"; then echo "error: Changed but target does not exist, aborting activation" >&2 echo "Add the darwin repo as a channel or set nix.nixPath:" >&2 - echo "$ nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin" >&2 - echo "$ nix-channel --update" >&2 + echo "$ sudo nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin" >&2 + echo "$ sudo nix-channel --update" >&2 echo >&2 echo "or set" >&2 echo >&2 @@ -225,8 +184,8 @@ let if ! test -e "$nixpkgsPath"; then echo "error: Changed but target does not exist, aborting activation" >&2 echo "Add a nixpkgs channel or set nix.nixPath:" >&2 - echo "$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable nixpkgs" >&2 - echo "$ nix-channel --update" >&2 + echo "$ sudo nix-channel --add http://nixos.org/channels/nixpkgs-unstable nixpkgs" >&2 + echo "$ sudo nix-channel --update" >&2 echo >&2 echo "or set" >&2 echo >&2 @@ -236,50 +195,12 @@ let fi ''; - nixStore = '' - if test -w /nix/var/nix/db -a ! -O /nix/store; then - echo >&2 "error: the store is not owned by this user, but /nix/var/nix/db is writable" - echo >&2 "If you are using the daemon:" - echo >&2 - echo >&2 " sudo chown -R root:wheel /nix/var/nix/db" - echo >&2 - echo >&2 "Otherwise:" - echo >&2 - echo >&2 " sudo chown -R $USER:staff /nix/store" - echo >&2 - exit 2 - fi - ''; - - nixGarbageCollector = '' - if test -O /nix/store; then - echo "error: A single-user install can't run gc as root, aborting activation" >&2 - echo "Configure the garbage collector to run as the current user:" >&2 - echo >&2 - echo " nix.gc.user = \"$USER\";" >&2 - echo >&2 - exit 2 - fi - ''; - - nixStoreOptimiser = '' - if test -O /nix/store; then - echo "error: A single-user install can't run optimiser as root, aborting activation" >&2 - echo "Configure the optimiser to run as the current user:" >&2 - echo >&2 - echo " nix.optimise.user = \"$USER\";" >&2 - echo >&2 - exit 2 - fi - ''; - # TODO: Remove this a couple years down the line when we can assume # that anyone who cares about security has upgraded. oldSshAuthorizedKeysDirectory = '' if [[ -d /etc/ssh/authorized_keys.d ]]; then printf >&2 '\e[1;31merror: /etc/ssh/authorized_keys.d exists, aborting activation\e[0m\n' printf >&2 'SECURITY NOTICE: The previous implementation of the\n' - # shellcheck disable=SC2016 printf >&2 '`users.users..openssh.authorizedKeys.*` options would not delete\n' printf >&2 'authorized keys files when the setting for a given user was removed.\n' printf >&2 '\n' @@ -302,36 +223,47 @@ let echo "Homebrew doesn't seem to be installed. Please install homebrew separately." >&2 echo "You can install homebrew using the following command:" >&2 echo >&2 - # shellcheck disable=SC2016 echo ' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' >&2 echo >&2 exit 2 fi ''; + + # some mac devices, notably notebook do not support restartAfterPowerFailure option + restartAfterPowerFailureIsSupported = '' + if sudo /usr/sbin/systemsetup -getRestartPowerFailure | grep -q "Not supported"; then + printf >&2 "\e[1;31merror: restarting after power failure is not supported on your machine\e[0m\n" >&2 + printf >&2 "Please ensure that \`power.restartAfterPowerFailure\` is not set.\n" >&2 + exit 2 + fi + ''; in { + imports = [ + (mkRemovedOptionModule [ "system" "checks" "verifyNixChannels" ] "This check has been removed.") + ]; + options = { system.checks.verifyNixPath = mkOption { type = types.bool; - default = true; + default = config.nix.enable; description = "Whether to run the NIX_PATH validation checks."; }; - system.checks.verifyNixChannels = mkOption { - type = types.bool; - default = config.nix.channel.enable; - description = "Whether to run the nix-channels validation checks."; - }; - system.checks.verifyBuildUsers = mkOption { type = types.bool; default = - (config.nix.useDaemon && !(config.nix.settings.auto-allocate-uids or false)) - || config.nix.configureBuildUsers; + config.nix.enable && !(config.nix.settings.auto-allocate-uids or false); description = "Whether to run the Nix build users validation checks."; }; + system.checks.verifyMacOSVersion = mkOption { + type = types.bool; + default = true; + description = "Whether to run the macOS version check."; + }; + system.checks.text = mkOption { internal = true; type = types.lines; @@ -342,21 +274,16 @@ in config = { system.checks.text = mkMerge [ - darwinChanges - runLink - (mkIf (cfg.verifyBuildUsers && !config.nix.configureBuildUsers) oldBuildUsers) - (mkIf cfg.verifyBuildUsers buildUsers) + (mkIf cfg.verifyMacOSVersion macOSVersion) + (mkIf config.nix.enable determinate) (mkIf cfg.verifyBuildUsers preSequoiaBuildUsers) - (mkIf config.nix.configureBuildUsers buildGroupID) - nixDaemon - nixStore - (mkIf (config.nix.gc.automatic && config.nix.gc.user == null) nixGarbageCollector) - (mkIf (config.nix.optimise.automatic && config.nix.optimise.user == null) nixStoreOptimiser) - (mkIf cfg.verifyNixChannels nixChannels) + (mkIf cfg.verifyBuildUsers buildGroupID) + (mkIf config.nix.enable nixDaemon) nixInstaller (mkIf cfg.verifyNixPath nixPath) oldSshAuthorizedKeysDirectory (mkIf config.homebrew.enable homebrewInstalled) + (mkIf (config.power.restartAfterPowerFailure != null) restartAfterPowerFailureIsSupported) ]; system.activationScripts.checks.text = '' diff --git a/modules/system/default.nix b/modules/system/default.nix index a1862fae..8351dcc6 100644 --- a/modules/system/default.nix +++ b/modules/system/default.nix @@ -135,7 +135,10 @@ in chmod u+x $out/activate-user unset activationUserScript - shellcheck $out/activate $out/activate-user + # We exclude the warnings for `…` in single‐quote strings and + # non‐ASCII quotation marks as they are noisy and lead to a lot + # of false positives in our user‐facing output: + shellcheck --exclude=SC2016,SC1112 $out/activate $out/activate-user echo -n "$systemConfig" > $out/systemConfig diff --git a/modules/system/defaults-write.nix b/modules/system/defaults-write.nix index 87b179b8..a00b0e42 100644 --- a/modules/system/defaults-write.nix +++ b/modules/system/defaults-write.nix @@ -9,6 +9,9 @@ let "defaults write ${domain} '${key}' $'${strings.escape [ "'" ] (generators.toPlist { } value)}'"; defaultsToList = domain: attrs: mapAttrsToList (writeDefault domain) (filterAttrs (n: v: v != null) attrs); + # Filter out options to not pass through + # dock has alias options that we need to ignore + dockFiltered = (builtins.removeAttrs cfg.dock ["expose-group-by-app"]); # defaults alf = defaultsToList "/Library/Preferences/com.apple.alf" cfg.alf; @@ -21,7 +24,7 @@ let LaunchServices = defaultsToList "com.apple.LaunchServices" cfg.LaunchServices; NSGlobalDomain = defaultsToList "-g" cfg.NSGlobalDomain; menuExtraClock = defaultsToList "com.apple.menuextra.clock" cfg.menuExtraClock; - dock = defaultsToList "com.apple.dock" cfg.dock; + dock = defaultsToList "com.apple.dock" dockFiltered; finder = defaultsToList "com.apple.finder" cfg.finder; hitoolbox = defaultsToList "com.apple.HIToolbox" cfg.hitoolbox; magicmouse = defaultsToList "com.apple.AppleMultitouchMouse" cfg.magicmouse; @@ -39,7 +42,7 @@ let CustomSystemPreferences = flatten (mapAttrsToList (name: value: defaultsToList name value) cfg.CustomSystemPreferences); - mkIfAttrs = list: mkIf (any (attrs: attrs != { }) list); + mkIfLists = list: mkIf (any (attrs: attrs != [ ]) list); in { @@ -54,7 +57,7 @@ in else types.float.check x; }; - system.activationScripts.defaults.text = mkIfAttrs [ + system.activationScripts.defaults.text = mkIfLists [ alf loginwindow smb @@ -71,7 +74,7 @@ in ${concatStringsSep "\n" CustomSystemPreferences} ''; - system.activationScripts.userDefaults.text = mkIfAttrs + system.activationScripts.userDefaults.text = mkIfLists [ GlobalPreferences LaunchServices diff --git a/modules/system/defaults/WindowManager.nix b/modules/system/defaults/WindowManager.nix index 38fbaa37..7b9048cc 100644 --- a/modules/system/defaults/WindowManager.nix +++ b/modules/system/defaults/WindowManager.nix @@ -57,6 +57,38 @@ with lib; ''; }; + system.defaults.WindowManager.EnableTilingByEdgeDrag = mkOption { + type = types.nullOr types.bool; + default = null; + description = '' + Enable dragging windows to screen edges to tile them. The default is true. + ''; + }; + + system.defaults.WindowManager.EnableTopTilingByEdgeDrag = mkOption { + type = types.nullOr types.bool; + default = null; + description = '' + Enable dragging windows to the menu bar to fill the screen. The default is true. + ''; + }; + + system.defaults.WindowManager.EnableTilingOptionAccelerator = mkOption { + type = types.nullOr types.bool; + default = null; + description = '' + Enable holding alt to tile windows. The default is true. + ''; + }; + + system.defaults.WindowManager.EnableTiledWindowMargins = mkOption { + type = types.nullOr types.bool; + default = null; + description = '' + Enable window margins when tiling windows. The default is true. + ''; + }; + system.defaults.WindowManager.StandardHideWidgets = mkOption { type = types.nullOr types.bool; default = null; diff --git a/modules/system/defaults/dock.nix b/modules/system/defaults/dock.nix index 8c43830b..53b63b35 100644 --- a/modules/system/defaults/dock.nix +++ b/modules/system/defaults/dock.nix @@ -6,6 +6,10 @@ let # Should only be used with options that previously used floats defined as strings. inherit (config.lib.defaults.types) floatWithDeprecationError; in { + imports = [ + (mkRenamedOptionModule [ "system" "defaults" "dock" "expose-group-by-app" ] [ "system" "defaults" "dock" "expose-group-apps" ]) + ]; + options = { system.defaults.dock.appswitcher-all-displays = mkOption { @@ -67,11 +71,11 @@ in { ''; }; - system.defaults.dock.expose-group-by-app = mkOption { + system.defaults.dock.expose-group-apps = mkOption { type = types.nullOr types.bool; default = null; description = '' - Whether to group windows by application in Mission Control's Exposé. The default is true. + Whether to group windows by application in Mission Control's Exposé. The default is false. ''; }; @@ -124,16 +128,72 @@ in { }; system.defaults.dock.persistent-apps = mkOption { - type = types.nullOr (types.listOf (types.either types.path types.str)); + type = let + taggedType = types.attrTag { + app = mkOption { + description = "An application to be added to the dock."; + type = types.str; + }; + file = mkOption { + description = "A file to be added to the dock."; + type = types.str; + }; + folder = mkOption { + description = "A folder to be added to the dock."; + type = types.str; + }; + spacer = mkOption { + description = "A spacer to be added to the dock. Can be small or regular size."; + type = types.submodule { + options.small = mkOption { + description = "Whether the spacer is small."; + type = types.bool; + default = false; + }; + }; + }; + }; + + simpleType = types.either types.str types.path; + toTagged = path: { app = path; }; + in + types.nullOr (types.listOf (types.coercedTo simpleType toTagged taggedType)); default = null; - example = [ "/Applications/Safari.app" "/System/Applications/Utilities/Terminal.app" ]; + example = [ + { app = "/Applications/Safari.app"; } + { spacer = { small = false; }; } + { spacer = { small = true; }; } + { folder = "/System/Applications/Utilities"; } + { file = "/User/example/Downloads/test.csv"; } + ]; description = '' - Persistent applications in the dock. + Persistent applications, spacers, files, and folders in the dock. ''; - apply = value: - if !(isList value) - then value - else map (app: { tile-data = { file-data = { _CFURLString = app; _CFURLStringType = 0; }; }; }) value; + apply = + let + toTile = item: if item ? app then { + tile-data.file-data = { + _CFURLString = item.app; + _CFURLStringType = 0; + }; + } else if item ? spacer then { + tile-data = { }; + tile-type = if item.spacer.small then "small-spacer-tile" else "spacer-tile"; + } else if item ? folder then { + tile-data.file-data = { + _CFURLString = "file://" + item.folder; + _CFURLStringType = 15; + }; + tile-type = "directory-tile"; + } else if item ? file then { + tile-data.file-data = { + _CFURLString = "file://" + item.file; + _CFURLStringType = 15; + }; + tile-type = "file-tile"; + } else item; + in + value: if value == null then null else map toTile value; }; system.defaults.dock.persistent-others = mkOption { @@ -230,7 +290,6 @@ in { Magnified icon size on hover. The default is 16. ''; }; - system.defaults.dock.wvous-tl-corner = mkOption { type = types.nullOr types.ints.positive; diff --git a/modules/system/defaults/screencapture.nix b/modules/system/defaults/screencapture.nix index 80dcaabb..351ca5f2 100644 --- a/modules/system/defaults/screencapture.nix +++ b/modules/system/defaults/screencapture.nix @@ -48,5 +48,20 @@ with lib; Show thumbnail after screencapture before writing to file. The default is true. ''; }; + + system.defaults.screencapture.target = mkOption { + type = types.nullOr (types.enum [ "file" "clipboard" "preview" "mail" "messages" ]); + default = null; + description = '' + Target to which screencapture should save screenshot to. The default is "file". + Valid values include: + + * `file`: Saves as a file in location specified by `system.defaults.screencapture.location` + * `clipboard`: Saves screenshot to clipboard + * `preview`: Opens screenshot in Preview app + * `mail` + * `messages` + ''; + }; }; } diff --git a/modules/system/defaults/universalaccess.nix b/modules/system/defaults/universalaccess.nix index 8a2de90f..996f23b6 100644 --- a/modules/system/defaults/universalaccess.nix +++ b/modules/system/defaults/universalaccess.nix @@ -28,7 +28,6 @@ with lib; default = null; description = '' Disable transparency in the menu bar and elsewhere. - Requires macOS Yosemite or later. The default is false. ''; }; diff --git a/modules/system/version.nix b/modules/system/version.nix index 826ad352..3fac46ef 100644 --- a/modules/system/version.nix +++ b/modules/system/version.nix @@ -51,7 +51,7 @@ in system.maxStateVersion = mkOption { internal = true; type = types.int; - default = 5; + default = 6; }; system.darwinLabel = mkOption { @@ -59,11 +59,18 @@ in description = "Label to be used in the names of generated outputs."; }; + system.darwinRelease = mkOption { + readOnly = true; + type = types.str; + default = (lib.importJSON ../../version.json).release; + description = "The nix-darwin release (e.g. `24.11`)."; + }; + system.darwinVersion = mkOption { internal = true; type = types.str; - default = "darwin${toString cfg.stateVersion}${cfg.darwinVersionSuffix}"; - description = "The full darwin version (e.g. `darwin4.2abdb5a`)."; + default = cfg.darwinRelease + cfg.darwinVersionSuffix; + description = "The full nix-darwin version (e.g. `24.11.2abdb5a`)."; }; system.darwinVersionSuffix = mkOption { @@ -72,7 +79,7 @@ in default = if cfg.darwinRevision != null then ".${substring 0 7 cfg.darwinRevision}" else ""; - description = "The short darwin version suffix (e.g. `.2abdb5a`)."; + description = "The short nix-darwin version suffix (e.g. `.2abdb5a`)."; }; system.darwinRevision = mkOption { @@ -86,14 +93,15 @@ in readOnly = true; type = types.str; default = lib.trivial.release; - description = "The nixpkgs release (e.g. `16.03`)."; + description = "The nixpkgs release (e.g. `24.11`)."; }; + # TODO: Shouldn’t mismatch the Darwin release, rethink all this… system.nixpkgsVersion = mkOption { internal = true; type = types.str; default = cfg.nixpkgsRelease + cfg.nixpkgsVersionSuffix; - description = "The full nixpkgs version (e.g. `16.03.1160.f2d4ee1`)."; + description = "The full nixpkgs version (e.g. `24.11.1160.f2d4ee1`)."; }; system.nixpkgsVersionSuffix = mkOption { @@ -124,7 +132,7 @@ in config = { # This default is set here rather than up there so that the options # documentation is not reprocessed on every commit - system.darwinLabel = mkDefault "${cfg.nixpkgsVersion}+${cfg.darwinVersion}"; + system.darwinLabel = mkDefault cfg.darwinVersion; assertions = [ { diff --git a/modules/users/default.nix b/modules/users/default.nix index 574f5a4e..706abbfa 100644 --- a/modules/users/default.nix +++ b/modules/users/default.nix @@ -147,9 +147,8 @@ in homeDirectory=''${homeDirectory#NFSHomeDirectory: } if ! sudo dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then - if [[ -n "$SSH_CONNECTION" ]]; then + if [[ "$(launchctl managername)" != Aqua ]]; then printf >&2 '\e[1;31merror: users cannot be %s over SSH without Full Disk Access, aborting activation\e[0m\n' "$2" - # shellcheck disable=SC2016 printf >&2 'The user %s could not be %s as `darwin-rebuild` was not executed with Full Disk Access over SSH.\n' "$1" "$2" printf >&2 'You can either:\n' printf >&2 '\n' @@ -157,7 +156,6 @@ in printf >&2 '\n' printf >&2 'or\n' printf >&2 '\n' - # shellcheck disable=SC2016 printf >&2 ' run `darwin-rebuild` in a graphical session.\n' printf >&2 '\n' printf >&2 'The option "Allow full disk access for remote users" can be found by\n' @@ -171,12 +169,10 @@ in if ! sudo dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then printf >&2 '\e[1;31merror: permission denied when trying to %s user %s, aborting activation\e[0m\n' "$2" "$1" - # shellcheck disable=SC2016 printf >&2 '`darwin-rebuild` requires permissions to administrate your computer,\n' printf >&2 'please accept the dialog that pops up.\n' printf >&2 '\n' - # shellcheck disable=SC2016 - printf >&2 'If you do not wish to be prompted every time `darwin-rebuild updates your users,\n' + printf >&2 'If you do not wish to be prompted every time `darwin-rebuild` updates your users,\n' printf >&2 'you can grant Full Disk Access to your terminal emulator in System Settings.\n' printf >&2 '\n' printf >&2 'This can be found in System Settings > Privacy & Security > Full Disk Access.\n' @@ -224,7 +220,6 @@ in if [ "$u" -gt 501 ]; then # TODO: add `darwin.primaryUser` as well if [[ ${name} == "$USER" ]]; then - # shellcheck disable=SC2016 printf >&2 '\e[1;31merror: refusing to delete the user calling `darwin-rebuild` (%s), aborting activation\e[0m\n', ${name} exit 1 fi diff --git a/pkgs/darwin-uninstaller/configuration.nix b/pkgs/darwin-uninstaller/configuration.nix index 295477a6..ce6be6ca 100644 --- a/pkgs/darwin-uninstaller/configuration.nix +++ b/pkgs/darwin-uninstaller/configuration.nix @@ -12,16 +12,16 @@ with lib; launchd.daemons = mkForce {}; launchd.user.agents = mkForce {}; - # Don't try to reload `nix-daemon` - nix.useDaemon = mkForce false; + # Restore any unmanaged `nix-daemon`. + nix.enable = false; system.activationScripts.postUserActivation.text = mkAfter '' - if [[ -L ~/.nix-defexpr/channels/darwin ]]; then - nix-channel --remove darwin || true - fi + nix-channel --remove darwin || true ''; system.activationScripts.postActivation.text = mkAfter '' + nix-channel --remove darwin || true + if [[ -L /Applications/Nix\ Apps ]]; then rm /Applications/Nix\ Apps fi @@ -30,14 +30,6 @@ with lib; rm /etc/static fi - # If the Nix Store is owned by root then we're on a multi-user system - if [[ -O /nix/store ]]; then - if [[ -e /nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist ]]; then - sudo cp /nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist /Library/LaunchDaemons/org.nixos.nix-daemon.plist - sudo launchctl load -w /Library/LaunchDaemons/org.nixos.nix-daemon.plist - fi - fi - # grep will return 1 when no lines matched which makes this line fail with `set -eo pipefail` dscl . -list /Users UserShell | { grep "\s/run/" || true; } | awk '{print $1}' | while read -r user; do shell=$(dscl . -read /Users/"$user" UserShell) diff --git a/pkgs/darwin-uninstaller/default.nix b/pkgs/darwin-uninstaller/default.nix index da58682e..6b43bcfc 100644 --- a/pkgs/darwin-uninstaller/default.nix +++ b/pkgs/darwin-uninstaller/default.nix @@ -31,8 +31,11 @@ in writeShellApplication { echo >&2 " - remove /Applications/Nix Apps symlink" echo >&2 " - cleanup static /etc files" echo >&2 " - disable and remove all launchd services managed by nix-darwin" - if [[ $(stat -f '%Su' /nix/store) == "root" ]]; then - echo >&2 " - restore nix-daemon service from nix installer as this is a multi-user install" + if [[ + -e /run/current-system/Library/LaunchDaemons/org.nixos.nix-daemon.plist + && -e /nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist + ]]; then + echo >&2 " - restore nix-daemon service from the Nix installer" fi echo >&2 @@ -56,7 +59,6 @@ in writeShellApplication { if [[ -L /run ]]; then if [[ -e /etc/synthetic.conf ]]; then sudo sed -i -E '/^run[[:space:]]/d' /etc/synthetic.conf - sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B &>/dev/null || true sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t &>/dev/null || true echo >&2 "NOTE: the /run symlink will be removed on reboot" else @@ -78,7 +80,7 @@ in writeShellApplication { echo >&2 echo >&2 "checking darwin channel" - test -e ~/.nix-defexpr/channels/darwin && exit 1 + nix-instantiate --find-file darwin && exit 1 echo >&2 "checking /etc" test -e /etc/static && exit 1 echo >&2 "checking /run/current-system" @@ -88,7 +90,7 @@ in writeShellApplication { launchctl print system/org.nixos.nix-daemon pgrep -l nix-daemon test -e /Library/LaunchDaemons/org.nixos.nix-daemon.plist - [[ "$(shasum -a 256 /Library/LaunchDaemons/org.nixos.nix-daemon.plist | awk '{print $1}')" == "$(shasum -a 256 /Library/LaunchDaemons/org.nixos.nix-daemon.plist | awk '{print $1}')" ]] + [[ "$(shasum -a 256 /Library/LaunchDaemons/org.nixos.nix-daemon.plist | awk '{print $1}')" == "$(shasum -a 256 /nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist | awk '{print $1}')" ]] nix-store --store daemon -q --hash ${stdenv.shell} fi echo >&2 ok diff --git a/pkgs/nix-tools/darwin-option.sh b/pkgs/nix-tools/darwin-option.sh index 8e40e93c..79ecee91 100755 --- a/pkgs/nix-tools/darwin-option.sh +++ b/pkgs/nix-tools/darwin-option.sh @@ -1,7 +1,9 @@ #! @shell@ set -e set -o pipefail -export PATH=@path@:$PATH + +export PATH=@path@ +export NIX_PATH=${NIX_PATH:-@nixPath@} evalNix() { nix-instantiate --eval --strict "${extraEvalFlags[@]}" -E "with import {}; $*" 2>/dev/null diff --git a/pkgs/nix-tools/darwin-rebuild.sh b/pkgs/nix-tools/darwin-rebuild.sh index 7824913d..8f207a7a 100644 --- a/pkgs/nix-tools/darwin-rebuild.sh +++ b/pkgs/nix-tools/darwin-rebuild.sh @@ -1,8 +1,9 @@ #! @shell@ set -e set -o pipefail -export PATH=@path@:$PATH +export PATH=@path@ +export NIX_PATH=${NIX_PATH:-@nixPath@} showSyntax() { echo "darwin-rebuild [--help] {edit | switch | activate | build | check | changelog}" >&2 @@ -22,15 +23,9 @@ showSyntax() { } sudo() { - # REMOVEME when support for macOS 10.13 is dropped - # macOS 10.13 does not support sudo --preserve-env so we make this conditional - if command sudo --help | grep -- --preserve-env= >/dev/null; then - # We use `env` before our command to ensure the preserved PATH gets checked - # when trying to resolve the command to execute - command sudo -H --preserve-env=PATH --preserve-env=SSH_CONNECTION env "$@" - else - command sudo -H "$@" - fi + # We use `env` before our command to ensure the preserved PATH gets checked + # when trying to resolve the command to execute + command sudo -H --preserve-env=PATH --preserve-env=SSH_CONNECTION env "$@" } # Parse the command line. @@ -177,8 +172,8 @@ if [ "$action" != build ]; then fi if [ "$action" = edit ]; then - darwinConfig=$(nix-instantiate --find-file darwin-config) if [ -z "$flake" ]; then + darwinConfig=$(nix-instantiate "${extraBuildFlags[@]}" --find-file darwin-config) exec "${EDITOR:-vi}" "$darwinConfig" else exec nix "${flakeFlags[@]}" edit "${extraLockFlags[@]}" -- "$flake#$flakeAttr" diff --git a/pkgs/nix-tools/default.nix b/pkgs/nix-tools/default.nix index 5fdc3821..8d6b89b7 100644 --- a/pkgs/nix-tools/default.nix +++ b/pkgs/nix-tools/default.nix @@ -5,12 +5,31 @@ , substituteAll , stdenv , profile ? "/nix/var/nix/profiles/system" -, nixPackage ? "/nix/var/nix/profiles/default" -, systemPath ? "$HOME/.nix-profile/bin:/etc/profiles/per-user/$USER/bin:/run/current-system/sw/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin" +, # This should be kept in sync with the default + # `environment.systemPath`. We err on side of including conditional + # things like the profile directories, since they’re more likely to + # help than hurt, and this default is mostly used for fresh + # installations anyway. + systemPath ? lib.concatStringsSep ":" [ + "$HOME/.nix-profile/bin" + "/etc/profiles/per-user/$USER/bin" + "/run/current-system/sw/bin" + "/nix/var/nix/profiles/default/bin" + "/usr/local/bin" + "/usr/bin" + "/bin" + "/usr/sbin" + "/sbin" +] +, # This should be kept in sync with the default `nix.nixPath`. + nixPath ? lib.concatStringsSep ":" [ + "darwin-config=/etc/nix-darwin/configuration.nix" + "/nix/var/nix/profiles/per-user/root/channels" +] }: let - extraPath = lib.makeBinPath [ nixPackage coreutils jq git ]; + extraPath = lib.makeBinPath [ coreutils jq git ]; writeProgram = name: env: src: substituteAll ({ @@ -25,14 +44,14 @@ in { darwin-option = writeProgram "darwin-option" { - inherit path; + inherit path nixPath; inherit (stdenv) shell; } ./darwin-option.sh; darwin-rebuild = writeProgram "darwin-rebuild" { - inherit path profile; + inherit path nixPath profile; inherit (stdenv) shell; postInstall = '' mkdir -p $out/share/zsh/site-functions diff --git a/release.nix b/release.nix index b3e2df7e..eaf30044 100644 --- a/release.nix +++ b/release.nix @@ -80,7 +80,6 @@ in { tests.activation-scripts = makeTest ./tests/activation-scripts.nix; tests.autossh = makeTest ./tests/autossh.nix; - tests.checks-nix-gc = makeTest ./tests/checks-nix-gc.nix; tests.environment-path = makeTest ./tests/environment-path.nix; tests.environment-terminfo = makeTest ./tests/environment-terminfo.nix; tests.homebrew = makeTest ./tests/homebrew.nix; @@ -88,6 +87,7 @@ in { tests.launchd-setenv = makeTest ./tests/launchd-setenv.nix; tests.networking-hostname = makeTest ./tests/networking-hostname.nix; tests.networking-networkservices = makeTest ./tests/networking-networkservices.nix; + tests.nix-enable = makeTest ./tests/nix-enable.nix; tests.nixpkgs-overlays = makeTest ./tests/nixpkgs-overlays.nix; tests.programs-ssh = makeTest ./tests/programs-ssh.nix; tests.programs-tmux = makeTest ./tests/programs-tmux.nix; @@ -103,6 +103,7 @@ in { tests.sockets-nix-daemon = makeTest ./tests/sockets-nix-daemon.nix; tests.services-aerospace = makeTest ./tests/services-aerospace.nix; tests.services-dnsmasq = makeTest ./tests/services-dnsmasq.nix; + tests.services-dnscrypt-proxy = makeTest ./tests/services-dnscrypt-proxy.nix; tests.services-eternal-terminal = makeTest ./tests/services-eternal-terminal.nix; tests.services-nix-gc = makeTest ./tests/services-nix-gc.nix; tests.services-nix-optimise = makeTest ./tests/services-nix-optimise.nix; diff --git a/tests/checks-nix-gc.nix b/tests/checks-nix-gc.nix deleted file mode 100644 index e3dccd7f..00000000 --- a/tests/checks-nix-gc.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ config, pkgs, ... }: - -let - nix = pkgs.runCommand "nix-2.2" {} "mkdir -p $out"; -in - -{ - nix.gc.automatic = true; - nix.package = nix; - - test = '' - echo checking nix-gc validation >&2 - grep "nix.gc.user = " ${config.out}/activate-user - - echo checking nix-gc service in /Library/LaunchDaemons >&2 - grep "org.nixos.nix-gc" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist - (! grep "UserName" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist) - ''; -} diff --git a/tests/environment-path.nix b/tests/environment-path.nix index 0bb9a055..b5e4754d 100644 --- a/tests/environment-path.nix +++ b/tests/environment-path.nix @@ -30,8 +30,8 @@ with lib; "afterProfile/bin" "/usr/local/bin" "/usr/bin" - "/usr/sbin" "/bin" + "/usr/sbin" "/sbin" "afterPath" ]}" diff --git a/tests/fixtures/system-defaults-write/activate-user.txt b/tests/fixtures/system-defaults-write/activate-user.txt index f6e9bbaa..7162a414 100644 --- a/tests/fixtures/system-defaults-write/activate-user.txt +++ b/tests/fixtures/system-defaults-write/activate-user.txt @@ -235,6 +235,11 @@ defaults write com.apple.dock 'autohide-delay' $' 0.240000 ' +defaults write com.apple.dock 'expose-group-apps' $' + + + +' defaults write com.apple.dock 'orientation' $' @@ -250,7 +255,7 @@ defaults write com.apple.dock 'persistent-apps' $'file-data _CFURLString - MyApp.app + /Applications/MyApp.app _CFURLStringType 0 @@ -262,12 +267,56 @@ defaults write com.apple.dock 'persistent-apps' $'file-data _CFURLString - Cool.app + /Applications/Cool.app _CFURLStringType 0 + + tile-data + + + + tile-type + small-spacer-tile + + + tile-data + + + + tile-type + spacer-tile + + + tile-data + + file-data + + _CFURLString + file:///Applications/Utilities + _CFURLStringType + 15 + + + tile-type + directory-tile + + + tile-data + + file-data + + _CFURLString + file:///Users/example/Downloads/test.csv + _CFURLStringType + 15 + + + tile-type + file-tile + ' defaults write com.apple.dock 'persistent-others' $' @@ -421,6 +470,11 @@ defaults write com.apple.screencapture 'location' $' /tmp ' +defaults write com.apple.screencapture 'target' $' + + +file +' defaults write com.apple.screensaver 'askForPassword' $' @@ -509,6 +563,26 @@ defaults write com.apple.WindowManager 'EnableStandardClickToShowDesktop' $' ' +defaults write com.apple.WindowManager 'EnableTiledWindowMargins' $' + + + +' +defaults write com.apple.WindowManager 'EnableTilingByEdgeDrag' $' + + + +' +defaults write com.apple.WindowManager 'EnableTilingOptionAccelerator' $' + + + +' +defaults write com.apple.WindowManager 'EnableTopTilingByEdgeDrag' $' + + + +' defaults write com.apple.WindowManager 'GloballyEnabled' $' @@ -568,4 +642,4 @@ defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'Sound' $' 24 -' \ No newline at end of file +' diff --git a/tests/nix-enable.nix b/tests/nix-enable.nix new file mode 100644 index 00000000..e052aa2f --- /dev/null +++ b/tests/nix-enable.nix @@ -0,0 +1,16 @@ +{ config, ... }: + +{ + nix.enable = false; + + test = '' + printf >&2 'checking for unexpected Nix binary in /sw/bin\n' + [[ -e ${config.out}/sw/bin/nix-env ]] && exit 1 + + printf >&2 'checking for unexpected nix-daemon plist in /Library/LaunchDaemons\n' + [[ -e ${config.out}/Library/LaunchDaemons/org.nixos.nix-daemon.plist ]] && exit 1 + + printf >&2 'checking for late‐bound Nix lookup in /activate\n' + grep nixEnvPath= ${config.out}/activate + ''; +} diff --git a/tests/services-aerospace.nix b/tests/services-aerospace.nix index 8cbd292f..088c92d9 100644 --- a/tests/services-aerospace.nix +++ b/tests/services-aerospace.nix @@ -8,6 +8,7 @@ in services.aerospace.enable = true; services.aerospace.package = aerospace; services.aerospace.settings = { + after-startup-command = [ "layout tiles" ]; gaps = { outer.left = 8; outer.bottom = 8; @@ -20,6 +21,32 @@ in alt-k = "focus up"; alt-l = "focus right"; }; + on-window-detected = [ + { + "if" = { + app-id = "Another.Cool.App"; + during-aerospace-startup = false; + }; + check-further-callbacks = false; + run = "move-node-to-workspace m"; + } + { + "if".app-name-regex-substring = "finder|calendar"; + run = "layout floating"; + } + { + "if".workspace = "1"; + run = "layout h_accordion"; + } + ]; + workspace-to-monitor-force-assignment = { + "1" = 1; + "2" = "main"; + "3" = "secondary"; + "4" = "built-in"; + "5" = "^built-in retina display$"; + "6" = [ "secondary" "dell" ]; + }; }; test = '' @@ -31,6 +58,35 @@ in ${config.out}/user/Library/LaunchAgents/org.nixos.aerospace.plist` echo >&2 "checking config in $conf" - if [ `cat $conf | wc -l` -eq "27" ]; then echo "aerospace.toml config correctly contains 27 lines"; else return 1; fi + grep 'after-startup-command = \["layout tiles"\]' $conf + + grep 'bottom = 8' $conf + grep 'left = 8' $conf + grep 'right = 8' $conf + grep 'top = 8' $conf + + grep 'alt-h = "focus left"' $conf + grep 'alt-j = "focus down"' $conf + grep 'alt-k = "focus up"' $conf + grep 'alt-l = "focus right"' $conf + + grep 'check-further-callbacks = false' $conf + grep 'run = "move-node-to-workspace m"' $conf + grep 'app-id = "Another.Cool.App"' $conf + grep 'during-aerospace-startup = false' $conf + + grep 'run = "layout floating"' $conf + grep 'app-name-regex-substring = "finder|calendar"' $conf + (! grep 'window-title-regex-substring' $conf) + + grep 'workspace = "1"' $conf + grep 'run = "layout h_accordion"' $conf + + grep '1 = 1' $conf + grep '2 = "main"' $conf + grep '3 = "secondary"' $conf + grep '4 = "built-in"' $conf + grep '5 = "^built-in retina display$"' $conf + grep '6 = \["secondary", "dell"\]' $conf ''; } diff --git a/tests/services-dnscrypt-proxy.nix b/tests/services-dnscrypt-proxy.nix new file mode 100644 index 00000000..15d59739 --- /dev/null +++ b/tests/services-dnscrypt-proxy.nix @@ -0,0 +1,23 @@ +{ + config, + pkgs, + ... +}: + +let + dnscrypt-proxy = pkgs.runCommand "dnscrypt-proxy-0.0.0" { } "mkdir $out"; +in +{ + services.dnscrypt-proxy.enable = true; + services.dnscrypt-proxy.package = dnscrypt-proxy; + + test = '' + + echo >&2 "checking dnscrypt-proxy service in /Library/LaunchDaemons" + grep -q "org.nixos.dnscrypt-proxy" -- ${config.out}/Library/LaunchDaemons/org.nixos.dnscrypt-proxy.plist + grep -q "dnscrypt-proxy-start" -- ${config.out}/Library/LaunchDaemons/org.nixos.dnscrypt-proxy.plist + + echo >&2 "checking dnscrypt-proxy system user in /Library/LaunchDaemons" + grep -q "_dnscrypt-proxy" -- ${config.out}/Library/LaunchDaemons/org.nixos.dnscrypt-proxy.plist + ''; +} diff --git a/tests/services-nix-daemon.nix b/tests/services-nix-daemon.nix index 3a217cbb..f8f06e5d 100644 --- a/tests/services-nix-daemon.nix +++ b/tests/services-nix-daemon.nix @@ -6,7 +6,6 @@ let in { - services.nix-daemon.enable = true; nix.package = nix; launchd.labelPrefix = "org.nix-darwin"; # should not have an effect on nix-daemon @@ -27,8 +26,5 @@ in echo checking nix-daemon reload in /activate >&2 grep "launchctl kill HUP system/org.nixos.nix-daemon" ${config.out}/activate - - echo checking NIX_REMOTE=daemon in setEnvironment >&2 - grep "NIX_REMOTE=daemon" ${config.system.build.setEnvironment} ''; } diff --git a/tests/services-nix-gc.nix b/tests/services-nix-gc.nix index 6d7cdef8..9c8ba817 100644 --- a/tests/services-nix-gc.nix +++ b/tests/services-nix-gc.nix @@ -7,19 +7,13 @@ in { nix.gc.automatic = true; nix.gc.options = "--delete-older-than 30d"; - nix.gc.user = "nixuser"; nix.package = nix; test = '' echo checking nix-gc service in /Library/LaunchDaemons >&2 grep "org.nixos.nix-gc" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist grep "/bin/wait4path /nix/store && exec ${nix}/bin/nix-collect-garbage --delete-older-than 30d" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist - grep "UserName" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist - grep "nixuser" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist (! grep "KeepAlive" ${config.out}/Library/LaunchDaemons/org.nixos.nix-gc.plist) - - echo checking nix-gc validation >&2 - (! grep "nix.gc.user = " ${config.out}/activate-user) ''; } diff --git a/tests/services-nix-optimise.nix b/tests/services-nix-optimise.nix index 4108eb0f..acdc1dce 100644 --- a/tests/services-nix-optimise.nix +++ b/tests/services-nix-optimise.nix @@ -6,7 +6,6 @@ in { nix.optimise.automatic = true; - nix.optimise.user = "nixuser"; nix.package = nix; test = '' @@ -15,11 +14,6 @@ in ${config.out}/Library/LaunchDaemons/org.nixos.nix-optimise.plist grep "/bin/wait4path /nix/store && exec ${nix}/bin/nix-store --optimise" \ ${config.out}/Library/LaunchDaemons/org.nixos.nix-optimise.plist - grep "UserName" ${config.out}/Library/LaunchDaemons/org.nixos.nix-optimise.plist - grep "nixuser" ${config.out}/Library/LaunchDaemons/org.nixos.nix-optimise.plist (! grep "KeepAlive" ${config.out}/Library/LaunchDaemons/org.nixos.nix-optimise.plist) - - echo checking nix-optimise validation >&2 - (! grep "nix.optimise.user = " ${config.out}/activate-user) ''; } diff --git a/tests/sockets-nix-daemon.nix b/tests/sockets-nix-daemon.nix index 606b7b6f..bafd6c10 100644 --- a/tests/sockets-nix-daemon.nix +++ b/tests/sockets-nix-daemon.nix @@ -5,7 +5,6 @@ let in { - services.nix-daemon.enable = true; services.nix-daemon.enableSocketListener = true; nix.package = nix; launchd.labelPrefix = "org.nix-darwin"; # should not have an effect on nix-daemon diff --git a/tests/system-defaults-write.nix b/tests/system-defaults-write.nix index 078cf82f..19ce3c4f 100644 --- a/tests/system-defaults-write.nix +++ b/tests/system-defaults-write.nix @@ -46,10 +46,18 @@ system.defaults.menuExtraClock.Show24Hour = false; system.defaults.menuExtraClock.ShowDayOfWeek = true; system.defaults.menuExtraClock.ShowDate = 2; + system.defaults.dock.expose-group-apps = true; system.defaults.dock.appswitcher-all-displays = false; system.defaults.dock.autohide-delay = 0.24; system.defaults.dock.orientation = "left"; - system.defaults.dock.persistent-apps = ["MyApp.app" "Cool.app"]; + system.defaults.dock.persistent-apps = [ + "/Applications/MyApp.app" + { app = "/Applications/Cool.app"; } + { spacer = { small = true; }; } + { spacer = { small = false; }; } + { folder = "/Applications/Utilities"; } + { file = "/Users/example/Downloads/test.csv"; } + ]; system.defaults.dock.persistent-others = ["~/Documents" "~/Downloads/file.txt"]; system.defaults.dock.scroll-to-open = false; system.defaults.finder.AppleShowAllFiles = true; @@ -73,6 +81,7 @@ system.defaults.finder.ShowRemovableMediaOnDesktop = false; system.defaults.hitoolbox.AppleFnUsageType = "Show Emoji & Symbols"; system.defaults.screencapture.location = "/tmp"; + system.defaults.screencapture.target = "file"; system.defaults.screencapture.include-date = true; system.defaults.screensaver.askForPassword = true; system.defaults.screensaver.askForPasswordDelay = 5; @@ -94,6 +103,10 @@ system.defaults.WindowManager.AppWindowGroupingBehavior = true; system.defaults.WindowManager.StandardHideDesktopIcons = false; system.defaults.WindowManager.HideDesktop = false; + system.defaults.WindowManager.EnableTilingByEdgeDrag = true; + system.defaults.WindowManager.EnableTopTilingByEdgeDrag = true; + system.defaults.WindowManager.EnableTilingOptionAccelerator = true; + system.defaults.WindowManager.EnableTiledWindowMargins = true; system.defaults.WindowManager.StandardHideWidgets = true; system.defaults.WindowManager.StageManagerHideWidgets = true; system.defaults.CustomUserPreferences = { diff --git a/version.json b/version.json new file mode 100644 index 00000000..1d2863a0 --- /dev/null +++ b/version.json @@ -0,0 +1,4 @@ +{ + "release": "25.05", + "isReleaseBranch": false +}