Skip to content

ci: SLSA L3 build-provenance attestations on the publish arm - #227

Merged
ronaldtse merged 2 commits into
mainfrom
feat/release-attestations
Sep 30, 2026
Merged

ronaldtse merged 2 commits into
mainfrom
feat/release-attestations

Conversation

@ronaldtse

Copy link
Copy Markdown
Contributor

What

Fans out the SLSA Build L3 artifact-attestation arm that landed in tamatebako/tebako#694 to this factory's publish path (unblocked now that #226 has merged).

  • .github/workflows/_build-platform.yml — the per-leg publish job (the shipping leg: Publish the leg's runtime package (spec 13 §2a) + Sign the leg's release assets (spec 09 §5)) gains a terminal Attest build provenance step (actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8, v4.2.2 — the same pin tebako's release.yml carries) with subject-path: runtime-packages/*: exactly the staged bytes the uploader ships — the runtime package (exe + env image), its .sha256 sidecars, the .manifest.json shard, plus the detached .asc set when the signing arm ran (the gem's signer converges them into runtime-packages/, its default local dir). The dot-hidden .leg-complete marker is never globbed, and the non-shipped *-ucrt-ruby*.dll boot-smoke copies are rm'd by the publish step before the attest runs.
  • The publish job declares attestations: write + id-token: write; .github/workflows/publish.yml's four platform caller jobs (windows, linux-gnu, linux-musl, macos) grant attestations: write down to the reusable workflow — the same flow as the existing windows-signing id-token: write grant (a caller's permissions cap the callee's).
  • The coordinator's release job (registry render + bot PRs) is untouched: the registry lands on main by PR, never as a release asset, so there is nothing to attest there.

Gate parity

The attest step carries the same gate as the leg's publish/sign steps: the publish job's own

if: ${{ always() && !cancelled() && needs.compute.outputs.run == 'true' && inputs.publish && !inputs.audit }}

Build CI, audit runs, and non-publish dispatches skip the publish job outright, so they never attest. A leg whose build failed dies at the artifact-completeness guard before the attest step.

Verification

  • python3 -c yaml.safe_load on both touched workflows: parse OK.
  • actionlint .github/workflows/publish.yml .github/workflows/_build-platform.yml: identical findings to main (four pre-existing shellcheck notes in unrelated steps; this change adds none).
  • bundle exec rspec: 295 examples, 0 failures, 38 pending (environmental — unchanged from main). spec/build_workflow_spec.rb (the ci: run the spawn-edge acceptance per leg before publish (#211) #226 step-ordering assertions): 22 examples, 0 failures — the publish-job ordering contract (download → guard → publish → sign) is preserved; the attest step appends after sign, so no spec change was needed.

Consumers verify with gh attestation verify <asset> --repo tamatebako/tebako-runtime-ruby.

Not to be merged by the author — the orchestrator merges after checks go green.

Each per-leg publish job in _build-platform.yml now closes with a
GitHub artifact attestation (actions/attest-build-provenance v4.2.2,
SHA-pinned — the pin tamatebako/tebako's release.yml carries) over the
exact staged bytes it ships: the runtime package, its .sha256 sidecars,
the .manifest.json shard, and the detached .asc set when the signing
arm ran (the signer converges them into runtime-packages/). The
.leg-complete marker is dot-hidden and the non-shipped *-ucrt-ruby*.dll
copies are rm'd by the publish step, so the glob names neither. The arm
rides the publish job's own gate (inputs.publish && !inputs.audit), so
build/audit runs never attest. The publish job declares
attestations:write + id-token:write, and publish.yml's four platform
caller jobs grant both down to the reusable workflow (a caller's grants
cap the callee's — the same flow as the windows-signing id-token
grant).
The publish.yml callers carried the grant, but the four build caller
jobs also invoke _build-platform.yml — and its publish job's new
permissions block requests attestations: write on every invocation,
build or not. A reusable workflow never elevates past the caller's
grant (the caller comment says so itself), so GitHub refused every
build-* run at startup (startup_failure, no jobs spawned).
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:35 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:35 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:35 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:35 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:36 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:37 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:37 — with GitHub Actions Active
@ronaldtse
ronaldtse deployed to windows-signing September 30, 2026 10:37 — with GitHub Actions Active
@ronaldtse
ronaldtse merged commit 476ddd5 into main Sep 30, 2026
56 checks passed
@ronaldtse
ronaldtse deleted the feat/release-attestations branch September 30, 2026 11:34

This branch was successfully deployed

1 active deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants