From b210066ec1785289dfa39da3b432f2a58040fab4 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 30 May 2026 18:12:54 +0100 Subject: [PATCH 01/11] Add local QA helpers --- Dockerfile.testing | 52 +++++++++++ LOCAL_QA.md | 61 +++++++++++++ check.sh | 1 + requirements-dev.lock | 201 ++++++++++++++++++++++++++++++++++++++++++ tox.ini | 12 ++- 5 files changed, 324 insertions(+), 3 deletions(-) create mode 100644 Dockerfile.testing create mode 100644 LOCAL_QA.md create mode 100644 requirements-dev.lock diff --git a/Dockerfile.testing b/Dockerfile.testing new file mode 100644 index 0000000..7d26610 --- /dev/null +++ b/Dockerfile.testing @@ -0,0 +1,52 @@ +# syntax=docker/dockerfile:1 +# Tagging convention: boilerplate-dev:python-v +# python-ver — Python minor version used in the image (e.g. 3.11) +# testing-ver — Independent semver for the testing environment itself. +# Unlike library repos (where the testing version tracks the +# library release), this boilerplate has no release version of +# its own. Start at v0.0.1 and increment whenever the +# Dockerfile or its pinned dependencies change materially: +# patch — dependency bumps, minor tool config tweaks +# minor — new tools added, Python minor version bump +# major — breaking changes to the dev workflow +# +# Build: +# docker build \ +# --build-arg UID=$(id -u) --build-arg GID=$(id -g) \ +# -f Dockerfile.testing -t boilerplate-dev:python3.11-v0.0.1 . +# +# Run: +# docker run --rm -it \ +# -v "$(pwd)":/app \ +# boilerplate-dev:python3.11-v0.0.1 \ +# make check + +FROM ghcr.io/astral-sh/uv:0.7.13 AS uv-base + +FROM python:3.11-slim-bookworm AS testing + +COPY --from=uv-base /uv /uvx /usr/local/bin/ + +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y --no-install-recommends \ + make \ + dos2unix \ + shellcheck \ + git \ + && rm -rf /var/lib/apt/lists/* + +ARG UID=1000 +ARG GID=1000 +RUN groupadd -g "${GID}" appuser \ + && useradd -l -u "${UID}" -g "${GID}" -m appuser + +WORKDIR /app + +COPY requirements-dev.lock ./ +RUN uv pip install --system --no-cache -r requirements-dev.lock + +COPY . . + +USER appuser + +RUN git config --global --add safe.directory /app diff --git a/LOCAL_QA.md b/LOCAL_QA.md new file mode 100644 index 0000000..4af72a8 --- /dev/null +++ b/LOCAL_QA.md @@ -0,0 +1,61 @@ +# Local QA with Docker + +A Docker-based testing image is provided so checks can be run on any machine without +installing tooling locally. Python 3.11 is the first version set up; others will follow. + +## Build the image + +Pass your host UID and GID so that files written inside the container are owned by your +user, not root: + +```bash +docker build -f Dockerfile.testing \ + --build-arg UID=$(id -u) \ + --build-arg GID=$(id -g) \ + -t boilerplate-dev:python3.11-v0.0.1 . +``` + +> **Image tag convention:** `boilerplate-dev:python-v` +> The testing version is independent of any library release. Start at `v0.0.1` and +> increment: patch for dependency bumps/minor tweaks, minor for new tools or a Python +> version bump, major for breaking changes to the dev workflow. + +## Run checks + +All commands below mount the repository into the container so changes are picked up +without a rebuild. Run them from the repository root. + +**Integrity checks** (trailing whitespace, DOS line-endings, CHANGELOG entry, git tag): + +```bash +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make check +``` + +**Shell script linting:** + +```bash +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make shellcheck +``` + +**QA** (ruff, isort, codespell, check-manifest, build, twine check): + +```bash +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make qa +``` + +**Tests:** + +```bash +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make pytest +``` + +## Dependency lock file + +The image installs from `requirements-dev.lock`. Regenerate it when `requirements-dev.txt` +changes: + +```bash +uv pip compile requirements-dev.txt --output-file requirements-dev.lock --python-version 3.11 +``` + +Then rebuild the image. diff --git a/check.sh b/check.sh index 3dfe6f1..34d490f 100755 --- a/check.sh +++ b/check.sh @@ -7,6 +7,7 @@ LIBRARY_NAME=$(hatch project metadata name) LIBRARY_VERSION=$(hatch version | awk -F "." '{print $1"."$2"."$3}') POST_VERSION=$(hatch version | awk -F "." '{print substr($4,0,length($4))}') TERM=${TERM:="xterm-256color"} +export TERM success() { echo -e "$(tput setaf 2)$1$(tput sgr0)" diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 0000000..83e7ff0 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,201 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements-dev.txt --output-file requirements-dev.lock --python-version 3.11 +anyio==4.13.0 + # via httpx +backports-tarfile==1.2.0 + # via jaraco-context +backports-zstd==1.5.0 + # via hatch +build==1.5.0 + # via check-manifest +cachetools==7.1.4 + # via tox +certifi==2026.5.20 + # via + # httpcore + # httpx + # requests +cffi==2.0.0 + # via cryptography +charset-normalizer==3.4.7 + # via requests +check-manifest==0.51 + # via -r requirements-dev.txt +click==8.4.1 + # via + # hatch + # userpath +codespell==2.4.2 + # via -r requirements-dev.txt +colorama==0.4.6 + # via tox +cryptography==48.0.0 + # via secretstorage +distlib==0.4.0 + # via virtualenv +docutils==0.23 + # via readme-renderer +filelock==3.29.0 + # via + # python-discovery + # tox + # virtualenv +h11==0.16.0 + # via httpcore +hatch==1.16.5 + # via -r requirements-dev.txt +hatch-fancy-pypi-readme==25.1.0 + # via -r requirements-dev.txt +hatch-requirements-txt==0.4.1 + # via -r requirements-dev.txt +hatchling==1.29.0 + # via + # hatch + # hatch-fancy-pypi-readme + # hatch-requirements-txt +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via hatch +hyperlink==21.0.0 + # via hatch +id==1.6.1 + # via twine +idna==3.17 + # via + # anyio + # httpx + # hyperlink + # requests +importlib-metadata==9.0.0 + # via keyring +isort==8.0.1 + # via -r requirements-dev.txt +jaraco-classes==3.4.0 + # via keyring +jaraco-context==6.1.2 + # via keyring +jaraco-functools==4.5.0 + # via keyring +jeepney==0.9.0 + # via + # keyring + # secretstorage +jinja2==3.1.6 + # via pdoc +keyring==25.7.0 + # via + # hatch + # twine +markdown-it-py==4.2.0 + # via rich +markdown2==2.5.5 + # via pdoc +markupsafe==3.0.3 + # via + # jinja2 + # pdoc +mdurl==0.1.2 + # via markdown-it-py +more-itertools==11.1.0 + # via + # jaraco-classes + # jaraco-functools +nh3==0.3.5 + # via readme-renderer +packaging==26.2 + # via + # build + # hatch + # hatch-requirements-txt + # hatchling + # pyproject-api + # tox + # twine +pathspec==1.1.1 + # via hatchling +pdoc==16.0.0 + # via -r requirements-dev.txt +pexpect==4.9.0 + # via hatch +platformdirs==4.10.0 + # via + # hatch + # python-discovery + # tox + # virtualenv +pluggy==1.6.0 + # via + # hatchling + # tox +ptyprocess==0.7.0 + # via pexpect +pycparser==3.0 + # via cffi +pygments==2.20.0 + # via + # pdoc + # readme-renderer + # rich +pyproject-api==1.10.1 + # via tox +pyproject-hooks==1.2.0 + # via + # build + # hatch +python-discovery==1.4.0 + # via + # hatch + # tox + # virtualenv +readme-renderer==44.0 + # via twine +requests==2.34.2 + # via + # requests-toolbelt + # twine +requests-toolbelt==1.0.0 + # via twine +rfc3986==2.0.0 + # via twine +rich==15.0.0 + # via + # hatch + # twine +ruff==0.15.15 + # via -r requirements-dev.txt +secretstorage==3.5.0 + # via keyring +setuptools==82.0.1 + # via check-manifest +shellingham==1.5.4 + # via hatch +tomli-w==1.2.0 + # via + # hatch + # tox +tomlkit==0.15.0 + # via hatch +tox==4.55.0 + # via -r requirements-dev.txt +trove-classifiers==2026.5.22.10 + # via hatchling +twine==6.2.0 + # via -r requirements-dev.txt +typing-extensions==4.15.0 + # via anyio +urllib3==2.7.0 + # via + # id + # requests + # twine +userpath==1.9.2 + # via hatch +uv==0.11.17 + # via hatch +virtualenv==21.4.1 + # via + # hatch + # tox +zipp==4.1.0 + # via importlib-metadata diff --git a/tox.ini b/tox.ini index 2b6d87b..3726598 100644 --- a/tox.ini +++ b/tox.ini @@ -5,16 +5,22 @@ isolated_build = true minversion = 4.0.0 [testenv] -commands = - coverage run -m pytest -v -r wsx - coverage report +# skip_install + manual --no-deps install keeps the test env clean and avoids +# pulling in any hardware-specific C extensions declared in project dependencies. +skip_install = true deps = mock pytest>=3.1 pytest-cov build +commands_pre = + pip install --no-deps {toxinidir} +commands = + coverage run -m pytest -v -r wsx + coverage report [testenv:qa] +skip_install = true commands = check-manifest python -m build --no-isolation From eac6693e749e2f6d1a62603ae414b9658aef6164 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 30 May 2026 20:07:12 +0100 Subject: [PATCH 02/11] Fix for Comment on uv version --- Dockerfile.testing | 2 +- LOCAL_QA.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile.testing b/Dockerfile.testing index 7d26610..dccb9f8 100644 --- a/Dockerfile.testing +++ b/Dockerfile.testing @@ -21,7 +21,7 @@ # boilerplate-dev:python3.11-v0.0.1 \ # make check -FROM ghcr.io/astral-sh/uv:0.7.13 AS uv-base +FROM ghcr.io/astral-sh/uv:0.11.17 AS uv-base FROM python:3.11-slim-bookworm AS testing diff --git a/LOCAL_QA.md b/LOCAL_QA.md index 4af72a8..d4f0f33 100644 --- a/LOCAL_QA.md +++ b/LOCAL_QA.md @@ -19,6 +19,7 @@ docker build -f Dockerfile.testing \ > The testing version is independent of any library release. Start at `v0.0.1` and > increment: patch for dependency bumps/minor tweaks, minor for new tools or a Python > version bump, major for breaking changes to the dev workflow. +> Current tag: `boilerplate-dev:python3.11-v0.0.1` ## Run checks @@ -52,9 +53,10 @@ docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make pytest ## Dependency lock file The image installs from `requirements-dev.lock`. Regenerate it when `requirements-dev.txt` -changes: +changes, using the same uv version as the Dockerfile `FROM` pin: ```bash +uv self update 0.11.17 # align host uv with Dockerfile pin uv pip compile requirements-dev.txt --output-file requirements-dev.lock --python-version 3.11 ``` From cf1c3b8f0311fbe004e7107528d1dd66177fc1db Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 30 May 2026 20:07:20 +0100 Subject: [PATCH 03/11] And this --- LOCAL_QA.md | 1 - 1 file changed, 1 deletion(-) diff --git a/LOCAL_QA.md b/LOCAL_QA.md index d4f0f33..ace7643 100644 --- a/LOCAL_QA.md +++ b/LOCAL_QA.md @@ -19,7 +19,6 @@ docker build -f Dockerfile.testing \ > The testing version is independent of any library release. Start at `v0.0.1` and > increment: patch for dependency bumps/minor tweaks, minor for new tools or a Python > version bump, major for breaking changes to the dev workflow. -> Current tag: `boilerplate-dev:python3.11-v0.0.1` ## Run checks From 93bf7dbd21824f87d0c197b48d2d0251fe02d748 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sat, 30 May 2026 20:09:41 +0100 Subject: [PATCH 04/11] Copy ownership --- Dockerfile.testing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.testing b/Dockerfile.testing index dccb9f8..c833078 100644 --- a/Dockerfile.testing +++ b/Dockerfile.testing @@ -45,7 +45,7 @@ WORKDIR /app COPY requirements-dev.lock ./ RUN uv pip install --system --no-cache -r requirements-dev.lock -COPY . . +COPY --chown=appuser:appuser . . USER appuser From f3f5732c6c70055d6af170900437018849583931 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 12:01:31 +0100 Subject: [PATCH 05/11] Decided against this - it may need some library specific interventions though --- tox.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tox.ini b/tox.ini index 3726598..b1009d2 100644 --- a/tox.ini +++ b/tox.ini @@ -5,16 +5,11 @@ isolated_build = true minversion = 4.0.0 [testenv] -# skip_install + manual --no-deps install keeps the test env clean and avoids -# pulling in any hardware-specific C extensions declared in project dependencies. -skip_install = true deps = mock pytest>=3.1 pytest-cov build -commands_pre = - pip install --no-deps {toxinidir} commands = coverage run -m pytest -v -r wsx coverage report From 9921898aa291654142ad5410a4f54423be671e4f Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 12:08:34 +0100 Subject: [PATCH 06/11] Restore original order --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index b1009d2..a0f3ca0 100644 --- a/tox.ini +++ b/tox.ini @@ -5,14 +5,14 @@ isolated_build = true minversion = 4.0.0 [testenv] +commands = + coverage run -m pytest -v -r wsx + coverage report deps = mock pytest>=3.1 pytest-cov build -commands = - coverage run -m pytest -v -r wsx - coverage report [testenv:qa] skip_install = true From aba5f3a01c46ac86871d5eaaefa89c99efdcc343 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 13:38:22 +0100 Subject: [PATCH 07/11] Add the other python versions in - including 3.13 --- Dockerfile.testing | 103 +++++++++++++++++++++++++++++++++++++++------ LOCAL_QA.md | 43 ++++++++++++++----- 2 files changed, 124 insertions(+), 22 deletions(-) diff --git a/Dockerfile.testing b/Dockerfile.testing index c833078..d88c73e 100644 --- a/Dockerfile.testing +++ b/Dockerfile.testing @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 # Tagging convention: boilerplate-dev:python-v -# python-ver — Python minor version used in the image (e.g. 3.11) +# python-ver — Python minor version used in the image (e.g. 3.13) # testing-ver — Independent semver for the testing environment itself. # Unlike library repos (where the testing version tracks the # library release), this boilerplate has no release version of @@ -10,23 +10,63 @@ # minor — new tools added, Python minor version bump # major — breaking changes to the dev workflow # -# Build: +# Build a specific target: # docker build \ # --build-arg UID=$(id -u) --build-arg GID=$(id -g) \ -# -f Dockerfile.testing -t boilerplate-dev:python3.11-v0.0.1 . +# --target testing-3.13 \ +# -f Dockerfile.testing -t boilerplate-dev:python3.13-v0.0.1 . +# +# Available targets: testing-3.9 testing-3.10 testing-3.11 testing-3.13 +# Default target : testing (alias for testing-3.13, matching Debian Trixie) +# +# Base image mapping (matches the Debian release that shipped each Python): +# 3.9 → python:3.9-slim-bullseye (Debian 11 — note: NOT buster; buster=3.7) +# 3.10 → python:3.10-slim-bookworm (Debian 12) +# 3.11 → python:3.11-slim-bookworm (Debian 12) +# 3.13 → python:3.13-slim-trixie (Debian 13) +# +# Note: testing-3.9 uses plain pip (not uv) because the shared lockfile +# was compiled for Python 3.11 and anyio>=4.0 requires Python>=3.10. # # Run: # docker run --rm -it \ # -v "$(pwd)":/app \ -# boilerplate-dev:python3.11-v0.0.1 \ +# boilerplate-dev:python3.13-v0.0.1 \ # make check +# ── Global build args ───────────────────────────────────────────────────────── +# Defaults live here; each stage re-declares with a bare ARG to inherit them. +ARG UID=1000 +ARG GID=1000 + +# ── Shared uv binary ────────────────────────────────────────────────────────── +# Pin uv version once here; all testing stages except 3.9 copy from this stage. FROM ghcr.io/astral-sh/uv:0.11.17 AS uv-base -FROM python:3.11-slim-bookworm AS testing +# ── Python 3.9 (Debian Bullseye) — plain pip, no lockfile ──────────────────── +FROM python:3.9-slim-bullseye AS testing-3.9 +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y --no-install-recommends \ + make \ + dos2unix \ + shellcheck \ + git \ + && rm -rf /var/lib/apt/lists/* +ARG UID +ARG GID +RUN groupadd -g "${GID}" appuser \ + && useradd -l -u "${UID}" -g "${GID}" -m appuser +WORKDIR /app +COPY requirements-dev.txt ./ +# hadolint ignore=DL3013 +RUN pip install --no-cache-dir -r requirements-dev.txt +COPY --chown=appuser:appuser . . +USER appuser +RUN git config --global --add safe.directory /app +# ── Python 3.10 (Debian Bookworm) ───────────────────────────────────────────── +FROM python:3.10-slim-bookworm AS testing-3.10 COPY --from=uv-base /uv /uvx /usr/local/bin/ - # hadolint ignore=DL3008 RUN apt-get update && apt-get install -y --no-install-recommends \ make \ @@ -34,19 +74,58 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ shellcheck \ git \ && rm -rf /var/lib/apt/lists/* - -ARG UID=1000 -ARG GID=1000 +ARG UID +ARG GID RUN groupadd -g "${GID}" appuser \ && useradd -l -u "${UID}" -g "${GID}" -m appuser - WORKDIR /app - COPY requirements-dev.lock ./ RUN uv pip install --system --no-cache -r requirements-dev.lock - COPY --chown=appuser:appuser . . +USER appuser +RUN git config --global --add safe.directory /app +# ── Python 3.11 (Debian Bookworm) ───────────────────────────────────────────── +FROM python:3.11-slim-bookworm AS testing-3.11 +COPY --from=uv-base /uv /uvx /usr/local/bin/ +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y --no-install-recommends \ + make \ + dos2unix \ + shellcheck \ + git \ + && rm -rf /var/lib/apt/lists/* +ARG UID +ARG GID +RUN groupadd -g "${GID}" appuser \ + && useradd -l -u "${UID}" -g "${GID}" -m appuser +WORKDIR /app +COPY requirements-dev.lock ./ +RUN uv pip install --system --no-cache -r requirements-dev.lock +COPY --chown=appuser:appuser . . USER appuser +RUN git config --global --add safe.directory /app +# ── Python 3.13 (Debian Trixie) ────────────────────────────────────────────── +FROM python:3.13-slim-trixie AS testing-3.13 +COPY --from=uv-base /uv /uvx /usr/local/bin/ +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y --no-install-recommends \ + make \ + dos2unix \ + shellcheck \ + git \ + && rm -rf /var/lib/apt/lists/* +ARG UID +ARG GID +RUN groupadd -g "${GID}" appuser \ + && useradd -l -u "${UID}" -g "${GID}" -m appuser +WORKDIR /app +COPY requirements-dev.lock ./ +RUN uv pip install --system --no-cache -r requirements-dev.lock +COPY --chown=appuser:appuser . . +USER appuser RUN git config --global --add safe.directory /app + +# ── Default alias (Trixie = 3.13) ───────────────────────────────────────────── +FROM testing-3.13 AS testing diff --git a/LOCAL_QA.md b/LOCAL_QA.md index ace7643..095eb1b 100644 --- a/LOCAL_QA.md +++ b/LOCAL_QA.md @@ -1,17 +1,34 @@ # Local QA with Docker A Docker-based testing image is provided so checks can be run on any machine without -installing tooling locally. Python 3.11 is the first version set up; others will follow. +installing tooling locally. Four Python versions are supported, each on the Debian release +that shipped it: + +| Target | Python | Debian base | +|---|---|---| +| `testing-3.9` | 3.9 | Bullseye (11) | +| `testing-3.10` | 3.10 | Bookworm (12) | +| `testing-3.11` | 3.11 | Bookworm (12) | +| `testing-3.13` | 3.13 | Trixie (13) — default | ## Build the image Pass your host UID and GID so that files written inside the container are owned by your -user, not root: +user, not root. Use `--target` to select a Python version; omit it to get the default +(Python 3.13 / Trixie): ```bash +# Default (Python 3.13 / Trixie) +docker build -f Dockerfile.testing \ + --build-arg UID=$(id -u) \ + --build-arg GID=$(id -g) \ + -t boilerplate-dev:python3.13-v0.0.1 . + +# Specific version docker build -f Dockerfile.testing \ --build-arg UID=$(id -u) \ --build-arg GID=$(id -g) \ + --target testing-3.11 \ -t boilerplate-dev:python3.11-v0.0.1 . ``` @@ -23,40 +40,46 @@ docker build -f Dockerfile.testing \ ## Run checks All commands below mount the repository into the container so changes are picked up -without a rebuild. Run them from the repository root. +without a rebuild. Run them from the repository root. Substitute the tag for whichever +Python version you built. **Integrity checks** (trailing whitespace, DOS line-endings, CHANGELOG entry, git tag): ```bash -docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make check +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.13-v0.0.1 make check ``` **Shell script linting:** ```bash -docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make shellcheck +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.13-v0.0.1 make shellcheck ``` **QA** (ruff, isort, codespell, check-manifest, build, twine check): ```bash -docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make qa +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.13-v0.0.1 make qa ``` **Tests:** ```bash -docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.11-v0.0.1 make pytest +docker run --rm -v "$(pwd)":/app boilerplate-dev:python3.13-v0.0.1 make pytest ``` ## Dependency lock file -The image installs from `requirements-dev.lock`. Regenerate it when `requirements-dev.txt` -changes, using the same uv version as the Dockerfile `FROM` pin: +The `testing-3.10`, `testing-3.11`, and `testing-3.13` targets install from +`requirements-dev.lock`. Regenerate it when `requirements-dev.txt` changes, using the +same uv version as the Dockerfile pin: ```bash uv self update 0.11.17 # align host uv with Dockerfile pin uv pip compile requirements-dev.txt --output-file requirements-dev.lock --python-version 3.11 ``` -Then rebuild the image. +Then rebuild the affected images. + +> **Note on Python 3.9:** the `testing-3.9` target uses plain `pip` directly from +> `requirements-dev.txt` (no lockfile) because the shared lockfile was compiled for +> Python 3.11 and several transitive dependencies require Python ≥ 3.10. From e543cc971e8877c27ac3035b650b527ba06c7f60 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 15:05:08 +0100 Subject: [PATCH 08/11] What I was really here for next - python 3.13 and trixie --- .github/workflows/build.yml | 2 +- .github/workflows/install.yml | 2 +- .github/workflows/qa.yml | 2 +- .github/workflows/test.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5365571..f60cee7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.9', '3.10', '3.11'] + python: ['3.9', '3.10', '3.11', '3.13'] env: TERM: xterm-256color diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index f3c1a2d..4f20026 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -14,7 +14,7 @@ jobs: TERM: xterm-256color strategy: matrix: - python: ['3.9', '3.10', '3.11'] + python: ['3.9', '3.10', '3.11', '3.13'] steps: - name: Checkout Code diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 2e166c0..08f4712 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Python '3,11' uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.13' - name: Install Dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e29cb9..46b4037 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: TERM: xterm-256color strategy: matrix: - python: ['3.9', '3.10', '3.11'] + python: ['3.9', '3.10', '3.11', '3.13'] steps: - name: Checkout Code From 8b916bb94c1b93ac2edfad2d3a99fc90678d935a Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 15:19:20 +0100 Subject: [PATCH 09/11] Doh --- Dockerfile.testing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.testing b/Dockerfile.testing index d88c73e..48a768e 100644 --- a/Dockerfile.testing +++ b/Dockerfile.testing @@ -35,7 +35,7 @@ # make check # ── Global build args ───────────────────────────────────────────────────────── -# Defaults live here; each stage re-declares with a bare ARG to inherit them. +# Defaults live here; each stage redeclares with a bare ARG to inherit them. ARG UID=1000 ARG GID=1000 From 43404340d4bca75c4657bbc8527240b3d82db0f3 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 16:02:12 +0100 Subject: [PATCH 10/11] Python 3.9 - looks like hatch no longer works? --- .github/workflows/build.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f60cee7..667baa4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,6 +13,12 @@ jobs: strategy: matrix: python: ['3.9', '3.10', '3.11', '3.13'] + build_cmd: ['hatch build'] + include: + - python: '3.9' + # hatch build creates a virtualenv that has dropped the propose_interpreters + # API used by hatch on Python 3.9. Use python -m build (stdlib venv) instead. + build_cmd: 'python -m build' env: TERM: xterm-256color @@ -33,7 +39,8 @@ jobs: - name: Build Packages run: | - make build + make check + ${{ matrix.build_cmd }} - name: Upload Packages uses: actions/upload-artifact@v4 From 559ad1fad20c637e4ff1aa71631688c1bda5a5d6 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Sun, 31 May 2026 16:06:30 +0100 Subject: [PATCH 11/11] Whoops --- .github/workflows/build.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 667baa4..f6796af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,12 +13,6 @@ jobs: strategy: matrix: python: ['3.9', '3.10', '3.11', '3.13'] - build_cmd: ['hatch build'] - include: - - python: '3.9' - # hatch build creates a virtualenv that has dropped the propose_interpreters - # API used by hatch on Python 3.9. Use python -m build (stdlib venv) instead. - build_cmd: 'python -m build' env: TERM: xterm-256color @@ -38,9 +32,18 @@ jobs: make dev-deps - name: Build Packages + if: matrix.python != '3.9' + run: | + make build + + - name: Build Packages (Python 3.9) + if: matrix.python == '3.9' + # hatch build fails on Python 3.9: the installed virtualenv dropped the + # propose_interpreters API that hatch depends on. python -m build uses + # stdlib venv instead and is unaffected. All other versions use hatch build. run: | make check - ${{ matrix.build_cmd }} + python -m build - name: Upload Packages uses: actions/upload-artifact@v4