Template library demonstrating Python package release CI/CD
Project description
test-python-package-release
Template repository demonstrating a complete CI/CD pipeline for Python packages published to PyPI.
Quick start
uv sync --group dev
uv run pytest
Project layout
.
├── .github/workflows/ # CI, Claude review, release-please, publish
├── src/ # library code (src/ layout)
├── tests/ # pytest tests
├── pyproject.toml # single source of truth: build, deps, ruff, pytest, tox
├── .pre-commit-config.yaml
├── commitlint.config.cjs
└── CHANGELOG.md # managed by release-please
Development tooling
The template ships with an opinionated, modern Python toolchain — all
configured in pyproject.toml (no scattered config files).
| Tool | Purpose | Where it runs |
|---|---|---|
| uv | Package manager, virtualenv handler, Python installer, build tool. Replaces pip + venv + pip-tools + pyenv. |
Local + CI |
| ruff | Linter + formatter (replaces flake8, isort, black, pyupgrade). | pre-commit + CI lint job |
| ty | Static type checker by Astral (faster mypy alternative). | CI lint job + tox -e type |
| pytest | Test runner. | Local + CI test matrix |
| pytest-cov | Coverage reporting via coverage.py. |
On-demand (pytest --cov) |
| tox + tox-uv | Isolated test environments per Python version (3.11, 3.12, 3.13) backed by uv for speed. | Local + CI test matrix |
| pre-commit | Git hook framework — runs ruff and conventional-commit checks before every commit. | Local |
| conventional-pre-commit | Pure-Python validator for Conventional Commits commit messages. | pre-commit (commit-msg stage) |
| commitlint | Node-based validator that re-checks every commit on a PR. | CI commitlint job |
| hatchling | PEP 517 build backend used by uv build to produce wheel/sdist. |
Build / publish |
| release-please | Parses Conventional Commits → bumps version → updates CHANGELOG → tags releases. | CI on push to main |
| claude-code-action | Claude reviews PRs on demand when you comment @claude. |
CI on PR comment |
| Dependabot | Weekly grouped PRs updating GitHub Actions and uv dev-dependencies. | GitHub native |
Development commands
# Install all dev deps + create .venv
uv sync --group dev
# Install hooks (runs ruff and conventional commit-msg validation)
uv run pre-commit install --hook-type pre-commit --hook-type commit-msg
# Full test matrix (3.11, 3.12, 3.13) — tox spawns isolated uv envs per version
uv run tox
# Single env (fastest iteration)
uv run tox -e py313 -- -k calculator # only tests matching "calculator"
# Lint / format / type check (one-off, without tox)
uv run ruff check .
uv run ruff format --check .
uv run ty check src
# Build the distribution locally
uv build
# Add a new dependency (auto-updates uv.lock — commit both files)
uv add httpx
uv add --group dev pytest-mock
Commits
Every commit MUST follow the Conventional Commits specification.
Examples:
feat: add support for negative numbersfix(calculator): correct divide by zero errorchore(deps): bump ruff to 0.9feat!: drop Python 3.10 support(breaking change → major bump)
Enforced locally by conventional-pre-commit (commit-msg hook) and in CI by
commitlint.
Release flow
PR opened ──▶ ci.yml (lint, ty, pytest matrix, commitlint)
│
└─ optional: comment "@claude" ──▶ claude-review.yml
PR merged ──▶ main ──▶ release-please.yml
│
└─ creates/updates a "Release PR"
(bumps version, regenerates CHANGELOG)
Release PR merged ──▶ release-please tags + creates GitHub Release
│
└─ publish.yml ──▶ uv build
──▶ TestPyPI (OIDC)
──▶ PyPI (OIDC, gated env)
First-time setup after cloning the template
-
Rename the package: update
nameinpyproject.toml, the directory undersrc/, and imports intests/. Also update thepackage-nameandextra-filespaths in.github/release-please-config.json. -
Workflow permissions: Settings → Actions → General → Workflow permissions. Enable:
- "Read and write permissions"
- "Allow GitHub Actions to create and approve pull requests"
-
Personal Access Token for release-please: create a fine-grained PAT (generate here) scoped to this repo with
Contents: read+write,Pull requests: read+write,Workflows: read+write. Add as repository secretRELEASE_PLEASE_TOKEN. (Required so the GitHub Release created by release-please triggers thepublish.ymlworkflow — events from the defaultGITHUB_TOKENdo not trigger other workflows.) -
Install the Claude Code GitHub App at github.com/apps/claude and grant access to this repo. Add
ANTHROPIC_API_KEYrepository secret. -
Configure Trusted Publishers on PyPI and TestPyPI for your repo. Use "pending publisher" if the project doesn't exist yet. Point each to:
- Workflow name:
publish.yml - Environment name:
pypi(production) andtestpypirespectively
- Workflow name:
-
Create GitHub Environments
testpypiandpypiunder Settings → Environments. For each, under Deployment branches and tags:- Select "Selected branches and tags"
- Add a branch rule:
main - Add a tag rule (toggle ref type to "Tag" in the modal): use the tag
pattern that release-please produces — by default
<package-name>-v*(e.g.test-python-package-release-v*)
-
(Optional) Branch protection ruleset for
main:- Require a PR before merging
- Require status checks:
lint,test (3.11),test (3.12),test (3.13),commitlint - Block force pushes, restrict deletions
- Add yourself to the bypass list so you can merge release-please PRs
-
(Optional) Production gate for PyPI publishing — on Pro/Team/Enterprise plans only: add a Required reviewer on the
pypienvironment to require manual approval before production publish.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file test_python_package_release-0.4.0.tar.gz.
File metadata
- Download URL: test_python_package_release-0.4.0.tar.gz
- Upload date:
- Size: 48.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f041bd3fc042f9373f056a407bc25f2cd70e38dff7998ebe673e817463dc003
|
|
| MD5 |
1855631dc6197276370eebeee1bb3460
|
|
| BLAKE2b-256 |
d96880723f6b6fcb039f0adfaf7d366bce00e7c0db80f2f400587203276ecba6
|
Provenance
The following attestation bundles were made for test_python_package_release-0.4.0.tar.gz:
Publisher:
publish.yml on wozniakos10/test_python_package_release
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
test_python_package_release-0.4.0.tar.gz -
Subject digest:
5f041bd3fc042f9373f056a407bc25f2cd70e38dff7998ebe673e817463dc003 - Sigstore transparency entry: 1680518220
- Sigstore integration time:
-
Permalink:
wozniakos10/test_python_package_release@f9c98a8d9891379ecfafd3ead511c534d9253368 -
Branch / Tag:
refs/tags/test-python-package-release-v0.4.0 - Owner: https://github.com/wozniakos10
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f9c98a8d9891379ecfafd3ead511c534d9253368 -
Trigger Event:
release
-
Statement type:
File details
Details for the file test_python_package_release-0.4.0-py3-none-any.whl.
File metadata
- Download URL: test_python_package_release-0.4.0-py3-none-any.whl
- Upload date:
- Size: 6.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
29d821e777591d9c23232c37103641bb7d3da190a37156d48f1bc557fb0702cb
|
|
| MD5 |
ae97108e5b0198df10a81ec68452b6e2
|
|
| BLAKE2b-256 |
d4bd84432d7bf08af6892ed32d581597d45e50de0422702d22ed06da27c289c8
|
Provenance
The following attestation bundles were made for test_python_package_release-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on wozniakos10/test_python_package_release
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
test_python_package_release-0.4.0-py3-none-any.whl -
Subject digest:
29d821e777591d9c23232c37103641bb7d3da190a37156d48f1bc557fb0702cb - Sigstore transparency entry: 1680518307
- Sigstore integration time:
-
Permalink:
wozniakos10/test_python_package_release@f9c98a8d9891379ecfafd3ead511c534d9253368 -
Branch / Tag:
refs/tags/test-python-package-release-v0.4.0 - Owner: https://github.com/wozniakos10
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f9c98a8d9891379ecfafd3ead511c534d9253368 -
Trigger Event:
release
-
Statement type: