CI/CD¶
At the end you will know what each of the product's two GitHub Actions workflows does, how they fit together, and the real failures hit while proving the release pipeline end to end — not just the happy path.
datagov has two workflows outside the docs-publishing one covered in
Deployment: ci.yml
(quality gates on every push/PR) and
release.yml
(tag-driven binary releases).
CI workflow: quality gates (ci.yml)¶
graph LR
A["push / PR to crates/**, Cargo.toml, Cargo.lock"] --> B[ci.yml: check job]
B --> C["cargo fmt --all --check"]
B --> D["cargo clippy --workspace -D warnings"]
B --> E["cargo test --workspace"]
Path-filtered to crates/**, Cargo.toml, Cargo.lock,
rust-toolchain.toml, and its own path — a docs-only commit never
triggers it. A guard step skips the whole job gracefully if
Cargo.toml doesn't exist yet (a holdover from before Bolt 1 landed
the workspace; harmless now that it does).
Release workflow: tag-driven binary releases (release.yml)¶
graph LR
A["push tag v*"] --> B[release.yml]
B --> C["build-required: darwin-arm64, linux-x86_64"]
B --> D["build-optional: darwin-x86_64, linux-arm64, windows-x86_64 (best-effort)"]
B --> E["sbom: SPDX SBOM"]
C --> F["release: Publish GitHub Release"]
D --> F
E --> F
darwin-arm64 and linux-x86_64 are the required targets for 0.1
(per the release-distribution spec); the rest are best-effort. The
release job's condition explicitly checks only build-required and
sbom succeeded, with !cancelled() overriding GitHub's default
all-needs-must-succeed gating — see why below.
Failures seen so far¶
Four fix(ci): commits landed on 2026-07-26 while proving the release
pipeline against real tags (v0.1.0-rc.1 through rc.4), each fixing
a problem the previous run actually hit:
| Time | Commit | What happened | Fix |
|---|---|---|---|
| 11:34 | e6d80d5 |
A tag like v0.1.0-rc.1 would display as "Latest" on GitHub, which is wrong for a pre-release snapshot ahead of Milestone 0.1 completion. |
Detect a hyphenated tag (rc/alpha/beta) and set prerelease: true on the GitHub Release. |
| 12:30 | d2de1ec |
Observed on rc.1: the macos-13 (darwin-x86_64) runner queued 50+ minutes with no capacity while the other four legs finished in under 3 minutes each, and the release job waited on the entire build matrix regardless of individual leg outcome — one slow best-effort runner stalled the whole release indefinitely. |
Tag each matrix target required: true/false (darwin-arm64 + linux-x86_64 required, the rest best-effort per PRD §29); continue-on-error set per leg from that flag; every leg gets a 20-minute timeout. |
| 13:10 | 24aaac6 |
Two consecutive release runs (rc.1, rc.2) both queued 39–54+ minutes on macos-13 with no runner ever assigned — a capacity problem specific to that runner pool, not something a timeout fixes (timeout only bounds execution time, not the queue wait before a runner exists). |
Cross-compile datagov-darwin-x86_64 from the same macos-14 (Apple Silicon) runner already building darwin-arm64, instead of using the scarce Intel macos-13 pool at all. |
| 13:33 | d9a6b1a |
rc.3's windows-x86_64 leg correctly timed out at 20 minutes (expected, best-effort) — but the "Publish GitHub Release" job was then skipped entirely rather than running. continue-on-error on one matrix leg does not make a downstream needs: job proceed, because GitHub collapses an entire matrix job's result into one worst-case value for needs.<job>.result; a downstream job can't tell "a required leg failed" from "only a best-effort leg failed" when they share one matrix. |
Split the single build job into separate build-required (no continue-on-error, blocks the release normally) and build-optional (continue-on-error: true) jobs. The release job's if checks only build-required and sbom succeeded, with !cancelled() overriding the default gating so build-optional's expected failures never skip it. |
v0.1.0-rc.4 published clean afterward: both required targets
succeeded, both best-effort Unix/ARM targets succeeded, the
best-effort Windows target timed out without blocking the release, and
the SBOM + checksums attached correctly with prerelease: true set.
v0.1.0-rc.5 (the current latest tag) followed the same green path.
Next: Deployment for how this documentation site itself — a separate, unrelated pipeline — gets built and published.