A Copier template for pipe23 Python projects. Generated projects build, lint, type-check, test, and document. Includes:
- uv — packaging, locking, and task running; no requirements.txt
- ruff (lint + format) and mypy in strict mode
- pytest with a 100% branch-coverage gate
- mkdocs-Material docs with a Read the Docs config
- GitHub Actions CI — lint / types / docs / test matrix (
python_minthrough 3.14) - Release pipeline — PyPI via Trusted Publishing; projects with a Dockerfile (web services,
currently) also push a multi-arch image to GHCR. No tokens in either path — Trusted Publishing
and
GITHUB_TOKENrespectively. Once the publish jobs succeed and the published package is verified to install and import, a tag push creates a GitHub Release with notes from the matching## <version>section ofCHANGELOG.md; the release fails if that section is missing. - Dependabot for actions and dependencies
- A generated SETUP.md — the go-public checklist (branch protection, RTD import, Trusted Publisher registration)
Answers to the Copier prompts select overlays:
is_application— console-script entry point;cli_libchooses argparse (default) or click. Library vs application is not a fork — an application is a package with a[project.scripts]entry.publish_to_pypi— include the Trusted-Publishing release workflownative_extension— C/C++ overlay: CMake + cffi build, cibuildwheel wheel matrixweb_service— FastAPI service: structlog logging, RFC 9457 problem+json error handling, TOML + env config via pydantic-settings, OpenAPI page in the docs site, multi-stage Dockerfile, compose file with Traefik routing and mkcert/ACME TLS, multi-arch GHCR image on releasedatabase(web services) — SQLite or PostgreSQL: SQLAlchemy 2.0 async, Alembic migrations applied on deploy by the entrypoint
copier.yml— the questions, defaults, and Jinja delimiter configtemplate/— the tree that gets rendered into a new project.github/workflows/smoke.yml— CI for the template itself: generates a project per answer combo and runs the generated project's own gates
File and directory names under template/ like [% if web_service %]deploy[% endif %] are
Copier conditional paths: the segment renders only when the condition holds, so a non-web-service
project contains no deploy/ at all. This template uses [% %] / [[ ]] as Jinja delimiters
instead of the defaults so GitHub Actions' own ${{ }} expressions pass through untouched.
See DOCS-STYLE.md for the README skeleton this template's README.md.jinja
implements.
# new project
uvx copier copy https://github.com/pipe23-org/python-template ../my-new-thing
# later, pull template improvements into a generated projectcd ../my-new-thing && uvx copier updateCopier copies from the latest tag; tags are the template's release channel. After generating,
run uv lock, then work through the generated SETUP.md.
Versions track what copier update does to a project generated from the previous tag, not the
size of the content change:
- patch — the update replays cleanly: no new questions, changes land inside existing files
- minor — the update adds surface: a new question or newly generated files
- major — the update needs manual work: a question renamed or removed, answer semantics changed, or the file layout restructured
Apache-2.0. The license covers this template's own files; projects you generate from it are yours, with no obligations back to this repo.