Repository navigation
Pin one uv version in every workflow, Dockerfile and Modal image - #967
Merged
Merged
Conversation
Workflows installed uv with astral-sh/setup-uv and no version input, so each run installed the newest uv release, and the two Dockerfiles and the four Modal images copied uv from the uv image's latest tag. Lock checks, installs and the daily relock could therefore run different uv releases from one day to the next. The new rule sim.dependencies.uv-version-pinned names one release, in the new .uv-version file (0.13.6), and the new uv-version quality guard (scripts/rules/uv_version.py) fails when an install pins no release or another one: an astral-sh/setup-uv step without with: version:, a uv container image reference without the release as its tag, a Modal uv_sync or uv_pip_install call without uv_version=, and a uv install script, release download, pip, pipx or uv tool install of uv, or uv self update that does not name the release. The proof fixtures hold the unpinned setup-uv step from pr.yml, the ghcr.io/astral-sh/uv:latest copy from the Dockerfiles and an unpinned Modal uv_sync call. The guard runs in the quality-guards job through run_quality_guards.py, and its tests run there with the rules tool's unittest suite. The dependency and CI workflow skill docs describe the rule and how to upgrade uv, and the rendered rules views are regenerated. This commit adds only the check: it fails on the 23 setup-uv steps, the two Dockerfiles and the four Modal uv_sync calls on main, which the next commit pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every uv install now pins 0.13.6, the release in .uv-version: - the 23 astral-sh/setup-uv steps in pr.yml (5), pr-image-smoke.yml, simulation-deploy.reusable.yml (13), simulation-rollback.yml, publish-clients.yml, check-policyengine-updates.yml and update-dependencies.yml set with: version: "0.13.6"; - the entrypoint and executor Dockerfiles copy uv from ghcr.io/astral-sh/uv:0.13.6 instead of :latest; - the uv_sync calls of the executor's runtime, v2 and Stage 12 precompute Modal images and of the gateway's Modal image pass uv_version="0.13.6", which Modal 1.4.2 and 1.5.1, the locked releases, accept. 0.13.6 is the release the current uv.lock files were relocked with in #959. check-uv-locks.sh passes for every project with both 0.13.4 (the release policyengine-api pins) and 0.13.6, so the newer one is used. No other workflow input, environment variable or secret changes. The Modal images rebuild once from their uv_sync layer, whose definition now names the release. The uv-version guard added in the previous commit now passes. Co-Authored-By: Claude Opus 5.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.
Fixes #966
Summary
Every place this repository installs uv now installs uv 0.13.6, the one release written in the new
.uv-versionfile, and a new registered rule fails CI when an install pins no release or another one.Before this change, the 23
astral-sh/setup-uv@v8.1.0steps had noversion:input, so each run installed the newest uv release, and the two Dockerfiles and the four Modal images copied uv fromghcr.io/astral-sh/uv:latest. Lock checks, installs and the daily relock could run different uv releases from one day to the next.Why 0.13.6
The current
uv.lockfiles were relocked with uv 0.13.6 in #959..github/scripts/check-uv-locks.shpasses for all eight projects with both 0.13.4 (the release policyengine-api pins) and 0.13.6. With both passing, the newer release is chosen: it is the one that wrote the current locks, and the one CI has been installing since 2026-10-26.The rule
sim.dependencies.uv-version-pinned(mechanical, enforced), checked by the newuv-versionquality guard (scripts/rules/uv_version.py) in thequality-guardsjob throughpython scripts/run_quality_guards.py. The guard reads every tracked workflow, composite action, shell script, Makefile, Dockerfile and Compose file, and the Modal image calls in Python modules outside tests and fixtures, and fails when:astral-sh/setup-uvstep sets nowith: version:, or another release;ghcr.io/astral-sh/uvorastral/uv) has no tag, a tag that is not a release (such aslatest), or another release; a variant tag such as0.13.6-python3.13-bookwormis accepted;uv_syncoruv_pip_installcall passes nouv_version=, or another release;pip,pipxoruv toolinstall of uv, oruv self updatenames no release, or another one.A workflow's
with:input can't be read from a file, and Modal imports its image definitions inside containers that don't hold the repository root, so each install writes the release itself and the guard keeps every copy equal to.uv-version. An upgrade changes.uv-version, and the guard then lists every install still on the old release.Proof:
rules/fixtures/sim.dependencies.uv-version-pinned/badholds the unpinned setup-uv step frompr.yml, theghcr.io/astral-sh/uv:latestcopy from the Dockerfiles and an unpinned Modaluv_synccall, as onmain;goodholds them pinned.scripts/rules/tests/test_uv_version.py::TestUvVersionProof::test_rule_proof_uv_version_pinnedruns in the same job'spython -m unittest discover -s scripts/rules/tests -t .step.docs/engineering/skills/dependencies.mddescribes the rule and how to upgrade uv,ci-workflows.mdpoints to it, anddocs/generated/rules.mdand theAGENTS.mdRules block are regenerated.The pins
pr.yml(5),pr-image-smoke.yml(1),simulation-deploy.reusable.yml(13),simulation-rollback.yml(1),publish-clients.yml(1),check-policyengine-updates.yml(1),update-dependencies.yml(1) each setversion: "0.13.6".projects/policyengine-simulation-entry/Dockerfileandprojects/policyengine-simulation-executor/Dockerfilecopy uv fromghcr.io/astral-sh/uv:0.13.6.uv_synccalls inprojects/policyengine-simulation-executor/src/modal/app.py(runtime image),v2_app.py(Stage 12 worker images),stage12_precompute_app.py(Stage 12 cache precompute image) andprojects/policyengine-simulation-gateway/src/policyengine_simulation_gateway/app.pypassuv_version="0.13.6". Both locked Modal releases (1.4.2 in the executor, 1.5.1 in the gateway) accept it.PATH.Red, then green
The first commit adds the check alone, and the guard fails on
main's 29 installs (23 setup-uv steps, 2 Dockerfiles, 4 Modaluv_synccalls), withuv-version: failedand every other guard passing. The second commit pins them, and the guard passes.Deployment verification
git archiveof this branch: the entrypoint for linux/amd64, as Cloud Run runs it, and the executor for the local platform (linux/arm64; its base stage uses$BUILDPLATFORM). Both pulledghcr.io/astral-sh/uv:0.13.6(index digestsha256:af22a52f5eafef3d78e551599d405c02df61919d18028f0cea8eda93bc9986d0), ranuv sync --frozen, and reportuv 0.13.6fromuv --versioninside the image. The PR'sDocker buildjobs build both for linux/amd64.uv_version. The Stage 12 cache precompute image, the US Stage 12 worker image and the full executor image with precomputed artifacts are built only at deployment, so they are not checked before merge. Because theuv_synclayer's definition changes, Modal rebuilds each image from that layer on its next build (the executor runtime image then downloads its certified datasets again).with:andversion: "0.13.6"on setup-uv steps. No environment variable, secret,env:block or other step input changes, and setup-uv's cache keys do not include the uv release.Checks run locally
uv tool run ruff@0.15.22 format --check projects/*/src libs/*/srcandruff check .: pass.python scripts/run_quality_guards.py: every guard passes, includinguv-version(at the first commit,uv-versionfails with the 29 installs listed above and every other guard passes).python -m scripts.rules guard --base-ref origin/main: passes; the only note is that the rule is new.python -m scripts.rules render: no diff.python -m unittest discover -s scripts/rules/tests -t .: 263 tests pass (at the first commit, onlytest_every_uv_install_in_the_repository_pins_the_one_releasefails)..github/scripts/check-uv-locks.shwith uv 0.13.6, after./scripts/generate-clients.sh: every lock passes.shellcheckon the two fixture scripts: clean. No script under.github/scripts/changed.uv run --frozen pytest tests/): gateway 268 passed, entrypoint 742 passed, executor 1,876 passed (22 skipped).python -m scripts.rules pyright --syncon the gateway and the executor: no Pyright errors.🤖 Generated with Claude Code