ci(release): adopt the verified release flow - #439
Merged
Conversation
The old arrangement created a draft, a human clicked Publish, and only then did the publish workflows run -- so everything that verified a release ran after it was already public. The release is now created as a prerelease and promoted only once everything that can fail has succeeded; the confirmation step is an environment approval on the job that tags, reached after lint and build are already green. The version no longer has to be typed: it is auto-detected from Conventional Commits, and passing one that disagrees needs `force`. `prerelease: rc` cuts a release candidate, verified exactly like a release but never promoted to latest. See RELEASING.md in reqstool/.github for the whole flow. release_prod.yml and release_test.yml are replaced. The GHCR image job is unchanged in substance -- it still builds from the PyPI release -- but now takes the version from the flow rather than from github.event.release.tag_name, so it works for a dispatched release too. Two things the old workflow did are deliberately not carried over: - `sign-artifacts: true` was passed to pypa/gh-action-pypi-publish, which has no such input. It was silently ignored; `attestations` (what it presumably meant) defaults to true and the shared workflow sets it explicitly. - `skip-existing: true` on the *real* index. A re-run now fails on a version collision instead of silently succeeding, which is the point -- a collision means something is wrong. Test PyPI keeps skip-existing, where a re-run after a downstream failure is routine. build.yml gains `ref` so the flow can re-run it against the tag it just created -- that build is what gives the artifacts their version, since hatch-vcs reads it from git -- and `artifact-name`, because the flow builds twice in one run and upload-artifact rejects a duplicate name. release_test.yml is renamed publish-dev-to-testpypi.yml and calls the shared workflow. Behaviour is unchanged. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
check-semantic-pr.yml referenced `reqstool/.github/.github/workflows/check-semantic-pr.yml@main`, but that file was renamed to common-check-semantic-pr.yml. GitHub cannot resolve a workflow_call to a path that does not exist, so the run fails at startup with no jobs -- and because the failure produces no check run, it never appeared in the PR checks list. PR titles have therefore not been validated here since the rename. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
common-release-tag.yml now validates the version and the ref itself rather than trusting that prepare validated the same values (CodeQL flagged the privileged checkout on an unvalidated ref in reqstool/.github#66). Validating the version needs to know which format to validate against. Signed-off-by: Jimisola Laursen <jimisola@jimisola.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & Why
Migrates this repo to the reworked org release flow in
reqstool/.github#66.
The old arrangement stopped at a draft. A human clicked Publish, and only then did
the publish workflows run — so everything that verified a release ran after it was already
public, and a failure meant a bad release was live.
Now the release is created as a prerelease and promoted only once everything that can
fail has succeeded. A prerelease rather than a draft because a draft is invisible to the
verification too (
/releases/tags/<tag>404s unauthenticated), while a prerelease isreadable by exact tag and still excluded from
/releases/latest.The confirmation step is an environment approval on the job that tags, reached after
lint and build are already green — so a reviewer approves something verified rather than a
version string.
The version is no longer typed by hand. It is auto-detected from Conventional Commits;
passing one that disagrees requires
force, which is the guard that catches "meant 0.3.0,typed 0.2.0" before it becomes a tag.
prerelease: rccuts a release candidate — verifiedexactly like a release, but never promoted to latest.
dry-rundefaults to on: dispatching Release shows the resolved version, the generatednotes, and the raw commits they came from, without tagging or creating anything.
See
RELEASING.mdfor thefull flow.
Author checklist
actionlintcleanTest Plan
Merge order matters: this cannot merge before reqstool/.github#66. The workflows it
calls do not exist on
mainuntil that lands.After merging:
dry-runleft on. Confirm the job summary shows the resolvedversion, the notes, and the commit list — and that no tag or release exists afterwards.
prerelease: rcanddry-runoff. Confirm it pauses for approval, thenproduces a prerelease with its artifacts attached, and that
/releases/latestisunchanged.
prerelease: none.Note on pinning
Workflows are referenced at
@mainrather than a commit SHA, because the SHA does not existuntil #66 is squash-merged. Re-pinning to that SHA is a follow-up.
Repo-specific
release_prod.ymlandrelease_test.ymlare replaced. The GHCR image job is unchanged in substance — it still builds from the PyPI release — but takes the version from the flow rather thangithub.event.release.tag_name, so it works for a dispatched release too.build.ymlgainsrefandartifact-name, and its checkout fetches tags.release_test.yml→publish-dev-to-testpypi.yml, now calling the shared workflow. Behaviour unchanged.Two things deliberately not carried over
sign-artifacts: trueonpypa/gh-action-pypi-publishattestations— presumably what was meant — defaults to true, and the shared workflow sets it explicitly.skip-existing: trueon the real indexskip-existing, where re-running after a downstream failure is routine.