All notable changes to this copier template are recorded here.
The format follows Keep a Changelog.
The template is versioned with Semantic Versioning:
MAJOR for changes that need a migration in generated projects, MINOR
for new copier options or features, PATCH for fixes.
1.5.5 - 2026-07-07
- Bumped generated dev-dependency minimums to the current latest:
ruff>=0.15.20,ty>=0.0.56, anduv_build>=0.11.27(<0.12kept).pyteststays>=9.1.1andpytest-covstays>=7.1.0. The generated[tool.ruff]required-versionfloor and the omitted-defaults note move to0.15.20in lockstep. Workspace dev tooling bumped to match (copier>=9.16.0,ruff>=0.15.20).
1.5.4 - 2026-06-22
- The generated
PyPI versionbadge (library mode) gains the PyPI logo (?logo=pypi). TheDownloadsbadge stays a logo-lesspepy.techimage — its shields equivalent was dropped in v1.0.2 for rate limiting.
1.5.3 - 2026-06-22
- Reordered the generated
README.mdbadges and gave two of them logos: theLicensebadge now leads (with the OSI logo via?logo=opensourceinitiative),Pythongains the Python logo, and theCopierbadge sits last (its documented placement). Library mode keeps itsPyPI/Downloadsbadges, now afterLicense. Badge URLs only — no rendered-content change beyond order and logos.
1.5.2 - 2026-06-22
- Bumped generated dev-dependency minimums to the current latest:
ruff>=0.15.18,ty>=0.0.51,pytest>=9.1.1, anduv_build>=0.11.23(<0.12kept).pytest-covstays>=7.1.0. The generated[tool.ruff]required-versionfloor and the omitted-defaults note move to0.15.18in lockstep. Workspace dev tooling bumped to match (copier>=9.15.2,pytest>=9.1.1,ruff>=0.15.18).
1.5.1 - 2026-06-22
- The generated
publish.yml'sgithub-releasejob (release_automation = github-oidc) now fails fast when no matchingCHANGELOG.mdsection is found for the tag, instead of silently creating a GitHub Release with an empty body. Promote[Unreleased]to a dated## [X.Y.Z]heading before tagging.
1.5.0 - 2026-06-22
- CI hardening in the generated
ci.yml: aconcurrencygroup (ci-${{ github.ref }},cancel-in-progress: true) that cancels superseded runs, atimeout-minutes: 20cap on the matrix job, and--lockedonuv syncso a staleuv.lockfails CI instead of being silently re-resolved. publish.yml(release_automation = github-oidc) gains three steps: thebuildjob fails fast when the pushedv*tag does not match thepyproject.tomlversion (uv version --short); the TestPyPI upload setsskip-existing: trueso a re-run tolerates an already-staged version; and a finalgithub-releasejob creates the GitHub Release for the tag, pulling notes from the matchingCHANGELOG.mdsection and attaching the sdist + wheel. The generatedAGENTS.md"Releases" section documents the new pipeline shape.
- Expanded the generated
AGENTS.mdconventions, distilled from a downstream project's review cycle:- Docstrings — summary-shape carve-outs (property getters and boolean methods take a noun phrase / "Whether ..."; boolean functions stay imperative), the self-explainable-consumed-method rule vs. generic abstract-base contracts, the closed-hierarchy family-map exception, and backticks on literal option values.
- Tests (
use_pytestonly) — flatdef test_*vs. grouping-onlyclass TestX:, which source files legitimately need no test, helper/fixture locations, and a test-quality bullet (assert values and side effects,pytest.raises(..., match=...), independently computed numbers, deterministic timing/IO, spy-verified batching). - Error messages — a content convention (name the valid set or
the fix, not just the failure) layered on top of the
EMruff rule's format enforcement. - Python style — blank-line grouping with a blank line before the
final
return, and boxed comment banners in long modules.
1.4.4 - 2026-06-01
- Stripped boilerplate comments from the generated
publish.yml(the# Reuse the CI workflow ...,# Build the artifacts ...,# Stage on TestPyPI first ...,# Manual-approval gate ..., and# OIDC for ... Trusted Publishingannotations) and normalized to single blank-line separation between jobs. Architectural rationale lives in the generatedAGENTS.md"Releases" section and in this CHANGELOG; the workflow file itself now reads as a clean declarative pipeline. Functional output is unchanged.
1.4.3 - 2026-06-01
- Pulled
buildout of thepublishjob so thegithub-oidcpublish.ymlpipeline is alwaysci → build → [testpypi →] pypi, withtestpypithe only conditional job. Previously,use_testpypi=falsecollapsed build + publish into a single job to save one runner; that meant the OIDC-holding job also ran the build, blurring the boundary between code that needs the Trusted Publishing token and code that doesn't. Now everypypijob downloads a pre-built artifact, soid-token: writeis scoped strictly to the publish jobs and thebuildjob has no token capability. Trade-off: one extra runner + ~10 s of artifact upload/download on everyuse_testpypi=falserelease. - Simplified the
publish.ymlJinja template alongside the structural change — the inner conditional now wraps just the optionaltestpypijob (~25 lines) and selects thepypijob'sneeds:target. Generateduse_testpypi=trueoutput is byte-identical to v1.4.2's.
1.4.2 - 2026-06-01
- Collapsed the two conditional-filename
publish.ymlvariants into a singletemplate/.github/workflows/publish.yml.jinjawith an internal{% if use_testpypi %}branch, gated by a Jinja-templated entry incopier.yml's_exclude. The previous two-file design worked around copier's empirically verified behavior that filename conditionals don't trigger.jinja-suffix stripping ({% if cond %}publish.yml.jinja{% endif %}renders as a literalpublish.yml.jinjawith un-templated content)._excludepatterns are Jinja-templated and matched against destination paths (post-.jinjastrip), so the same exclusion semantics carry over without splitting the source. Generatedpublish.ymloutput is byte-identical to the previous variants for bothuse_testpypivalues; the change is purely internal.
1.4.1 - 2026-06-01
- Aligned the generated
publish.ymlwith the provenkaparoo-pythonworkflow shape (bothuse_testpypivariants):- Swapped
uv publish --trusted-publishing alwaysforpypa/gh-action-pypi-publish@release/v1— PyPA's official OIDC publisher — withprint-hash: trueandattestations: trueexplicit. Functional behavior is unchanged (still Trusted Publishing + PEP 740 attestations), but the action is the de-facto standard and decouples the publish step fromuv's release cadence. - Each job gets a human-readable
name:(Verify,Build distributions,Publish to TestPyPI,Publish to PyPI). - The build job's
setup-uvstep enables the action's cache (enable-cache: true) for faster repeat builds. - Switched the tag-pattern quotes from
"v*.*.*"to'v*.*.*'for consistency withkaparoo-python.
- Swapped
1.4.0 - 2026-06-01
- New
use_testpypicopier question (defaulttrue, gated onis_library) — controls whether generated projects stage releases on TestPyPI before pushing to real PyPI. PyPI uploads are permanent, so a TestPyPI rehearsal catches install-time issues (dependency resolution, missing files in the wheel, entry points) thatuvx twine checkdoesn't. - When
release_automation = manual:pyproject.tomlships bothpypiandtestpypinamed[[tool.uv.index]]entries withpublish-url, and the generatedAGENTS.md"Releases" section includes auv publish --index testpypirehearsal step before PyPI. Withuse_testpypi=false, only thepypiindex ships and the rehearsal step is dropped. - When
release_automation = github-oidc:publish.ymlbecomes a 4-job pipeline (ci → build → testpypi → pypi) that uploads built distributions as an artifact, publishes them to TestPyPI via Trusted Publishing, then — once thepypienvironment is approved — publishes the same artifacts to PyPI. The setup checklist gains a "TestPyPI Trusted Publisher" item. Withuse_testpypi=false, the previous 2-job pipeline ships unchanged.
1.3.1 - 2026-05-28
- Hardened the generated
publish.yml(release_automation=github-oidc) to match the provenkaparoo-pythonworkflow: added auvx twine check dist/*metadata-verification step before upload, and tightened the trigger tov*.*.*(X.Y.Z only — no strayvtags).uv publishalready uploads PEP 740 attestations by default, so no change was needed there.
1.3.0 - 2026-05-28
- Opt-in PyPI publish automation via a new
release_automationcopier question (manualdefault /github-oidc), gated onis_library.github-oidcships.github/workflows/publish.yml: av*tag push reuses the CI workflow as a release gate (workflow_call), then apypiGitHub environment (manual-approval reviewer) gates a build anduv publish --trusted-publishing always— PyPI Trusted Publishing over OIDC, no stored token. The generatedAGENTS.md"Releases" section branches on the answer: automated mode gets a tag-triggered procedure plus a one-time setup checklist (register the PyPI Trusted Publisher, create thepypienvironment with a required reviewer);manualkeeps the TestPyPI → PyPI keyring flow. TestPyPI staging stays a documented manual step.
1.2.0 - 2026-05-28
Real-world feedback batch from kaparoo-python (generated from
v1.0.2): CI, coverage, docstrings, line endings, dependency bumps.
.github/workflows/ci.yml— a CI workflow running on push tomain, PRs, andworkflow_call. A 3-OS matrix (ubuntu/windows/macos) runsuv sync,ruff format --check,ruff check,ty check, and (only whenuse_pytest)pytest. Action majors pinned to the Node.js-24 natives current as of 2026-05 (actions/checkout@v6,astral-sh/setup-uv@v8.1.0).pytest-covintegration:pytest-cov>=7.1.0dev dependency,--cov/--cov-report=term-missinginaddopts, and[tool.coverage.*]config (branch coverage, data under.cache/coverage/). Newcoverage_fail_undercopier question (default0= measure-only so a fresh project doesn't failpytest; raise it once a baseline exists). Alluse_pytest-gated..gitattributesnormalizing line endings (* text=autopluseol=lffor yml/yaml/toml/lock/py/md) — removes the WindowsLF will be replaced by CRLFwarnings.
- Bumped generated dev-dependency minimums to the current latest:
ruff>=0.15.14,ty>=0.0.40,uv_build>=0.11.16(<0.12kept).pyteststays>=9.0.3. - Expanded the generated
AGENTS.mddocstring guidance from format-only to the intent/contracts philosophy (one-line summary shape, surface what the signature can't, Google sections incl.Yields:/Type Parameters:, backtick identifiers).
1.1.0 - 2026-05-27
- Generated projects now ship a Keep-a-Changelog
CHANGELOG.mdskeleton (empty[Unreleased]) so the documented release workflow has a target from the first commit. - New
include_todocopier question (defaulttrue) generates aTODO.mdskeleton at the project root and links to it from theREADME.md📋 TODO section. Disable to skip both. template/AGENTS.md.jinjagains a 17-row Gitmoji palette table under "Commit convention" so generated projects ship a uniform commit vocabulary distilled from thekaparoo-pythonv0.2.0 cycle.- Library mode only: two named
[[tool.uv.index]]entries inpyproject.toml(pypias default,testpypiasexplicit = true) configured withpublish-urlsouv publish --index {testpypi,pypi}Just Works against OS keyring tokens. - Library mode only: new
## Releasessection inAGENTS.mddocumenting the 7-step procedure (CHANGELOG trim → version bump →uv build+uvx twine check→ isolated install smoke test → TestPyPI → PyPI → annotated tag).
template/README.md.jinjareplaces the bare badges + License body with a 5-section structure: 📦 Installation / 🧩 Modules / 📋 TODO / 📜 Changelog / ⚖️ License. Library mode showsuv add/pip installplus a Modules placeholder; application mode swaps to a 📦 Development setup section (clone +uv sync) and drops Modules.
copier.yml_excludeno longer listsCHANGELOG.md. The entry was a leftover from before_subdirectory: templateand was silently discarding the newtemplate/CHANGELOG.md.jinjafrom every generation.
1.0.3 - 2026-05-27
- Empty placeholder
tests/conftest.pyfrom generated projects. Real-world author experience (kaparoo-pythoncommitf446b26) showed the file as friction without value — create it when a real fixture lands. Thetests/__init__.pypackage marker remains (it satisfies ruff'sINPrule and keeps the directory as a proper package).
- The generated
AGENTS.md"Conventions" section gains three bullets: where to putconftest.py(defaulttests/conftest.py; root only forpytest_pluginsdeclarations, doctest fixtures shared with source files, or project-wide collection hooks); mirroring the package layout undertests/; and usingty's own error names in# ty: ignore[<error-name>]suppressions rather than mypy / pyright codes.
- Adopted PEP 639 license metadata in the generated
pyproject.toml: addedlicense-files = ["LICENSE"]and dropped the deprecated"License :: OSI Approved :: MIT License"classifier (it emitted auv builddeprecation warning).
1.0.2 - 2026-05-21
-
Generated projects with
is_library=false+use_pytest=truenow setpythonpath = ["."]under[tool.pytest.ini_options]. Without[build-system]uv does not editable-install the project, so the flat-layout package was not importable fromtests/under pytest's default--import-mode=importlib. Added atests/test_generate.pycase for the previously-untested combination. -
Replaced the
shields.ioPyPI monthly downloads badge in the generatedREADME.mdwith a Pepy badge. Theshields.ioendpoint frequently rendered as "rate limited by upstream service" once the generated project was actually published to PyPI (badges/shields#11620). Pepy serves the badge image directly and isn't subject to the same rate limits.
1.0.1 - 2026-05-21
tests/test_generate.pyrenders the template atHEADvia copier'svcs_ref. Without it copier defaults to the latest tag, so commits made after a release tag were silently not exercised by the suite.
1.0.0 - 2026-05-21
First tagged release — a personal copier template for bootstrapping
Python projects with the Astral toolchain (uv, ruff, ty) plus
pytest.
- Copier template rendered from
template/, with 11 questions: project identity (project_name,project_description,package_name), author, GitHub,python_version,license_year, and theis_library/use_pytestfeature toggles. - Validators for
project_name(PEP 503 form) andpackage_name(Python identifier). - Pre-configured toolchain in the generated
pyproject.toml:uvwith theuv_buildbackend,ruff,ty, andpytest. is_libraryoption — toggles[build-system],[tool.uv.build-backend], thepy.typedmarker, theTyping :: Typedclassifier, and the PyPI badges.use_pytestoption — toggles thetests/suite,[tool.pytest.ini_options], the ruffPTrule withflake8-pytest-style, per-file-ignores, and the test-adapter VSCode extension.- Post-generation
_tasks—git init,uv lock,uv sync, and an initial commit (run withcopier copy --UNSAFE). - After-action messages for
copier copyandcopier update, plus a_migrationsscaffold for future breaking changes. - Enforced Python conventions in generated projects:
from __future__ import annotationsin every module (empty__init__.pyexempt), builtin generics overtyping.List/Dict/Tuple/Type, and ruff-driven import ordering. - Generated
AGENTS.md— a starter agent guide covering the toolchain, commands, the Gitmoji commit convention, and Python style (including Google-style docstrings and PEP 723 scripts). - Workspace tooling:
copier+pytest+ruff, 15 generation tests intests/test_generate.py, and anAGENTS.mdfor the template repository itself.