Commit Graph
2 Commits
Author SHA1 Message Date
4grayandClaude Opus 5 1471e7af36 ci(release): sweep PR draft releases the close event never reaches (#1641)
The `pull_request: closed` cleanup is the fast path, not a guarantee: GitHub
does not run that workflow when the head ref is already gone at event time,
which is what Dependabot does when it supersedes one of its own PRs. 15
`test-pr-<n>` drafts had been orphaned that way, 13 of them Dependabot's.

Add a daily scheduled (and manually dispatchable) sweep to the same workflow.
It lists every draft tagged `^test-pr-[0-9]+$`, asks GitHub for that PR's live
state, and deletes only when the PR reports closed. It fails closed: a PR
lookup error leaves the draft untouched, a failed release listing fails the job
rather than sweeping a short list, and only a confirmed HTTP 404 excuses a
failed delete — `gh api` exits 1 for every failure alike, so the re-check reads
the response status instead of the exit code.

Workflow permissions drop to `contents: read`; the event job keeps
`actions: write` + `contents: write`, the sweep takes only `contents: write`.
No new actions. Docs: new "Rolling test drafts" section in
docs/architecture/release-pipeline.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 13:28:30 +02:00
4grayandClaude Fable 5 29e624b0dd ci(release): make test draft releases traceable and self-cleaning (#1202)
* ci(release): make test draft releases traceable and self-cleaning

Every PR and master build created a draft named "Release v<version>"
with tag test-<github.sha>, so 70+ identical drafts piled up and PR
drafts were untraceable (for pull_request events github.sha is the
ephemeral merge-commit SHA that resolves to nothing in the repo).

- Title test drafts as "v<ver> — PR #<n> @ <sha> [test]" /
  "v<ver> — master @ <sha> [test]"; tag releases keep "Release v<ver>"
- Prepend a context header (PR, head commit, workflow run links) to the
  auto-generated release notes
- Use the PR head SHA and pass target_commitish so generated notes
  actually cover the PR commits
- Use stable tags (test-pr-<n>, test-master) so action-gh-release
  updates one rolling draft in place instead of creating a new one per
  push
- Mark all non-tag drafts as prerelease
- Cancel superseded in-progress PR builds via a concurrency group
- Delete a PR's rolling draft when the PR closes (new workflow)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): close review-bot race windows in draft release flow

- Move concurrency from workflow level to job level: cancelling a whole
  run could interrupt action-gh-release mid-asset-replacement and leave
  the rolling draft incomplete. Build slots still cancel superseded PR
  work (matrix-aware groups); the release job gets its own serializing,
  never-cancelling group.
- Re-check the live PR state in the release job right before touching
  the draft, so a build that outlives its PR cannot recreate the draft
  after cleanup deleted it.
- In the cleanup workflow, cancel still-running builds of the closed PR
  (dead work anyway) and wait for them to settle before deleting.
- Emit an explicit empty `body=` output for tag builds instead of a
  blank-line heredoc.

Addresses Codex and Greptile review feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): grant actions:write so PR-close cleanup can cancel builds

gh run cancel needs the actions scope; with only contents: write the
cancellation 403s silently and the settle-poll burns its full window.
Also skip the cleanup job for fork PRs entirely: they never get a
draft and their token is read-only regardless of the permissions block.

Addresses Greptile P1 / Codex P2 follow-up on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): re-assert rolling draft title after asset upload

action-gh-release@v2 updates name/body/target_commitish on the normal
draft-reuse path, but in a rare race (release listing transiently
missing the draft) it uploads assets to the canonical oldest draft
without refreshing its metadata. PATCH the title and commitish on the
release id the action actually used, so the draft title always names
the current head SHA; the body is left alone to preserve generated
notes.

Addresses Codex round-2 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): prune stale assets before updating a rolling draft

The release action only replaces same-name assets, so a PR that bumps
the app version would leave old-version installers beside the new set
in its rolling draft. Delete all existing assets of the matched draft
before the upload; the action re-uploads the full current set right
after. Published releases are never touched.

Addresses Codex round-3 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): rebuild full draft metadata after asset upload

Extend the post-upload metadata step to also rebuild the body (context
header + notes from the same generate-notes API the action uses), not
just title/commitish. The rolling draft now ends up with correct
metadata regardless of which internal action-gh-release path ran,
including the rare canonicalize-duplicate fallback. If notes
generation fails, the body is left as the action set it.

Addresses Codex round-4 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): only cancel pull_request runs when cleaning up a closed PR

A manually dispatched build on the same head branch is not the PR's
work; filter the cancellation list by event so PR-close cleanup cannot
abort it.

Addresses Codex round-5 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): guard PR-close cleanup against close-reopen races

Re-check the live PR state at the start of the cleanup job and again
right before deleting the draft, so a PR that is reopened while the
cleanup is queued or waiting keeps its rolling draft and its fresh
reopened-run builds are not cancelled.

Addresses Codex round-6 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): keep tag_name when patching rolling draft metadata

PATCHing a draft release without tag_name makes GitHub drop the
pending tag (the draft turns into untagged-<hash>), so the next run
cannot find the rolling draft by tag and creates a duplicate — observed
live on this PR's own drafts. Include tag_name in both PATCH payloads
of the metadata step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 07:59:53 +02:00