PyPI Release Runbook
This runbook defines the phase-18 release path for FEMIC:
Package checks and deterministic wheel verification.
TestPyPI publication and install smoke.
Production PyPI publication using the exact same artifact set.
Release traceability updates in roadmap/changelog.
Pre-release prerequisites
A clean git working tree.
Passing quality gates:
ruff format src testsruff check src testsmypy srcpytestpre-commit run --all-filessphinx-build -b html docs _build/html -W
Packaging tools installed:
python -m pip install --upgrade build twine
Packaging and deterministic wheel checks
Run the project helper:
scripts/release_package_checks.sh
The helper enforces:
deterministic build epoch (
SOURCE_DATE_EPOCH),python -m build,twine check dist/*,wheel install/init smoke,
wheel reproducibility across consecutive builds.
Current known limitation
The wheel is reproducible when SOURCE_DATE_EPOCH is fixed.
The sdist archive may still vary byte-wise across repeated local builds due
to upstream setuptools archive timestamp behavior.
Release flow therefore treats the wheel as the deterministic artifact and uses
the same built dist/* files for TestPyPI and PyPI publication.
TestPyPI publication and smoke
Preferred path: token-free trusted publishing (OIDC).
Before first TestPyPI publish:
Ensure GitHub environment
testpypiexists inUBC-FRESH/femic.In TestPyPI, open account-level publishing settings:
https://test.pypi.org/manage/account/publishing/.Add a pending publisher: - project name:
femic, - owner/repo:UBC-FRESH/femic, - workflow:publish-testpypi.yml, - environment:testpypi.Trigger GitHub workflow
publish-testpypionmain.
Notes:
If there is no
Add projectbutton under/manage/projects, this account-level pending-publisher flow is the correct entry point.First successful OIDC publish will create the TestPyPI project and attach the publisher.
Token fallback (not preferred)
If trusted publishing is temporarily unavailable, upload with token:
export TWINE_USERNAME=__token__
export TWINE_PASSWORD="$TEST_PYPI_API_TOKEN"
python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
Install smoke in a clean environment (after publish):
python -m venv /tmp/femic-testpypi-smoke
/tmp/femic-testpypi-smoke/bin/pip install --upgrade pip
/tmp/femic-testpypi-smoke/bin/pip install \
--index-url https://test.pypi.org/simple \
--extra-index-url https://pypi.org/simple \
femic==0.1.0
/tmp/femic-testpypi-smoke/bin/femic --help
Production PyPI publication
Preferred path: token-free trusted publishing (OIDC).
Before first production publish:
PyPI project:
femic.Publisher type: GitHub Actions.
Repository owner/name:
UBC-FRESH/femic.Workflow filename:
publish-pypi.yml.Environment name:
pypi.
Publish using workflow publish-pypi after TestPyPI validation passes.
Token fallback (not preferred):
export TWINE_USERNAME=__token__
export TWINE_PASSWORD="$PYPI_API_TOKEN"
python -m twine upload dist/*
Post-release traceability checklist
After successful PyPI publication:
Create and push the matching git tag (for example
v0.1.0).Record artifact hashes from
sha256sum dist/*inCHANGE_LOG.md.Mark completed phase-18 checklist items in
ROADMAP.mdand update any linked planning note with validation outcomes.Keep install instructions in
README.mdaligned with the published version.
Trusted publishing troubleshooting
If GitHub Actions fails with invalid-publisher during publish:
confirm the workflow filename and environment name exactly match the trusted-publisher entry on TestPyPI/PyPI,
confirm repository owner/name is
UBC-FRESH/femic,confirm the publish job runs from
refs/heads/main(or an allowed ref),re-run the workflow after saving trusted-publisher settings.