Releases are built from a clean, version-aligned tag and published only after artifact validation. The current release tag is v1.3.3.
Before tagging:
- update
src/python_extensions/_version.py; - update
CHANGELOG.mdanddocs/RELEASE_NOTES.md; - synchronize README, support/security, citation, compatibility, and maintainer metadata that actually depend on the release version;
- add new benchmark/certification evidence only when its provenance changes—do not rewrite historical evidence to match a newer version;
- verify
pyproject.toml,CITATION.cff, andLICENSEagree on the current license; - run full runtime diagnostics and relevant stress/differential qualification.
Local gates:
python tools/check_repo.py
python -m compileall -q src tests tools benchmarks/scripts
python -m pytest
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -X dev -W error -m pytestInstall release tooling without installing the local project first:
python tools/install_dependencies.py \
--include-build-system \
--include-runtime \
--upgrade build test
python tools/check_metadata.pyInstalling the local project before clean-tree preflight can create build/ or *.egg-info output and correctly make the repository dirty.
Use the release commit timestamp as SOURCE_DATE_EPOCH input to the project build helper:
EPOCH=$(git show -s --format=%ct HEAD)
python tools/build_release.py --out-dir dist --epoch "$EPOCH"PowerShell:
$epoch = git show -s --format=%ct HEAD
python tools/build_release.py --out-dir dist --epoch $epochThe local helper emits an sdist, host-native wheel, and checksum manifest. On Linux, a raw linux_x86_64 wheel is an internal build product and is not publishable to PyPI.
The tagged GitHub workflow rebuilds from the canonical sdist in manylinux, verifies reproducibility, checks metadata, installs/smokes the exact wheel, reproduces the exact sdist, and stages only publishable distributions.
Twine validates Python distributions, not the checksum file:
python -m twine check dist/*.whl dist/*.tar.gzFor a release candidate, verify final checksums and test the exact artifacts that will be attached/published rather than rebuilding a different copy for smoke tests.
The stable tag must exactly match the package version:
git tag -s v1.3.3 -m "cpython-extensions 1.3.3"
git push origin v1.3.3If signing is not configured, use an annotated tag (git tag -a).
Do not move or overwrite a pushed stable tag. If a stable tag is wrong, fix the source, increment the package version, and create a new tag.
Preview labels (alpha, beta, rc, preview, test, optionally numbered) can exercise the build/GitHub-prerelease path without entering PyPI publication.
git tag -a v1.3.3-rc1 -m "cpython-extensions 1.3.3 release candidate"
git push origin v1.3.3-rc1Stable tags create normal releases; preview tags create prereleases. The workflow attaches the already-validated wheel, sdist, and SHA256SUMS.txt.
Release creation is idempotent for matching assets. A stable asset whose bytes differ from newly certified output is a hard failure rather than something to overwrite silently.
Stable tags can enter the protected pypi environment after artifact/GitHub Release validation. Publishing uses GitHub OIDC (id-token: write) and should not require a stored PyPI API token.
Configure the PyPI Trusted Publisher for Karvp/cpython-extensions and ensure required environment reviewers/protection rules are satisfiable by the release process.
Only *.whl and *.tar.gz are staged to the PyPI publishing directory. The checksum manifest remains a GitHub Release integrity artifact.
- Tag/version mismatch before build: do not rerun the bad stable tag; fix source metadata, increment the version, and create a new stable tag.
- Preview release failure: repair/recreate the preview as allowed by the workflow and rerun validation.
- Stable GitHub Release already exists: never replace a differing stable asset; investigate and publish a new version if bytes must change.
- PyPI upload failure: first determine whether PyPI accepted any file. Published files are immutable; if any artifact for the version was accepted, do not attempt to replace it.
Keep workflow permissions explicit and scoped: artifact readers need actions: read, GitHub Release creation needs contents: write, and PyPI OIDC needs id-token: write only in the publishing job.