Skip to content

fix(release): publish the GitHub Release only after verified R2 staging - #497

Merged
Teakowa merged 2 commits into
mainfrom
feat/293-r2-promotion
Oct 3, 2026
Merged

Teakowa merged 2 commits into
mainfrom
feat/293-r2-promotion

Conversation

@e54-bot

@e54-bot e54-bot commented Oct 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #293.

What changed

publish-r2.sh now has three phases — objects (immutable versioned uploads + public verification, plus the stable installer scripts), pointer (the channel's wright/<latest|nightly>/version write + public verify), and all (the previous combined behavior). The release workflow uses them to invert the old order:

  • publish-r2 runs objects right after release-assets — before the GitHub Release — so an R2 upload or public-verification failure blocks promotion.
  • publish-release needs publish-r2; a failed/skipped R2 stage leaves no newly public release.
  • New advance-latest job runs pointer only after publish-release succeeds, so wright/latest/version never points at a version lacking a canonical public release.
  • Nightly has no GitHub Release, so it runs objects then pointer inside publish-r2.

verify-dist.py gains release_promotion_order(): it parses the workflow's needs edges and step bodies (comments stripped, job bodies bounded to the jobs section) and fails unless publish-r2 needs release-assets, publish-release needs publish-r2 (and publish-r2 doesn't need it back), the objects step precedes any pointer step, the in-job pointer is gated on channel == 'nightly', advance-latest is stable-gated and runs pointer, and publish-tap still waits on publish-release.

docs/release.md and ADR-0019 now describe the new order, including that a promotion cancelled after staging can leave verified immutable R2 objects inert (pointer never advanced; retries byte-check them).

Acceptance criteria mapping

  • Same verified artifacts: both R2 and the Release still download verified-release-assets — no second build/packaging path.
  • R2 failure leaves no public release: enforced by the publish-release → publish-r2 edge (job-level needs gating, no always() bypass).
  • Pointer never outruns the release: advance-latest needs publish-release.
  • Independent verification: release_promotion_order() runs inside verify-dist (CI dist-validation leg).

Verification

  • python3 scripts/verify-dist.py — all checks pass, including the new ordering guard.
  • Negative tests: mutated workflows (pointer before objects, missing publish-r2 edge, comment-fabricated edges, wrong-polarity/ungated channel gates, missing stable gate) each fail the guard.
  • bash -n scripts/publish-r2.sh.
  • Mocked aws/curl runs: objects publishes 8 artifacts (+2 installer scripts for stable) with zero pointer writes; pointer writes only wright/<latest|nightly>/version and needs no ARTIFACTS_DIR; all preserves the old single-invocation flow; bogus phase exits 2; missing env vars fail fast.
  • Independent review of the diff completed; findings fixed in the follow-up commit (guard ordering + parser holes, fail-fast env check, doc drift).

Fixes #293. The workflow published the public GitHub Release before the
immutable R2 objects existed, so a failed R2 stage could leave a
complete-looking canonical release with no distribution artifacts.

publish-r2.sh now has objects|pointer|all phases: the immutable versioned
objects (and stable installer scripts) are uploaded and publicly verified
in `objects`; the channel's version pointer advances in `pointer`. The
publish-r2 job runs objects for both channels right after artifact
verification, and nightly's pointer inside the same job since nightlies
have no GitHub Release. publish-release needs publish-r2, so an R2 upload
or verification failure leaves no newly public release. A new
advance-latest job runs the stable pointer after the canonical release is
public, so wright/latest/version never points at a version lacking one.
publish-tap already waits on publish-release and keeps that ordering.

verify-dist.py now parses the workflow's needs edges and asserts this
promotion order, the independent release-flow check #293 asks for.
…env checks

Review follow-up on the #293 reorder. verify-dist.py's workflow parser
now strips comments before reading needs edges and step bodies, bounds the
jobs scan at the next top-level key, asserts publish-r2 needs
release-assets, requires the literal stable/nightly channel gates, and
checks the pointer step runs after objects inside publish-r2 — previously
a reordered or wrong-polarity step could pass the guard. publish-r2.sh
again requires R2_INSTALLER_PUBLIC_BASE_URL up front for stable instead of
after the uploads. docs/release.md notes that a promotion cancelled after
R2 staging can leave verified immutable objects inert, and ADR-0019 no
longer describes the superseded ordering.
@Teakowa
Teakowa merged commit 60d8bfa into main Oct 3, 2026
22 checks passed
@Teakowa
Teakowa deleted the feat/293-r2-promotion branch October 3, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Publish the GitHub Release only after verified R2 staging

2 participants