ci: don't lose the GitHub Release when the PyPI upload fails - #50
Merged
Conversation
Release incident 2026-07-26 (v0.6.0): github-release had needs: [publish-pypi] and no if:, so a PyPI-side trusted-publisher misconfiguration — which fails the OIDC token exchange before any upload — skipped the GitHub Release too. The tag and the container image were published, but the release record and its artifacts were lost, and re-running could not recover them because a tag's workflow is pinned at the tag ref. v0.6.0's release had to be created by hand. A GitHub Release documents the tag; PyPI is an independent channel with its own failure modes. github-release now gates on build and runs even when publish-pypi failed, with an explicit needs.build.result check so it is still skipped when there is no artifact to attach. publish-pypi stays in needs: for ordering only.
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 happened (v0.6.0 release incident, 2026-07-26)
github-releasehadneeds: [publish-pypi]and noif:, so it inherited the default "skip if any dependency failed".The PyPI trusted-publisher exchange failed with
invalid-publisher— a registry-side configuration problem that uploads nothing:Consequences: the tag was pushed and the container image published and attested, but the GitHub Release was skipped and its artifacts (wheel, sdist, CycloneDX SBOM) went with it. Re-running could not recover it, because a tag's workflow is pinned at the tag ref — so v0.6.0's release had to be created by hand from the run's artifacts.
Fix
A GitHub Release documents the tag. PyPI is an independent distribution channel with its own failure modes (registry outage, publisher config, version already present). One should not erase the other.
always()so a failedpublish-pypino longer skips it.needs.build.result == 'success'becausealways()alone would also run the job after a failedbuild, when there is no artifact to attach — it would fail confusingly instead of being skipped.publish-pypistays inneeds:for ordering only, so the release is still created after the upload attempt rather than racing it.Verification
test_github_release_survives_a_failed_pypi_publish— fails onmain'srelease.yml(assert 'build' in ['publish-pypi']) and passes here.pytest— 718 passedruff format --check/ruff check src/ tests//mypy src/— cleangitleaksscoped toorigin/main..HEAD— no leaksStill requires operator action (cannot be done from repo files)
blackaxgit, repositoryssh-mcp, workflowrelease.yml, environmentpypi. PyPI's latest is still 0.1.4; 0.6.0 was never uploaded, so the version is still free.pypienvironment. It was auto-created by the first run with no protection rules, so the reviewer gate thatAGENTS.mdandCHANGELOG.mdboth describe does not currently exist.