publishing.md
July 15, 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 not, 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 exists. -
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 a release on GitHub:
-
Commit code and make sure it’s running correctly.
-
Go to your GitHub project page, then click on Actions tab.
-
Confirm all tests are passing in the last CI workflow. (If you want, you can even publish this template when it’s empty as just a stub project, to try all this out.)
-
Go to your GitHub project page, click on Releases.
-
Fill in the tag and the release name. Select to create a new tag, and pick a version. A good option is
v0.1.0. (It’s wise to have it start with av.) -
Submit to create the 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
Follow this checklist for each new release.
Pre-Release Checklist
-
Cut the release from an up-to-date
main:git checkout main git pull origin main git status # confirm a clean working tree -
Verify all changes are committed and pushed:
git log origin/main..HEAD # should be empty if pushed -
Run linting and tests locally:
make lint make test -
Confirm CI is passing on
main:gh run list --branch main --limit 3Or check the Actions tab on GitHub. The most recent run for the commit you’re about to tag must be green (a superseded older failure is fine).
-
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
-
Review changes since the last release:
# Get the last release tag: LAST_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName') # View commits since last release: git log ${LAST_TAG}..HEAD --oneline # View full diff: git diff ${LAST_TAG}..HEAD -
Write the release notes in a file, then create the release:
Author the notes as plain Markdown in a file (see Release Notes Format below), then pass it with
--notes-file. Writing the notes in a file keeps the shell out of the way: release notes routinely contain backticks and$, which a shell heredoc would try to run as commands or expand as variables. End the notes with a concrete compare link built from the actual tags (substitute the realLAST_TAGandNEW_TAGvalues into the URL), e.g.https://github.com/OWNER/PROJECT/compare/v0.1.0...v0.2.0.NEW_TAG="vX.Y.Z" # Replace with the actual version # Edit release-notes.md in your editor, ending with the concrete compare link. gh release create "${NEW_TAG}" --title "${NEW_TAG}" --notes-file release-notes.mdAlternatively, use
--generate-notesfor GitHub’s auto-generated notes. -
Verify the release published successfully:
# Check the release workflow: gh run list --workflow=publish.yml --limit 1 # Verify on PyPI (may take a minute): # https://pypi.org/project/PROJECTOnce it appears on PyPI, smoke-test that the published artifact actually resolves and installs from PyPI. If your project exposes a CLI:
uvx --from PROJECT==X.Y.Z PROJECT --version
Agent Runbook: kash-docs Specifics
Repo-specific additions to the checklist above (keep this section when updating the template).
-
Release order: kash-docs releases only after the kash-shell release it pins (
kash-shell==X.Y.Zin pyproject); after releasing kash-docs, bump the plainkash-docs==pin in kash-media. Media and transcription installs intentionally do not request document-conversion or browser extras. -
Release notes MUST state the dependency reality: the base install omits PDF/DOCX conversion, AWS publishing, layout-aware PDF conversion, and browser automation. Those features require the
documents,aws,pdf,browser, orfullextra. Loadtbd guidelines release-notes-guidelinesfor the notes themselves. -
Same-day first-party pins: a kash-shell released today is inside the 14-day cool-off; allow it surgically (do not weaken the global policy), and remove the exception at the next routine upgrade:
[tool.uv] exclude-newer-package = { kash-shell = "<tomorrow>T00:00:00Z" } -
If
ghis unavailable: annotated tag + push, then triggerpublish.ymlon the tag viaworkflow_dispatch— it publishes to PyPI and auto-creates the matching GitHub Release from the annotated tag's message (backfill older tags by dispatchinggithub-release.yml). Confirm withcurl -s https://pypi.org/pypi/kash-docs/jsonand smoke-testuvx --from kash-docs==X.Y.Z kash --version.
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.