Skip to content

build: install CI dependencies from a lockfile - #8

Merged
leiverkus merged 2 commits into
mainfrom
build/ci-lockfile
Jul 26, 2026
Merged

build: install CI dependencies from a lockfile#8
leiverkus merged 2 commits into
mainfrom
build/ci-lockfile

Conversation

@leiverkus

Copy link
Copy Markdown
Owner

Closes the drift class that produced the last two CI failures. No source changes.

Why

CI resolved dependencies fresh on every run, so an unchanged tree could go red whenever a transitive dependency published. That happened twice this week:

Both were fixed at the symptom. Neither fix prevents the next one: numpy>=1.26, mypy>=1.10, pytest>=8 and the whole geo stack are still open-ended.

What changed

CI installs from uv.lock via uv sync --locked --extra dev --extra geo instead of resolving.

The lock is universal, which this project specifically needs. numpy resolves to 2.4.6 on 3.11 and 2.5.1 on 3.12 — that is the exact split that caused the mypy failure. A flat pinned requirements.txt cannot express it in one artefact; uv.lock records both behind resolution markers, so one file covers the matrix.

--locked is the guard. It fails when the lock is stale with respect to pyproject.toml, so a dependency edit cannot land without a refreshed lock in the same commit. Verified by narrowing the numpy constraint without re-locking:

error: The lockfile at `uv.lock` needs to be updated, but `--locked` was provided.

uv (0.11.32) and setup-uv (v9) are pinned exactly, on the same reasoning as the existing ruff==0.16.0 pin. setup-uv v9 runs on the node24 runtime.

Local development is untouched. pip install -e ".[dev]" still works and stays unlocked — convenient day to day, with CI as the authority on versions. docs/development.md gains a Dependency locking section covering the split, uv lock --upgrade, and the hint to compare versions when something reproduces in CI but not locally.

Verification

Run with the workflow's own commands (uv sync --lockeduv run --no-sync) on both matrix versions:

3.11 3.12
ruff check . pass pass
mypy src 26 files clean 26 files clean
pytest -q 57 passed, 2 skipped 57 passed, 2 skipped

Confirmed the locked resolution is what actually lands: on 3.12 the synced env reports numpy 2.5.1 and ruff 0.16.0.

Note

ruff==0.16.0 stays pinned in pyproject.toml even though the lock now pins it too. It is not redundant: it keeps the unlocked pip install -e ".[dev]" path on the same linter as CI, which is the case where a version mismatch is most confusing.

🤖 Generated with Claude Code

leiverkus and others added 2 commits July 26, 2026 15:40
CI resolved dependencies fresh on every run, so an unchanged tree could
go red whenever a transitive dependency published. That happened twice
this week: ruff 0.16.0 enabled RUF100 by default, and numpy 2.5 shipped
PEP 695 stubs that tripped mypy's pinned target. Both were fixed at the
symptom; this closes the class.

CI now installs from uv.lock via `uv sync --locked`. The lock is
universal — it records a resolution per Python version, which this
project needs: numpy resolves to 2.4.6 on 3.11 and 2.5.1 on 3.12, and a
flat pinned requirements file could not express that in one artefact.

`--locked` fails when the lock is stale with respect to pyproject.toml,
so a dependency edit cannot land without a refreshed lock. Verified by
narrowing the numpy constraint without re-locking: the sync refuses with
"the lockfile at `uv.lock` needs to be updated".

uv (0.11.32) and setup-uv (v9, node24 runtime) are pinned exactly, on the
same reasoning as the existing ruff pin. Local `pip install -e ".[dev]"`
is untouched and stays unlocked; docs/development.md documents the split
and how to refresh.

Verified with the workflow's own commands on both matrix versions:
ruff clean, mypy clean on 26 files, 57 passed / 2 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects in the previous commit's workflow, both of which stopped
GitHub from creating any job at all:

- `name: Install (locked: core + dev + geo)` — the unquoted colon-space
  inside the parentheses parses as a nested mapping. Renamed and quoted.
- `astral-sh/setup-uv@v9` does not resolve: the action publishes no
  floating major tag above v7, only the exact release. Pinned to v9.0.0,
  which is what this workflow argues for anyway.

Validated by parsing the file and resolving both action refs against the
GitHub API before pushing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@leiverkus
leiverkus merged commit 5ea60ab into main Jul 26, 2026
2 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