release.yaml becomes test (once, host) -> build (matrix) over the four
cargo-zigbuild targets; each matrix job uploads its binary + .sha256 to
the shared release (idempotent create-or-get). Records the expansion in
ADR-ci-001 (2026-06-13 amendment); macOS stays deferred.
Add paths-ignore (docs/**, **/*.md) to the gate's push + pull_request
triggers so markdown/docs-only changes don't run a full clippy+test that
can't change the outcome. Mixed code+docs pushes still gate (not all
files are ignored); flake/toolchain changes are deliberately not ignored.
Also refresh a stale ADR-0049 -> ADR-ci-002 comment reference.
- Run cargo test before the build so a tag never publishes untested code.
- Pin shell: bash on the scripted steps; the runner defaults to dash,
which rejected `set -o pipefail` and failed run 22's package step.
- Swap `file` (absent in the slim image) for `ls -l`.
Tag pushes ignore paths: filters, so a release tag spuriously rebuilt
the unchanged CI image and re-ran the gate on a commit the branch push
already gated. Add branches: ['**'] to both push triggers — tag pushes
no longer fire them (release.yaml owns tags). Pushing commits + a tag
together still gates the commits via the branch push.
On a v* tag, builds the x86_64-unknown-linux-musl binary in the CI image
and publishes it (+ .sha256) to a Gitea release via the API and the
auto GITEA_TOKEN. x86_64 Linux only for now; rest of the D1 matrix and
D3 packaging layer on later. Correctness comes from the branch gate.
Job-container image the gate runs in. node:22-bookworm-slim satisfies
the act_runner contract (sleep/bash/node) far more cheaply than the
catthehacker images; single-user nix installed on top (pre-create /nix
+ empty build-users-group so it installs as root in a container) with
the flake's devShell pre-warmed — CI enters a ready 1.95.0 toolchain in
~1.4s. Verified by local build. ~5.5GB (rust toolchain closure); dep/
target caching is a noted follow-up.
Diagnostic to determine how the ci-public runner executes jobs and
where the nix toolchain is reachable (host vs default container vs a
custom container:), so the real gate is built on facts. Delete once
the gate lands.