1
0
Fork 0
mirror of https://github.com/LnL7/nix-darwin.git synced 2024-12-14 11:57:34 +00:00

Merge branch 'LnL7:master' into keyboard-shortcuts-update

This commit is contained in:
Jun Matsushita 2024-11-24 11:53:53 +01:00 committed by GitHub
commit 07543d1519
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
71 changed files with 1869 additions and 1119 deletions

View file

@ -1,18 +0,0 @@
name: "Build"
on:
# curl -fsSL -XPOST \
# -H "Accept: application/vnd.github.everest-preview+json" \
# -H "Authorization: token $GITHUB_TOKEN" \
# --data '{"event_type": "build", "client_payload": {"args": "-f channel:nixpkgs-unstable hello"}}' \
# https://api.github.com/repos/LnL7/nix-darwin/dispatches
repository_dispatch:
types:
- build
jobs:
build:
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
- run: |
nix build ${{ github.event.client_payload.args }} -vL

View file

@ -1,23 +0,0 @@
name: "Debug"
on:
# curl -fsSL -XPOST \
# -H "Accept: application/vnd.github.everest-preview+json" \
# -H "Authorization: token $GITHUB_TOKEN" \
# --data '{"event_type": "debug"}' \
# https://api.github.com/repos/LnL7/nix-darwin/dispatches
repository_dispatch:
types:
- debug
jobs:
debug:
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
- run: |
nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs
nix-channel --update
- run: |
nix-shell -A installer
nix-shell -A installer.check
- uses: mxschmitt/action-tmate@v3

View file

@ -2,144 +2,144 @@ name: "Test"
on:
pull_request:
push:
branches:
- master
env:
CURRENT_STABLE_CHANNEL: nixpkgs-24.05-darwin
jobs:
test-stable:
runs-on: macos-12
timeout-minutes: 30
runs-on: macos-13
steps:
- uses: actions/checkout@v3
# We use the Determinate Systems installer for 2.18 because the
# Sequoia UID/GID changes have not yet been backported to the
# official installer for that version.
- uses: actions/checkout@v4
- name: Install nix corresponding to latest stable channel
uses: DeterminateSystems/nix-installer-action@main
uses: cachix/install-nix-action@v30
with:
nix-package-url: https://releases.nixos.org/nix/nix-2.18.5/nix-2.18.5-x86_64-darwin.tar.xz
- run: nix-build ./release.nix -I nixpkgs=channel:${{ env.CURRENT_STABLE_CHANNEL }} -I darwin=. -A tests
- run: nix-build ./release.nix -I nixpkgs=channel:${{ env.CURRENT_STABLE_CHANNEL }} -I darwin=. -A manpages
- run: nix-build ./release.nix -I nixpkgs=channel:${{ env.CURRENT_STABLE_CHANNEL }} -I darwin=. -A examples.simple
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-12
timeout-minutes: 30
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install nix from current unstable channel
uses: cachix/install-nix-action@v27
uses: cachix/install-nix-action@v30
with:
install_url: https://releases.nixos.org/nix/nix-2.24.6/install
- run: nix-build ./release.nix -I nixpkgs=channel:nixpkgs-unstable -I darwin=. -A tests
- run: nix-build ./release.nix -I nixpkgs=channel:nixpkgs-unstable -I darwin=. -A manpages
- run: nix-build ./release.nix -I nixpkgs=channel:nixpkgs-unstable -I darwin=. -A examples.simple
install_url: https://releases.nixos.org/nix/nix-2.24.9/install
- run: nix flake check --override-input nixpkgs nixpkgs/nixpkgs-unstable
install-against-stable:
runs-on: macos-12
timeout-minutes: 30
runs-on: macos-13
steps:
- uses: actions/checkout@v3
# We use the Determinate Systems installer for 2.18 because the
# Sequoia UID/GID changes have not yet been backported to the
# official installer for that version.
- uses: actions/checkout@v4
- name: Install nix corresponding to latest stable channel
uses: DeterminateSystems/nix-installer-action@main
uses: cachix/install-nix-action@v30
with:
nix-package-url: https://releases.nixos.org/nix/nix-2.18.5/nix-2.18.5-x86_64-darwin.tar.xz
- name: Install ${{ env.CURRENT_STABLE_CHANNEL }} channel
install_url: https://releases.nixos.org/nix/nix-2.18.8/install
nix_path: nixpkgs=channel:${{ env.CURRENT_STABLE_CHANNEL }}
- 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 --update
- name: Install nix-darwin and test
- name: Install nix-darwin
run: |
export NIX_PATH=nixpkgs=channel:${{ env.CURRENT_STABLE_CHANNEL }}
export NIX_PATH=$HOME/.nix-defexpr/channels
# We run nix-darwin twice to test that it can create darwin-configuration correctly for us
# but we expect it to fail setting up /etc/nix/nix.conf
nix-shell -A installer || true
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/# nix.package = pkgs.nix;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \
~/.nixpkgs/darwin-configuration.nix
"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-shell -A installer
nix-shell -A installer.check
- name: Build and activate default derivation
nix run .#darwin-rebuild \
-- switch \
-I darwin-config=$HOME/.config/nix-darwin/configuration.nix
- name: Switch to new configuration
run: |
. /etc/static/bashrc
. /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: |
export NIX_PATH=$HOME/.nix-defexpr/channels
nix-shell -A uninstaller
nix-shell -A uninstaller.check
- name: Debugging tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15
with:
limit-access-to-actor: true
# 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/${{ env.CURRENT_STABLE_CHANNEL }}
nix run .#darwin-uninstaller.tests.uninstaller \
--extra-experimental-features "nix-command flakes" \
--override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }}
install-against-unstable:
runs-on: macos-12
runs-on: macos-13
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install nix from current unstable channel
uses: cachix/install-nix-action@v27
uses: cachix/install-nix-action@v30
with:
install_url: https://releases.nixos.org/nix/nix-2.24.6/install
install_url: https://releases.nixos.org/nix/nix-2.24.9/install
nix_path: nixpkgs=channel:nixpkgs-unstable
- name: Install nixpkgs-unstable channel
- 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
- name: Install nix-darwin and test
- name: Install nix-darwin
run: |
export NIX_PATH=$HOME/.nix-defexpr/channels
# We run nix-darwin twice to test that it can create darwin-configuration correctly for us
# but we expect it to fail setting up /etc/nix/nix.conf
nix-shell -A installer || true
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/# nix.package = pkgs.nix;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \
~/.nixpkgs/darwin-configuration.nix
"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-shell -A installer
nix-shell -A installer.check
- name: Build and activate default derivation
nix run .#darwin-rebuild \
-- switch \
-I darwin-config=$HOME/.config/nix-darwin/configuration.nix
- name: Switch to new configuration
run: |
. /etc/static/bashrc
. /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: |
export NIX_PATH=$HOME/.nix-defexpr/channels
nix-shell -A uninstaller
nix-shell -A uninstaller.check
- name: Debugging tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15
with:
limit-access-to-actor: true
# 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-12
timeout-minutes: 30
runs-on: macos-13
steps:
- uses: actions/checkout@v3
# We use the Determinate Systems installer for 2.18 because the
# Sequoia UID/GID changes have not yet been backported to the
# official installer for that version.
- name: Install nix corresponding to latest stable channel
uses: DeterminateSystems/nix-installer-action@main
- uses: actions/checkout@v4
- name: Install nix version corresponding to latest stable channel
uses: cachix/install-nix-action@v30
with:
nix-package-url: https://releases.nixos.org/nix/nix-2.18.5/nix-2.18.5-x86_64-darwin.tar.xz
install_url: https://releases.nixos.org/nix/nix-2.18.8/install
- name: Install nix-darwin
run: |
mkdir -p ~/.config/nix-darwin
@ -148,81 +148,43 @@ jobs:
nix flake init -t $darwin
nixConfHash=$(shasum -a 256 /etc/nix/nix.conf | cut -d ' ' -f 1)
/usr/bin/sed -i.bak \
"s/# nix.package = pkgs.nix;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \
"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/${{ env.CURRENT_STABLE_CHANNEL }}
- name: Rebuild and activate simple flake, but this time using nix-darwin's flake interface
- name: Switch to new configuration
run: |
. /etc/static/bashrc
darwin-rebuild build --flake ./modules/examples/flake#simple --override-input nix-darwin . --override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }}
- name: Test git submodules
. /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/${{ env.CURRENT_STABLE_CHANNEL }}
hello
- name: Test uninstallation of nix-darwin
run: |
. /etc/static/bashrc
mkdir -p /tmp/{test-nix-darwin-submodules,example-submodule}
pushd /tmp/example-submodule
echo '"hello"' > hello.nix
git init
git add .
git commit -m "add a submodule we will import"
popd
cp -a ./modules/examples/. /tmp/test-nix-darwin-submodules
cp -a ./modules/examples/flake/flake.nix /tmp/test-nix-darwin-submodules
pushd /tmp/test-nix-darwin-submodules
/usr/bin/sed -i.bak \
'\#modules = \[#s#configuration#configuration ./simple.nix#' \
./flake.nix
/usr/bin/sed -i.bak \
's#pkgs.vim#pkgs."${import ./submodule-test/hello.nix}"#' \
./simple.nix
git init
git add flake.nix simple.nix
git \
-c protocol.file.allow=always \
submodule add /tmp/example-submodule submodule-test
popd
# Should fail
darwin-rebuild build \
--flake /tmp/test-nix-darwin-submodules#simple \
--override-input nix-darwin . \
--override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} \
&& {
printf 'succeeded while expecting failure due to submodule\n' >/dev/stderr
exit 1
}
# Should also fail
darwin-rebuild build \
--flake /tmp/test-nix-darwin-submodules?submodules=0#simple \
--override-input nix-darwin . \
--override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} \
&& {
printf 'succeeded while expecting failure due to submodule\n' >/dev/stderr
exit 1
}
# Should succeed
darwin-rebuild build \
--flake /tmp/test-nix-darwin-submodules?submodules=1#simple \
--override-input nix-darwin . \
--override-input nixpkgs nixpkgs/${{ env.CURRENT_STABLE_CHANNEL }} \
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-12
runs-on: macos-13
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install nix from current unstable channel
uses: cachix/install-nix-action@v27
uses: cachix/install-nix-action@v30
with:
install_url: https://releases.nixos.org/nix/nix-2.24.6/install
install_url: https://releases.nixos.org/nix/nix-2.24.9/install
- name: Install nix-darwin
run: |
mkdir -p ~/.config/nix-darwin
@ -231,75 +193,30 @@ jobs:
nix flake init -t $darwin
nixConfHash=$(shasum -a 256 /etc/nix/nix.conf | cut -d ' ' -f 1)
/usr/bin/sed -i.bak \
"s/# nix.package = pkgs.nix;/nix.settings.access-tokens = [ \"github.com=\${{ secrets.GITHUB_TOKEN }}\" ]; environment.etc.\"nix\/nix.conf\".knownSha256Hashes = [ \"$nixConfHash\" ];/" \
"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: Rebuild and activate simple flake, but this time using nix-darwin's flake interface
- name: Switch to new configuration
run: |
. /etc/static/bashrc
darwin-rebuild build --flake ./modules/examples/flake#simple --override-input nix-darwin . --override-input nixpkgs nixpkgs/nixpkgs-unstable
- name: Test git submodules
run: |
. /etc/static/bashrc
. /etc/bashrc
mkdir -p /tmp/{test-nix-darwin-submodules,example-submodule}
/usr/bin/sed -i.bak \
"s/pkgs.vim/pkgs.hello/" \
~/.config/nix-darwin/flake.nix
pushd /tmp/example-submodule
echo '"hello"' > hello.nix
git init
git add .
git commit -m "add a submodule we will import"
popd
cp -a ./modules/examples/. /tmp/test-nix-darwin-submodules
cp -a ./modules/examples/flake/flake.nix /tmp/test-nix-darwin-submodules
pushd /tmp/test-nix-darwin-submodules
/usr/bin/sed -i.bak \
'\#modules = \[#s#configuration#configuration ./simple.nix#' \
./flake.nix
/usr/bin/sed -i.bak \
's#pkgs.vim#pkgs."${import ./submodule-test/hello.nix}"#' \
./simple.nix
git init
git add flake.nix simple.nix
git \
-c protocol.file.allow=always \
submodule add /tmp/example-submodule submodule-test
popd
# Should fail
darwin-rebuild build \
--flake /tmp/test-nix-darwin-submodules#simple \
--override-input nix-darwin . \
--override-input nixpkgs nixpkgs/nixpkgs-unstable \
&& {
printf 'succeeded while expecting failure due to submodule\n' >/dev/stderr
exit 1
}
# Should also fail
darwin-rebuild build \
--flake /tmp/test-nix-darwin-submodules?submodules=0#simple \
--override-input nix-darwin . \
--override-input nixpkgs nixpkgs/nixpkgs-unstable \
&& {
printf 'succeeded while expecting failure due to submodule\n' >/dev/stderr
exit 1
}
# Should succeed
darwin-rebuild build \
--flake /tmp/test-nix-darwin-submodules?submodules=1#simple \
darwin-rebuild switch --flake ~/.config/nix-darwin#simple \
--override-input nix-darwin . \
--override-input nixpkgs nixpkgs/nixpkgs-unstable
# Should also succeed
darwin-rebuild build \
--flake git+file:///tmp/test-nix-darwin-submodules?submodules=1#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

View file

@ -3,25 +3,23 @@ on:
push:
branches:
- master
paths:
- '**.nix'
jobs:
update-manual:
runs-on: macos-12
runs-on: macos-13
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# So that we fetch all branches, since we need to checkout the `gh-pages` branch later.
fetch-depth: 0
- name: Install Nix
uses: cachix/install-nix-action@v27
uses: cachix/install-nix-action@v30
- name: Build manual
run: |
nix-build ./release.nix -I nixpkgs=channel:nixpkgs-24.05-darwin -I darwin=. -A manualHTML
nix build .#manualHTML
- name: Push update to manual
run: |

143
README.md
View file

