Repository navigation
Publish releases to PyPI from version tags - #12
Merged
Merged
Conversation
Pushing a vX.Y.Z tag now tests the package on Python 3.12 to 3.14, builds it, publishes it to PyPI through trusted publishing, and creates a GitHub Release whose notes are that version's CHANGELOG.md section. The release stops early if the tag, the version in pyproject.toml and the changelog disagree. docs/RELEASING.md covers the one-time setup and the release steps. CI now runs the tests on Python 3.12, 3.13 and 3.14, the versions requires-python allows, instead of only the pinned 3.12. The sdist lists what it includes, so files kept out of git some other way can't slip into a package built from a working copy. The README installs from PyPI, and the project metadata gains keywords and links.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Changelog validation can accept malformed headings and detects empty release notes only after PyPI publication.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (3)
What changed in this PR
Adds tag-driven PyPI and GitHub releases, expands supported-Python CI coverage, and documents packaging and release procedures.
Changes:
- Adds a four-stage release workflow using PyPI trusted publishing.
- Tests Python 3.12–3.14 and defines explicit source-distribution contents.
- Updates installation, metadata, changelog, and release documentation.
| File | Description |
|---|---|
.github/workflows/release.yml |
Adds testing, building, publishing, and GitHub release jobs. |
.github/workflows/ci.yml |
Adds a supported-Python test matrix. |
pyproject.toml |
Expands package metadata and configures sdist contents. |
docs/RELEASING.md |
Documents release setup and procedures. |
README.md |
Adds PyPI installation and release guidance. |
CHANGELOG.md |
Records PyPI availability. |
CLAUDE.md |
Documents the new release workflow and guide. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
A published PyPI version can never be reused, so every check that can stop a release now runs before the publish job. The changelog heading must match exactly, with the version's dots taken literally, and be dated within a day of the tag. The release notes are taken and checked in the build job, and handed to the release job as their own artifact, so an empty section no longer fails only after publishing. Keep the README's install on the repository until 0.1.0 is on PyPI; the PyPI install moves to the release branch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


What changes
Releases publish from a tag. Pushing a tag such as
v0.1.0runs.github/workflows/release.yml, which runs four jobs in order:pyproject.toml, and thatCHANGELOG.mdhas exactly## [X.Y.Z] - YYYY-MM-DD, dated within a day of the tag.uv build, and takes that version'sCHANGELOG.mdsection as the release notes, failing if it's empty.pypienvironment.buildand both files attached.Every check that can stop a release runs before
publish, because a PyPI version number can never be reused.It follows the forbin-mcp release workflow, adapted to this repo: every action pinned to a commit SHA like the existing CI,
uvfor the build, release notes from the changelog instead of generated ones, and no Homebrew. Permissions are read-only by default; onlypublish(id-token: write) andrelease(contents: write) ask for more.CI tests every supported Python. The CI test job now runs on 3.12, 3.13 and 3.14, the versions
requires-pythonallows, instead of only the pinned 3.12. Each job's version overrides.python-version.docs/RELEASING.mdcovers the one-time PyPI and GitHub setup, the release steps (changelog, docs sweep, version and date, review, tag), how to check a release, and troubleshooting.pyproject.tomlgains keywords, Documentation, Changelog and Issues links, and 3.13 and 3.14 classifiers. The sdist now lists what it includes. Hatchling honours.gitignorebut not.git/info/exclude, so a hand-built sdist could otherwise pick up untracked local files.README.mdpoints to the release guide. Its install section stays on the git install until 0.1.0 is on PyPI; switching touv tool install akceois part of the release branch.Setup
The PyPI trusted publisher (pending, for
akceo: ownerLunarCommand, repoakceo, workflowrelease.yml, environmentpypi) and the GitHubpypienvironment are already in place.Not in this PR
Releasing 0.1.0. That gets its own branch: finishing the changelog, sweeping the docs, dating the section and reviewing everything that ships, before any tag is pushed.
Testing
dateand the runner'sbash -eo pipefail:0x1x0look-alike, on an invalid date (2026-02-30), on a five-day-old date, and on a section still calledUnreleased. A tag that doesn't matchpyproject.tomlfails too.uv build: the sdist holds only the listed files, and the wheel's metadata has the license expression, links and keywords.Metadata-Version: 2.5, which hatchling now writes; hatchling 1.32.4's own wheel was published with it.py3.