Releasing¶
A release is a tag push. .github/workflows/python-publish.yml verifies the
tag against docxtpl.__version__, runs the tests, builds with uv, attaches
the distributions to the GitHub release and publishes them to PyPI over
trusted publishing — no API token
exists anywhere, and none can leak.
One-time setup on PyPI¶
docxtpl-ng has never been published, so the publisher is registered before
the project exists, as a pending publisher:
pypi.org/manage/account/publishing.
| Field | Value |
|---|---|
| PyPI Project Name | docxtpl-ng |
| Owner | toxicphreAK |
| Repository name | docxtpl-ng |
| Workflow name | python-publish.yml |
| Environment name | pypi |
The workflow name is the file name, not the name: inside the file — PyPI
matches on .github/workflows/<this>. The environment name must match the
environment: key of the deploy job exactly; an empty box there would let any
workflow run in the repository publish, which is the thing the environment is
there to prevent.
The first successful publish converts the pending publisher into a normal one and creates the project.
What is already in place¶
- the
pypienvironment exists on the repository, and its deployment policy restricts it to tags matchingv*. The workflow triggers on the same pattern, so a stray tag can neither start a publish nor reach the environment if the trigger is ever widened; - GitHub Pages is enabled and serving the documentation from the
Documentationworkflow; - CI runs the tests on Python 3.9 through 3.14, plus ruff and pyright, and validates rendered documents against the ISO/IEC 29500 schemas.
Cutting a release¶
- Update
HISTORY.md: give the version a heading and a date. - Set
__version__insrc/docxtpl/__init__.py. The tag must match it, with an optional leadingv— the workflow fails the build if it does not. -
Check the distributions build and describe themselves correctly:
-
Commit, then tag and push:
Pushing the tag is what publishes. There is no upload target in the Makefile
on purpose: a release should be a thing that happened to the repository, not a
thing that happened on somebody's laptop.
Version numbers¶
PEP 440, and the distinction that matters
is what pip install docxtpl-ng gives someone by default:
0.1.0— a normal release. Installed by default;0.1.0a1,0.1.0rc1— a pre-release. Not installed unless the user asks for it with--preor pins it exactly.
While the API is settling, the 0. major is doing the work of saying so, and
the Development Status :: 3 - Alpha classifier says it again on the project
page.
Dependency floors¶
python-docx-ng>=2.0.0 is a floor the test suite is actually run against, not
a guess — see the note in pyproject.toml. Two things follow:
- raising the floor is a deliberate act: it needs a reason and a test run against the new floor;
- the floor must not name a pre-release. A specifier like
>=2.0.0.dev0opts that dependency into pre-releases for everyone who installs this package, so a future2.2.0.dev1would be picked up by an ordinarypip install.