# Nomarchy CI — eval + lint. # # Catches the regressions that hurt today: # 1. Flake stops evaluating (broken option ref, missing import, etc.). # Checked per-output rather than via `nix flake check` so we can skip # packages.allThemeVariants — the 22-generation linkFarm that OOMs the # runner; every palette is validated one-at-a-time by the matrix (4). # 2. A `nomarchy-*` shell script has a syntax error or a shellcheck # error-severity issue. # 3. `docs/SCRIPTS.md` drifts from the repo state because somebody # added / removed / renamed a script and didn't run the generator # (the pre-commit hook handles this, but only when enabled per-clone). # 4. An opt-in nomarchy.* toggle stops evaluating. `nix flake check` # only evaluates the four default configs, so a bug that only fires # when a toggle is flipped sails through (e.g. the impermanence # systemd-stage-1 assertion). nomarchy-eval-matrix layers each toggle # onto the default config and forces system.build.toplevel. # # Doesn't build ISOs — that needs a binary cache. Add a separate job # once Cachix/Attic is in place. name: Check on: push: branches: [main] pull_request: jobs: eval-and-lint: runs-on: ubuntu-latest # Flakes for every `nix` invocation in the job (the bare `nix flake # check` below relies on this; the scripts pass the flag themselves). # sandbox=false because Stylix/base16.nix do import-from-derivation — # eval realizes fetched `-source` paths mid-check, and the single-user # Nix in this container can't set up the build sandbox (no user # namespaces), which otherwise surfaces as "path '…-source' is not # valid". NIX_SSL_CERT_FILE because we add nix to PATH without sourcing # the installer's profile (which is what normally exports it). env: NIX_CONFIG: | experimental-features = nix-command flakes sandbox = false NIX_SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt steps: - name: Checkout uses: actions/checkout@v4 - name: Install Nix # Plain shell install rather than a JS action: act_runner's bundled # `act` only supports up to node20, and nix-installer-action@main # moved to node24 ("runs.using ... got node24"). A run step has no # node-runtime coupling. Single-user (--no-daemon) needs no systemd, # which the catthehacker container doesn't run as PID 1. # # The installer runs as root here and honours build-users-group= # nixbld from its bundled nix.conf, aborting unless that group exists # AND has members. Create the nixbld group + build users (what a # multi-user install does) before running it. # # Pin the Nix version: the latest (2.34) uses lazy-trees / a git # cache that doesn't materialise flake-input `-source` paths into # the store, so Stylix's import-from-derivation reads fail with # "path '…-source' is not valid". 2.31.5 matches the Nix that wrote # flake.lock locally and evaluates the flake cleanly. run: | NIX_VERSION=2.31.5 groupadd -r nixbld 2>/dev/null || true for i in $(seq 1 10); do useradd -r -g nixbld -G nixbld -d /var/empty \ -s /usr/sbin/nologin -c "Nix build user $i" "nixbld$i" 2>/dev/null || true done curl -L "https://releases.nixos.org/nix/nix-${NIX_VERSION}/install" | sh -s -- --no-daemon echo "$HOME/.nix-profile/bin" >> "$GITHUB_PATH" - name: Materialize flake-input sources for eval # Walker's flake reads its own source tree during evaluation: # importTOML resources/config.toml in its home-manager module and # lib.fileset.toSource for the package src (cargoLock.lockFile = # "${src}/Cargo.lock"). On a cold store the input source isn't # materialised, so flake check / eval die with "path '…-source' is # not valid" (works locally only because prior builds materialised # it; `nix flake archive` reports the input but doesn't make the # fileset-derived src readable). `nix build` of walker's src # eagerly realises it — and the input it reads from — priming the # store so the later eval steps find every path valid. run: | nix build --no-link --impure --expr 'let f = builtins.getFlake (toString ./.); in f.inputs.walker.packages.${builtins.currentSystem}.default.src' - name: Check flake outputs (except the 22-variant pre-cache package) # Stand-in for `nix flake check --no-build`. That also evaluates # packages.allThemeVariants — a linkFarm whose drvPath forces all 22 # home generations into memory at once, OOM-killing the runner. The # eval matrix below already validates every palette one-at-a-time, so # that package's CI value is near-zero. Here we force every other # output (all 4 system closures incl. assertions, both standalone # home generations, the installer VM package, the overlay, the app) # and skip only allThemeVariants/default. Each tryEval is GC'd before # the next, so peak memory is one config, not twenty-two. run: | nix eval --impure --raw --expr 'let f = builtins.getFlake (toString ./.); lib = f.inputs.nixpkgs.lib; sys = "x86_64-linux"; ok = x: (builtins.tryEval (builtins.seq x true)).success; checks = { "nixos.default" = ok f.nixosConfigurations.default.config.system.build.toplevel.drvPath; "nixos.nomarchy-installer" = ok f.nixosConfigurations.nomarchy-installer.config.system.build.toplevel.drvPath; "nixos.installerVm" = ok f.nixosConfigurations.installerVm.config.system.build.toplevel.drvPath; "nixos.nomarchy-live" = ok f.nixosConfigurations.nomarchy-live.config.system.build.toplevel.drvPath; "home.nomarchy" = ok f.homeConfigurations.nomarchy.activationPackage.drvPath; "home.nixos" = ok f.homeConfigurations.nixos.activationPackage.drvPath; "packages.installerVm" = ok f.packages.${sys}.installerVm.drvPath; "overlays.default" = ok (builtins.isFunction f.overlays.default); "apps.installerVm" = ok f.apps.${sys}.installerVm.program; }; failed = builtins.attrNames (lib.filterAttrs (_: v: !v) checks); in if failed == [] then "ALL OK" else throw ("flake-output checks failed: " + builtins.concatStringsSep ", " failed)' - name: Evaluate opt-in toggle / palette / home matrix # flake check never flips a nomarchy.* toggle; this does. jq is # provided via nix shell so the step doesn't depend on the runner # image preinstalling it. run: nix shell nixpkgs#jq --command ./bin/utils/nomarchy-eval-matrix - name: Lint nomarchy-* scripts (bash -n + shellcheck) run: | # Mirror what .githooks/pre-commit runs locally, but across the # whole tree instead of just changed files. Pre-commit gates # individual commits; CI gates branches (including --no-verify # bypasses). set -e fail=0 while IFS= read -r script; do [[ -f "$script" ]] || continue # Python helpers ship under the same nomarchy- prefix # (e.g. nomarchy-haptic-touchpad). Skip non-bash. head -1 "$script" | grep -qE '^#!.*\bbash\b' || continue if ! bash -n "$script"; then echo "::error file=$script::bash syntax error" fail=1 fi if ! nix shell nixpkgs#shellcheck --command shellcheck \ --severity=error --shell=bash "$script"; then echo "::error file=$script::shellcheck error-severity issue" fail=1 fi done < <(find features/scripts/utils core/system/scripts \ themes/engine/scripts \ -maxdepth 1 -type f -name 'nomarchy-*') exit "$fail" - name: docs/SCRIPTS.md is up to date run: | # Regenerate to a temp file and compare. If different, the # contributor forgot to run the generator (or skipped the # pre-commit hook). Fail loudly and tell them the fix. ./bin/utils/nomarchy-docs-scripts --out /tmp/SCRIPTS.regen.md if ! diff -q docs/SCRIPTS.md /tmp/SCRIPTS.regen.md >/dev/null; then echo "::error::docs/SCRIPTS.md is stale." echo "Run: ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md" echo "Then commit the regenerated file." echo "--- diff ---" diff -u docs/SCRIPTS.md /tmp/SCRIPTS.regen.md || true exit 1 fi - name: installer/hardware-db.sh references real nixos-hardware modules run: | # Every 4th-pipe-field in HARDWARE_DB is a nixos-hardware module # name. Half the DB used to point at modules that don't exist # (e.g. microsoft-surface-pro-8 — there's only -pro-intel and # -pro-9), which made the install fail at eval time with # cryptic "attribute not found" errors on real laptops. This # step catches that regression class. awk -F'|' '/^ "/ { gsub(/"/,"",$4); gsub(/^[[:space:]]+|[[:space:]]+$/,"",$4); if ($4) print $4 }' \ installer/hardware-db.sh | sort -u > /tmp/db-refs.txt nix eval --impure --json --expr ' let nh = (builtins.getFlake (toString ./.)).inputs.nixos-hardware.nixosModules; in builtins.attrNames nh' \ | nix shell nixpkgs#jq --command jq -r '.[]' | sort -u > /tmp/db-real.txt missing=$(comm -23 /tmp/db-refs.txt /tmp/db-real.txt) if [[ -n "$missing" ]]; then echo "::error::hardware-db.sh references nixos-hardware modules that don't exist:" printf ' - %s\n' $missing echo "Either fix the name (check the actual attr in nixos-hardware) or drop the row." exit 1 fi