@ -2,80 +2,30 @@
# nix-darwin
![Test](https://github.com/LnL7/nix-darwin/workflows/Test/badge.svg)
[![Test](https://github.com/LnL7/nix-darwin/actions/workflows/test.yml/badge.svg)](https://github.com/LnL7/nix-darwin/actions/workflows/test.yml)
Nix modules for darwin, `/etc/nixos/configuration.nix` for macOS.
This project aims to bring the convenience of a declarative system approach to macOS.
nix-darwin is built up around [Nixpkgs](https://github.com/NixOS/nixpkgs), quite similar to [NixOS](https://nixos.org/).
## Installing
## Prerequisites
To install nix-darwin, a working installation of [Nix](https://github.com/NixOS/nix#installation) is required.
The only prerequisite is a Nix implementation, both Nix and Lix are supported.
If you wish to use nix-darwin with flakes, please refer to the [flakes](#flakes) section.
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:
```bash
nix-build https://github.com/LnL7/nix-darwin/archive/master.tar.gz -A installer
./result/bin/darwin-installer
```
- 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 [Lix installer](https://lix.systems/install/#on-any-other-linuxmacos-system) supports both flake-based and channel-based setups.
> NOTE: the system activation scripts don't overwrite existing etc files, so files like `/etc/bashrc` and `/etc/zshrc` won't be
> updated by default. If you didn't use the installer or skipped some of the options you'll have to take care of this yourself.
> Either modify the existing file to source/import the one from `/etc/static` or remove it. Some examples:
- `mv /etc/bashrc /etc/bashrc.before-nix-darwin`
- `echo 'if test -e /etc/static/bashrc; then . /etc/static/bashrc; fi' | sudo tee -a /etc/bashrc`
## Updating
## Getting started
The installer will configure a channel for this repository.
Despite being an experimental feature in Nix currently, nix-darwin recommends that beginners use flakes to manage their nix-darwin configurations.
```bash
nix-channel --update darwin
darwin-rebuild changelog
```
> NOTE: If you are using Nix as a daemon service the channel for that will be owned by root.
> Use `sudo -i nix-channel --update darwin` instead.
## Uninstalling
To run the latest version of the uninstaller, you can run the following command:
```
nix --extra-experimental-features "nix-command flakes" run nix-darwin#darwin-uninstaller
```
If that command doesn't work for you, you can try the locally installed uninstaller:
```
darwin-uninstaller
```
## Example configuration
Configuration lives in `~/.nixpkgs/darwin-configuration.nix`. Check out
[modules/examples](https://github.com/LnL7/nix-darwin/tree/master/modules/examples) for some example configurations.
```nix
{ pkgs, ... }:
{
# List packages installed in system profile. To search by name, run:
# $ nix-env -qaP | grep wget
environment.systemPackages =
[ pkgs.vim
];
# Auto upgrade nix package and the daemon service.
services.nix-daemon.enable = true;
nix.package = pkgs.nix;
}
```
## Flakes
nix-darwin aims for both non-flake and flake configurations to be well supported despite flakes being an experimental feature in Nix.
<details>
<summary>Flakes (Recommended for beginners)</summary>
### Step 1. Creating `flake.nix`
@ -128,7 +78,7 @@ Make sure to set `nixpkgs.hostPlatform` in your `configuration.nix` to either `x
### Step 2. Installing `nix-darwin`
Instead of using `darwin-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:
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
@ -160,34 +110,67 @@ nix-darwin.lib.darwinSystem {
{ pkgs, lib, inputs }:
# inputs.self, inputs.nix-darwin, and inputs.nixpkgs can be accessed here
```
</details>
<details>
<summary>Channels</summary>
### Step 1. Creating `configuration.nix`
Copy the [simple](./modules/examples/simple.nix) example to `~/.config/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
```
### Step 3. Installing `nix-darwin`
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
```
### Step 4. Using `nix-darwin`
After installing, you can run `darwin-rebuild` to apply changes to your system:
```bash
darwin-rebuild switch
```
### Step 5. Updating `nix-darwin`
You can update `nix-darwin` using the following command:
```bash
nix-channel --update darwin
```
</details>
## Documentation
Reference documentation of all the options is available [here](https://daiderd.com/nix-darwin/manual/index.html).
This can also be accessed locally using `man 5 configuration.nix`.
`darwin-help` will open up a local copy of the reference documentation, it can also be found online [here](https://daiderd.com/nix-darwin/manual/index.html).
`darwin-help` will open a HTML version of the manpage in the default browser.
The documentation is also available as manpages by running `man 5 configuration.nix`.
Furthermore there's `darwin-option` to introspect the settings of a system and its available options.
> NOTE: `darwin-option` is only available to non-flake installations.
## Uninstalling
To run the latest version of the uninstaller, you can run the following command:
```
$ darwin-option services.activate-system.enable
Value:
true
Default:
false
Example:
no example
Description:
Whether to activate system at boot time.
nix --extra-experimental-features "nix-command flakes" run nix-darwin#darwin-uninstaller
```
There's also a small wiki https://github.com/LnL7/nix-darwin/wiki about
specific topics, like macOS upgrades.
If that command doesn't work for you, you can try the locally installed uninstaller:
```
darwin-uninstaller
```
## Tests

View file

@ -1,8 +1,8 @@
{ nixpkgs ? <nixpkgs>
, configuration ? <darwin-config>
, lib ? pkgs.lib
, pkgs ? import nixpkgs { inherit system; }
, system ? builtins.currentSystem
, pkgs ? import nixpkgs { inherit system; }
, lib ? pkgs.lib
}:
let
@ -15,20 +15,9 @@ let
nixpkgs.system = lib.mkDefault system;
};
};
# The source code of this repo needed by the installer.
nix-darwin = lib.cleanSource (
lib.cleanSourceWith {
# We explicitly specify a name here otherwise `cleanSource` will use the
# basename of ./. which might be different for different clones of this
# repo leading to non-reproducible outputs.
name = "nix-darwin";
src = ./.;
}
);
in
eval // {
installer = pkgs.callPackage ./pkgs/darwin-installer { inherit nix-darwin; };
uninstaller = pkgs.callPackage ./pkgs/darwin-uninstaller { };
darwin-uninstaller = pkgs.callPackage ./pkgs/darwin-uninstaller { };
inherit (pkgs.callPackage ./pkgs/nix-tools { }) darwin-option darwin-rebuild darwin-version;
}

View file

@ -79,11 +79,17 @@ in rec {
'@DARWIN_OPTIONS_JSON@' \
${optionsJSON}/share/doc/darwin/options.json
# Pass --redirects option if nixos-render-docs supports it
if nixos-render-docs manual html --help | grep --silent -E '^\s+--redirects\s'; then
redirects_opt="--redirects ${./redirects.json}"
fi
# TODO: --manpage-urls?
nixos-render-docs -j $NIX_BUILD_CORES manual html \
--manpage-urls ${pkgs.writeText "manpage-urls.json" "{}"} \
--revision ${lib.escapeShellArg revision} \
--generator "nixos-render-docs ${lib.version}" \
$redirects_opt \
--stylesheet style.css \
--stylesheet highlightjs/mono-blue.css \
--script ./highlightjs/highlight.pack.js \
@ -118,18 +124,18 @@ in rec {
# TODO: get these parameterized in upstream nixos-render-docs
sed -i -e '
/^\.TH / s|NixOS|Darwin|g
/^\.TH / s|NixOS|nix-darwin|g
/^\.SH "NAME"$/ {
N
s|NixOS|Darwin|g
s|NixOS|nix-darwin|g
}
/^\.SH "DESCRIPTION"$/ {
N; N
s|/etc/nixos/configuration|configuration|g
s|NixOS|Darwin|g
s|nixos|darwin|g
s|NixOS|nix-darwin|g
s|nixos|nix-darwin|g
}
/\.SH "AUTHORS"$/ {

View file

@ -1,4 +1,4 @@
# Darwin Configuration Options {#book-darwin-manual}
# nix-darwin Configuration Options {#book-darwin-manual}
## Version @DARWIN_VERSION@
```{=include=} options

View file

@ -0,0 +1,5 @@
{
"book-darwin-manual": [
"index.html#book-darwin-manual"
]
}

View file

@ -1,9 +1,15 @@
{
# WARNING this is very much still experimental.
description = "A collection of darwin modules";
outputs = { self, nixpkgs }: let
forAllSystems = nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" ];
forAllSystems = nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ];
forDarwinSystems = nixpkgs.lib.genAttrs [ "aarch64-darwin" "x86_64-darwin" ];
jobs = forAllSystems (system: import ./release.nix {
inherit nixpkgs system;
nix-darwin = self;
});
in {
lib = {
evalConfig = import ./eval-config.nix;
@ -48,7 +54,6 @@
darwinModules.hydra = ./modules/examples/hydra.nix;
darwinModules.lnl = ./modules/examples/lnl.nix;
darwinModules.ofborg = ./modules/examples/ofborg.nix;
darwinModules.simple = ./modules/examples/simple.nix;
templates.default = {
@ -56,23 +61,11 @@
description = "nix flake init -t nix-darwin";
};
checks = forAllSystems (system: let
simple = self.lib.darwinSystem {
modules = [
self.darwinModules.simple
{ nixpkgs.hostPlatform = system; }
];
};
in {
simple = simple.system;
checks = forDarwinSystems (system: jobs.${system}.tests // jobs.${system}.examples);
inherit (simple.config.system.build.manual)
optionsJSON
manualHTML
manpages;
});
packages = forAllSystems (system: let
packages = forAllSystems (system: {
inherit (jobs.${system}.docs) manualHTML manpages optionsJSON;
} // (nixpkgs.lib.optionalAttrs (nixpkgs.lib.hasSuffix "darwin" system) (let
pkgs = import nixpkgs {
inherit system;
overlays = [ self.overlays.default ];
@ -81,6 +74,6 @@
default = self.packages.${system}.darwin-rebuild;
inherit (pkgs) darwin-option darwin-rebuild darwin-version darwin-uninstaller;
});
})));
};
}

View file

@ -18,6 +18,11 @@ in
{
imports = [
(mkRenamedOptionModule ["environment" "postBuild"] ["environment" "extraSetup"])
(mkRemovedOptionModule [ "environment" "loginShell" ] ''
This option was only used to change the default command in tmux.
This has been removed in favour of changing the default command or default shell in tmux directly.
'')
];
options = {
@ -74,12 +79,6 @@ in
'';
};
environment.loginShell = mkOption {
type = types.str;
default = "$SHELL -l";
description = "Configure default login shell.";
};
environment.variables = mkOption {
type = types.attrsOf (types.either types.str (types.listOf types.str));
default = {};
@ -198,6 +197,7 @@ in
name = "system-path";
paths = cfg.systemPackages;
postBuild = cfg.extraSetup;
ignoreCollisions = true;
inherit (cfg) pathsToLink extraOutputsToInstall;
};

View file

@ -1,5 +1,5 @@
{
description = "Example Darwin system flake";
description = "Example nix-darwin system flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
@ -16,15 +16,10 @@
[ pkgs.vim
];
# Auto upgrade nix package and the daemon service.
services.nix-daemon.enable = true;
# nix.package = pkgs.nix;
# Necessary for using flakes on this system.
nix.settings.experimental-features = "nix-command flakes";
# Create /etc/zshrc that loads the nix-darwin environment.
programs.zsh.enable = true; # default shell on catalina
# Enable alternative shell support in nix-darwin.
# programs.fish.enable = true;
# Set Git commit hash for darwin-version.
@ -35,7 +30,7 @@
system.stateVersion = 5;
# The platform the configuration will be used on.
nixpkgs.hostPlatform = "x86_64-darwin";
nixpkgs.hostPlatform = "aarch64-darwin";
};
in
{
@ -44,8 +39,5 @@
darwinConfigurations."simple" = nix-darwin.lib.darwinSystem {
modules = [ configuration ];
};
# Expose the package set, including overlays, for convenience.
darwinPackages = self.darwinConfigurations."simple".pkgs;
};
}

View file

@ -1,25 +1,14 @@
{ config, lib, pkgs, ... }:
with lib;
let
environment = concatStringsSep " "
environment = lib.concatStringsSep " "
[ "NIX_REMOTE=daemon"
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
];
in
{
# Create /etc/bashrc that loads the nix-darwin environment.
programs.bash.enable = true;
programs.bash.enableCompletion = false;
# Recreate /run/current-system symlink after boot.
services.activate-system.enable = true;
services.nix-daemon.enable = true;
nix.settings.substituters = [ http://cache1 ];
nix.settings.substituters = [ "http://cache1" ];
nix.settings.trusted-public-keys = [ "cache.daiderd.com-1:R8KOWZ8lDaLojqD+v9dzXAqGn29gEzPTTbr/GIpCTrI=" ];
nix.settings.trusted-users = [ "@admin" "@hydra" ];
@ -31,7 +20,7 @@ in
nix.gc.automatic = true;
nix.gc.options = "--max-freed $((25 * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | awk '{ print $4 }')))";
environment.etc."per-user/hydra/ssh/authorized_keys".text = concatStringsSep "\n"
environment.etc."per-user/hydra/ssh/authorized_keys".text = lib.concatStringsSep "\n"
[ "command=\"${environment} ${config.nix.package}/bin/nix-store --serve --write\" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCVsc0pHGsskoayziMhA2e59bHPWe0bbKgusmqhuJFBGQ1BAk9UmPzKCWE3nCiV6CLD1+SygVkBjb06DYtc+94BnzviCa9qZtL0G4+2vhp6x8OvXh8xlf/eWw3k5MWlvu+kjJFpbW8wHWTiUqzH+uEeHklAosT0lFNjiIYd/Vs3JAezhUR62a6c7ZjWOd5F7ALGEKzOiwC4i37kSgGsIWNCbe0Ku7gyr718zhMGeyxax6saHhnkSpIB+7d6oHhKeiJSFMWctNmz1/qxXUPbxNaJvqgdKlVHhN+B7x/TIbkVr5pTC59Okx9LTcpflFIv79VT+Gf1K7VypZpSvJjG0xFRt8iDs1+ssWFBfvpo94vUbZ+ZwMDcBGR5iJeO41Gj5fYn5aaDl32RXfJ9Fkwael1L6pcXtkIc66jk+KQQpgoeNj8Y3Emntpqva/2AM41wDDvr5tKp5KhEKFLM95CoiWq+g88pZLcpqLK7wooDVqNkVUEbMaj9lBN0AzU9mcsIRGvTa6CmWAdBvwqS2fRZD97Oarqct9AWgb0X6mOUq9BJNi4i4xvjgnVkylLwtLUnibR/PeXMtkb9bv6BEZXNf5ACqxSjKXJyaIHI65I5TILCr5eEgaujgvmkREn6U3T1NZAUIeVe9aVYLqehYh79OHUBzggoHqidRrXBB/6zdg9UgQ=="
"command=\"${environment} ${config.nix.package}/bin/nix-store --serve --write\" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCnubA1pRqlpoAXkZ1q5nwhqi1RY2z840wFLFDj7vAMSups9E2U8PNIVtuVYApZpkBWIpzD4GGbQTF5Itnu5uBpJswc2Yat9yGWO/guuVyXIaRoBIM0Pg1WBWcWsz+k4rNludu9UQ74FHqEiqZIuIuOcgV+RIZn8xQlGt2kUqN9TWboHhZz8Zhx7EtGSJH6MJRLn3mA/pPjOF6k1jiiFG1pVDuqBTZPANkelWYCWAJ46jCyhxXltWE/jkBYGc/XbB8yT7DFE1XC6TVsSEp68R9PhVG3yqxqY06sniEyduSoGt/TDr6ycERd93bvLElXFATes85YiFszeaUgayYSKwQPe0q7YeHMhIXL0UYJYaKVVgT9saFDiHDzde7kKe+NA+J4+TbIk7Y/Ywn0jepsYV13M7TyEqgqbu9fvVGF3JI9+4g0m1gAzHTa7n6iiAedtz+Pi79uCEpRD2hWSSoLWroyPlep8j1p2tygtFsrieePEukesoToCTwqg1Ejnjh+yKdtUbc6xpyRvl3hKeO8QbCpfaaVd27e4vE4lP2JMW6nOo8b0wlVXQIFe5K2zh52q1MSwhLAq6Kg8oPmgj0lru4IivmPc+/NVwd3Qj3E9ZB8LRfTesfbcxHrC8lF5dL/QpLMeLwebrwCxL19gI0kxmDIaUQuHSyP3B2z+EmBKcN/Xw=="
];
@ -53,4 +42,6 @@ in
chown hydra:hydra ~hydra ~hydra/.ssh ~hydra/.ssh/authorized_keys
echo "ok"
'';
system.stateVersion = 5;
}

View file

@ -1,10 +1,6 @@
{ config, lib, inputs, pkgs, ... }:
{
# imports = [ ~/.config/nixpkgs/darwin/local-configuration.nix ];
# system.patches = [ ./pam.patch ];
system.defaults.NSGlobalDomain.AppleKeyboardUIMode = 3;
system.defaults.NSGlobalDomain.ApplePressAndHoldEnabled = false;
system.defaults.NSGlobalDomain.InitialKeyRepeat = 10;
@ -50,13 +46,10 @@
pkgs.gnupg
pkgs.htop
pkgs.jq
pkgs.mosh
pkgs.ripgrep
pkgs.shellcheck
pkgs.vault
pkgs.qes
pkgs.darwin-zsh-completions
];
services.yabai.enable = true;
@ -84,7 +77,6 @@
# serviceConfig.ProcessType = "Background";
# };
services.nix-daemon.enable = true;
# services.nix-daemon.enableSocketListener = true;
nix.extraOptions = ''
@ -96,7 +88,7 @@
'';
nix.settings.trusted-public-keys = [ "cache.daiderd.com-1:R8KOWZ8lDaLojqD+v9dzXAqGn29gEzPTTbr/GIpCTrI=" ];
nix.settings.trusted-substituters = [ https://d3i7ezr9vxxsfy.cloudfront.net ];
nix.settings.trusted-substituters = [ "https://d3i7ezr9vxxsfy.cloudfront.net" ];
nix.settings.sandbox = true;
nix.settings.extra-sandbox-paths = [ "/private/tmp" "/private/var/tmp" "/usr/bin/env" ];
@ -199,7 +191,7 @@
# Dotfiles.
# programs.vim.package = mkForce pkgs.lnl.vim;
programs.bash.enableCompletion = true;
programs.bash.completion.enable = true;
programs.zsh.enable = true;
programs.zsh.enableBashCompletion = true;
@ -277,7 +269,6 @@
zle -N up-line-or-beginning-search
'';
environment.loginShell = "${pkgs.zsh}/bin/zsh -l";
environment.variables.SHELL = "${pkgs.zsh}/bin/zsh";
environment.variables.LANG = "en_US.UTF-8";
@ -303,8 +294,6 @@
fi
'';
# environment.darwinConfig = "$HOME/.config/nixpkgs/darwin/configuration.nix";
nixpkgs.config.allowUnfree = true;
nixpkgs.overlays = [
@ -332,4 +321,6 @@
nix.configureBuildUsers = true;
nix.nrBuildUsers = 32;
system.stateVersion = 5;
}

View file

@ -1,29 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
{
# Logs are enabled by default.
# $ tail -f /var/log/ofborg.log
services.ofborg.enable = true;
# services.ofborg.configFile = "/var/lib/ofborg/config.json";
# $ nix-channel --add https://github.com/NixOS/ofborg/archive/released.tar.gz ofborg
# $ nix-channel --update
services.ofborg.package = (import <ofborg> {}).ofborg.rs;
# Keep nix-daemon updated.
services.nix-daemon.enable = true;
nix.gc.automatic = true;
nix.gc.options = "--max-freed $((25 * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | awk '{ print $4 }')))";
# Manage user for ofborg, this enables creating/deleting users
# depending on what modules are enabled.
users.knownGroups = [ "ofborg" ];
users.knownUsers = [ "ofborg" ];
# Used for backwards compatibility, please read the changelog before changing.
# $ darwin-rebuild changelog
system.stateVersion = 5;
}

View file

@ -7,16 +7,10 @@
[ pkgs.vim
];
# Use a custom configuration.nix location.
# $ darwin-rebuild switch -I darwin-config=$HOME/.config/nixpkgs/darwin/configuration.nix
# environment.darwinConfig = "$HOME/.config/nixpkgs/darwin/configuration.nix";
# Use custom location for configuration.nix.
environment.darwinConfig = "$HOME/.config/nix-darwin/configuration.nix";
# Auto upgrade nix package and the daemon service.
# services.nix-daemon.enable = true;
# nix.package = pkgs.nix;
# Create /etc/zshrc that loads the nix-darwin environment.
programs.zsh.enable = true; # default shell on catalina
# Enable alternative shell support in nix-darwin.
# programs.fish.enable = true;
# Used for backwards compatibility, please read the changelog before changing.

View file

@ -38,10 +38,12 @@ in
ids.uids = {
nixbld = lib.mkDefault 350;
_prometheus-node-exporter = 534;
};
ids.gids = {
nixbld = lib.mkDefault (if config.system.stateVersion < 5 then 30000 else 350);
_prometheus-node-exporter = 534;
};
};

View file

@ -14,6 +14,7 @@
./system/activation-scripts.nix
./system/applications.nix
./system/defaults-write.nix
./system/defaults/controlcenter.nix
./system/defaults/LaunchServices.nix
./system/defaults/NSGlobalDomain.nix
./system/defaults/GlobalPreferences.nix
@ -21,6 +22,7 @@
./system/defaults/clock.nix
./system/defaults/dock.nix
./system/defaults/finder.nix
./system/defaults/hitoolbox.nix
./system/defaults/screencapture.nix
./system/defaults/screensaver.nix
./system/defaults/alf.nix
@ -52,7 +54,10 @@
./environment
./fonts
./launchd
./power
./power/sleep.nix
./services/activate-system
./services/aerospace
./services/autossh.nix
./services/buildkite-agents.nix
./services/chunkwm.nix
@ -72,6 +77,7 @@
./services/mopidy.nix
./services/monitoring/telegraf.nix
./services/monitoring/netdata.nix
./services/monitoring/prometheus-node-exporter.nix
./services/netbird.nix
./services/nix-daemon.nix
./services/nix-gc

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
with lib;
@ -8,15 +8,16 @@ let
hostnameRegEx = ''^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'';
emptyList = lst: if lst != [] then lst else ["empty"];
quoteStrings = concatMapStringsSep " " (str: "'${str}'");
onOff = cond: if cond then "on" else "off";
setNetworkServices = optionalString (cfg.knownNetworkServices != []) ''
networkservices=$(networksetup -listallnetworkservices)
${concatMapStringsSep "\n" (srv: ''
case "$networkservices" in
*'${srv}'*)
networksetup -setdnsservers '${srv}' ${quoteStrings (emptyList cfg.dns)}
networksetup -setsearchdomains '${srv}' ${quoteStrings (emptyList cfg.search)}
*${lib.escapeShellArg srv}*)
networksetup -setdnsservers ${lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.dns))}
networksetup -setsearchdomains ${lib.escapeShellArgs ([ srv ] ++ (emptyList cfg.search))}
;;
esac
'') cfg.knownNetworkServices}
@ -94,6 +95,16 @@ in
default = [];
description = "The list of search paths used when resolving domain names.";
};
networking.wakeOnLan.enable = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Enable Wake-on-LAN for the device.
Battery powered devices may require being connected to power.
'';
};
};
config = {
@ -107,6 +118,7 @@ in
echo "configuring networking..." >&2
${optionalString (cfg.computerName != null) ''
# shellcheck disable=SC1112
scutil --set ComputerName ${escapeShellArg cfg.computerName}
''}
${optionalString (cfg.hostName != null) ''
@ -117,6 +129,10 @@ in
''}
${setNetworkServices}
${optionalString (cfg.wakeOnLan.enable != null) ''
systemsetup -setWakeOnNetworkAccess '${onOff cfg.wakeOnLan.enable}' &> /dev/null
''}
'';
};

View file

@ -191,9 +191,6 @@ in
description = ''
Whether to distribute builds to the machines listed in
{option}`nix.buildMachines`.
NOTE: This requires services.nix-daemon.enable for a
multi-user install.
'';
};
@ -404,7 +401,7 @@ in
{ darwin-config = "${config.environment.darwinConfig}"; }
"/nix/var/nix/profiles/per-user/root/channels"
];
defaultText = lib.literalExpression ''
lib.optionals cfg.channel.enable [
# Include default path <darwin-config>.
@ -527,8 +524,10 @@ in
description = ''
If set to true, Nix automatically detects files in the store that have
identical contents, and replaces them with hard links to a single copy.
This saves disk space. If set to false (the default), you can still run
nix-store --optimise to get rid of duplicate files.
This saves disk space. If set to false (the default), you can enable
{option}`nix.optimise.automatic` to run {command}`nix-store --optimise`
periodically to get rid of duplicate files. You can also run
{command}`nix-store --optimise` manually.
'';
};
@ -686,7 +685,7 @@ in
nixPackage
pkgs.nix-info
]
++ optional (config.programs.bash.enableCompletion) pkgs.nix-bash-completions;
++ optional (config.programs.bash.completion.enable) pkgs.nix-bash-completions;
environment.etc."nix/nix.conf".source = nixConf;
@ -761,11 +760,17 @@ in
{ 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 = 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"));
message = "`nix.settings.auto-optimise-store` is known to corrupt the Nix Store, please use `nix.optimise.automatic` instead.";
}
];
# Not in NixOS module
warnings = [
(mkIf (!config.services.activate-system.enable && cfg.distributedBuilds) "services.activate-system is not enabled, a reboot could cause distributed builds to stop working.")
(mkIf (!cfg.distributedBuilds && cfg.buildMachines != []) "nix.distributedBuilds is not enabled, build machines won't be configured.")
];

47
modules/power/default.nix Normal file
View file

@ -0,0 +1,47 @@
{ config, lib, ... }:
let
cfg = config.power;
types = lib.types;
onOff = cond: if cond then "on" else "off";
in
{
options = {
power.restartAfterPowerFailure = lib.mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to restart the computer after a power failure.
'';
};
power.restartAfterFreeze = lib.mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to restart the computer after a system freeze.
'';
};
};
config = {
system.activationScripts.power.text = ''
echo "configuring power..." >&2
${lib.optionalString (cfg.restartAfterPowerFailure != null) ''
systemsetup -setRestartPowerFailure \
'${onOff cfg.restartAfterPowerFailure}' &> /dev/null
''}
${lib.optionalString (cfg.restartAfterFreeze != null) ''
systemsetup -setRestartFreeze \
'${onOff cfg.restartAfterFreeze}' &> /dev/null
''}
'';
};
}

80
modules/power/sleep.nix Normal file
View file

@ -0,0 +1,80 @@
{ config, lib, ... }:
let
cfg = config.power.sleep;
types = lib.types;
onOff = cond: if cond then "on" else "off";
in
{
options = {
power.sleep.computer = lib.mkOption {
type = types.nullOr (types.either types.ints.positive (types.enum ["never"]));
default = null;
example = "never";
description = ''
Amount of idle time (in minutes) until the computer sleeps.
`"never"` disables computer sleeping.
The system might not be considered idle before connected displays sleep, as
per the `power.sleep.display` option.
'';
};
power.sleep.display = lib.mkOption {
type = types.nullOr (types.either types.ints.positive (types.enum ["never"]));
default = null;
example = "never";
description = ''
Amount of idle time (in minutes) until displays sleep.
`"never"` disables display sleeping.
'';
};
power.sleep.harddisk = lib.mkOption {
type = types.nullOr (types.either types.ints.positive (types.enum ["never"]));
default = null;
example = "never";
description = ''
Amount of idle time (in minutes) until hard disks sleep.
`"never"` disables hard disk sleeping.
'';
};
power.sleep.allowSleepByPowerButton = lib.mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether the power button can sleep the computer.
'';
};
};
config = {
system.activationScripts.power.text = lib.mkAfter ''
${lib.optionalString (cfg.computer != null) ''
systemsetup -setComputerSleep '${toString cfg.computer}' &> /dev/null
''}
${lib.optionalString (cfg.display != null) ''
systemsetup -setDisplaySleep '${toString cfg.display}' &> /dev/null
''}
${lib.optionalString (cfg.harddisk != null) ''
systemsetup -setHardDiskSleep '${toString cfg.harddisk}' &> /dev/null
''}
${lib.optionalString (cfg.allowSleepByPowerButton != null) ''
systemsetup -setAllowPowerButtonToSleepComputer \
'${onOff cfg.allowSleepByPowerButton}' &> /dev/null
''}
'';
};
}

View file

@ -7,6 +7,10 @@ let
in
{
imports = [
(mkRenamedOptionModule [ "programs" "bash" "enableCompletion" ] [ "programs" "bash" "completion" "enable" ])
];
options = {
programs.bash.enable = mkOption {
@ -21,14 +25,18 @@ in
type = types.lines;
};
programs.bash.enableCompletion = mkOption {
type = types.bool;
default = false;
description = ''
Enable bash completion for all interactive bash shells.
programs.bash.completion = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable bash completion for all interactive bash shells.
NOTE. This doesn't work with bash 3.2, which is the default on macOS.
'';
NOTE: This doesn't work with bash 3.2, which is installed by default on macOS by Apple.
'';
};
package = mkPackageOption pkgs "bash-completion" { };
};
};
@ -38,9 +46,9 @@ in
environment.systemPackages =
[ # Include bash package
pkgs.bashInteractive
] ++ optional cfg.enableCompletion pkgs.bash-completion;
] ++ optional cfg.completion.enable cfg.completion.package;
environment.pathsToLink =
environment.pathsToLink = optionals cfg.completion.enable
[ "/etc/bash_completion.d"
"/share/bash-completion/completions"
];
@ -70,9 +78,9 @@ in
${config.environment.interactiveShellInit}
${cfg.interactiveShellInit}
${optionalString cfg.enableCompletion ''
${optionalString cfg.completion.enable ''
if [ "$TERM" != "dumb" ]; then
source "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
source "${cfg.completion.package}/etc/profile.d/bash_completion.sh"
nullglobStatus=$(shopt -p nullglob)
shopt -s nullglob

View file

@ -41,6 +41,7 @@ in
{
imports = [
(mkRenamedOptionModule [ "programs" "tmux" "tmuxConfig" ] [ "programs" "tmux" "extraConfig" ])
(mkRemovedOptionModule [ "programs" "tmux" "defaultCommand" ] "Use `programs.tmux.extraConfig` to configure the default command instead. If unset, tmux will default to using your system configured login shell.")
];
options = {
programs.tmux.enable = mkOption {
@ -84,11 +85,6 @@ in
description = "Cater to iTerm2 and its tmux integration, as appropriate.";
};
programs.tmux.defaultCommand = mkOption {
type = types.either types.str types.package;
description = "The default command to use for tmux panes.";
};
programs.tmux.tmuxOptions = mkOption {
internal = true;
type = types.attrsOf (types.submodule text);
@ -120,12 +116,6 @@ in
source-file -q /etc/tmux.conf.local
'';
programs.tmux.defaultCommand = mkDefault config.environment.loginShell;
programs.tmux.tmuxOptions.login-shell.text = ''
set -g default-command "${cfg.defaultCommand}"
'';
programs.tmux.tmuxOptions.sensible.text = mkIf cfg.enableSensible ''
set -g default-terminal "screen-256color"
setw -g aggressive-resize on

View file

@ -18,7 +18,7 @@ in
options = {
programs.zsh.enable = mkOption {
type = types.bool;
default = false;
default = true;
description = "Whether to configure zsh as an interactive shell.";
};
@ -107,15 +107,24 @@ in
default = false;
description = "Enable zsh-syntax-highlighting.";
};
programs.zsh.enableFastSyntaxHighlighting = mkEnableOption "zsh-fast-syntax-highlighting";
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !(cfg.enableSyntaxHighlighting && cfg.enableFastSyntaxHighlighting);
message = "zsh-syntax-highlighting and zsh-fast-syntax-highlighting are mutually exclusive, please disable one of them.";
}
];
environment.systemPackages =
[ # Include zsh package
pkgs.zsh
] ++ optional cfg.enableCompletion pkgs.nix-zsh-completions
++ optional cfg.enableSyntaxHighlighting pkgs.zsh-syntax-highlighting;
++ optional cfg.enableSyntaxHighlighting pkgs.zsh-syntax-highlighting
++ optional cfg.enableFastSyntaxHighlighting pkgs.zsh-fast-syntax-highlighting;
environment.pathsToLink = [ "/share/zsh" ];
@ -127,17 +136,19 @@ in
if [ -n "''${__ETC_ZSHENV_SOURCED-}" ]; then return; fi
__ETC_ZSHENV_SOURCED=1
if [ -z "''${__NIX_DARWIN_SET_ENVIRONMENT_DONE-}" ]; then
. ${config.system.build.setEnvironment}
if [[ -o rcs ]]; then
if [ -z "''${__NIX_DARWIN_SET_ENVIRONMENT_DONE-}" ]; then
. ${config.system.build.setEnvironment}
fi
# Tell zsh how to find installed completions
for p in ''${(z)NIX_PROFILES}; do
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
done
${cfg.shellInit}
fi
# Tell zsh how to find installed completions
for p in ''${(z)NIX_PROFILES}; do
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
done
${cfg.shellInit}
# Read system-wide modifications.
if test -f /etc/zshenv.local; then
source /etc/zshenv.local
@ -192,6 +203,10 @@ in
"source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
}
${optionalString cfg.enableFastSyntaxHighlighting
"source ${pkgs.zsh-fast-syntax-highlighting}/share/zsh-fast-syntax-highlighting/zsh-fast-syntax-highlighting.zsh"
}
${optionalString cfg.enableFzfCompletion "source ${fzfCompletion}"}
${optionalString cfg.enableFzfGit "source ${fzfGit}"}
${optionalString cfg.enableFzfHistory "source ${fzfHistory}"}

View file

@ -1,22 +1,11 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.activate-system;
in
{
options = {
services.activate-system.enable = mkOption {
type = types.bool;
default = true;
description = "Whether to activate system at boot time.";
};
};
config = mkIf cfg.enable {
imports = [
(lib.mkRemovedOptionModule [ "services" "activate-system" "enable" ] "The `activate-system` service is now always enabled as it is necessary for a working `nix-darwin` setup.")
];
config = {
launchd.daemons.activate-system = {
script = ''
set -e
@ -41,6 +30,5 @@ in
serviceConfig.RunAtLoad = true;
serviceConfig.KeepAlive.SuccessfulExit = false;
};
};
}

View file

@ -0,0 +1,163 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.aerospace;
format = pkgs.formats.toml { };
configFile = format.generate "aerospace.toml" cfg.settings;
in
{
options = {
services.aerospace = with lib.types; {
enable = lib.mkEnableOption "AeroSpace window manager";
package = lib.mkPackageOption pkgs "aerospace" { };
settings = lib.mkOption {
type = submodule {
freeformType = format.type;
options = {
start-at-login = lib.mkOption {
type = bool;
default = false;
description = "Do not start AeroSpace at login. (Managed by launchd instead)";
};
after-login-command = lib.mkOption {
type = listOf str;
default = [ ];
description = "Do not use AeroSpace to run commands after login. (Managed by launchd instead)";
};
after-startup-command = lib.mkOption {
type = listOf str;
default = [ ];
description = "Do not use AeroSpace to run commands after startup. (Managed by launchd instead)";
};
enable-normalization-flatten-containers = lib.mkOption {
type = bool;
default = true;
description = "Containers that have only one child are \"flattened\".";
};
enable-normalization-opposite-orientation-for-nested-containers = lib.mkOption {
type = bool;
default = true;
description = "Containers that nest into each other must have opposite orientations.";
};
accordion-padding = lib.mkOption {
type = int;
default = 30;
description = "Padding between windows in an accordion container.";
};
default-root-container-layout = lib.mkOption {
type = enum [
"tiles"
"accordion"
];
default = "tiles";
description = "Default layout for the root container.";
};
default-root-container-orientation = lib.mkOption {
type = enum [
"horizontal"
"vertical"
"auto"
];
default = "auto";
description = "Default orientation for the root container.";
};
on-window-detected = lib.mkOption {
type = listOf str;
default = [ ];
description = "Commands to run every time a new window is detected.";
};
on-focus-changed = lib.mkOption {
type = listOf str;
default = [ ];
description = "Commands to run every time focused window or workspace changes.";
};
on-focused-monitor-changed = lib.mkOption {
type = listOf str;
default = [ "move-mouse monitor-lazy-center" ];
description = "Commands to run every time focused monitor changes.";
};
exec-on-workspace-change = lib.mkOption {
type = listOf str;
default = [ ];
example = [
"/bin/bash"
"-c"
"sketchybar --trigger aerospace_workspace_change FOCUSED=$AEROSPACE_FOCUSED_WORKSPACE"
];
description = "Commands to run every time workspace changes.";
};
key-mapping.preset = lib.mkOption {
type = enum [
"qwerty"
"dvorak"
];
default = "qwerty";
description = "Keymapping preset.";
};
};
};
default = { };
example = lib.literalExpression ''
{
gaps = {
outer.left = 8;
outer.bottom = 8;
outer.top = 8;
outer.right = 8;
};
mode.main.binding = {
alt-h = "focus left";
alt-j = "focus down";
alt-k = "focus up";
alt-l = "focus right";
};
}
'';
description = ''
AeroSpace configuration, see
<link xlink:href="https://nikitabobko.github.io/AeroSpace/guide#configuring-aerospace"/>
for supported values.
'';
};
};
};
config = (
lib.mkIf (cfg.enable) {
assertions = [
{
assertion = !cfg.settings.start-at-login;
message = "AeroSpace started at login is managed by home-manager and launchd instead of itself via this option.";
}
{
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 ];
launchd.user.agents.aerospace = {
command =
"${cfg.package}/Applications/AeroSpace.app/Contents/MacOS/AeroSpace"
+ (lib.optionalString (cfg.settings != { }) " --config-path ${configFile}");
serviceConfig = {
KeepAlive = true;
RunAtLoad = true;
};
};
}
);
}

View file

@ -237,7 +237,7 @@ in
tagStr = lib.concatStringsSep "," (lib.mapAttrsToList (name: value: "${name}=${value}") cfg.tags);
in
optionalString (cfg.privateSshKeyPath != null) ''
mkdir -m 0700 -p "${sshDir}"
mkdir -m 0700 "${sshDir}"
install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
'' + ''
cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF

View file

@ -44,9 +44,11 @@ in {
launchd.user.agents.emacs = {
path = cfg.additionalPath ++ [ config.environment.systemPath ];
serviceConfig.ProgramArguments =
[ "${cfg.package}/bin/${cfg.exec}" "--fg-daemon" ];
serviceConfig.RunAtLoad = true;
serviceConfig = {
ProgramArguments = [ "${cfg.package}/bin/${cfg.exec}" "--fg-daemon" ];
RunAtLoad = true;
KeepAlive = true;
};
};
};

View file

@ -48,14 +48,20 @@ in
text = mkBefore (''
echo >&2 "setting up GitHub Runner '${cfg.name}'..."
${pkgs.coreutils}/bin/mkdir -p -m 0750 ${escapeShellArg (mkStateDir cfg)}
${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkStateDir cfg)}
(
umask -S u=rwx,g=rx,o= > /dev/null
${pkgs.coreutils}/bin/mkdir -p -m 0750 ${escapeShellArg (mkLogDir cfg)}
${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkLogDir cfg)}
'' + optionalString (cfg.workDir == null) ''
${pkgs.coreutils}/bin/mkdir -p -m 0750 ${escapeShellArg (mkWorkDir cfg)}
${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkWorkDir cfg)}
${pkgs.coreutils}/bin/mkdir -p ${escapeShellArg (mkStateDir cfg)}
${pkgs.coreutils}/bin/chown ${user}:${group} ${escapeShellArg (mkStateDir cfg)}
${pkgs.coreutils}/bin/mkdir -p ${escapeShellArg (mkLogDir cfg)}
${pkgs.coreutils}/bin/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)}
''}
)
'');
};
}));
@ -88,6 +94,10 @@ in
script =
let
# https://github.com/NixOS/nixpkgs/pull/333744 introduced an inconsistency with different
# versions of nixpkgs. Use the old version of escapeShellArg to make sure that labels
# are always escaped to avoid https://www.shellcheck.net/wiki/SC2054
escapeShellArgAlways = string: "'${replaceStrings ["'"] ["'\\''"] (toString string)}'";
configure = pkgs.writeShellApplication {
name = "configure-github-runner-${name}";
text = /*bash*/''
@ -98,7 +108,7 @@ in
--disableupdate
--work ${escapeShellArg workDir}
--url ${escapeShellArg cfg.url}
--labels "${escapeShellArg (concatStringsSep "," cfg.extraLabels)}"
--labels ${escapeShellArgAlways (concatStringsSep "," cfg.extraLabels)}
${optionalString (cfg.name != null ) "--name ${escapeShellArg cfg.name}"}
${optionalString cfg.replace "--replace"}
${optionalString (cfg.runnerGroup != null) "--runnergroup ${escapeShellArg cfg.runnerGroup}"}

View file

@ -9,18 +9,19 @@ let
in
{
options = {
services.karabiner-elements.enable = mkEnableOption "Karabiner-Elements";
options.services.karabiner-elements = {
enable = mkEnableOption "Karabiner-Elements";
package = mkPackageOption pkgs "karabiner-elements" { };
};
config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.karabiner-elements ];
environment.systemPackages = [ cfg.package ];
system.activationScripts.preActivation.text = ''
rm -rf ${parentAppDir}
mkdir -p ${parentAppDir}
# Kernel extensions must reside inside of /Applications, they cannot be symlinks
cp -r ${pkgs.karabiner-elements.driver}/Applications/.Karabiner-VirtualHIDDevice-Manager.app ${parentAppDir}
cp -r ${cfg.package.driver}/Applications/.Karabiner-VirtualHIDDevice-Manager.app ${parentAppDir}
'';
system.activationScripts.postActivation.text = ''
@ -49,7 +50,7 @@ in
launchd.daemons.karabiner_grabber = {
serviceConfig.ProgramArguments = [
"${pkgs.karabiner-elements}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_grabber"
"${cfg.package}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_grabber"
];
serviceConfig.ProcessType = "Interactive";
serviceConfig.Label = "org.pqrs.karabiner.karabiner_grabber";
@ -60,7 +61,7 @@ in
launchd.daemons.karabiner_observer = {
serviceConfig.ProgramArguments = [
"${pkgs.karabiner-elements}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_observer"
"${cfg.package}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_observer"
];
serviceConfig.Label = "org.pqrs.karabiner.karabiner_observer";
@ -70,7 +71,7 @@ in
};
launchd.daemons.Karabiner-DriverKit-VirtualHIDDeviceClient = {
command = "\"${pkgs.karabiner-elements.driver}/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-DriverKit-VirtualHIDDeviceClient.app/Contents/MacOS/Karabiner-DriverKit-VirtualHIDDeviceClient\"";
command = "\"${cfg.package.driver}/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-DriverKit-VirtualHIDDeviceClient.app/Contents/MacOS/Karabiner-DriverKit-VirtualHIDDeviceClient\"";
serviceConfig.ProcessType = "Interactive";
serviceConfig.Label = "org.pqrs.Karabiner-DriverKit-VirtualHIDDeviceClient";
serviceConfig.KeepAlive = true;
@ -91,7 +92,7 @@ in
script = ''
rm -rf /run/wrappers
mkdir -p /run/wrappers/bin
install -m4555 "${pkgs.karabiner-elements}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_session_monitor" /run/wrappers/bin
install -m4555 "${cfg.package}/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_session_monitor" /run/wrappers/bin
'';
serviceConfig.RunAtLoad = true;
serviceConfig.KeepAlive.SuccessfulExit = false;
@ -106,8 +107,8 @@ in
serviceConfig.KeepAlive = true;
};
environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_grabber.plist".source = "${pkgs.karabiner-elements}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_grabber.plist";
environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_observer.plist".source = "${pkgs.karabiner-elements}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_observer.plist";
environment.userLaunchAgents."org.pqrs.karabiner.karabiner_console_user_server.plist".source = "${pkgs.karabiner-elements}/Library/LaunchAgents/org.pqrs.karabiner.karabiner_console_user_server.plist";
environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_grabber.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_grabber.plist";
environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_observer.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_observer.plist";
environment.userLaunchAgents."org.pqrs.karabiner.karabiner_console_user_server.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.karabiner_console_user_server.plist";
};
}

View file

@ -0,0 +1,121 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
concatStringsSep
escapeShellArgs
getExe
mkEnableOption
mkIf
mkOption
mkPackageOption
mkRemovedOptionModule
types
;
cfg = config.services.prometheus.exporters.node;
in {
imports = [
(mkRemovedOptionModule [ "services" "prometheus" "exporters" "node" "openFirewall" ] "No nix-darwin equivalent to this NixOS option.")
(mkRemovedOptionModule [ "services" "prometheus" "exporters" "node" "firewallFilter" ] "No nix-darwin equivalent to this NixOS option.")
(mkRemovedOptionModule [ "services" "prometheus" "exporters" "node" "firewallRules" ] "No nix-darwin equivalent to this NixOS option.")
];
options = {
services.prometheus.exporters.node = {
enable = mkEnableOption "Prometheus Node exporter";
package = mkPackageOption pkgs "prometheus-node-exporter" { };
listenAddress = mkOption {
type = types.str;
default = "";
example = "0.0.0.0";
description = ''
Address where Node exporter exposes its HTTP interface. Leave empty to bind to all addresses.
'';
};
port = mkOption {
type = types.port;
default = 9100;
description = ''
Port where the Node exporter exposes its HTTP interface.
'';
};
extraFlags = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "--log.level=debug" ];
description = ''
Extra commandline options to pass to the Node exporter executable.
'';
};
enabledCollectors = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Collectors to enable in addition to the ones that are [enabled by default](https://github.com/prometheus/node_exporter#enabled-by-default).
'';
};
disabledCollectors = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "boottime" ];
description = ''
Collectors to disable from the list of collectors that are [enabled by default](https://github.com/prometheus/node_exporter#enabled-by-default).
'';
};
};
};
config = mkIf cfg.enable {
users.users._prometheus-node-exporter = {
uid = config.ids.uids._prometheus-node-exporter;
gid = config.ids.gids._prometheus-node-exporter;
home = "/var/lib/prometheus-node-exporter";
createHome = true;
shell = "/usr/bin/false";
description = "System user for the Prometheus Node exporter";
};
users.groups._prometheus-node-exporter = {
gid = config.ids.gids._prometheus-node-exporter;
description = "System group for the Prometheus Node exporter";
};
users.knownGroups = [ "_prometheus-node-exporter" ];
users.knownUsers = [ "_prometheus-node-exporter" ];
launchd.daemons.prometheus-node-exporter = {
script = concatStringsSep " "
([
(getExe cfg.package)
"--web.listen-address"
"${cfg.listenAddress}:${toString cfg.port}"
]
++ (map (collector: "--collector.${collector}") cfg.enabledCollectors)
++ (map (collector: "--no-collector.${collector}") cfg.disabledCollectors)
) + escapeShellArgs cfg.extraFlags;
serviceConfig = let
logPath = config.users.users._prometheus-node-exporter.home
+ "/prometheus-node-exporter.log";
in {
KeepAlive = true;
RunAtLoad = true;
StandardErrorPath = logPath;
StandardOutPath = logPath;
GroupName = "_prometheus-node-exporter";
UserName = "_prometheus-node-exporter";
};
};
};
}

View file

@ -1,16 +1,16 @@
{ config, lib, pkgs, ... }:
with lib;
{ config, lib, ... }:
let
cfg = config.services.nix-daemon;
inherit (lib) mkDefault mkIf mkMerge mkOption types;
in
{
options = {
services.nix-daemon.enable = mkOption {
type = types.bool;
default = false;
default = true;
description = "Whether to enable the nix-daemon service.";
};

View file

@ -46,12 +46,6 @@ in
};
config = mkIf cfg.enable {
assertions = [
{ assertion = elem "ofborg" config.users.knownGroups; message = "set users.knownGroups to enable ofborg group"; }
{ assertion = elem "ofborg" config.users.knownUsers; message = "set users.knownUsers to enable ofborg user"; }
];
warnings = mkIf (isDerivation cfg.configFile) [
"services.ofborg.configFile is a derivation, credentials will be world readable"
];
@ -87,9 +81,13 @@ in
users.users.ofborg.shell = "/bin/bash";
users.users.ofborg.description = "OfBorg service user";
users.knownUsers = [ "ofborg" ];
users.groups.ofborg.gid = mkDefault 531;
users.groups.ofborg.description = "Nix group for OfBorg service";
users.knownGroups = [ "ofborg" ];
# FIXME: create logfiles automatically if defined.
system.activationScripts.preActivation.text = ''
mkdir -p '${user.home}'

View file

@ -237,10 +237,10 @@ in
for an overview of `postgresql.conf`.
::: {.note}
String values will automatically be enclosed in single quotes. Single quotes will be
escaped with two single quotes as described by the upstream documentation linked above.
:::
'';
example = literalExpression ''
@ -355,11 +355,14 @@ in
"${cfg.dataDir}/recovery.conf"
''}
exec ${postgresql}/bin/postgres -D ${cfg.dataDir}
exec ${postgresql}/bin/postgres
'';
serviceConfig.KeepAlive = true;
serviceConfig.RunAtLoad = true;
serviceConfig.EnvironmentVariables = {
PGDATA = cfg.dataDir;
};
};
};

View file

@ -29,6 +29,7 @@ in
};
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
environment.etc."skhdrc".text = cfg.skhdConfig;

View file

@ -67,6 +67,7 @@ in
${cfg.activationScripts.nix-daemon.text}
${cfg.activationScripts.time.text}
${cfg.activationScripts.networking.text}
${cfg.activationScripts.power.text}
${cfg.activationScripts.keyboard.text}
${cfg.activationScripts.fonts.text}
${cfg.activationScripts.nvram.text}
@ -85,6 +86,7 @@ in
exit $_status
'';
# FIXME: activationScripts.checks should be system level
system.activationScripts.userScript.text = ''
#! ${stdenv.shell}
set -e

View file

@ -2,22 +2,49 @@
{
system.activationScripts.createRun.text = ''
if ! test -L /run; then
IFS="." read -r -a macOSVersion <<< "$(sw_vers -productVersion)"
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..."
echo -e "run\tprivate/var/run" | sudo tee -a /etc/synthetic.conf >/dev/null
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
if ! test -L /run; then
echo "warning: apfs.util failed to symlink /run"
fi
echo "setting up /run via /etc/synthetic.conf..."
printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf >/dev/null
fi
if ! test -L /run; then
echo "setting up /run..."
sudo ln -sfn private/var/run /run
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 ! test -L /run; then
echo "warning: failed to symlink /run"
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"
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
fi
'';

View file

@ -3,6 +3,9 @@
with lib;
let
# Similar to lib.escapeShellArg but escapes "s instead of 's, to allow for parameter expansion in shells
escapeDoubleQuote = arg: ''"${replaceStrings ["\""] ["\"\\\"\""] (toString arg)}"'';
cfg = config.system.checks;
darwinChanges = ''
@ -22,28 +25,13 @@ let
'';
runLink = ''
if ! test -e /run; then
echo "error: Directory /run does not exist, aborting activation" >&2
echo "Create a symlink to /var/run with:" >&2
if test -e /etc/synthetic.conf; then
echo >&2
echo "$ printf 'run\tprivate/var/run\n' | sudo tee -a /etc/synthetic.conf" >&2
echo "$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -B # For Catalina" >&2
echo "$ sudo /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t # For Big Sur and later" >&2
echo >&2
echo "The current contents of /etc/synthetic.conf is:" >&2
echo >&2
sed 's/^/ /' /etc/synthetic.conf >&2
echo >&2
else
echo >&2
echo "$ sudo ln -s private/var/run /run" >&2
echo >&2
fi
exit 2
if [[ ! -e /run ]]; then
printf >&2 'error: directory /run does not exist, aborting activation\n'
exit 1
fi
'';
oldBuildUsers = ''
if dscl . -list /Users | grep -q '^nixbld'; then
echo "error: Detected old style nixbld users, aborting activation" >&2
@ -59,7 +47,7 @@ let
exit 2
fi
'';
preSequoiaBuildUsers = ''
${lib.optionalString config.nix.configureBuildUsers ''
# Dont complain when were about to migrate oldstyle build users…
@ -104,7 +92,7 @@ let
buildUsers = ''
buildUser=$(dscl . -read /Groups/nixbld GroupMembership 2>&1 | awk '/^GroupMembership: / {print $2}') || true
if [ -z $buildUser ]; then
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
@ -120,7 +108,7 @@ let
buildGroupID = ''
buildGroupID=$(dscl . -read /Groups/nixbld PrimaryGroupID | awk '{print $2}')
expectedBuildGroupID=${toString config.ids.gids.nixbld}
if [[ $buildGroupID != $expectedBuildGroupID ]]; then
if [[ $buildGroupID != "$expectedBuildGroupID" ]]; then
printf >&2 '\e[1;31merror: Build user group has mismatching GID, aborting activation\e[0m\n'
printf >&2 'The default Nix build user group ID was changed from 30000 to 350.\n'
printf >&2 'You are currently managing Nix build users with nix-darwin, but your\n'
@ -130,6 +118,7 @@ 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'
@ -143,18 +132,26 @@ let
fi
'';
singleUser = ''
if grep -q 'build-users-group =' /etc/nix/nix.conf; then
echo "error: The daemon is not enabled but this is a multi-user install, aborting activation" >&2
echo "Enable the nix-daemon service:" >&2
echo >&2
echo " services.nix-daemon.enable = true;" >&2
echo >&2
echo "or set" >&2
echo >&2
echo " nix.useDaemon = true;" >&2
echo >&2
exit 2
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'
printf >&2 '\n'
printf >&2 ' services.nix-daemon.enable = false;\n'
printf >&2 '\n'
# shellcheck disable=SC2016
printf >&2 'and remove `nix.useDaemon` from your configuration if it is present.\n'
printf >&2 '\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
'';
@ -194,7 +191,7 @@ let
'';
nixPath = ''
nixPath=${concatStringsSep ":" config.nix.nixPath}:$HOME/.nix-defexpr/channels
nixPath=${concatMapStringsSep ":" escapeDoubleQuote config.nix.nixPath}:$HOME/.nix-defexpr/channels
darwinConfig=$(NIX_PATH=$nixPath nix-instantiate --find-file darwin-config) || true
if ! test -e "$darwinConfig"; then
@ -282,6 +279,7 @@ let
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.<name>.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'
@ -297,6 +295,19 @@ let
exit 2
fi
'';
homebrewInstalled = ''
if [[ ! -f ${escapeShellArg config.homebrew.brewPrefix}/brew && -z "''${INSTALLING_HOMEBREW:-}" ]]; then
echo "error: Using the homebrew module requires homebrew installed, aborting activation" >&2
echo "Homebrew doesn't seem to be installed. Please install homebrew separately." >&2
echo "You can install homebrew using the following command:" >&2
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
'';
in
{
@ -337,7 +348,7 @@ in
(mkIf cfg.verifyBuildUsers buildUsers)
(mkIf cfg.verifyBuildUsers preSequoiaBuildUsers)
(mkIf config.nix.configureBuildUsers buildGroupID)
(mkIf (!config.nix.useDaemon) singleUser)
nixDaemon
nixStore
(mkIf (config.nix.gc.automatic && config.nix.gc.user == null) nixGarbageCollector)
(mkIf (config.nix.optimise.automatic && config.nix.optimise.user == null) nixStoreOptimiser)
@ -345,12 +356,13 @@ in
nixInstaller
(mkIf cfg.verifyNixPath nixPath)
oldSshAuthorizedKeysDirectory
(mkIf config.homebrew.enable homebrewInstalled)
];
system.activationScripts.checks.text = ''
${cfg.text}
if test ''${checkActivation:-0} -eq 1; then
if [[ "''${checkActivation:-0}" -eq 1 ]]; then
echo "ok" >&2
exit 0
fi

View file

@ -92,6 +92,8 @@ in
name = "darwin-system-${cfg.darwinLabel}";
preferLocalBuild = true;
nativeBuildInputs = [ pkgs.shellcheck ];
activationScript = cfg.activationScripts.script.text;
activationUserScript = cfg.activationScripts.userScript.text;
inherit (cfg) darwinLabel;
@ -133,6 +135,8 @@ in
chmod u+x $out/activate-user
unset activationUserScript
shellcheck $out/activate $out/activate-user
echo -n "$systemConfig" > $out/systemConfig
echo -n "$darwinLabel" > $out/darwin-version

View file

@ -14,7 +14,7 @@ let
alf = defaultsToList "/Library/Preferences/com.apple.alf" cfg.alf;
loginwindow = defaultsToList "/Library/Preferences/com.apple.loginwindow" cfg.loginwindow;
smb = defaultsToList "/Library/Preferences/SystemConfiguration/com.apple.smb.server" cfg.smb;
SoftwareUpdate = defaultsToList "/Library/Preferences/SystemConfiguration/com.apple.SoftwareUpdate" cfg.SoftwareUpdate;
SoftwareUpdate = defaultsToList "/Library/Preferences/com.apple.SoftwareUpdate" cfg.SoftwareUpdate;
# userDefaults
GlobalPreferences = defaultsToList ".GlobalPreferences" cfg.".GlobalPreferences";
@ -23,6 +23,7 @@ let
menuExtraClock = defaultsToList "com.apple.menuextra.clock" cfg.menuExtraClock;
dock = defaultsToList "com.apple.dock" cfg.dock;
finder = defaultsToList "com.apple.finder" cfg.finder;
hitoolbox = defaultsToList "com.apple.HIToolbox" cfg.hitoolbox;
magicmouse = defaultsToList "com.apple.AppleMultitouchMouse" cfg.magicmouse;
magicmouseBluetooth = defaultsToList "com.apple.driver.AppleMultitouchMouse.mouse" cfg.magicmouse;
screencapture = defaultsToList "com.apple.screencapture" cfg.screencapture;
@ -33,9 +34,11 @@ let
universalaccess = defaultsToList "com.apple.universalaccess" cfg.universalaccess;
ActivityMonitor = defaultsToList "com.apple.ActivityMonitor" cfg.ActivityMonitor;
WindowManager = defaultsToList "com.apple.WindowManager" cfg.WindowManager;
controlcenter = defaultsToList "~/Library/Preferences/ByHost/com.apple.controlcenter" cfg.controlcenter;
CustomUserPreferences = flatten (mapAttrsToList (name: value: defaultsToList name value) cfg.CustomUserPreferences);
CustomSystemPreferences = flatten (mapAttrsToList (name: value: defaultsToList name value) cfg.CustomSystemPreferences);
mkIfAttrs = list: mkIf (any (attrs: attrs != { }) list);
in
@ -76,6 +79,7 @@ in
menuExtraClock
dock
finder
hitoolbox
magicmouse
magicmouseBluetooth
screencapture
@ -87,6 +91,7 @@ in
ActivityMonitor
CustomUserPreferences
WindowManager
controlcenter
]
''
# Set defaults
@ -99,6 +104,7 @@ in
${concatStringsSep "\n" menuExtraClock}
${concatStringsSep "\n" dock}
${concatStringsSep "\n" finder}
${concatStringsSep "\n" hitoolbox}
${concatStringsSep "\n" magicmouse}
${concatStringsSep "\n" magicmouseBluetooth}
${concatStringsSep "\n" screencapture}
@ -110,10 +116,11 @@ in
${concatStringsSep "\n" ActivityMonitor}
${concatStringsSep "\n" CustomUserPreferences}
${concatStringsSep "\n" WindowManager}
${concatStringsSep "\n" controlcenter}
${optionalString (length dock > 0) ''
# Only restart Dock if current user is logged in
if pgrep -xu $UID Dock; then
if pgrep -xu $UID Dock >/dev/null; then
echo >&2 "restarting Dock..."
killall Dock || true
fi

View file

@ -5,6 +5,14 @@ with lib;
{
options = {
system.defaults.menuExtraClock.FlashDateSeparators = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
When enabled, the clock indicator (which by default is the colon) will flash on and off each second. Default is null.
'';
};
system.defaults.menuExtraClock.IsAnalog = mkOption {
type = types.nullOr types.bool;
default = null;

View file

@ -0,0 +1,100 @@
{ config, lib, ... }:
{
options = {
system.defaults.controlcenter.BatteryShowPercentage = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
default = null;
description = ''
Apple menu > System Preferences > Control Center > Battery
Show a battery percentage in menu bar. Default is null.
'';
};
system.defaults.controlcenter.Sound = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
apply = v: if v == null then null else if v == true then 18 else 24;
default = null;
description = ''
Apple menu > System Preferences > Control Center > Sound
Show a sound control in menu bar . Default is null.
18 = Display icon in menu bar
24 = Hide icon in menu bar
'';
};
system.defaults.controlcenter.Bluetooth = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
apply = v: if v == null then null else if v == true then 18 else 24;
default = null;
description = ''
Apple menu > System Preferences > Control Center > Bluetooth
Show a bluetooth control in menu bar. Default is null.
18 = Display icon in menu bar
24 = Hide icon in menu bar
'';
};
system.defaults.controlcenter.AirDrop = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
apply = v: if v == null then null else if v == true then 18 else 24;
default = null;
description = ''
Apple menu > System Preferences > Control Center > AirDrop
Show a AirDrop control in menu bar. Default is null.
18 = Display icon in menu bar
24 = Hide icon in menu bar
'';
};
system.defaults.controlcenter.Display = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
apply = v: if v == null then null else if v == true then 18 else 24;
default = null;
description = ''
Apple menu > System Preferences > Control Center > Display
Show a Screen Brightness control in menu bar. Default is null.
18 = Display icon in menu bar
24 = Hide icon in menu bar
'';
};
system.defaults.controlcenter.FocusModes = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
apply = v: if v == null then null else if v == true then 18 else 24;
default = null;
description = ''
Apple menu > System Preferences > Control Center > Focus
Show a Focus control in menu bar. Default is null.
18 = Display icon in menu bar
24 = Hide icon in menu bar
'';
};
system.defaults.controlcenter.NowPlaying = lib.mkOption {
type = lib.types.nullOr lib.types.bool;
apply = v: if v == null then null else if v == true then 18 else 24;
default = null;
description = ''
Apple menu > System Preferences > Control Center > Now Playing
Show a Now Playing control in menu bar. Default is null.
18 = Display icon in menu bar
24 = Hide icon in menu bar
'';
};
};
}

View file

@ -149,6 +149,14 @@ in {
else map (folder: { tile-data = { file-data = { _CFURLString = "file://" + folder; _CFURLStringType = 15; }; }; tile-type = if strings.hasInfix "." (last (splitString "/" folder)) then "file-tile" else "directory-tile"; }) value;
};
system.defaults.dock.scroll-to-open = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Scroll up on a Dock icon to show all Space's opened windows for an app, or open stack. The default is false.
'';
};
system.defaults.dock.show-process-indicators = mkOption {
type = types.nullOr types.bool;
default = null;

View file

@ -1,7 +1,10 @@
{ config, lib, ... }:
with lib;
let
inherit (lib) mkOption types;
cfg = config.system.defaults.finder;
in
{
options = {
@ -38,6 +41,15 @@ with lib;
'';
};
system.defaults.finder.FXRemoveOldTrashItems = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Remove items in the trash after 30 days.
The default is false.
'';
};
system.defaults.finder.FXPreferredViewStyle = mkOption {
type = types.nullOr types.str;
default = null;
@ -52,7 +64,7 @@ with lib;
type = types.nullOr types.bool;
default = null;
description = ''
Whether to always show file extensions. The default is false.
Whether to always show file extensions. The default is false.
'';
};
@ -68,7 +80,39 @@ with lib;
type = types.nullOr types.bool;
default = null;
description = ''
Whether to allow quitting of the Finder. The default is false.
Whether to allow quitting of the Finder. The default is false.
'';
};
system.defaults.finder.ShowExternalHardDrivesOnDesktop = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show external disks on desktop. The default is true.
'';
};
system.defaults.finder.ShowHardDrivesOnDesktop = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show hard disks on desktop. The default is false.
'';
};
system.defaults.finder.ShowMountedServersOnDesktop = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show connected servers on desktop. The default is false.
'';
};
system.defaults.finder.ShowRemovableMediaOnDesktop = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show removable media (CDs, DVDs and iPods) on desktop. The default is true.
'';
};
@ -76,7 +120,7 @@ with lib;
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show the full POSIX filepath in the window title. The default is false.
Whether to show the full POSIX filepath in the window title. The default is false.
'';
};
@ -88,13 +132,70 @@ with lib;
'';
};
system.defaults.finder._FXSortFoldersFirstOnDesktop = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Keep folders on top when sorting by name on the desktop. The default is false.
'';
};
system.defaults.finder.FXEnableExtensionChangeWarning = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Whether to show warnings when change the file extension of files. The default is true.
Whether to show warnings when change the file extension of files. The default is true.
'';
};
system.defaults.finder.NewWindowTarget = mkOption {
type = types.nullOr (types.enum [
"Computer"
"OS volume"
"Home"
"Desktop"
"Documents"
"Recents"
"iCloud Drive"
"Other"
]);
apply = key: if key == null then null else {
"Computer" = "PfCm";
"OS volume" = "PfVo";
"Home" = "PfHm";
"Desktop" = "PfDe";
"Documents" = "PfDo";
"Recents" = "PfAF";
"iCloud Drive" = "PfID";
"Other" = "PfLo";
}.${key};
default = null;
description = ''
Change the default folder shown in Finder windows. "Other" corresponds to the value of
NewWindowTargetPath. The default is unset ("Recents").
'';
};
system.defaults.finder.NewWindowTargetPath = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Sets the URI to open when NewWindowTarget is "Other". Spaces and similar characters must be
escaped. If the value is invalid, Finder will open your home directory.
Example: "file:///Users/foo/long%20cat%20pics".
The default is unset.
'';
};
};
config = {
assertions = [{
assertion = cfg.NewWindowTargetPath != null -> cfg.NewWindowTarget == "PfLo";
message = "`system.defaults.finder.NewWindowTarget` should be set to `Other` when `NewWindowTargetPath` is non-null.";
}
{
assertion = cfg.NewWindowTarget == "PfLo" -> cfg.NewWindowTargetPath != null;
message = "`system.defaults.finder.NewWindowTargetPath` should be non-null when `NewWindowTarget` is set to `Other`.";
}];
};
}

View file

@ -0,0 +1,29 @@
{ lib, ... }:
{
options = {
system.defaults.hitoolbox.AppleFnUsageType = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [
"Do Nothing"
"Change Input Source"
"Show Emoji & Symbols"
"Start Dictation"
]);
apply = key: if key == null then null else {
"Do Nothing" = 0;
"Change Input Source" = 1;
"Show Emoji & Symbols" = 2;
"Start Dictation" = 3;
}.${key};
default = null;
description = ''
Chooses what happens when you press the Fn key on the keyboard. A restart is required for
this setting to take effect.
The default is unset ("Show Emoji & Symbols").
'';
};
};
}

View file

@ -29,6 +29,18 @@ with lib;
'';
};
system.defaults.screencapture.include-date = mkOption {
type = types.nullOr types.bool;
default = null;
description = ''
Include date and time in screenshot filenames. The default is true.
Screenshot 2024-01-09 at 13.27.20.png would be an example for true.
Screenshot.png
Screenshot 1.png would be an example for false.
'';
};
system.defaults.screencapture.show-thumbnail = mkOption {
type = types.nullOr types.bool;
default = null;

View file

@ -105,19 +105,29 @@ in
${concatMapStringsSep "\n" (attr: launchdActivation "LaunchAgents" attr.target) launchAgents}
${concatMapStringsSep "\n" (attr: launchdActivation "LaunchDaemons" attr.target) launchDaemons}
for f in $(ls /run/current-system/Library/LaunchAgents 2> /dev/null); do
if test ! -e "${cfg.build.launchd}/Library/LaunchAgents/$f"; then
echo "removing service $(basename $f .plist)" >&2
for f in /run/current-system/Library/LaunchAgents/*; do
[[ -e "$f" ]] || break # handle when directory is empty
f=''${f#/run/current-system/Library/LaunchAgents/}
if [[ ! -e "${cfg.build.launchd}/Library/LaunchAgents/$f" ]]; then
echo "removing service $(basename "$f" .plist)" >&2
launchctl unload "/Library/LaunchAgents/$f" || true
if test -e "/Library/LaunchAgents/$f"; then rm -f "/Library/LaunchAgents/$f"; fi
if [[ -e "/Library/LaunchAgents/$f" ]]; then
rm -f "/Library/LaunchAgents/$f"
fi
fi
done
for f in $(ls /run/current-system/Library/LaunchDaemons 2> /dev/null); do
if test ! -e "${cfg.build.launchd}/Library/LaunchDaemons/$f"; then
echo "removing service $(basename $f .plist)" >&2
for f in /run/current-system/Library/LaunchDaemons/*; do
[[ -e "$f" ]] || break # handle when directory is empty
f=''${f#/run/current-system/Library/LaunchDaemons/}
if [[ ! -e "${cfg.build.launchd}/Library/LaunchDaemons/$f" ]]; then
echo "removing service $(basename "$f" .plist)" >&2
launchctl unload "/Library/LaunchDaemons/$f" || true
if test -e "/Library/LaunchDaemons/$f"; then rm -f "/Library/LaunchDaemons/$f"; fi
if [[ -e "/Library/LaunchDaemons/$f" ]]; then
rm -f "/Library/LaunchDaemons/$f"
fi
fi
done
'';
@ -133,11 +143,16 @@ in
''}
${concatMapStringsSep "\n" (attr: userLaunchdActivation attr.target) userLaunchAgents}
for f in $(ls /run/current-system/user/Library/LaunchAgents 2> /dev/null); do
if test ! -e "${cfg.build.launchd}/user/Library/LaunchAgents/$f"; then
echo "removing user service $(basename $f .plist)" >&2
launchctl unload ~/Library/LaunchAgents/$f || true
if test -e ~/Library/LaunchAgents/$f; then rm -f ~/Library/LaunchAgents/$f; fi
for f in /run/current-system/user/Library/LaunchAgents/*; do
[[ -e "$f" ]] || break # handle when directory is empty
f=''${f#/run/current-system/user/Library/LaunchAgents/}
if [[ ! -e "${cfg.build.launchd}/user/Library/LaunchAgents/$f" ]]; then
echo "removing user service $(basename "$f" .plist)" >&2
launchctl unload ~/Library/LaunchAgents/"$f" || true
if [[ -e ~/Library/LaunchAgents/"$f" ]]; then
rm -f ~/Library/LaunchAgents/"$f"
fi
fi
done
'';

View file

@ -30,9 +30,9 @@ in
Set of patches to apply to {file}`/`.
::: {.warning}
This can modify everything so use with caution.
:::
Useful for safely changing system files. Unlike the etc module this
@ -56,10 +56,13 @@ in
# Applying patches to /.
echo "applying patches..." >&2
for f in $(ls /run/current-system/patches 2> /dev/null); do
if test ! -e "${config.system.build.patches}/patches/$f"; then
patch --force --reverse --backup -d / -p1 < "/run/current-system/patches/$f" || true
fi
for f in /run/current-system/patches/*; do
[[ -e "$f" ]] || break # handle when directory is empty
f=''${f#/run/current-system/patches/}
if [[ ! -e "${config.system.build.patches}/patches/$f" ]]; then
patch --force --reverse --backup -d / -p1 < "/run/current-system/patches/$f" || true
fi
done
${concatMapStringsSep "\n" (f: ''

View file

@ -14,9 +14,15 @@ in
example = literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
description = ''
A list of permissible login shells for user accounts.
No need to mention `/bin/sh`
and other shells that are available by default on
macOS.
The default macOS shells will be automatically included:
- /bin/bash
- /bin/csh
- /bin/dash
- /bin/ksh
- /bin/sh
- /bin/tcsh
- /bin/zsh
'';
apply = map (v: if types.shellPackage.check v then "/run/current-system/sw${v.shellPath}" else v);
};

View file

@ -7,7 +7,7 @@ let
cfg = config.time;
timeZone = optionalString (cfg.timeZone != null) ''
if [ -z $(systemsetup -listtimezones | grep "^ ${cfg.timeZone}$") ]; then
if ! systemsetup -listtimezones | grep -q "^ ${cfg.timeZone}$"; then
echo "${cfg.timeZone} is not a valid timezone. The command 'listtimezones' will show a list of valid time zones." >&2
false
fi

View file

@ -1,14 +1,16 @@
{ config, lib, pkgs, ... }:
with lib;
let
inherit (lib) concatStringsSep concatMapStringsSep elem escapeShellArg
escapeShellArgs filter filterAttrs flatten flip mapAttrs' mapAttrsToList
mkAfter mkIf mkMerge mkOption mkOrder mkRemovedOptionModule optionals
optionalString types;
cfg = config.users;
group = import ./group.nix;
user = import ./user.nix;
toArguments = concatMapStringsSep " " (v: "'${v}'");
toGID = v: { "${toString v.gid}" = v.name; };
toUID = v: { "${toString v.uid}" = v.name; };
@ -32,9 +34,19 @@ let
then "/run/current-system/sw${v.shellPath}"
else v;
systemShells =
let
shells = mapAttrsToList (_: u: u.shell) cfg.users;
in
filter types.shellPackage.check shells;
in
{
imports = [
(mkRemovedOptionModule [ "users" "forceRecreate" ] "")
];
options = {
users.knownGroups = mkOption {
type = types.listOf types.str;
@ -79,62 +91,185 @@ in
type = types.attrsOf types.str;
default = {};
};
users.forceRecreate = mkOption {
internal = true;
type = types.bool;
default = false;
description = "Remove and recreate existing groups/users.";
};
};
config = {
assertions = [
{
# We don't check `root` like the rest of the users as on some systems `root`'s
# home directory is set to `/var/root /private/var/root`
assertion = cfg.users ? root -> (cfg.users.root.home == null || cfg.users.root.home == "/var/root");
message = "`users.users.root.home` must be set to either `null` or `/var/root`.";
}
{
assertion = !builtins.elem "root" deletedUsers;
message = "Remove `root` from `users.knownUsers` if you no longer want nix-darwin to manage it.";
}
] ++ flatten (flip mapAttrsToList cfg.users (name: user:
map (shell: {
assertion = let
s = user.shell.pname or null;
in
!user.ignoreShellProgramCheck -> (s == shell || (shell == "bash" && s == "bash-interactive")) -> (config.programs.${shell}.enable == true);
message = ''
users.users.${user.name}.shell is set to ${shell}, but
programs.${shell}.enable is not true. This will cause the ${shell}
shell to lack the basic Nix directories in its PATH and might make
logging in as that user impossible. You can fix it with:
programs.${shell}.enable = true;
If you know what you're doing and you are fine with the behavior,
set users.users.${user.name}.ignoreShellProgramCheck = true;
instead.
'';
}) [
"bash"
"fish"
"zsh"
]
));
warnings = flatten (flip mapAttrsToList cfg.users (name: user:
mkIf
(user.shell.pname or null == "bash")
"Set `users.users.${name}.shell = pkgs.bashInteractive;` instead of `pkgs.bash` as it does not include `readline`."
));
users.gids = mkMerge gids;
users.uids = mkMerge uids;
# NOTE: We put this in `system.checks` as we want this to run first to avoid partial activations
# however currently that runs at user level activation as that runs before system level activation
# TODO: replace `$USER` with `$SUDO_USER` when system.checks runs from system level
system.checks.text = mkIf (builtins.length (createdUsers ++ deletedUsers) > 0) (mkAfter ''
ensurePerms() {
homeDirectory=$(dscl . -read /Users/nobody NFSHomeDirectory)
homeDirectory=''${homeDirectory#NFSHomeDirectory: }
if ! sudo dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" &> /dev/null; then
if [[ -n "$SSH_CONNECTION" ]]; 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'
printf >&2 ' grant Full Disk Access to all programs run over SSH\n'
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'
printf >&2 'navigating to System Settings > General > Sharing > Remote Login\n'
printf >&2 'and then pressing on the i icon next to the switch.\n'
exit 1
else
# The TCC service required to change home directories is `kTCCServiceSystemPolicySysAdminFiles`
# and we can reset it to ensure the user gets another prompt
tccutil reset SystemPolicySysAdminFiles > /dev/null
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 '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'
exit 1
fi
fi
fi
}
${concatMapStringsSep "\n" (v: let
name = escapeShellArg v.name;
dsclUser = escapeShellArg "/Users/${v.name}";
in ''
u=$(id -u ${name} 2> /dev/null) || true
if ! [[ -n "$u" && "$u" -ne "${toString v.uid}" ]]; then
if [ -z "$u" ]; then
ensurePerms ${name} create
${optionalString (v.home != null && v.name != "root") ''
else
homeDirectory=$(dscl . -read ${dsclUser} NFSHomeDirectory)
homeDirectory=''${homeDirectory#NFSHomeDirectory: }
if [[ ${escapeShellArg v.home} != "$homeDirectory" ]]; then
printf >&2 '\e[1;31merror: config contains the wrong home directory for %s, aborting activation\e[0m\n' ${name}
printf >&2 'nix-darwin does not support changing the home directory of existing users.\n'
printf >&2 '\n'
printf >&2 'Please set:\n'
printf >&2 '\n'
printf >&2 ' users.users.%s.home = "%s";\n' ${name} "$homeDirectory"
printf >&2 '\n'
printf >&2 'or remove it from your configuration.\n'
exit 1
fi
''}
fi
fi
'') createdUsers}
${concatMapStringsSep "\n" (v: let
name = escapeShellArg v;
in ''
u=$(id -u ${name} 2> /dev/null) || true
if [ -n "$u" ]; then
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
ensurePerms ${name} delete
fi
fi
'') deletedUsers}
'');
system.activationScripts.groups.text = mkIf (cfg.knownGroups != []) ''
echo "setting up groups..." >&2
${concatMapStringsSep "\n" (v: ''
${optionalString cfg.forceRecreate ''
g=$(dscl . -read '/Groups/${v.name}' PrimaryGroupID 2> /dev/null) || true
g=''${g#PrimaryGroupID: }
if [[ "$g" -eq ${toString v.gid} ]]; then
echo "deleting group ${v.name}..." >&2
dscl . -delete '/Groups/${v.name}' 2> /dev/null
else
echo "warning: existing group '${v.name}' has unexpected gid $g, skipping..." >&2
fi
''}
g=$(dscl . -read '/Groups/${v.name}' PrimaryGroupID 2> /dev/null) || true
${concatMapStringsSep "\n" (v: let
dsclGroup = escapeShellArg "/Groups/${v.name}";
in ''
g=$(dscl . -read ${dsclGroup} PrimaryGroupID 2> /dev/null) || true
g=''${g#PrimaryGroupID: }
if [ -z "$g" ]; then
echo "creating group ${v.name}..." >&2
dscl . -create '/Groups/${v.name}' PrimaryGroupID ${toString v.gid}
dscl . -create '/Groups/${v.name}' RealName '${v.description}'
dscl . -create ${dsclGroup} PrimaryGroupID ${toString v.gid}
dscl . -create ${dsclGroup} RealName ${escapeShellArg v.description}
g=${toString v.gid}
fi
if [ "$g" -eq ${toString v.gid} ]; then
g=$(dscl . -read '/Groups/${v.name}' GroupMembership 2> /dev/null) || true
g=$(dscl . -read ${dsclGroup} GroupMembership 2> /dev/null) || true
if [ "$g" != 'GroupMembership: ${concatStringsSep " " v.members}' ]; then
echo "updating group members ${v.name}..." >&2
dscl . -create '/Groups/${v.name}' GroupMembership ${toArguments v.members}
dscl . -create ${dsclGroup} GroupMembership ${escapeShellArgs v.members}
fi
else
echo "warning: existing group '${v.name}' has unexpected gid $g, skipping..." >&2
fi
'') createdGroups}
${concatMapStringsSep "\n" (name: ''
g=$(dscl . -read '/Groups/${name}' PrimaryGroupID 2> /dev/null) || true
${concatMapStringsSep "\n" (name: let
dsclGroup = escapeShellArg "/Groups/${name}";
in ''
g=$(dscl . -read ${dsclGroup} PrimaryGroupID 2> /dev/null) || true
g=''${g#PrimaryGroupID: }
if [ -n "$g" ]; then
if [ "$g" -gt 501 ]; then
echo "deleting group ${name}..." >&2
dscl . -delete '/Groups/${name}' 2> /dev/null
dscl . -delete ${dsclGroup}
else
echo "warning: existing group '${name}' has unexpected gid $g, skipping..." >&2
fi
@ -145,44 +280,51 @@ in
system.activationScripts.users.text = mkIf (cfg.knownUsers != []) ''
echo "setting up users..." >&2
${concatMapStringsSep "\n" (v: ''
${optionalString cfg.forceRecreate ''
u=$(dscl . -read '/Users/${v.name}' UniqueID 2> /dev/null) || true
u=''${u#UniqueID: }
if [[ "$u" -eq ${toString v.uid} ]]; then
echo "deleting user ${v.name}..." >&2
dscl . -delete '/Users/${v.name}' 2> /dev/null
else
echo "warning: existing user '${v.name}' has unexpected uid $u, skipping..." >&2
fi
''}
u=$(dscl . -read '/Users/${v.name}' UniqueID 2> /dev/null) || true
u=''${u#UniqueID: }
${concatMapStringsSep "\n" (v: let
name = escapeShellArg v.name;
dsclUser = escapeShellArg "/Users/${v.name}";
in ''
u=$(id -u ${name} 2> /dev/null) || true
if [[ -n "$u" && "$u" -ne "${toString v.uid}" ]]; then
echo "warning: existing user '${v.name}' has unexpected uid $u, skipping..." >&2
else
if [ -z "$u" ]; then
echo "creating user ${v.name}..." >&2
dscl . -create '/Users/${v.name}' UniqueID ${toString v.uid}
dscl . -create '/Users/${v.name}' PrimaryGroupID ${toString v.gid}
dscl . -create '/Users/${v.name}' IsHidden ${if v.isHidden then "1" else "0"}
dscl . -create '/Users/${v.name}' RealName '${v.description}'
dscl . -create '/Users/${v.name}' NFSHomeDirectory '${v.home}'
${optionalString v.createHome "createhomedir -cu '${v.name}'"}
sysadminctl -addUser ${escapeShellArgs ([
v.name
"-UID" v.uid
"-GID" v.gid ]
++ (optionals (v.description != null) [ "-fullName" v.description ])
++ [ "-home" (if v.home != null then v.home else "/var/empty") ]
++ [ "-shell" (if v.shell != null then shellPath v.shell else "/usr/bin/false") ])} 2> /dev/null
# We need to check as `sysadminctl -addUser` still exits with exit code 0 when there's an error
if ! id ${name} &> /dev/null; then
printf >&2 '\e[1;31merror: failed to create user %s, aborting activation\e[0m\n' ${name}
exit 1
fi
dscl . -create ${dsclUser} IsHidden ${if v.isHidden then "1" else "0"}
# `sysadminctl -addUser` won't create the home directory if we use the `-home`
# flag so we need to do it ourselves
${optionalString (v.home != null && v.createHome) "createhomedir -cu ${name} > /dev/null"}
fi
# Always set the shell path, in case it was updated
dscl . -create '/Users/${v.name}' UserShell ${lib.escapeShellArg (shellPath v.shell)}
# Update properties on known users to keep them inline with configuration
dscl . -create ${dsclUser} PrimaryGroupID ${toString v.gid}
${optionalString (v.description != null) "dscl . -create ${dsclUser} RealName ${escapeShellArg v.description}"}
${optionalString (v.shell != null) "dscl . -create ${dsclUser} UserShell ${escapeShellArg (shellPath v.shell)}"}
fi
'') createdUsers}
${concatMapStringsSep "\n" (name: ''
u=$(dscl . -read '/Users/${name}' UniqueID 2> /dev/null) || true
u=''${u#UniqueID: }
u=$(id -u ${escapeShellArg name} 2> /dev/null) || true
if [ -n "$u" ]; then
if [ "$u" -gt 501 ]; then
echo "deleting user ${name}..." >&2
dscl . -delete '/Users/${name}' 2> /dev/null
dscl . -delete ${escapeShellArg "/Users/${name}"}
else
echo "warning: existing user '${name}' has unexpected uid $u, skipping..." >&2
fi
@ -190,6 +332,9 @@ in
'') deletedUsers}
'';
# Install all the user shells
environment.systemPackages = systemShells;
environment.etc = mapAttrs' (name: { packages, ... }: {
name = "profiles/per-user/${name}";
value.source = pkgs.buildEnv {

View file

@ -1,11 +1,12 @@
{ name, lib, ... }:
with lib;
{
options = {
options = let
inherit (lib) mkOption types;
in {
name = mkOption {
type = types.str;
default = name;
description = ''
The group's name. If undefined, the name of the attribute set
will be used.
@ -29,10 +30,4 @@ with lib;
description = "The group's description.";
};
};
config = {
name = mkDefault name;
};
}

View file

@ -1,11 +1,12 @@
{ name, lib, ... }:
with lib;
{
options = {
options = let
inherit (lib) literalExpression mkOption types;
in {
name = mkOption {
type = types.str;
type = types.nonEmptyStr;
default = name;
description = ''
The name of the user account. If undefined, the name of the
attribute set will be used.
@ -13,12 +14,18 @@ with lib;
};
description = mkOption {
type = types.str;
default = "";
type = types.nullOr types.nonEmptyStr;
default = null;
example = "Alice Q. User";
description = ''
A short description of the user account, typically the
user's full name.
This defaults to `null` which means, on creation, `sysadminctl`
will pick the description which is usually always {option}`name`.
Using an empty name is not supported and breaks macOS like
making the user not appear in Directory Utility.
'';
};
@ -46,9 +53,15 @@ with lib;
# };
home = mkOption {
type = types.path;
default = "/var/empty";
description = "The user's home directory.";
type = types.nullOr types.path;
default = null;
description = ''
The user's home directory. This defaults to `null`.
When this is set to `null`, if the user has not been created yet,
they will be created with the home directory `/var/empty` to match
the old default.
'';
};
createHome = mkOption {
@ -58,10 +71,28 @@ with lib;
};
shell = mkOption {
type = types.either types.shellPackage types.path;
default = "/sbin/nologin";
type = types.nullOr (types.either types.shellPackage types.path);
default = null;
example = literalExpression "pkgs.bashInteractive";
description = "The user's shell.";
description = ''
The user's shell. This defaults to `null`.
When this is set to `null`, if the user has not been created yet,
they will be created with the shell `/usr/bin/false` to prevent
interactive login. If the user already exists, the value is
considered managed by macOS and `nix-darwin` will not change it.
'';
};
ignoreShellProgramCheck = mkOption {
type = types.bool;
default = false;
description = ''
By default, nix-darwin will check that programs.SHELL.enable is set to
true if the user has a custom shell specified. If that behavior isn't
required and there are custom overrides in place to make sure that the
shell is functional, set this to true.
'';
};
packages = mkOption {
@ -75,10 +106,4 @@ with lib;
'';
};
};
config = {
name = mkDefault name;
};
}

View file

@ -1,147 +0,0 @@
{ stdenv, nix, pkgs, nix-darwin }:
let
nixPath = pkgs.lib.concatStringsSep ":" [
"darwin=${nix-darwin}"
"nixpkgs=${pkgs.path}"
"$HOME/.nix-defexpr/channels"
"/nix/var/nix/profiles/per-user/root/channels"
"$NIX_PATH"
];
in
stdenv.mkDerivation {
name = "darwin-installer";
preferLocalBuild = true;
unpackPhase = ":";
installPhase = ''
mkdir -p $out/bin
echo "$shellHook" > $out/bin/darwin-installer
chmod +x $out/bin/darwin-installer
'';
shellHook = ''
#!${stdenv.shell}
set -e
_PATH=$PATH
export PATH=/nix/var/nix/profiles/default/bin:${nix}/bin:${pkgs.gnused}/bin:${pkgs.openssh}/bin:/usr/bin:/bin:/usr/sbin:/sbin
action=switch
while [ "$#" -gt 0 ]; do
i="$1"; shift 1
case "$i" in
--help)
echo "darwin-installer: [--help] [--check]"
exit
;;
--check)
action=check
;;
esac
done
echo >&2
echo >&2 "Installing nix-darwin..."
echo >&2
config="$HOME/.nixpkgs/darwin-configuration.nix"
if ! test -f "$config"; then
echo "copying example configuration.nix" >&2
mkdir -p "$HOME/.nixpkgs"
cp "${../../modules/examples/simple.nix}" "$config"
chmod u+w "$config"
# Enable nix-daemon service for multi-user installs.
if [ ! -w /nix/var/nix/db ]; then
sed -i 's/# services.nix-daemon.enable/services.nix-daemon.enable/' "$config"
fi
fi
# Skip when stdin is not a tty, eg.
# $ yes | darwin-installer
if test -t 0; then
read -p "Would you like to edit the default configuration.nix before starting? [y/N] " i
case "$i" in
y|Y)
PATH=$_PATH ''${EDITOR:-nano} "$config"
;;
esac
fi
i=y
darwinPath=$(NIX_PATH=$HOME/.nix-defexpr/channels nix-instantiate --eval -E '<darwin>' 2> /dev/null) || true
if ! test -e "$darwinPath"; then
if test -t 0; then
read -p "Would you like to manage <darwin> with nix-channel? [y/N] " i
fi
case "$i" in
y|Y)
nix-channel --add https://github.com/LnL7/nix-darwin/archive/master.tar.gz darwin
nix-channel --update
;;
esac
fi
export NIX_PATH=${nixPath}
system=$(nix-build '<darwin>' -I "darwin-config=$config" -A system --no-out-link --show-trace)
export PATH=$system/sw/bin:$PATH
darwin-rebuild "$action" -I "darwin-config=$config"
echo >&2
echo >&2 " Open '$config' to get started."
echo >&2 " See the README for more information: https://github.com/LnL7/nix-darwin/blob/master/README.md"
echo >&2
echo >&2 " Don't forget to start a new shell or source /etc/static/bashrc."
echo >&2
exit
'';
passthru.check = stdenv.mkDerivation {
name = "run-darwin-test";
shellHook = ''
set -e
echo >&2 "running installer tests..."
echo >&2
echo >&2 "checking configuration.nix"
test -f ~/.nixpkgs/darwin-configuration.nix
test -w ~/.nixpkgs/darwin-configuration.nix
echo >&2 "checking darwin channel"
readlink ~/.nix-defexpr/channels/darwin
test -e ~/.nix-defexpr/channels/darwin
echo >&2 "checking /etc"
readlink /etc/static
test -e /etc/static
echo >&2 "checking profile"
cat /etc/profile
grep -v nix-daemon.sh /etc/profile
echo >&2 "checking /run/current-system"
readlink /run
test -e /run
readlink /run/current-system
test -e /run/current-system
echo >&2 "checking system profile"
readlink /nix/var/nix/profiles/system
test -e /nix/var/nix/profiles/system
echo >&2 "checking bash environment"
env -i USER=john HOME=/Users/john bash -li -c 'echo $PATH'
env -i USER=john HOME=/Users/john bash -li -c 'echo $PATH' | grep /Users/john/.nix-profile/bin:/run/current-system/sw/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
env -i USER=john HOME=/Users/john bash -li -c 'echo $NIX_PATH'
env -i USER=john HOME=/Users/john bash -li -c 'echo $NIX_PATH' | grep darwin-config=/Users/john/.nixpkgs/darwin-configuration.nix:/nix/var/nix/profiles/per-user/root/channels
echo >&2 "checking zsh environment"
env -i USER=john HOME=/Users/john zsh -l -c 'echo $PATH'
env -i USER=john HOME=/Users/john zsh -l -c 'echo $PATH' | grep /Users/john/.nix-profile/bin:/run/current-system/sw/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
env -i USER=john HOME=/Users/john zsh -l -c 'echo $NIX_PATH'
env -i USER=john HOME=/Users/john zsh -l -c 'echo $NIX_PATH' | grep darwin-config=/Users/john/.nixpkgs/darwin-configuration.nix:/nix/var/nix/profiles/per-user/root/channels
echo >&2 ok
exit
'';
};
}

View file

@ -1,4 +1,4 @@
{ lib, ... }:
{ lib, pkgs, ... }:
with lib;
@ -7,46 +7,49 @@ with lib;
assertions = mkForce [];
system.activationScripts.checks.text = mkForce "";
# Disable etc, launchd, ...
environment.etc = mkForce {};
launchd.agents = mkForce {};
launchd.daemons = mkForce {};
launchd.user.agents = mkForce {};
# Don't try to reload `nix-daemon`
nix.useDaemon = mkForce false;
system.activationScripts.postUserActivation.text = mkAfter ''
if test -L ~/.nix-defexpr/channels/darwin; then
if [[ -L ~/.nix-defexpr/channels/darwin ]]; then
nix-channel --remove darwin || true
fi
'';
system.activationScripts.postActivation.text = mkAfter ''
if test -L /Applications/Nix\ Apps; then
if [[ -L /Applications/Nix\ Apps ]]; then
rm /Applications/Nix\ Apps
fi
if test -L /etc/static; then
if [[ -L /etc/static ]]; then
rm /etc/static
fi
if test -O /nix/store; then
if ! test -e /Library/LaunchDaemons/org.nixos.nix-daemon.plist; then
sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist || true
sudo launchctl remove org.nixos.nix-daemon 2> /dev/null || true
# 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
if ! grep -q etc/profile.d/nix-daemon.sh /etc/bashrc; then
echo >&2 "Found no nix-daemon.sh reference in /etc/bashrc"
echo >&2 "add this snippet back to /etc/bashrc:"
echo >&2
echo >&2 " # Nix"
echo >&2 " if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then"
echo >&2 " . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'"
echo >&2 " fi"
echo >&2 " # End Nix"
echo >&2
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)
if [[ "$shell" != */bin/zsh ]]; then
echo >&2 "warning: changing $user's shell from $shell to /bin/zsh"
fi
dscl . -create /Users/"$user" UserShell /bin/zsh
done
while IFS= read -r -d "" file; do
mv "$file" "''${file%.*}"
done < <(find /etc -name '*.before-nix-darwin' -follow -print0)
'';
}

View file

@ -1,4 +1,4 @@
{ stdenv, lib, pkgs }:
{ lib, path, stdenv, writeShellApplication }:
let
uninstallSystem = import ../../eval-config.nix {
@ -6,39 +6,23 @@ let
modules = [
./configuration.nix
{
nixpkgs.source = pkgs.path;
nixpkgs.hostPlatform = pkgs.stdenv.hostPlatform.system;
nixpkgs.source = path;
nixpkgs.hostPlatform = stdenv.hostPlatform.system;
system.includeUninstaller = false;
}
];
};
in
stdenv.mkDerivation {
in writeShellApplication {
name = "darwin-uninstaller";
preferLocalBuild = true;
unpackPhase = ":";
installPhase = ''
mkdir -p $out/bin
echo "$shellHook" > $out/bin/darwin-uninstaller
chmod +x $out/bin/darwin-uninstaller
'';
shellHook = ''
#!${stdenv.shell}
set -e
action=switch
text = ''
while [ "$#" -gt 0 ]; do
i="$1"; shift 1
case "$i" in
--help)
echo "darwin-uninstaller: [--help]"
exit
;;
esac
i="$1"; shift 1
case "$i" in
--help)
echo "darwin-uninstaller: [--help]"
exit
;;
esac
done
echo >&2
@ -47,54 +31,67 @@ stdenv.mkDerivation {
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"
echo >&2 " - restore daemon service from nix installer (only when this is a multi-user install)"
if [[ $(stat -f '%Su' /nix/store) == "root" ]]; then
echo >&2 " - restore nix-daemon service from nix installer as this is a multi-user install"
fi
echo >&2
if test -t 0; then
read -p "Proceed? [y/n] " i
case "$i" in
y|Y)
;;
*)
exit 3
;;
esac
if [[ -t 0 ]]; then
read -r -p "Proceed? [y/n] " i
case "$i" in
y|Y)
;;
*)
exit 3
;;
esac
fi
${uninstallSystem.system}/sw/bin/darwin-rebuild activate
if test -L /run/current-system; then
if [[ -L /run/current-system ]]; then
sudo rm /run/current-system
fi
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
sudo rm /run
fi
fi
echo >&2
echo >&2 "NOTE: The /nix/var/nix/profiles/system* profiles still exist and won't be garbage collected."
echo >&2
echo >&2 "Done!"
echo >&2
exit
'';
passthru.check = stdenv.mkDerivation {
name = "run-darwin-test";
shellHook = ''
set -e
echo >&2 "running uninstaller tests..."
echo >&2
derivationArgs.passthru.tests.uninstaller = writeShellApplication {
name = "post-uninstall-test";
text = ''
echo >&2 "running uninstaller tests..."
echo >&2
echo >&2 "checking darwin channel"
! test -e ~/.nix-defexpr/channels/darwin
echo >&2 "checking /etc"
! test -e /etc/static
echo >&2 "checking /run/current-system"
! test -e /run/current-system
echo >&2 "checking nix-daemon service (assuming a multi-user install)"
sudo launchctl list | grep org.nixos.nix-daemon || echo "FIXME? sudo launchctl list | grep org.nixos.nix-daemon"
pgrep -l nix-daemon || echo "FIXME? pgrep -l nix-daemon"
readlink /Library/LaunchDaemons/org.nixos.nix-daemon.plist || echo "FIXME? readlink /Library/LaunchDaemons/org.nixos.nix-daemon.plist"
grep /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt /Library/LaunchDaemons/org.nixos.nix-daemon.plist || echo "FIXME? grep /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt /Library/LaunchDaemons/org.nixos.nix-daemon.plist"
echo >&2 ok
exit
echo >&2 "checking darwin channel"
test -e ~/.nix-defexpr/channels/darwin && exit 1
echo >&2 "checking /etc"
test -e /etc/static && exit 1
echo >&2 "checking /run/current-system"
test -e /run/current-system && exit 1
if [[ $(stat -f '%Su' /nix/store) == "root" ]]; then
echo >&2 "checking nix-daemon service"
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}')" ]]
nix-store --store daemon -q --hash ${stdenv.shell}
fi
echo >&2 ok
'';
};
}

View file

@ -9,17 +9,25 @@ showSyntax() {
echo " [--list-generations] [{--profile-name | -p} name] [--rollback]" >&2
echo " [{--switch-generation | -G} generation] [--verbose...] [-v...]" >&2
echo " [-Q] [{--max-jobs | -j} number] [--cores number] [--dry-run]" >&2
echo " [--keep-going] [-k] [--keep-failed] [-K] [--fallback] [--show-trace]" >&2
echo " [-I path] [--option name value] [--arg name value] [--argstr name value]" >&2
echo " [--flake flake] [--no-flake] [--update-input input flake] [--impure]" >&2
echo " [--recreate-lock-file] [--no-update-lock-file] [--refresh]" >&2
echo " [--offline] [--substituters substituters-list] ..." >&2
echo " [--keep-going | -k] [--keep-failed | -K] [--fallback] [--show-trace]" >&2
echo " [--print-build-logs | -L] [--impure] [-I path]" >&2
echo " [--option name value] [--arg name value] [--argstr name value]" >&2
echo " [--no-flake | [--flake flake]" >&2
echo " [--commit-lock-file] [--recreate-lock-file]" >&2
echo " [--no-update-lock-file] [--no-write-lock-file]" >&2
echo " [--override-input input flake] [--update-input input]" >&2
echo " [--no-registries] [--offline] [--refresh]]" >&2
echo " [--substituters substituters-list] ..." >&2
exit 1
}
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
command sudo -H --preserve-env=PATH env "$@"
# 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
@ -149,43 +157,17 @@ fi
# For convenience, use the hostname as the default configuration to
# build from the flake.
if [ -n "$flake" ]; then
# Offical regex from https://www.rfc-editor.org/rfc/rfc3986#appendix-B
if [[ "${flake}" =~ ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? ]]; then
scheme=${BASH_REMATCH[1]} # eg. http:
authority=${BASH_REMATCH[3]} # eg. //www.ics.uci.edu
path=${BASH_REMATCH[5]} # eg. /pub/ietf/uri/
queryWithQuestion=${BASH_REMATCH[6]}
fragment=${BASH_REMATCH[9]}
flake=${scheme}${authority}${path}${queryWithQuestion}
flakeAttr=${fragment}
if [[ -n "$flake" ]]; then
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
flake="${BASH_REMATCH[1]}"
flakeAttr="${BASH_REMATCH[2]}"
fi
if [ -z "$flakeAttr" ]; then
if [[ -z "$flakeAttr" ]]; then
flakeAttr=$(scutil --get LocalHostName)
fi
flakeAttr=darwinConfigurations.${flakeAttr}
fi
if [ -n "$flake" ]; then
if nix "${flakeFlags[@]}" flake metadata --version &>/dev/null; then
cmd=metadata
else
cmd=info
fi
metadata=$(nix "${flakeFlags[@]}" flake "$cmd" --json "${extraMetadataFlags[@]}" "${extraLockFlags[@]}" -- "$flake")
flake=$(jq -r .url <<<"${metadata}")
if [ "$(jq -r .resolved.submodules <<<"${metadata}")" = "true" ]; then
if [[ "$flake" == *'?'* ]]; then
flake="${flake}&submodules=1"
else
flake="${flake}?submodules=1"
fi
fi
fi
if [ "$action" != build ]; then
if [ -n "$flake" ]; then
extraBuildFlags+=("--no-link")

View file

@ -17,6 +17,7 @@ let
inherit name src;
dir = "bin";
isExecutable = true;
meta.mainProgram = name;
} // env);
path = "${extraPath}:${systemPath}";

View file

@ -1,21 +1,16 @@
{ nixpkgs ? <nixpkgs>
, supportedSystems ? [ "x86_64-darwin" ]
# Adapted from https://github.com/NixOS/nixpkgs/blob/e818264fe227ad8861e0598166cf1417297fdf54/pkgs/top-level/release.nix#L11
, nix-darwin ? { }
, system ? builtins.currentSystem
, supportedSystems ? [ "x86_64-darwin" "aarch64-darwin" ]
, scrubJobs ? true
}:
let
inherit (release) mapTestOn packagePlatforms pkgs all linux darwin;
system = "x86_64-darwin";
mapPlatforms = systems: pkgs.lib.mapAttrs (n: v: systems);
buildFromConfig = configuration: sel: sel
(import ./. { inherit nixpkgs configuration system; }).config;
makeSystem = configuration: pkgs.lib.genAttrs [ system ] (system:
buildFromConfig configuration (config: config.system.build.toplevel)
);
makeSystem = configuration: buildFromConfig configuration (config: config.system.build.toplevel);
makeTest = test:
let
@ -61,103 +56,76 @@ let
in
buildFromConfig configuration (config: config.system.build.run-test);
release = import <nixpkgs/pkgs/top-level/release-lib.nix> {
inherit supportedSystems scrubJobs;
packageSet = import nixpkgs;
};
packageSet = {
inherit (pkgs)
stdenv bash zsh nix
tmux reattach-to-user-namespace
nano emacs vim;
};
manual = buildFromConfig ({ lib, config, ... }: {
system.stateVersion = lib.mkDefault config.system.maxStateVersion;
system.darwinVersionSuffix = let
shortRev = nix-darwin.shortRev or nix-darwin.dirtyShortRev or null;
in
lib.mkIf (shortRev != null) ".${shortRev}";
system.darwinRevision = let
rev = nix-darwin.rev or nix-darwin.dirtyRev or null;
in
lib.mkIf (rev != null) rev;
}) (config: config.system.build.manual);
jobs = {
in {
docs = {
inherit (manual) manualHTML manpages optionsJSON;
};
unstable = pkgs.releaseTools.aggregate {
name = "darwin-${pkgs.lib.nixpkgsVersion}";
constituents =
[ jobs.stdenv.x86_64-darwin
jobs.bash.x86_64-darwin
jobs.zsh.x86_64-darwin
jobs.nix.x86_64-darwin
jobs.reattach-to-user-namespace.x86_64-darwin
jobs.tmux.x86_64-darwin
jobs.nano.x86_64-darwin
jobs.vim.x86_64-darwin
jobs.emacs.x86_64-darwin
jobs.examples.hydra.x86_64-darwin
jobs.examples.lnl.x86_64-darwin
jobs.examples.simple.x86_64-darwin
];
meta.description = "Release-critical builds for the darwin channel";
};
examples.hydra = makeSystem ./modules/examples/hydra.nix;
examples.lnl = makeSystem ./modules/examples/lnl.nix;
examples.simple = makeSystem ./modules/examples/simple.nix;
manualHTML = manual.manualHTML;
manpages = manual.manpages;
options = manual.optionsJSON;
examples.hydra = makeSystem ./modules/examples/hydra.nix;
examples.lnl = makeSystem ./modules/examples/lnl.nix;
examples.simple = makeSystem ./modules/examples/simple.nix;
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;
tests.launchd-daemons = makeTest ./tests/launchd-daemons.nix;
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.nixpkgs-overlays = makeTest ./tests/nixpkgs-overlays.nix;
tests.programs-ssh = makeTest ./tests/programs-ssh.nix;
tests.programs-tmux = makeTest ./tests/programs-tmux.nix;
tests.programs-zsh = makeTest ./tests/programs-zsh.nix;
tests.programs-ssh-empty-known-hosts = makeTest ./tests/programs-ssh-empty-known-hosts.nix;
tests.security-pki = makeTest ./tests/security-pki.nix;
tests.services-activate-system = makeTest ./tests/services-activate-system.nix;
tests.services-activate-system-changed-label-prefix = makeTest ./tests/services-activate-system-changed-label-prefix.nix;
tests.services-buildkite-agent = makeTest ./tests/services-buildkite-agent.nix;
tests.services-github-runners = makeTest ./tests/services-github-runners.nix;
tests.services-lorri = makeTest ./tests/services-lorri.nix;
tests.services-nix-daemon = makeTest ./tests/services-nix-daemon.nix;
tests.sockets-nix-daemon = makeTest ./tests/sockets-nix-daemon.nix;
tests.services-dnsmasq = makeTest ./tests/services-dnsmasq.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;
tests.services-nextdns = makeTest ./tests/services-nextdns.nix;
tests.services-netdata = makeTest ./tests/services-netdata.nix;
tests.services-ofborg = makeTest ./tests/services-ofborg.nix;
tests.services-offlineimap = makeTest ./tests/services-offlineimap.nix;
tests.services-privoxy = makeTest ./tests/services-privoxy.nix;
tests.services-redis = makeTest ./tests/services-redis.nix;
tests.services-skhd = makeTest ./tests/services-skhd.nix;
tests.services-spacebar = makeTest ./tests/services-spacebar.nix;
tests.services-spotifyd = makeTest ./tests/services-spotifyd.nix;
tests.services-synapse-bt = makeTest ./tests/services-synapse-bt.nix;
tests.services-synergy = makeTest ./tests/services-synergy.nix;
tests.services-yabai = makeTest ./tests/services-yabai.nix;
tests.services-jankyborders = makeTest ./tests/services-jankyborders.nix;
tests.system-defaults-write = makeTest ./tests/system-defaults-write.nix;
tests.system-environment = makeTest ./tests/system-environment.nix;
tests.system-keyboard-mapping = makeTest ./tests/system-keyboard-mapping.nix;
tests.system-packages = makeTest ./tests/system-packages.nix;
tests.system-path = makeTest ./tests/system-path.nix;
tests.system-shells = makeTest ./tests/system-shells.nix;
tests.users-groups = makeTest ./tests/users-groups.nix;
tests.users-packages = makeTest ./tests/users-packages.nix;
tests.fonts = makeTest ./tests/fonts.nix;
}
// (mapTestOn (packagePlatforms packageSet));
in
jobs
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;
tests.launchd-daemons = makeTest ./tests/launchd-daemons.nix;
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.nixpkgs-overlays = makeTest ./tests/nixpkgs-overlays.nix;
tests.programs-ssh = makeTest ./tests/programs-ssh.nix;
tests.programs-tmux = makeTest ./tests/programs-tmux.nix;
tests.programs-zsh = makeTest ./tests/programs-zsh.nix;
tests.programs-ssh-empty-known-hosts = makeTest ./tests/programs-ssh-empty-known-hosts.nix;
tests.security-pki = makeTest ./tests/security-pki.nix;
tests.services-activate-system = makeTest ./tests/services-activate-system.nix;
tests.services-activate-system-changed-label-prefix = makeTest ./tests/services-activate-system-changed-label-prefix.nix;
tests.services-buildkite-agent = makeTest ./tests/services-buildkite-agent.nix;
tests.services-github-runners = makeTest ./tests/services-github-runners.nix;
tests.services-lorri = makeTest ./tests/services-lorri.nix;
tests.services-nix-daemon = makeTest ./tests/services-nix-daemon.nix;
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-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;
tests.services-nextdns = makeTest ./tests/services-nextdns.nix;
tests.services-netdata = makeTest ./tests/services-netdata.nix;
tests.services-ofborg = makeTest ./tests/services-ofborg.nix;
tests.services-offlineimap = makeTest ./tests/services-offlineimap.nix;
tests.services-privoxy = makeTest ./tests/services-privoxy.nix;
tests.services-redis = makeTest ./tests/services-redis.nix;
tests.services-skhd = makeTest ./tests/services-skhd.nix;
tests.services-spacebar = makeTest ./tests/services-spacebar.nix;
tests.services-spotifyd = makeTest ./tests/services-spotifyd.nix;
tests.services-synapse-bt = makeTest ./tests/services-synapse-bt.nix;
tests.services-synergy = makeTest ./tests/services-synergy.nix;
tests.services-yabai = makeTest ./tests/services-yabai.nix;
tests.services-jankyborders = makeTest ./tests/services-jankyborders.nix;
tests.system-defaults-write = makeTest ./tests/system-defaults-write.nix;
tests.system-environment = makeTest ./tests/system-environment.nix;
tests.system-keyboard-mapping = makeTest ./tests/system-keyboard-mapping.nix;
tests.system-packages = makeTest ./tests/system-packages.nix;
tests.system-path = makeTest ./tests/system-path.nix;
tests.system-shells = makeTest ./tests/system-shells.nix;
tests.users-groups = makeTest ./tests/users-groups.nix;
tests.users-packages = makeTest ./tests/users-packages.nix;
tests.fonts = makeTest ./tests/fonts.nix;
}

View file

@ -205,6 +205,11 @@ defaults write .GlobalPreferences 'com.apple.sound.beep.sound' $'<?xml version="
<string>/System/Library/Sounds/Funk.aiff</string>
</plist>'
defaults write com.apple.menuextra.clock 'FlashDateSeparators' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.menuextra.clock 'Show24Hour' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@ -299,6 +304,11 @@ defaults write com.apple.dock 'persistent-others' $'<?xml version="1.0" encoding
</dict>
</array>
</plist>'
defaults write com.apple.dock 'scroll-to-open' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.finder 'AppleShowAllExtensions' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@ -329,16 +339,51 @@ defaults write com.apple.finder 'FXPreferredViewStyle' $'<?xml version="1.0" enc
<plist version="1.0">
<string>Flwv</string>
</plist>'
defaults write com.apple.finder 'FXRemoveOldTrashItems' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.finder 'NewWindowTarget' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<string>PfLo</string>
</plist>'
defaults write com.apple.finder 'NewWindowTargetPath' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<string>file:///Library/Apple</string>
</plist>'
defaults write com.apple.finder 'QuitMenuItem' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<true/>
</plist>'
defaults write com.apple.finder 'ShowExternalHardDrivesOnDesktop' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.finder 'ShowHardDrivesOnDesktop' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.finder 'ShowMountedServersOnDesktop' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.finder 'ShowPathbar' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<true/>
</plist>'
defaults write com.apple.finder 'ShowRemovableMediaOnDesktop' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.finder 'ShowStatusBar' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@ -354,8 +399,23 @@ defaults write com.apple.finder '_FXSortFoldersFirst' $'<?xml version="1.0" enco
<plist version="1.0">
<true/>
</plist>'
defaults write com.apple.finder '_FXSortFoldersFirstOnDesktop' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<false/>
</plist>'
defaults write com.apple.HIToolbox 'AppleFnUsageType' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>2</integer>
</plist>'
defaults write com.apple.screencapture 'include-date' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<true/>
</plist>'
defaults write com.apple.screencapture 'location' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@ -474,3 +534,38 @@ defaults write com.apple.WindowManager 'StandardHideWidgets' $'<?xml version="1.
<plist version="1.0">
<true/>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'AirDrop' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>18</integer>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'BatteryShowPercentage' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<true/>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'Bluetooth' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>18</integer>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'Display' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>24</integer>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'FocusModes' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>24</integer>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'NowPlaying' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>18</integer>
</plist>'
defaults write ~/Library/Preferences/ByHost/com.apple.controlcenter 'Sound' $'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<integer>24</integer>
</plist>'

View file

@ -1,4 +1,4 @@
{ config, pkgs, ... }:
{ config, lib, ... }:
{
networking.knownNetworkServices = [ "Wi-Fi" "Thunderbolt Ethernet" ];
@ -6,10 +6,10 @@
test = ''
echo checking dns settings in /activate >&2
grep "networksetup -setdnsservers 'Wi-Fi' '8.8.8.8' '8.8.4.4'" ${config.out}/activate
grep "networksetup -setdnsservers 'Thunderbolt Ethernet' '8.8.8.8' '8.8.4.4'" ${config.out}/activate
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Wi-Fi" "8.8.8.8" "8.8.4.4" ]}" ${config.out}/activate
grep "networksetup -setdnsservers ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "8.8.8.8" "8.8.4.4" ]}" ${config.out}/activate
echo checking empty searchdomain settings in /activate >&2
grep "networksetup -setsearchdomains 'Wi-Fi' 'empty'" ${config.out}/activate
grep "networksetup -setsearchdomains 'Thunderbolt Ethernet' 'empty'" ${config.out}/activate
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Wi-Fi" "empty" ]}" ${config.out}/activate
grep "networksetup -setsearchdomains ${lib.escapeShellArgs [ "Thunderbolt Ethernet" "empty" ]}" ${config.out}/activate
'';
}

View file

@ -0,0 +1,10 @@
{ config, pkgs, ... }:
{
networking.wakeOnLan.enable = true;
test = ''
echo checking wake on network access settings in /activate >&2
grep "systemsetup -setWakeOnNetworkAccess 'on'" ${config.out}/activate
'';
}

12
tests/power-restart.nix Normal file
View file

@ -0,0 +1,12 @@
{ config, pkgs, ... }:
{
power.restartAfterPowerFailure = true;
power.restartAfterFreeze = true;
test = ''
echo checking restart power settings in /activate >&2
grep "systemsetup -setRestartPowerFailure 'on'" ${config.out}/activate
grep "systemsetup -setRestartFreeze 'on'" ${config.out}/activate
'';
}

16
tests/power-sleep.nix Normal file
View file

@ -0,0 +1,16 @@
{ config, pkgs, ... }:
{
power.sleep.computer = "never";
power.sleep.display = 15;
power.sleep.harddisk = 5;
power.sleep.allowSleepByPowerButton = false;
test = ''
echo checking power sleep settings in /activate >&2
grep "systemsetup -setComputerSleep 'never'" ${config.out}/activate
grep "systemsetup -setDisplaySleep '15'" ${config.out}/activate
grep "systemsetup -setHardDiskSleep '5'" ${config.out}/activate
grep "systemsetup -setAllowPowerButtonToSleepComputer 'off'" ${config.out}/activate
'';
}

View file

@ -34,7 +34,7 @@
echo >&2 "checking compinit in /etc/zshrc"
grep 'autoload -U compinit && compinit' ${config.out}/etc/zshrc
echo >&2 "checking bashcompinit in /etc/zshrc"
grep -vq 'bashcompinit' ${config.out}/etc/zshrc
(! grep 'bashcompinit' ${config.out}/etc/zshrc)
echo >&2 "checking zprofile.d in /etc/zprofile"
grep 'source /etc/zprofile.d/\*.conf' ${config.out}/etc/zprofile

View file

@ -1,7 +1,6 @@
{ config, pkgs, ... }:
{
services.activate-system.enable = true;
launchd.labelPrefix = "org.nix-darwin";
test = ''

View file

@ -1,8 +1,6 @@
{ config, pkgs, ... }:
{
services.activate-system.enable = true;
test = ''
echo checking activation service in /Library/LaunchDaemons >&2
grep "org.nixos.activate-system" ${config.out}/Library/LaunchDaemons/org.nixos.activate-system.plist

View file

@ -0,0 +1,36 @@
{ config, pkgs, ... }:
let
aerospace = pkgs.runCommand "aerospace-0.0.0" { } "mkdir $out";
in
{
services.aerospace.enable = true;
services.aerospace.package = aerospace;
services.aerospace.settings = {
gaps = {
outer.left = 8;
outer.bottom = 8;
outer.top = 8;
outer.right = 8;
};
mode.main.binding = {
alt-h = "focus left";
alt-j = "focus down";
alt-k = "focus up";
alt-l = "focus right";
};
};
test = ''
echo >&2 "checking aerospace service in ~/Library/LaunchAgents"
grep "org.nixos.aerospace" ${config.out}/user/Library/LaunchAgents/org.nixos.aerospace.plist
grep "${aerospace}/Applications/AeroSpace.app/Contents/MacOS/AeroSpace" ${config.out}/user/Library/LaunchAgents/org.nixos.aerospace.plist
conf=`sed -En 's/^[[:space:]]*<string>.*--config-path (.*)<\/string>$/\1/p' \
${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
'';
}

View file

@ -42,6 +42,7 @@
system.defaults.NSGlobalDomain."com.apple.springing.delay" = 0.0;
system.defaults.NSGlobalDomain."com.apple.swipescrolldirection" = true;
system.defaults.".GlobalPreferences"."com.apple.sound.beep.sound" = "/System/Library/Sounds/Funk.aiff";
system.defaults.menuExtraClock.FlashDateSeparators = false;
system.defaults.menuExtraClock.Show24Hour = false;
system.defaults.menuExtraClock.ShowDayOfWeek = true;
system.defaults.menuExtraClock.ShowDate = 2;
@ -50,18 +51,29 @@
system.defaults.dock.orientation = "left";
system.defaults.dock.persistent-apps = ["MyApp.app" "Cool.app"];
system.defaults.dock.persistent-others = ["~/Documents" "~/Downloads/file.txt"];
system.defaults.dock.scroll-to-open = false;
system.defaults.finder.AppleShowAllFiles = true;
system.defaults.finder.ShowStatusBar = true;
system.defaults.finder.ShowPathbar = true;
system.defaults.finder.FXDefaultSearchScope = "SCcf";
system.defaults.finder.FXPreferredViewStyle = "Flwv";
system.defaults.finder.FXRemoveOldTrashItems = false;
system.defaults.finder.AppleShowAllExtensions = true;
system.defaults.finder.CreateDesktop = false;
system.defaults.finder.NewWindowTarget = "Other";
system.defaults.finder.NewWindowTargetPath = "file:///Library/Apple";
system.defaults.finder.QuitMenuItem = true;
system.defaults.finder._FXShowPosixPathInTitle = true;
system.defaults.finder._FXSortFoldersFirst = true;
system.defaults.finder._FXSortFoldersFirstOnDesktop = false;
system.defaults.finder.FXEnableExtensionChangeWarning = false;
system.defaults.finder.ShowExternalHardDrivesOnDesktop = false;
system.defaults.finder.ShowHardDrivesOnDesktop = false;
system.defaults.finder.ShowMountedServersOnDesktop = false;
system.defaults.finder.ShowRemovableMediaOnDesktop = false;
system.defaults.hitoolbox.AppleFnUsageType = "Show Emoji & Symbols";
system.defaults.screencapture.location = "/tmp";
system.defaults.screencapture.include-date = true;
system.defaults.screensaver.askForPassword = true;
system.defaults.screensaver.askForPasswordDelay = 5;
system.defaults.smb.NetBIOSName = "IMAC-000000";
@ -91,6 +103,13 @@
true;
};
};
system.defaults.controlcenter.BatteryShowPercentage = true;
system.defaults.controlcenter.Sound = false;
system.defaults.controlcenter.Bluetooth = true;
system.defaults.controlcenter.AirDrop = true;
system.defaults.controlcenter.Display = false;
system.defaults.controlcenter.FocusModes = false;
system.defaults.controlcenter.NowPlaying = true;
test = lib.strings.concatMapStringsSep "\n"
(x: ''
echo >&2 "checking defaults write in /${x}"

View file

@ -19,45 +19,62 @@
users.users.foo.shell = pkgs.bashInteractive;
users.users."created.user".uid = 42001;
users.users."created.user".description = null;
users.users."created.user".home = null;
users.users."created.user".shell = null;
users.users."unknown.user".uid = 42002;
test = ''
echo "checking group creation in /activate" >&2
grep "dscl . -create '/Groups/foo' PrimaryGroupID 42000" ${config.out}/activate
grep "dscl . -create '/Groups/foo' RealName 'Foo group'" ${config.out}/activate
grep "dscl . -create '/Groups/created.group' PrimaryGroupID 42001" ${config.out}/activate
grep -qv "dscl . -delete '/Groups/created.group'" ${config.out}/activate
set -v
echo "checking group deletion in /activate" >&2
grep "dscl . -delete '/Groups/deleted.group'" ${config.out}/activate
grep -qv "dscl . -create '/Groups/deleted.group'" ${config.out}/activate
# checking group creation in /activate
grep "dscl . -create ${lib.escapeShellArg "/Groups/foo"} PrimaryGroupID 42000" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Groups/foo"} RealName ${lib.escapeShellArg "Foo group"}" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Groups/created.group"} PrimaryGroupID 42001" ${config.out}/activate
(! grep "dscl . -delete ${lib.escapeShellArg "/Groups/created.group"}" ${config.out}/activate)
# checking group deletion in /activate
grep "dscl . -delete ${lib.escapeShellArg "/Groups/deleted.group"}" ${config.out}/activate
(! grep "dscl . -create ${lib.escapeShellArg "/Groups/deleted.group"}" ${config.out}/activate)
echo "checking group membership in /activate" >&2
grep "dscl . -create '/Groups/foo' GroupMembership 'admin' 'foo'" ${config.out}/activate
grep "dscl . -create '/Groups/created.group' GroupMembership" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Groups/foo"} GroupMembership ${lib.escapeShellArgs [ "admin" "foo" ]}" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Groups/created.group"} GroupMembership" ${config.out}/activate
echo "checking unknown group in /activate" >&2
grep -qv "dscl . -create '/Groups/unknown.group'" ${config.out}/activate
grep -qv "dscl . -delete '/Groups/unknown.group'" ${config.out}/activate
# checking unknown group in /activate
# checking groups not in knownGroups don't appear in /activate
(! grep "dscl . -create ${lib.escapeShellArg "/Groups/unknown.group"}" ${config.out}/activate)
(! grep "dscl . -delete ${lib.escapeShellArg "/Groups/unknown.group"}" ${config.out}/activate)
echo "checking user creation in /activate" >&2
grep "dscl . -create '/Users/foo' UniqueID 42000" ${config.out}/activate
grep "dscl . -create '/Users/foo' PrimaryGroupID 42000" ${config.out}/activate
grep "dscl . -create '/Users/foo' IsHidden 0" ${config.out}/activate
grep "dscl . -create '/Users/foo' RealName 'Foo user'" ${config.out}/activate
grep "dscl . -create '/Users/foo' NFSHomeDirectory '/Users/foo'" ${config.out}/activate
grep "dscl . -create '/Users/foo' UserShell ${lib.escapeShellArg "/run/current-system/sw/bin/bash"}" ${config.out}/activate
grep "dscl . -create '/Users/created.user' UniqueID 42001" ${config.out}/activate
grep "dscl . -create '/Users/created.user' UserShell ${lib.escapeShellArg "/sbin/nologin"}" ${config.out}/activate
grep "createhomedir -cu 'foo'" ${config.out}/activate
grep -qv "dscl . -delete '/Groups/created.user'" ${config.out}/activate
# checking user creation in /activate
grep "sysadminctl -addUser ${lib.escapeShellArgs [ "foo" "-UID" 42000 "-GID" 42000 "-fullName" "Foo user" "-home" "/Users/foo" "-shell" "/run/current-system/sw/bin/bash" ]}" ${config.out}/activate
grep "createhomedir -cu ${lib.escapeShellArg "foo"}" ${config.out}/activate
grep "sysadminctl -addUser ${lib.escapeShellArgs [ "created.user" "-UID" 42001 ]} .* ${lib.escapeShellArgs [ "-shell" "/usr/bin/false" ] }" ${config.out}/activate
grep "sysadminctl -addUser ${lib.escapeShellArg "created.user"} .* ${lib.escapeShellArgs [ "-home" "/var/empty" ]}" ${config.out}/activate
(! grep "dscl . -delete ${lib.escapeShellArg "/Users/created.user"}" ${config.out}/activate)
(! grep "dscl . -delete ${lib.escapeShellArg "/Groups/created.user"}" ${config.out}/activate)
echo "checking user deletion in /activate" >&2
grep "dscl . -delete '/Users/deleted.user'" ${config.out}/activate
grep -qv "dscl . -create '/Users/deleted.user'" ${config.out}/activate
# checking user properties always get updated in /activate
grep "dscl . -create ${lib.escapeShellArg "/Users/foo"} PrimaryGroupID 42000" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Users/foo"} RealName ${lib.escapeShellArg "Foo user"}" ${config.out}/activate
grep "createhomedir -cu ${lib.escapeShellArg "foo"}" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Users/foo"} UserShell ${lib.escapeShellArg "/run/current-system/sw/bin/bash"}" ${config.out}/activate
grep "dscl . -create ${lib.escapeShellArg "/Users/foo"} IsHidden 0" ${config.out}/activate
echo "checking unknown user in /activate" >&2
grep -qv "dscl . -create '/Users/unknown.user'" ${config.out}/activate
grep -qv "dscl . -delete '/Users/unknown.user'" ${config.out}/activate
# checking user properties that are null don't get updated in /activate
(! grep "dscl . -create ${lib.escapeShellArg "/Users/created.user"} RealName" ${config.out}/activate)
(! grep "dscl . -create ${lib.escapeShellArg "/Users/created.user"} UserShell" ${config.out}/activate)
# checking user deletion in /activate
grep "dscl . -delete ${lib.escapeShellArg "/Users/deleted.user"}" ${config.out}/activate
(! grep "sysadminctl -addUser ${lib.escapeShellArg "deleted.user"}" ${config.out}/activate)
# checking that users not specified in knownUsers doesn't get changed in /activate
(! grep "sysadminctl -addUser ${lib.escapeShellArg "unknown.user"}" ${config.out}/activate)
(! grep "dscl . -delete ${lib.escapeShellArg "/Users/unknown.user"}" ${config.out}/activate)
(! grep "dscl . -create ${lib.escapeShellArg "/Users/unknown.user"}" ${config.out}/activate)
set +v
'';
}