fix(release): attach the binaries before the release is published - #16
Conversation
A published GitHub release is immutable, so whichever workflow publishes it
first locks every other one out. binaries.yml and release.yml were both
triggered by the v* tag, and two tag-triggered workflows are unordered by
construction — so mint's release job published the release before binaries.yml
could attach, and the upload died with
HTTP 422: Cannot upload assets to an immutable release.
That is how v0.2.0 AND v0.3.0 both shipped with no binaries. Both workflows
were written create-or-upload to be "order-independent", which is correct for
mutable releases and precisely wrong for immutable ones — order-independence is
what puts the publish first half the time.
Fix it with one writer, the path mint's #30 added for exactly this: fold
binaries.yml into release.yml as a `needs:` job that uploads a workflow
artifact, and hand the artifact name to release-provenance via the new
`assets-artifact` input. It downloads it from the same run, attaches it next to
the provenance, and publishes once. Nothing races for the release.
Pin notes, since the pair looks mismatched on purpose:
- the workflow pin moves to mint main @ #30 (576e904), the commit carrying
the draft-until-attached fix. No mint tag contains it yet — v0.5.0 predates
it — so a SHA is the only way to consume it, which is what the org
convention asks for anyway.
- `ref` stays at v0.3.1. It selects the mint code that RUNS (`mint attest`),
and release-provenance.yml was byte-identical from v0.3.1 until #30, so
this change carries the ordering fix and nothing else. Moving `ref` would
put drift-gate's signed provenance onto three minors of unrelated mint
changes. version.yml is on v0.3.1 too; bump both when mint tags.
The binaries job keeps the workflow's default contents: read; only the release
job is granted contents/id-token write plus `actions: read`, which a caller has
to declare itself because a called workflow only gets the intersection.
release_workflow_test.ts guards the shape rather than a run, so a regression
fails on the PR instead of six weeks later on a tag that ships an empty and
now-immutable release. It asserts the `needs:`, that the uploaded artifact name
matches the one requested, that `actions: read` is granted, that no other
workflow writes to the release at all, and that the mint pin has not regressed
below the fix.
That last one is not hypothetical. Dependabot converges `uses:` pins onto the
referenced repo's LATEST TAG (.github/dependabot.yml spells this out), mint's
latest tag is v0.5.0, and v0.5.0 predates #30 and does not define
`assets-artifact` — so next Monday's grouped "actions" bump is a live path back
to this bug. The guard accepts a 40-hex SHA or a tag from v0.6.0 up, so it
blocks the revert without standing in the way of the real re-pin later.
Incidental: the v* trigger now also builds binaries for prerelease tags, which
binaries.yml's stricter v[0-9]+.[0-9]+.[0-9]+ filter excluded. release.yml
already ran provenance on v*, so this aligns them.
Does NOT backfill the v0.2.0/v0.3.0 binaries the issue also asks for: both
releases are already published and immutable, so nothing can be attached to
them. See the PR for the options — it needs a maintainer decision.
Refs bounded-systems/mint#30.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014P74tnmfWfpq5PaZ67BKsh
|
CI status:
Flagging rather than fixing, since the lane is One scope note on this PR: the guard tests read the workflows as fixtures, so CI proves the shape on every PR, but the end-to-end attach can only really be exercised by a real Generated by Claude Code |
Dependabot group update for the github-actions ecosystem: re-pins the actions used across ci.yml, deps.yml, front-desk-add.yml, publish-jsr.yml, release.yml and version.yml to their new commit SHAs. Pin bumps only — no workflow behavior changes. Supersedes #15, which was closed after #16 removed binaries.yml out from under it.
Fixes #5 — the forward half. The backfill half is not in this PR and needs a decision from you; see the last section.
Downstream of bounded-systems/mint#30, which landed the
assets-artifactpath an hour before this.The bug
A published GitHub release is immutable, so whichever workflow publishes it first locks every other one out.
binaries.ymlandrelease.ymlwere both triggered by thev*tag, and two tag-triggered workflows are unordered by construction — so mint's release job published the release beforebinaries.ymlcould attach, and the upload died withBoth workflows were written create-or-upload to be "order-independent", which is correct for mutable releases and precisely wrong for immutable ones — order-independence is what puts the publish first half the time.
Worth noting: this hit v0.3.0 too, not just v0.2.0. The issue predates that release, so it only names v0.2.0, but both are published with
immutable: trueand carry only the two provenance assets:mint-release.intoto.json,…sigstore.jsonmint-release.intoto.json,…sigstore.jsonThe fix — one writer
binaries.ymlis folded intorelease.ymlas aneeds:job that uploads a workflow artifact; the artifact name goes to release-provenance via mint's newassets-artifactinput. It downloads it from the same run, attaches it next to the provenance, and publishes once. Nothing races for the release.Least privilege: the
binariesjob keeps the workflow's defaultcontents: read. Only the release job getscontents: write+id-token: write+actions: read— the last one has to be declared by the caller, because a called workflow only ever gets the intersection with its caller's grants.The pin pair looks mismatched on purpose
576e904), the commit that carries the draft-until-attached fix. No mint tag contains it yet — v0.5.0 predates it — so a commit SHA is the only way to consume it, which is what the org convention asks for regardless.refstays at v0.3.1. It selects the mint code that runs (mint attest), not the workflow. I checked:release-provenance.ymlwas byte-identical from v0.3.1 until #30 (v0.3.1's blob2c510dcis exactly #30's pre-image), so bumping only the workflow pin carries the ordering fix and nothing else. Movingrefwould put drift-gate's signed provenance onto three minors of unrelated mint changes, unreviewed here.version.ymlis on v0.3.1 too — bump both together when mint tags.Dependabot would revert this next Monday
Not hypothetical, and worth a look even if you skim the rest.
.github/dependabot.ymldocuments that Dependabot convergesuses:pins onto the referenced repo's latest tag, mint's latest tag is v0.5.0, and v0.5.0 predates #30 and does not defineassets-artifactat all. So the weekly groupedactionsbump is a live path straight back to this bug.release_workflow_test.tsguards the predicate that matters: a 40-hex commit SHA passes, and a tag passes only from v0.6.0 up. That blocks the revert without standing in the way of the real re-pin later. If you'd rather belt-and-braces it, a Dependabotignoreentry for the mint pin until v0.6.0 would do it too.Tests
release_workflow_test.tsreads the workflows as fixtures rather than asserting on a run, so a regression fails on the PR instead of six weeks later on a tag that ships an empty and now-immutable release. It asserts theneeds:, that the uploaded artifact name matches the one requested, thatactions: readis granted, that no other workflow writes to the release at all, and the pin floor above.Each was mutation-checked — every one of these fails the suite, and the legitimate future re-pin does not:
needs: binariesactions: readassets-artifactbinaries.ymlFull local run, matching
ci.yml:deno task check,deno lint,deno fmt --check,deno task test12/12, and the dogfooddrift checkstill 21/21 symbols.The new test sits at the repo root rather than in
src/(it tests CI, not the published surface), so it is added todeno.json'sfmt/lintincludes to stay covered.publish.excludealready drops**/*_test.ts.Incidental
The
v*trigger now also builds binaries for prerelease tags, whichbinaries.yml's stricterv[0-9]+.[0-9]+.[0-9]+filter excluded.release.ymlalready ran provenance onv*, so this aligns them rather than widening anything new.The backfill needs your call — I did not do it
The issue also asks to backfill the v0.2.0 binaries. That cannot be done as asked: both v0.2.0 and v0.3.0 are already published and
immutable: true, which is the very condition that caused the 422. There is no upload that attaches to them. The options, and none of them is mine to pick:patchintent in.release/resolves 0.3.0 → 0.3.1, so cutting that gets users binaries promptly without touching immutable history.Happy to implement whichever you pick.
Manual, not attempted here
Nothing in
[settings]/[org]is required for this. If you want option 2, that is a maintainer-run release deletion.Generated by Claude Code