Repository navigation
[ci] Allow release corrections before PyPI publication - #108
Conversation
Allow an unpublished release tag to move to a corrected commit without recovering artifacts from the obsolete attempt. Check target-version eligibility before CI, always build fresh distributions, and replace only draft releases. Public GitHub releases require owner cleanup, and any existing PyPI version blocks normal publishing, including partial uploads. After `pypi` approval, recheck the live tag, original bundle, draft assets and PyPI absence. Upload PyPI first, then finalize GitHub in a separate job after both public hashes match. Failed GitHub finalization can rerun independently using the original artifact ID. Keep per-tag serialization and owner cancellation of obsolete waiting runs. Retain generated attestations and publication/finalization outcomes for 90 days, even after failure. Replace the upload-subset artifact with immutable attempt-specific bundles and evidence artifacts. Document manual partial-upload recovery and the limits of saved attestations. nhairs-bot-ref: T0027 ## Test Plan - 413 local tests passed, including 42 release-policy tests. - actionlint passed for release.yml; formatting, pyproject validation, package Pylint and type checks for src/tests/benchmarks plus release.py passed. - Built wheel/sdist, verified their metadata and installed the wheel in a clean environment; isolated release smoke check passed. - GitHub/PyPI API operations and attestation creation still require owner-triggered live validation after merge. No tags, releases, uploads or repository settings were changed.
| from packaging.utils import canonicalize_name # pylint: disable=import-outside-toplevel | ||
| from packaging.version import Version # pylint: disable=import-outside-toplevel |
There was a problem hiding this comment.
Why aren't these imported at top level? (same for other functions)
There was a problem hiding this comment.
These local imports keep the publishing preflight standard-library-only. The approved OIDC job checks out release.py but deliberately does not install packaging; it uses only the bundle/live-tag/GitHub/PyPI verification helpers. Version parsing and distribution metadata validation need packaging, so those commands import it when called in jobs that already install it. A top-level import would make every command, including preflight, require packaging. This was the tradeoff made when moving preflight into the shared script. Keeping imports at module scope would also be reasonable if we install packaging in the publishing job, or split the build-only commands into another module.
Allow an unpublished release tag to move to a corrected commit without recovering artifacts from the obsolete attempt. Check target-version eligibility before CI, always build fresh distributions, and replace only draft releases. Public GitHub releases require owner cleanup, and any existing PyPI version blocks normal publishing, including partial uploads.
After
pypiapproval, recheck the live tag, original bundle, draft assets and PyPI absence through the same Python validation helpers used by finalization. Check out only release.py at the run commit, without persisted credentials; preflight uses isolated Python and only the standard library. Upload PyPI first, then finalize GitHub in a separate job after both public hashes match. Failed GitHub finalization can rerun independently using the original artifact ID. Keep per-tag serialization and owner cancellation of obsolete waiting runs.Retain generated attestations and publication/finalization outcomes for 90 days, even after failure. Finalization evidence is recorded through release.py rather than inline YAML. Replace the upload-subset artifact with immutable attempt-specific bundles and evidence artifacts. Document manual partial-upload recovery and the limits of saved attestations.
nhairs-bot-ref: T0027
Test Plan