Release workflow#
runpod-deploy ships to PyPI via a tag-triggered GitHub Actions
workflow at .github/workflows/release.yml.
The flow uses PyPI Trusted Publishing (OIDC) so no API tokens
live in repo secrets.
One-time PyPI setup#
Before the first publish succeeds, configure the publisher on PyPI. This is a manual step, done once per project.
Option A: Pre-publish (recommended for first release)#
If the project name runpod-deploy hasn’t been claimed on PyPI yet:
Sign in to https://pypi.org with the account that will own the project.
Click Add a new pending publisher.
Fill in:
PyPI Project Name:
runpod-deployOwner:
brandon-behringRepository name:
runpod-deployWorkflow filename:
release.ymlEnvironment name:
pypi
Save. The publisher now exists in “pending” state — the first successful workflow run will claim the project name.
Option B: Post-claim (if runpod-deploy is already on PyPI)#
If the package already exists on PyPI (e.g., from a manual upload):
Sign in as the project maintainer.
Go to https://pypi.org/manage/project/runpod-deploy/settings/publishing/.
Add a new trusted publisher with the same fields as Option A.
GitHub-side environment setup#
The workflow’s publish-to-pypi job uses
environment: name: pypi. GitHub will auto-create this environment
on first workflow run, but you can also create it explicitly under
Settings → Environments → New environment if you want to add
approval gates or restrict the deploying branch.
For our use case (tag-triggered release from main-merged commits),
no additional environment protection is needed — the tag itself is
the approval gate.
Cutting a release#
Confirm all merged PRs since the previous tag have CHANGELOG entries.
Bump
pyproject.tomlversionandsrc/runpod_deploy/__init__.py__version__. Match across both.Move CHANGELOG
[Unreleased]to[<version>] - YYYY-MM-DD — <theme>.make cigreen locally.Commit:
chore: bump to X.Y.Z — <theme>.Push:
git push origin main.Tag:
git tag vX.Y.Z -m "vX.Y.Z — <theme>".Push tag:
git push origin vX.Y.Z. This triggers the release workflow.
Watch the workflow run at brandon-behring/runpod-deploy. The flow:
build— producesdist/runpod_deploy-X.Y.Z.tar.gz(sdist) +runpod_deploy-X.Y.Z-py3-none-any.whl(wheel). Artifacts uploaded to the workflow run.publish-to-pypi— downloads the artifacts, callspypa/gh-action-pypi-publish@release/v1with OIDC. PyPI verifies the signed token against the configured trusted publisher.
On success: runpod-deploy X.Y.Z appears at
https://pypi.org/project/runpod-deploy/ within ~1 minute.
Failure modes#
Trusted publisher not configured— Trusted Publishing wasn’t set up on PyPI per “One-time PyPI setup” above. Thebuildjob still succeeds; download the sdist + wheel from the workflow run artifacts and upload manually viauv pip install twine && twine upload dist/*if needed.Version already exists— the tagged version was previously published. Bump the version and re-tag.Invalid metadata— hatchling produced an invalid sdist. Reproduce locally viauv run python -m buildand inspect.
Iterating on the workflow#
For workflow changes, push to a branch first. The workflow only
triggers on tag push (v*), so feature-branch pushes don’t trigger
a publish. To test the build path without publishing, you can:
Temporarily flip the
on:trigger to includepull_request.Push to a branch; the
buildjob runs.Inspect the uploaded
dist/artifact.Revert the
on:change before merging.
Or, use TestPyPI: uncomment the repository-url line in the
workflow pointing at https://test.pypi.org/legacy/. You’ll need a
separate Trusted Publisher configured at https://test.pypi.org for
this to succeed.
Skipping a release#
If a tag was pushed by accident, delete it before the workflow finishes:
git push --delete origin vX.Y.Z # remote
git tag -d vX.Y.Z # local
Cancel the in-progress workflow at the Actions tab. If the
publish-to-pypi job already ran, the version is on PyPI and you
must bump to the next version (PyPI doesn’t allow re-uploads of the
same version, even after deletion).