Skip to content

Testing

How to validate changes before deploying them. The goal: catch lint, format, and evaluation errors locally so deploys to remote machines (vanessa, emily, zoe) don’t break them.

lea is the local dev workstation (x86_64-linux). Most building/testing happens there; it can build NixOS configs natively and activate locally with nh os switch ..

Terminal window
just lint # deadnix + statix
just fmt # nixfmt over all tracked *.nix files (writes changes)
  • just lint runs deadnix . (dead-code detection) then statix check . (anti-pattern lints). Read-only — it reports, it doesn’t fix.
  • just fmt formats in place. The same tools run as pre-commit hooks (git-hooks-nix + treefmt-nix) and in CI.
Terminal window
just check

Runs, in order:

  1. deadnix .
  2. statix check .
  3. git ls-files '*.nix' | xargs nixfmt --check (fails if anything is unformatted — run just fmt to fix)
  4. nix flake check

nix flake check evaluates the flake outputs: all nixosConfigurations, darwinConfigurations, packages, devShells, and treefmt/pre-commit checks. Use nix flake check --no-build for a faster eval-only pass that skips building derivations.

Build a config without activating it to confirm it compiles:

Terminal window
## NixOS (nh) — builds the toplevel
nh os build . # current host (lea)
nh os build '.#nixosConfigurations.vanessa.config.system.build.toplevel'
## Darwin (nh)
nh darwin build '.#darwinConfigurations.emily.system'
## Dry-run helpers from the justfile (-n / no realisation)
just build-nixos vanessa
just build-darwin emily
just build-all # emily + vanessa dry-run
## Raw nix build (produces ./result)
nix build '.#nixosConfigurations.lea.config.system.build.toplevel'

Eval-only (fastest — just checks the config evaluates, no build):

Terminal window
nix eval .#nixosConfigurations.<machine>.config.system.build.toplevel.drvPath \
--no-write-lock-file

Before nh os switch . (local) or clan machines update <name> (remote):

  1. just fmt — format.
  2. just lint — deadnix + statix clean.
  3. nix flake check (or just check for everything at once).
  4. Build the affected machine without activating:
    • NixOS: nh os build '.#nixosConfigurations.<machine>.config.system.build.toplevel'
    • Darwin: nh darwin build '.#darwinConfigurations.<machine>.system'
  5. If secrets changed: clan vars generate --machine <machine>.
  6. For a config you can’t fully trust, prefer a stage that’s easy to undo:
    • NixOS: nh os test . (activate without making it the boot default) or nh os boot . (apply on next boot only).
  7. Deploy. If anything goes wrong, see Rollback & Recovery.

The repo also ships a just ci target (fmt + check + build-all) that mirrors CI, and just pre-commit (stage + dry-run builds + fmt).

Terminal window
scripts/validate-inventory.sh # sanity-check the Clan inventory
scripts/diff-machines.sh <m1> <m2> # compare two machine configs
NixOS (lea, vanessa) Darwin (emily, zoe)
Build attr nixosConfigurations.<m>.config.system.build.toplevel darwinConfigurations.<m>.system
Local build nh os build . nh darwin build '.#…'
Local switch nh os switch . nh darwin switch '.#…'
nh os test (non-default activation) yes no equivalent
Remote deploy clan machines update <m> clan machines update <m>

Notes:

  • lea (x86_64-linux) can natively build the other NixOS machine (vanessa). Darwin machines must build their own toplevel (cross-building Darwin from Linux isn’t done here); rely on nix flake check for eval coverage and build on the Mac.
  • zoe is x86_64-darwin: clan-core has dropped x86_64-darwin support, so the devShell with the clan CLI is unavailable there (see flake.nix perSystem guard). Validate zoe-related changes from another host where possible.
  • Some packages don’t build on aarch64-darwin — see Troubleshooting.