Coordinated GitHub Release and PyPI publication¶
Version 0.9.0 uses the release machinery qualified during the 0.9.0rc1 ceremony. The final release is a separate immutable version; the RC remains a prerelease record.
One build, two publication surfaces¶
.github/workflows/release.yml owns the final ceremony:
merge the fully qualified 0.9.0 PR to protected main
|
v
build wheel + sdist once
|
+--> twine check
+--> fresh-wheel install + smoke test
+--> fresh-sdist install + smoke test
|
v
wait for every exact-main required check
|
v
create annotated tag v0.9.0
create final GitHub Release
attach wheel + sdist + SHA256SUMS
|
v
download those exact GitHub Release assets
verify SHA256SUMS
remove checksum manifest from upload directory
|
v
publish the SAME wheel + sdist to production PyPI through OIDC
|
v
install eyetrajectoriespy==0.9.0 from production PyPI
run installed-package smoke test
The wheel and sdist are never rebuilt separately for GitHub and PyPI.
Production and recovery triggers¶
Release automation is manual-dispatch only. Normal pushes and merges to
main never publish.
The workflow exposes four explicit targets:
build-only
testpypi
production
resume-production
production is the ordinary publication path. It is intentionally strict:
- all version declarations must agree;
- the version must not be a development release;
- GitHub must report exact protected
main; - governance issue #64 must be closed;
- all required exact-main qualification checks must pass;
- the target GitHub tag/release must not already exist;
- the target PyPI version must not already exist.
If any release/tag/version already exists, ordinary production fails. It does
not silently retain an existing GitHub Release and it does not use
skip-existing on production PyPI.
resume-production is a deliberately separate recovery path. It requires an
existing GitHub Release for the same version whose tag resolves to exact current
main. It then reuses those immutable GitHub Release artifacts and may use
PyPI skip-existing semantics to recover from a partially completed upload or
post-upload verification failure.
This separation enforces the ordinary invariant:
one production invocation -> one previously unpublished version
while still providing an explicit audited recovery mechanism.
Dedicated production PyPI authority¶
Production and recovery jobs use only:
environment: pypi
PYPI_TRUSTED_PUBLISHING_CONFIGURED
PYPI_REQUIRED_REVIEWER_CONFIGURED
The testpypi environment is no longer accepted as production authority.
Issue #69 is therefore a mandatory pre-release blocker until the production
PyPI Trusted Publisher is registered with:
Owner: stefanosbalaskas
Repository: eyetrajectoriespy
Workflow: release.yml
Environment: pypi
The successful 0.9.0rc1/0.9.0 OIDC publications remain historical evidence that
Trusted Publishing works, but they do not count as evidence that the dedicated
pypi publisher claim has been migrated.
Optional TestPyPI rehearsal¶
Manual workflow dispatch remains available for:
build-only;testpypi.
TestPyPI is optional for the final 0.9.0 ceremony because the RC already exercised the complete production PyPI path successfully. It remains useful for future publisher/environment migrations.
Final 0.9.0 ceremony¶
- Align package, citation, validation, workflow and documentation versions to
0.9.0. - Archive the qualified RC performance snapshot.
- Qualify a fresh
0.9.0performance envelope. - Pass the complete pull-request matrix.
- Merge to protected
main. - Pass the complete exact-main matrix.
- Create annotated tag
v0.9.0and final GitHub Release. - Attach the exact wheel, sdist and
SHA256SUMS. - Download and checksum-verify those exact release assets.
- Publish only the wheel and sdist to production PyPI through OIDC.
- Install exactly
eyetrajectoriespy==0.9.0from production PyPI. - Run the installed-package canonical smoke test.
The ceremony is complete only after step 12 succeeds.
Post-release development¶
After a stable release, main moves to a development version such as
0.9.1.dev0, and RELEASE_READINESS.json is disarmed. Ordinary development
therefore cannot accidentally republish the previous version or create a new
GitHub Release. A future release requires a reviewed version/readiness change
plus explicit target=production dispatch.