diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 761d28aa..7d82fc9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,6 +126,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true # `--locked`, not a bare `uv sync`, and it does two jobs. It refuses to @@ -217,6 +218,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Sync environment @@ -548,6 +550,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Sync environment @@ -641,6 +644,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Install @@ -715,6 +719,7 @@ jobs: if: needs.wheel.result == 'success' && (needs.changes.result != 'success' || needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true') uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Download the distribution the `wheel` job already built @@ -776,6 +781,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Install @@ -882,6 +888,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Sync environment with the local-inference extra @@ -948,6 +955,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Sync environment with the format groups @@ -1017,6 +1025,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs['browser-models'] == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true # The sparse upstream checkout and the checkpoint download, never the exported @@ -1031,9 +1040,8 @@ jobs: # `--group browser-models` is repeated on every `uv run` below rather than left to # this sync. `uv run` performs its own sync first, and whether that prunes a group the - # command did not name is behaviour this workflow should not have to know: `setup-uv` - # is not version-pinned, so the uv deciding it is whichever one is current on the day. - # Naming the group each time costs nothing and makes the step say what it needs. + # command did not name is behaviour this workflow should not have to know. Naming the + # group each time costs nothing and makes the step say what it needs. - name: Sync environment with the browser-models group if: needs.changes.result != 'success' || needs.changes.outputs['browser-models'] == 'true' run: uv sync --locked --group browser-models @@ -1238,6 +1246,7 @@ jobs: if: needs.changes.result != 'success' || needs.changes.outputs.python == 'true' uses: astral-sh/setup-uv@v7 with: + version: "0.12.3" enable-cache: true - name: Sync environment diff --git a/tests/scripts/uv_tool_version.test.mjs b/tests/scripts/uv_tool_version.test.mjs new file mode 100644 index 00000000..fe25ac41 --- /dev/null +++ b/tests/scripts/uv_tool_version.test.mjs @@ -0,0 +1,28 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { test } from "node:test"; + +const ROOT = join(fileURLToPath(new URL("../..", import.meta.url))); + +const read = (...parts) => readFileSync(join(ROOT, ...parts), "utf8"); + +test("CI requests an exact reviewed uv version", () => { + const workflow = read(".github", "workflows", "ci.yml"); + const setupUvCount = [...workflow.matchAll(/uses: astral-sh\/setup-uv@v7/g)].length; + const installSteps = workflow + .split(/^ {6}- name: Install uv\n/m) + .slice(1) + .map((step) => step.split(/^ {6}- /m, 1)[0]); + + assert.ok(setupUvCount > 0, "CI must install uv"); + assert.equal(installSteps.length, setupUvCount, "every setup-uv use must have an Install uv step"); + for (const step of installSteps) { + assert.match( + step, + /^ {10}version: "0\.12\.3"$/m, + "each setup-uv action must request the reviewed version instead of resolving latest", + ); + } +});