Skip to main content

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 numbers
  • fix(calculator): correct divide by zero error
  • chore(deps): bump ruff to 0.9
  • feat!: 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

  1. Rename the package: update name in pyproject.toml, the directory under src/, and imports in tests/. Also update the package-name and extra-files paths in .github/release-please-config.json.

  2. Workflow permissions: Settings → Actions → General → Workflow permissions. Enable:

    • "Read and write permissions"
    • "Allow GitHub Actions to create and approve pull requests"
  3. 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 secret RELEASE_PLEASE_TOKEN. (Required so the GitHub Release created by release-please triggers the publish.yml workflow — events from the default GITHUB_TOKEN do not trigger other workflows.)

  4. Install the Claude Code GitHub App at github.com/apps/claude and grant access to this repo. Add ANTHROPIC_API_KEY repository secret.

  5. 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) and testpypi respectively
  6. Create GitHub Environments testpypi and pypi under 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*)
  7. (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
  8. (Optional) Production gate for PyPI publishing — on Pro/Team/Enterprise plans only: add a Required reviewer on the pypi environment 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

test_python_package_release-0.4.0.tar.gz (48.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

test_python_package_release-0.4.0-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file test_python_package_release-0.4.0.tar.gz.

File metadata

File hashes

Hashes for test_python_package_release-0.4.0.tar.gz
Algorithm Hash digest
SHA256 5f041bd3fc042f9373f056a407bc25f2cd70e38dff7998ebe673e817463dc003
MD5 1855631dc6197276370eebeee1bb3460
BLAKE2b-256 d96880723f6b6fcb039f0adfaf7d366bce00e7c0db80f2f400587203276ecba6

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file test_python_package_release-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for test_python_package_release-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 29d821e777591d9c23232c37103641bb7d3da190a37156d48f1bc557fb0702cb
MD5 ae97108e5b0198df10a81ec68452b6e2
BLAKE2b-256 d4bd84432d7bf08af6892ed32d581597d45e50de0422702d22ed06da27c289c8

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page