ci: evaluate with nix-eval-jobs under a memory cap, then build the checks
This commit is contained in:
parent
0f67ec34b1
commit
cf2f69d19c
3 changed files with 60 additions and 3 deletions
|
|
@ -18,7 +18,9 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: check
|
- name: check
|
||||||
run: nix flake check
|
# The job name stays "nix flake check": branch protection requires it.
|
||||||
|
# `--inputs-from .` resolves `nixpkgs` to the flake's locked input.
|
||||||
|
run: nix shell --inputs-from . nixpkgs#nix-eval-jobs nixpkgs#jq --command sh scripts/ci-flake-check.sh
|
||||||
|
|
||||||
tracker-tags:
|
tracker-tags:
|
||||||
name: tracker-tag lint
|
name: tracker-tag lint
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ table can assert ahead of it happening.
|
||||||
|
|
||||||
| Job | What it runs | Required? |
|
| Job | What it runs | Required? |
|
||||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
|
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
|
||||||
| **nix flake check** | treefmt formatting, `cargo clippy -D warnings`, `cargo test`, module evaluation | yes |
|
| **nix flake check** | `scripts/ci-flake-check.sh`: nix-eval-jobs, then builds each check (treefmt, clippy, tests, module eval) | yes |
|
||||||
| **tracker-tag lint** | flags `#NNN` issue tags in source and comments (`scripts/check-issue-refs.sh`) | yes |
|
| **tracker-tag lint** | flags `#NNN` issue tags in source and comments (`scripts/check-issue-refs.sh`) | yes |
|
||||||
| **comment-block lint** | flags contiguous comment blocks over 30 lines (`scripts/check-comment-blocks.sh`) | yes |
|
| **comment-block lint** | flags contiguous comment blocks over 30 lines (`scripts/check-comment-blocks.sh`) | yes |
|
||||||
| **doc-pointer lint** | flags `docs/…md` paths and relative markdown links that no longer resolve (`scripts/check-doc-refs.sh`) | yes |
|
| **doc-pointer lint** | flags `docs/…md` paths and relative markdown links that no longer resolve (`scripts/check-doc-refs.sh`) | yes |
|
||||||
|
|
@ -270,7 +270,7 @@ A malicious `default.nix` or build script in a PR can therefore:
|
||||||
|
|
||||||
No forge admin token enters the container: swarm-controller holds it and publishes only the registration token, which only `swarm-ci-runner-register.service` loads, so a job step can't read it. A build process can still reach the forge over the network, and it runs as the runner's own user, so it can read `.runner` and act as this runner; it can't issue admin API calls.
|
No forge admin token enters the container: swarm-controller holds it and publishes only the registration token, which only `swarm-ci-runner-register.service` loads, so a job step can't read it. A build process can still reach the forge over the network, and it runs as the runner's own user, so it can read `.runner` and act as this runner; it can't issue admin API calls.
|
||||||
|
|
||||||
Note: `nix flake check --no-build` (eval-only) reduces the attack surface but doesn't eliminate it — `builtins.fetchGit`, `builtins.fetchurl`, and import-from-derivation can reach the network and filesystem during evaluation. The default CI workflow runs full `nix flake check` (builds derivations), which is the higher-risk path.
|
Note: `nix flake check --no-build` (eval-only) reduces the attack surface but doesn't eliminate it — `builtins.fetchGit`, `builtins.fetchurl`, and import-from-derivation can reach the network and filesystem during evaluation. The CI workflow's `nix flake check` job builds every check derivation, which is the higher-risk path.
|
||||||
|
|
||||||
### Mitigation
|
### Mitigation
|
||||||
|
|
||||||
|
|
|
||||||
55
scripts/ci-flake-check.sh
Executable file
55
scripts/ci-flake-check.sh
Executable file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# CI's "nix flake check" job: the same coverage as `nix flake check` on
|
||||||
|
# x86_64-linux, as two processes so the evaluator has exited before any build
|
||||||
|
# starts and no single evaluator holds the whole suite's heap.
|
||||||
|
#
|
||||||
|
# 1. nix-eval-jobs evaluates every check, package, dev shell, the formatter
|
||||||
|
# and each nixosConfiguration's toplevel, one worker at a time. A worker
|
||||||
|
# past --max-memory-size is replaced after its current attribute, so the
|
||||||
|
# peak is about the cap plus the heaviest single attribute.
|
||||||
|
# 2. nix build builds the checks' derivations. The other attributes are
|
||||||
|
# evaluated only, as `nix flake check` does.
|
||||||
|
#
|
||||||
|
# nix-eval-jobs reports an evaluation error as an `error` field on that
|
||||||
|
# attribute's line, not as its exit status, so this script fails on any.
|
||||||
|
#
|
||||||
|
# Needs nix-eval-jobs and jq on PATH; ci.yml takes both from the flake's
|
||||||
|
# pinned nixpkgs.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
system=x86_64-linux
|
||||||
|
work="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
||||||
|
nix-eval-jobs \
|
||||||
|
--workers 1 \
|
||||||
|
--max-memory-size 4096 \
|
||||||
|
--gc-roots-dir "$work/gcroots" \
|
||||||
|
--flake . \
|
||||||
|
--select "flake: let o = flake.outputs; group = s: s // { recurseForDerivations = true; }; in {
|
||||||
|
checks = group o.checks.$system;
|
||||||
|
packages = group o.packages.$system;
|
||||||
|
devShells = group o.devShells.$system;
|
||||||
|
formatter = o.formatter.$system;
|
||||||
|
nixosConfigurations = group (builtins.mapAttrs (_: c: c.config.system.build.toplevel) o.nixosConfigurations);
|
||||||
|
}" >"$work/jobs.json"
|
||||||
|
|
||||||
|
jq -r 'select(has("error")) | "\(.attr): \(.error)"' "$work/jobs.json" >"$work/errors"
|
||||||
|
if [ -s "$work/errors" ]; then
|
||||||
|
echo "evaluation failed for $(jq -s 'map(select(has("error"))) | length' "$work/jobs.json") attribute(s):" >&2
|
||||||
|
cat "$work/errors" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq -r 'select(.attrPath[0] == "checks") | .drvPath + "^*"' "$work/jobs.json" >"$work/build"
|
||||||
|
if [ ! -s "$work/build" ]; then
|
||||||
|
echo "nix-eval-jobs returned no checks; the --select expression matched nothing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "evaluated $(jq -s length "$work/jobs.json") attributes; building $(wc -l <"$work/build") checks"
|
||||||
|
|
||||||
|
set --
|
||||||
|
while IFS= read -r drv; do
|
||||||
|
set -- "$@" "$drv"
|
||||||
|
done <"$work/build"
|
||||||
|
nix build --no-link --keep-going "$@"
|
||||||
Loading…
Reference in a new issue