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.3.2.tar.gz (47.8 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.3.2-py3-none-any.whl (6.6 kB view details)

Uploaded Python 3

File details

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

File metadata

File hashes

Hashes for test_python_package_release-0.3.2.tar.gz
Algorithm Hash digest
SHA256 8cebfaf9627bab6116e0fe269f74685cdfc94184ee82d39e0e8f5a3ec9622065
MD5 befc9a8b297ad835c1741d27646c07f9
BLAKE2b-256 0c1ed014e5ec169bf7f677de612275ee5aaba952581aedaf4552da054e24edb9

See more details on using hashes here.

Provenance

The following attestation bundles were made for test_python_package_release-0.3.2.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.3.2-py3-none-any.whl.

File metadata

File hashes

Hashes for test_python_package_release-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 653e4b5307d81eabf3ae565d1efb2acc2b695e7f8c6d4848bda990b9ad837e6f
MD5 bd981ee952be423b4c2160de87793d8d
BLAKE2b-256 a5d47e334970ce08c07dc9d54320e688720f4b1bf72675947504e03ce2b93070

See more details on using hashes here.

Provenance

The following attestation bundles were made for test_python_package_release-0.3.2-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