Publishing runs through GitHub Actions OIDC trusted publishing — no PyPI tokens are stored anywhere. The two lanes are separate workflow files, so a TestPyPI dry-run never shows (or touches) a PyPI job:
- TestPyPI —
.github/workflows/publish-testpypi.yml, manualworkflow_dispatchonly. - PyPI —
.github/workflows/publish.yml, automatic on a published GitHub release only.
Both lanes build sdist + wheel, run twine check --strict, and smoke-test the
wheel (CLI entry point, import, pytest plugin registration) before uploading.
- PyPI (pypi.org → account → Publishing → "Add a new pending publisher"):
- PyPI Project Name:
pytest-gpu-proof - Owner:
A2R-Lab, Repository:pytest-gpu-proof - Workflow name:
publish.yml - Environment:
pypi
- PyPI Project Name:
- TestPyPI (test.pypi.org, same form):
- Workflow name:
publish-testpypi.yml← note: NOT publish.yml - Environment:
testpypi
- Workflow name:
- GitHub (repo → Settings → Environments): create environments
pypiandtestpypi. Strongly recommended: add yourself as a required reviewer onpypi— every real publish then waits for your explicit approval click, as defense-in-depth on top of the release-only trigger.
The first trusted-publisher upload CREATES the project on (Test)PyPI and registers the name — no separate name registration step.
- Update
versioninpyproject.tomland retitle theunreleasedsection inCHANGELOG.mdwith the date. Commit via the normal branch → PR → CI green → merge flow. - Dry run: Actions → Publish (TestPyPI) → "Run workflow", then
pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ pytest-gpu-proofin a scratch venv and rungpu-proof --help. (TestPyPI never accepts the same version twice — a duplicate-version error on a re-run is expected and harmless.) - Tag + release:
The release event runs the Publish workflow → PyPI (pausing for your environment approval if configured).
git tag v0.X.Y && git push origin v0.X.Y gh release create v0.X.Y --title "v0.X.Y" --generate-notes
- Sanity:
pip install pytest-gpu-proof==0.X.Yin a scratch venv.
Pre-1.0 SemVer: patch = fixes, minor = features or breaking changes (called out in the CHANGELOG). Consumers pinning the git submodule are unaffected by PyPI releases; keep the two install paths (git submodule, PyPI) documented in the README.