From 1292369291ac9698d1e41191c65007c210211aef Mon Sep 17 00:00:00 2001 From: Night Date: Mon, 26 Oct 2026 06:47:05 +1100 Subject: [PATCH 1/3] feat: project version comes from VERSION * generated projects take their package version from the VERSION file that CI releases, so the two can no longer drift apart * the wheel and sdist from make release carry that version, without the leading v * pyproject.toml names setuptools as the build backend and the package to ship, so stray folders cannot break a release build * the installed project is rebuilt when VERSION changes, so its metadata stays current --- template/README.md.jinja | 2 ++ template/pyproject.toml.jinja | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/template/README.md.jinja b/template/README.md.jinja index 35c4205..760c7fb 100644 --- a/template/README.md.jinja +++ b/template/README.md.jinja @@ -54,6 +54,8 @@ $ make format-dry-run # list files that need formatting `make release` builds a wheel and an sdist into `dist/`. They install on any platform with Python 3.13. +The package version is read from `VERSION`, the same version CI releases, so bump only that file. + ## Copier Update diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 40d1f78..cbed935 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -1,6 +1,10 @@ +[build-system] +requires = ["setuptools>=80"] +build-backend = "setuptools.build_meta" + [project] name = "{{ project_slug }}" -version = "0.1.0" +dynamic = ["version"] description = "{{ project_description }}" authors = [ { name = "{{ user_name }}"{% if user_email %}, email = "{{ user_email }}"{% endif %} }, @@ -23,8 +27,17 @@ dependencies = [ [project.scripts] main = "{{ module_slug }}.main:main" +# setuptools reads VERSION, the version CI releases, and drops the leading v +[tool.setuptools.dynamic] +version = { file = "VERSION" } + +[tool.setuptools.packages.find] +include = ["{{ module_slug }}*"] + [tool.uv] package = true +# rebuild the installed project when VERSION changes, so its metadata stays current +cache-keys = [{ file = "pyproject.toml" }, { file = "VERSION" }] [dependency-groups] dev = [ From b142bc51cc0b49cd556b0e72d67d04b999dec873 Mon Sep 17 00:00:00 2001 From: Night Date: Mon, 26 Oct 2026 06:47:05 +1100 Subject: [PATCH 2/3] chore: version v2.2.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5969682..a4b6ac3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.1.2 +v2.2.0 From 933fd61abc2dd8893d7181bdcaa889f59028280e Mon Sep 17 00:00:00 2001 From: Night Date: Mon, 26 Oct 2026 06:47:13 +1100 Subject: [PATCH 3/3] docs: readme and changelog for feature/version-file * README says the package version comes from VERSION and that older projects refresh uv.lock once after updating * CHANGELOG.md records the branch --- CHANGELOG.md | 1 + README.md | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f9989..e1a82d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,3 +11,4 @@ * feature/root-answers-file: generated projects keep their Copier answers in `.copier-py-answers.yml` at the root rather than in `.copier-generic-project/`; existing projects move the file once before their next update. * fix/devcontainer-timezone: the dev container, and CI with it, starts on hosts that have no /etc/timezone. * fix/bash-history-mount: shell history in the dev container persists between sessions. +* feature/version-file: generated projects take their package version from VERSION; existing projects run `uv lock` once after updating. diff --git a/README.md b/README.md index fc1c42b..b6d0995 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ Projects generated before the answers file moved keep it in `.copier-generic-pro $ git mv .copier-generic-project/.copier-py-answers.yml .copier-py-answers.yml ``` +Projects generated before v2.2.0 hold a fixed version in `uv.lock`. After the update that takes `pyproject.toml` to the version in `VERSION`, refresh the lock once and commit it, or `make build` fails on the out-of-date lock: + +```bash +$ uv lock +``` + ### Versioning and CI @@ -73,7 +79,7 @@ Every generated project has a `Makefile` at its root: * `make build` installs the locked dependencies and byte-compiles the package. * `make format` formats the Python sources with ruff; `make format-dry-run` lists unformatted files and fails if there are any. * `make test` runs the tests with pytest. -* `make release` builds a wheel and an sdist into `dist/`. +* `make release` builds a wheel and an sdist into `dist/`, versioned from the project's `VERSION` file. ### GitHub CI