Install CI environments from uv.lock - #8
Merged
Conversation
Ports artifex's uv sync block, and pins the interpreter for the sync step: uv sync selects its own Python and does not inherit the one uv venv just created, so it resolved against 3.14 and refused the lock because tensorflow 2.20.0 has no cp314 wheel.
mahdi-shafiei
force-pushed
the
ci/install-from-the-lock
branch
from
August 27, 2026 22:03
c8c13e6 to
942851d
Compare
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.
Install CI environments from uv.lock
What
The composite action
.github/actions/setup-diffbiostops runninguv pip install -eand parses the extras into auv sync --lockedcall, ported line for line from artifex'ssetup-artifex.Why
uv pip install -e ".[dev]"resolves fresh and ignoresuv.lock. The nextuv runre-syncs the project's default dependencies fromuv.lock. The result isan environment that is part freshly resolved and part locked, which no single resolution
ever validated, and which changes from one day to the next because nobody pinned it.
It is not theoretical. On 2026-08-27 it produced two unrelated-looking CI failures across
this ecosystem:
twine 7.0.0, which requirespackaging>=26.1;uv runthen reverted
packagingto the locked version and twine died onImportError: cannot import name 'errors' from 'packaging'.ruff 0.16.5against a lock pinning0.15.2, and the lint gatereported 33 errors on a tree that is clean under every 0.15 release.
Both are the same defect: the gate depends on when it runs, not on what the repository
declares.
The pattern this adopts
artifex already solved this in
.github/actions/setup-artifex, and its own comment sayswhy:
This brings that pattern here rather than inventing a new one.
Verification
Every referenced extra is declared in
pyproject.toml, checked by parsing the manifestrather than by eye. Every changed workflow parses as YAML. The replacement was run locally
on a clean checkout:
uv sync --locked --extra devinstalls exactly the versionsuv.lockpins, whereuv pip install -e ".[dev]"installed newer ones.Deliberately not changed
Installing a built wheel into an isolated virtual environment, installing a sibling
package from git, and installing type stubs that are absent from the lock. Those are not
this defect and are left alone.