Releases are driven by a git tag; nothing publishes on an ordinary push to main.
Pushing a tag matching v*.*.* (for example v0.1.0) triggers
.github/workflows/release.yml, which:
- Builds the container image and pushes it to
ghcr.io/nybarius/svrfas both:<version>(the tag with its leadingvstripped) and:latest. - Builds the sdist and wheel with
python -m build, installs the wheel into a fresh virtualenv and runssvrf --versionagainst it as a smoke test, uploads both as a workflow artifact, and hands them to a separatepublish-pypijob that uploads them to PyPI using Trusted Publishing (OIDC, viapypa/gh-action-pypi-publish) — no stored token or secret. That job needs a trusted publisher registered on pypi.org first (see below); until then it fails, but only that job, so the build and smoke test still run and report clearly.
On every other push and on pull requests, the same workflow builds the image and the wheel and smoke-tests both, but pushes nothing — so a broken build fails CI before it ever reaches a tag.
# 1. Update the version and the changelog.
# - bump `version` in pyproject.toml
# - move the CHANGELOG.md "Unreleased" section under a new "## [X.Y.Z] - YYYY-MM-DD" heading
git add pyproject.toml CHANGELOG.md
git commit -m "Release vX.Y.Z"
git push origin main
# 2. Tag it and push the tag.
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.ZThen watch the release workflow run on the tag. The container is at
ghcr.io/nybarius/svrf:X.Y.Z once it finishes; make it visible to docker pull without
authentication by setting the package's visibility to public once (GitHub packages are
private by default the first time they're pushed): repository → Packages → svrf →
Package settings → Change visibility.
The workflow is ready (build, smoke test, and a publish-pypi job using Trusted
Publishing), but PyPI does not yet know about this repository. One-time setup on
pypi.org, done by the account that will own the svrf project:
- Create a PyPI account and turn on two-factor authentication (required to manage trusted publishers).
- The
svrfproject does not exist on PyPI yet, so add a pending trusted publisher at https://pypi.org/manage/account/publishing/ with:- PyPI project name:
svrf - Owner:
nybarius - Repository name:
SVRF - Workflow name:
release.yml - Environment name:
pypi
- PyPI project name:
- Push a tag matching
v*.*.*. Thepublish-pypijob's first successful run creates thesvrfproject on PyPI from the pending publisher and uploads the sdist and wheel; later releases just need the tag push.
No API token is created or stored anywhere; id-token: write on the publish-pypi job
is what lets pypa/gh-action-pypi-publish mint the short-lived OIDC credential PyPI
trusts instead. The pypi GitHub Actions environment is created automatically the
first time the job runs against it; add required reviewers there for a manual approval
gate before a publish, if wanted.
make wheel # builds dist/svrf-*.{whl,tar.gz} and smoke tests the wheel in a throwaway venv
# container
docker build -t ghcr.io/nybarius/svrf:X.Y.Z -t ghcr.io/nybarius/svrf:latest .
docker login ghcr.io -u <your-username> # a token with write:packages
docker push ghcr.io/nybarius/svrf:X.Y.Z
docker push ghcr.io/nybarius/svrf:latest
# PyPI (manual, if you are not using the trusted-publishing workflow job — needs an
# API token, since a human running this locally has no OIDC identity for PyPI to trust)
python -m pip install twine
twine upload dist/*PyPI publishing is off by default: set the repository variable
PUBLISH_PYPI=trueafter adding a trusted publisher on pypi.org. Until then, install from GitHub (pipx install git+https://github.com/nybarius/SVRF) or use the container image.