Skip to content

ci: install from the committed lock instead of regenerating it - #224

Merged
RichardAtCT merged 2 commits into
mainfrom
ci/strict-lockfile-install
Sep 11, 2026
Merged

RichardAtCT merged 2 commits into
mainfrom
ci/strict-lockfile-install

Conversation

@RichardAtCT

Copy link
Copy Markdown
Collaborator

CI ran poetry lock && poetry install in both the lint and test jobs. poetry lock regenerates poetry.lock in place, so CI never saw a stale lock — it silently fixed one and carried on.

Two consequences:

  1. Drift passed every PR and failed only at release. A pyproject.toml dependency change with a stale lock went green on its own PR. This broke the v1.7.0 release: fix: use start_webhook instead of run_webhook for async context #196 added the webhooks extra without regenerating the lock, passed CI twice, and then failed the release workflow, which installs strictly.
  2. CI never tested the dependency set that ships. Resolving fresh on every run means a transitive dependency could publish a breaking version and CI would pick it up independently of what poetry.lock says.

Changes

  • ci.yml: both jobs now run poetry check --lock, then poetry install --no-interaction. No poetry lock.
  • release.yml: gains the same read-only poetry check --lock. It already installed strictly and still does. poetry lock is deliberately not added here — release is the one place drift currently gets caught.
  • CONTRIBUTING.md and the PR template: say to run poetry lock and commit the result when dependencies change.
  • CHANGELOG.md: entry under [Unreleased].

The separate check step exists for the error message. poetry install alone reports the same problem, but the check makes the failing step in the Actions UI read "Verify lockfile is up to date" rather than "Install dependencies".

This also fixes a latent cache bug: the key hashes the committed lock while the venv was built from the regenerated one, so a cache hit could serve a venv matching no lockfile at all.

On the poetry check deprecation warning

Poetry 2.1.3 warns that [tool.poetry.extras] is deprecated. It is a Warning:, not an Error:, and does not affect the exit codepoetry check --lock exits 0 on clean main. So no pyproject.toml change and no scoping workaround was needed. Fixing the deprecation properly would mean moving dependencies out of dynamic = ["dependencies"] into [project.dependencies], which rewrites the whole dependency block for no functional gain.

Testing

  • poetry check --lock on main: passes (exit 0).
  • Guard proven: added a throwaway rich = "^13.0.0" to pyproject.toml in a scratch worktree without running poetry lock. Result was exit 1 with Error: pyproject.toml changed significantly since poetry.lock was last generated. Run 'poetry lock' to fix the lock file. Reverted.
  • poetry install --no-interaction from the committed lock: succeeds.
  • make test: 559 passed.
  • black, isort, flake8: clean.

mypy not run — it has pre-existing failures and is not in CI.

Expected effect on contributors

Any PR changing pyproject.toml dependencies without regenerating poetry.lock will now go red. That is the point, but it is a change in contributor experience, hence the docs update.

Checked the currently open PRs: #167 is the only one touching these files, and its lock is self-consistent today (poetry check --lock on its head exits 0), so this adds no new failure to it as it stands. It is being sent back for a rebase anyway; note posted there about resolving the poetry.lock conflict with poetry lock rather than by hand.

Dependabot is unaffected — both pip entries update pyproject.toml and poetry.lock together.

🤖 Generated with Claude Code

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 <noreply@anthropic.com>
@RichardAtCT
RichardAtCT merged commit 444c20e into main Sep 11, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant