publishing.md
May 23, 2026 · View on GitHub
Publishing Releases
This is how to publish a Python package to PyPI from GitHub Actions, when using the simple-modern-uv template.
Thanks to
the dynamic versioning plugin and
the publish.yml
workflow,
you can simply create tagged releases (using standard format for the tag name, e.g.
v0.1.0) on GitHub and the tag will trigger a release build, which then uploads it to
PyPI.
First-Time Setup
This part is a little confusing the first time. Here is the simplest way to do it. For the purposes of this example replace OWNER and PROJECT with the right values.
Note: These steps assume you already have a GitHub repo with your code pushed. If
you used uvx uvtemplate, it handles repo
creation for you. If you're setting up manually, create an empty GitHub repo (no
README, no .gitignore, no license — the template already provides these) and push your
code to it. See the
README
for details.
-
Get a PyPI account at pypi.org and sign in.
-
Pick a name for the project that isn’t already taken.
-
Go to
https://pypi.org/project/PROJECTto see if another project with that name already exits. -
If needed, update your
pyproject.tomlwith the correct name.
-
-
Authorize your repository to publish to PyPI:
-
Go to the publishing settings page.
-
Find “Trusted Publisher Management” and register your GitHub repo as a new “pending” trusted publisher.
-
Enter the project name, repo owner, repo name, and
publish.ymlas the workflow name. (You can leave the “environment name” field blank.)
-
-
Create the first release by pushing a tag:
-
Commit code and make sure CI is green in the Actions tab.
-
Tag and push:
git tag v0.1.0 git push origin v0.1.0(Tag names should start with
v.) -
The
publish.ymlworkflow will run automatically, upload to PyPI, and create the matching GitHub Release.
-
-
Confirm it publishes to PyPI
-
Watch for the release workflow in the GitHub Actions tab.
-
If it succeeds, you should see it appear at
https://pypi.org/project/PROJECT.
-
Publishing Subsequent Releases
This project uses a tag-triggered workflow: pushing a v* tag to GitHub builds,
uploads to PyPI via trusted publishing, and creates the GitHub Release automatically.
An agent (or human) only needs to push a tag.
Follow this checklist for each new release.
Pre-Release Checklist
-
Verify all changes are committed and pushed:
git status git log origin/main..HEAD # should be empty if pushed -
Run linting and tests locally:
make lint make test -
Confirm CI is passing:
gh run list --limit 3Or check the Actions tab on GitHub.
-
Determine the new version number:
# Check current/latest version: gh release list --limit 1Use semantic versioning:
-
Patch (e.g.,
v0.5.8→v0.5.9): Bug fixes, minor changes -
Minor (e.g.,
v0.5.9→v0.6.0): New features, backward-compatible -
Major (e.g.,
v0.6.0→v1.0.0): Breaking changes
-
Create the Release
-
Tag and push:
NEW_TAG="vX.Y.Z" # Replace with actual version git tag "${NEW_TAG}" git push origin "${NEW_TAG}"This triggers the
publish.ymlworkflow, which:-
Runs the test suite
-
Builds the wheel and sdist
-
Uploads to PyPI via trusted publishing
-
Creates a GitHub Release with auto-generated notes (release notes are generated by GitHub from PR titles / commit messages since the last release)
-
-
Watch the workflow:
gh run list --workflow=publish.yml --limit 1 gh run watch # interactive, follow the latest run -
(Optional) Curate the release notes:
The workflow uses GitHub's auto-generated notes by default. To replace them with curated notes (recommended for non-trivial releases), see the Release Notes Format section below and run:
gh release edit "${NEW_TAG}" --notes-file release-notes.mdOr write notes inline:
LAST_TAG=$(gh release list --limit 2 --json tagName -q '.[1].tagName') gh release edit "${NEW_TAG}" --notes "$(cat <<EOF ## What's Changed [Summarize changes here -- see format guide below] ### Full Changelog https://github.com/OWNER/PROJECT/compare/${LAST_TAG}...${NEW_TAG} EOF )" -
Verify the release published successfully:
# Verify on PyPI (may take a minute): # https://pypi.org/project/PROJECT # Verify the GitHub Release exists: gh release view "${NEW_TAG}"
Release Notes Format
Use this structure for release notes:
## What's Changed
### Bug Fixes
**Short title of fix**
Description of what was fixed and why it matters.
### New Features
**Short title of feature**
Description of the new capability.
### Breaking Changes
**Short title of breaking change**
Description of what changed and how to migrate.
### Full Changelog
https://github.com/OWNER/PROJECT/compare/vPREVIOUS...vNEW
Guidelines:
-
Use
## What's Changedas the top-level heading. -
Group changes under
### Bug Fixes,### New Features,### Breaking Changes, etc. as appropriate. -
Use
**bold**for short titles of individual changes. -
Include technical details only when helpful for users.
-
Always include the Full Changelog compare link at the end.
-
For small releases, a simple bullet list is acceptable instead of full sections.
This file was built with simple-modern-uv.