#!/usr/bin/env bash # nomarchy-eval-matrix # # Evaluates the opt-in nomarchy.* surface that the per-output flake check # never touches. flake check only evaluates the default configs, so a bug # that only fires when a toggle is flipped — a renamed option, a failed # assertion, a stale reference — sails straight through. Two such bugs # shipped to main before this existed (the vscode option rename and the # impermanence systemd-stage-1 assertion); both would have been caught # here. # # Cost matters: this runs in CI on a small self-hosted runner, and the # first cut (one full `extendModules` eval per individual toggle, plus one # per palette) did ~39 whole-system evaluations and ran ~3h. So: # # * Compatible toggles are COMBINED into a few configs (a failure means # re-run the offending toggle alone to pinpoint it — see the trace hint # at the end). laptop vs desktop formFactor conflict, so they split; # impermanence's multi-disk variant gets its own config for the alt # mainLuksName. # * Palettes are checked DIRECTLY via the lib — the per-palette risk is # the system-side Plymouth base00 → RGB math (fromHexString of 2-char # slices in themes/engine/plymouth.nix), which is pure and needs no # module-system instantiation. Forcing the whole palette attrset also # catches a missing/garbled palette. # * The standalone homeConfigurations aren't re-checked here — the # per-output flake-check step already forces both. # # Net: a handful of full evals instead of ~39. Results come from # builtins.tryEval so one failure doesn't mask the rest. # # nomarchy-eval-matrix # run the matrix, table + exit code # # Add a new opt-in option's coverage by dropping it into the relevant # combined scenario in the `scenarios` attrset below. set -u repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$repo_root" NIX=(nix --extra-experimental-features 'nix-command flakes') read -r -d '' EXPR <<'NIXEOF' || true let f = builtins.getFlake (toString ./.); lib = f.inputs.nixpkgs.lib; nl = import ./lib { inherit lib; }; default = f.nixosConfigurations.default; forced = x: (builtins.tryEval (builtins.seq x true)).success; # Combined opt-in scenarios. Each enables as many compatible toggles as # possible so CI does ~3 full-system evals, not one per toggle. Home-side # options go under home-manager.users.nomarchy.nomarchy.*. scenarios = { # laptop formFactor + every compatible system/home toggle + impermanence. "laptop-stack" = { nomarchy.system = { formFactor = "laptop"; laptop.enable = true; accessibility.enable = true; gaming.enable = true; features.hybridGPU = true; hibernation.enable = true; virtualization.docker.enable = true; impermanence.enable = true; }; nomarchy.hardware.fwupd = true; home-manager.users.nomarchy.nomarchy = { accessibility.enable = true; gaming.enable = true; overrides.enable = true; panelPosition = "bottom"; keymap = { layout = "de"; variant = "nodeadkeys"; }; toggles = { waybar = false; idle = false; nightlight = false; }; }; }; # desktop preset — conflicts with the laptop formFactor, so its own eval. "desktop-stack" = { nomarchy.system = { formFactor = "desktop"; desktop.enable = true; }; }; # impermanence multi-disk variant (alternate mainLuksName). "impermanence-multi" = { nomarchy.system.impermanence = { enable = true; mainLuksName = "crypted_main"; }; }; }; evalToplevel = mod: forced (default.extendModules { modules = [ mod ]; }).config.system.build.toplevel.drvPath; # base00 → the three byte values the Plymouth template substitutes; plus # the whole palette attrset, so a missing/garbled palette is caught. paletteOk = name: let p = nl.getPalette name; b = p.base00; bytes = [ (lib.fromHexString (lib.substring 0 2 b)) (lib.fromHexString (lib.substring 2 2 b)) (lib.fromHexString (lib.substring 4 2 b)) ]; in forced (builtins.deepSeq [ p bytes ] true); in { scenarios = builtins.mapAttrs (_: evalToplevel) scenarios; palettes = builtins.listToAttrs (map (n: { name = n; value = paletteOk n; }) nl.themeNames); } NIXEOF echo "Evaluating combined toggle scenarios + per-palette colour math..." json="$("${NIX[@]}" eval --impure --json --expr "$EXPR" 2>/tmp/eval-matrix.err)" status=$? if [[ $status -ne 0 || -z "$json" ]]; then echo "ERROR: the matrix evaluation aborted before producing results." >&2 echo "This usually means an uncatchable error (abort/infinite recursion) in one" >&2 echo "scenario, or a syntax error in the expression. Full output:" >&2 cat /tmp/eval-matrix.err >&2 exit 1 fi # Render each section and tally failures. fails=0 render() { local section="$1" echo echo "=== $section ===" while IFS=$'\t' read -r name ok; do if [[ "$ok" == "true" ]]; then printf ' ok %s\n' "$name" else printf ' FAIL %s\n' "$name" fails=$((fails + 1)) fi done < <(echo "$json" | jq -r --arg s "$section" '.[$s] | to_entries[] | "\(.key)\t\(.value)"' | sort) } render scenarios render palettes echo if [[ $fails -gt 0 ]]; then echo "$fails scenario(s) failed to evaluate. A combined scenario bundles several" echo "toggles — re-run the suspects individually for the trace, e.g.:" echo " nix eval --impure --show-trace --expr 'let f = builtins.getFlake (toString ./.); in (f.nixosConfigurations.default.extendModules { modules = [ { } ]; }).config.system.build.toplevel.drvPath'" exit 1 fi echo "All scenarios evaluated cleanly."