👷 ci(release): split into release and tag-triggered publish#3042
Merged
gaborbernat merged 2 commits intopypa:mainfrom Feb 16, 2026
Merged
👷 ci(release): split into release and tag-triggered publish#3042gaborbernat merged 2 commits intopypa:mainfrom
gaborbernat merged 2 commits intopypa:mainfrom
Conversation
4439552 to
50a52b1
Compare
The release workflow was broken in multiple ways: local releases didn't trigger any CI publishing, towncrier output wasn't formatted before committing (failing pre-commit in check workflow), and the GitHub release was never created. Split into two workflows that converge on the same path regardless of how the release is triggered (locally or via workflow_dispatch): - release.yaml (workflow_dispatch): generates changelog, runs pre-commit, commits, tags, and pushes - publish.yaml (tag push): builds sdist/wheel/zipapp, publishes to PyPI via trusted publisher, creates GitHub Release with zipapp, updates get-virtualenv, with automatic rollback on failure Also fixes towncrier config naming tox instead of virtualenv. Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
Symlinks with .cmd extension get routed through cmd.exe on Windows, which cannot execute Python scripts. Only .exe and bare symlinks are valid for Python interpreter discovery.
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.
The release workflow was broken in several ways after the recent restructure: running
tox r -e releaselocally pushed a commit and tag but nothing in CI reacted to the tag push, so no PyPI publish, no GitHub Release, and noget-virtualenvupdate happened. Theworkflow_dispatchpath worked end-to-end but the release commit failed the check workflow becausetowncrier-generatedchangelog.rstwas committed without runningdocstrfmt. The towncrier config also hadname = "tox"instead of"virtualenv".This splits the monolithic
release.yamlinto two workflows that converge on the same publish path regardless of how the release is triggered. 🔀release.yaml(workflow_dispatch) now only handles Phase 1: generating the changelog, running pre-commit to ensure formatting is correct, committing, tagging, and pushing. A newpublish.yamltriggers on any tag push matching*.*.*and handles Phase 2: building sdist/wheel/zipapp, publishing to PyPI via trusted publisher (OIDC), creating a GitHub Release with the zipapp attached, and updatingget-virtualenv. If the publish fails, a rollback job automatically reverts everything.The key insight is that both local (
tox r -e release) and CI (workflow_dispatch) releases now produce the same artifact — a tagged commit onmain— and the tag push is what triggers publishing. 🔐 PyPI publishing always happens via the trusted publisher in thereleaseenvironment, never locally. Therelease.yamlusesGH_RELEASE_TOKENfor checkout so the push triggerspublish.yaml(pushes withGITHUB_TOKENdon't trigger other workflows).