build: install CI dependencies from a lockfile - #8
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
RUF100by default → 65 errors in files untouched since June (fix: pin ruff and select the lint rule set explicitly #6).Both were fixed at the symptom. Neither fix prevents the next one:
numpy>=1.26,mypy>=1.10,pytest>=8and the wholegeostack are still open-ended.What changed
CI installs from
uv.lockviauv sync --locked --extra dev --extra geoinstead 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.txtcannot express it in one artefact;uv.lockrecords both behind resolution markers, so one file covers the matrix.--lockedis the guard. It fails when the lock is stale with respect topyproject.toml, so a dependency edit cannot land without a refreshed lock in the same commit. Verified by narrowing the numpy constraint without re-locking:uv (0.11.32) and setup-uv (v9) are pinned exactly, on the same reasoning as the existing
ruff==0.16.0pin. setup-uv v9 runs on thenode24runtime.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.mdgains 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 --locked→uv run --no-sync) on both matrix versions:ruff check .mypy srcpytest -qConfirmed 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.0stays pinned inpyproject.tomleven though the lock now pins it too. It is not redundant: it keeps the unlockedpip 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