From 48cf02feffa9818d817a7cc98da0cc51ee7b3ce9 Mon Sep 17 00:00:00 2001 From: Richard A Date: Fri, 11 Sep 2026 17:44:25 +0400 Subject: [PATCH] ci: install from the committed lock instead of regenerating it The lint and test jobs ran `poetry lock && poetry install`. That regenerates poetry.lock in place, so CI never saw a stale lock -- it silently fixed one and carried on. A pyproject.toml dependency change without a matching lock update therefore passed every PR and failed only at release time, which is what broke the v1.7.0 release (#196). It also meant CI resolved fresh dependencies on every run, so the dependency set under test was never the one that ships. Both jobs now verify the lock with `poetry check --lock` and install from it. The separate check step is there for the error message: it names the lockfile in the Actions UI instead of failing inside "Install dependencies". release.yml gains the same read-only check for symmetry. It already installed strictly and still does; `poetry lock` is deliberately not added there, because release is the one place drift currently gets caught. This also makes the dependency cache honest. The key hashes the committed lock, but the venv was built from the regenerated one, so a cache hit could serve a venv matching no lockfile at all. Contributor docs and the PR template now say to run `poetry lock` and commit the result when dependencies change, because this turns that omission into a red build. Co-Authored-By: Claude Opus 5 --- .github/pull_request_template.md | 1 + .github/workflows/ci.yml | 10 ++++++++-- .github/workflows/release.yml | 3 +++ CHANGELOG.md | 3 +++ CONTRIBUTING.md | 4 ++++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 73da9ae54..a2b82cd65 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -25,6 +25,7 @@ Closes # - [ ] One concern per PR; unrelated changes are split out - [ ] `CHANGELOG.md` has an entry under `[Unreleased]` +- [ ] If `pyproject.toml` dependencies changed, `poetry lock` was run and the updated `poetry.lock` is committed - [ ] Documentation updated (`README.md`, `docs/`, `.env.example`, `CLAUDE.md`) where settings or commands changed - [ ] New settings default to current behaviour - [ ] If AI tools helped write this change, I reviewed every line and the hand-testing above is mine diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33887d3f5..2b9ed2cf2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,8 +31,11 @@ jobs: path: .venv key: venv-${{ runner.os }}-3.11-${{ hashFiles('**/poetry.lock') }} + - name: Verify lockfile is up to date + run: poetry check --lock + - name: Install dependencies - run: poetry lock && poetry install + run: poetry install --no-interaction - name: Check formatting (black) run: poetry run black --check src tests @@ -67,8 +70,11 @@ jobs: path: .venv key: venv-${{ runner.os }}-3.11-${{ hashFiles('**/poetry.lock') }} + - name: Verify lockfile is up to date + run: poetry check --lock + - name: Install dependencies - run: poetry lock && poetry install + run: poetry install --no-interaction - name: Test run: make test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 697f4369d..8af7f05ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,9 @@ jobs: path: .venv key: venv-${{ runner.os }}-3.11-${{ hashFiles('**/poetry.lock') }} + - name: Verify lockfile is up to date + run: poetry check --lock + - name: Install dependencies run: poetry install --no-interaction diff --git a/CHANGELOG.md b/CHANGELOG.md index 75a7bdf6e..f51c5180e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- **CI now catches lockfile drift**: the `lint` and `test` jobs ran `poetry lock && poetry install`, which regenerated `poetry.lock` in place. A `pyproject.toml` dependency change with a stale lock therefore passed every PR and failed only at release time, as it did for v1.7.0 (#196). Both jobs now verify the lock with `poetry check --lock` and install from the committed lock, so CI tests the dependency set that actually ships rather than resolving a fresh one on every run. + ## [1.7.0] - 2026-09-11 Released as a minor rather than a patch: the security fix below changes runtime diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d189471e..a646eea28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,6 +95,10 @@ five-layer security model. Read it before touching `src/security/` or - Tests for behaviour changes. `make test` and `make lint` pass. CI runs black, isort, flake8 and the test suite on every PR. +- **If you change dependencies, run `poetry lock` and commit the updated + `poetry.lock` in the same PR.** CI installs from the committed lock and + fails the build if the lock and `pyproject.toml` disagree, so a + `pyproject.toml` dependency change without a matching lock update goes red. - A line under `[Unreleased]` in `CHANGELOG.md`, in the Keep a Changelog style already used there. - Docs updated where a setting or command changed: `README.md`,