Skip to content

Commit 137aa9b

Browse files
authored
ISSUE opendatahub-io#3032: chore(uv): pin uv version to fix CI check-generated-code failure (opendatahub-io#3034)
# Context The check-generated-code CI job is failing on main and all PRs because astral-sh/setup-uv@v7 installs version: "latest" (currently uv 0.10.6), which produces different pylock.toml output than the committed files. The root cause is astral-sh/uv#18081 (released in uv 0.10.5, 2026-02-23) which added wheel filtering to pylock.toml even in --universal mode. See opendatahub-io#3032. Fix: pin uv version in uv.toml using required-version, remove version: "latest" from all workflows so setup-uv auto-detects the pin, and regenerate pylocks. # Changes 1. Create uv.toml at repo root required-version = "==0.10.6" setup-uv@v7 auto-detects this and installs the pinned version. Locally, uv errors if the running version doesn't match. 2. Update .github/workflows/code-quality.yaml Two setup-uv blocks (lines 17-24 and 46-53): - Remove version: "latest" line - Rename step from "Install the latest version of uv" to "Install uv" 3. Update .github/workflows/docs.yaml One setup-uv block (lines 21-28): - Remove version: "latest" line - Rename step 4. Update .github/workflows/security.yaml One setup-uv block (lines 19-26): - Remove version: "latest" line - Rename step 5. Update .github/workflows/build-notebooks-TEMPLATE.yaml One setup-uv block (lines 304-311): - Remove version: "latest" line - Rename step 6. Update ci/generate_code.sh Line 4 has a fallback pip install "uv==0.9.6" — update to "uv==0.10.6" to match the pinned version. 7. Regenerate pylocks bash ci/generate_code.sh This regenerates the 6 affected pylock.toml files with uv 0.10.6 filtering. # Files modified - uv.toml (new) - .github/workflows/code-quality.yaml - .github/workflows/docs.yaml - .github/workflows/security.yaml - .github/workflows/build-notebooks-TEMPLATE.yaml - ci/generate_code.sh - 6 pylock.*.toml files (regenerated) # Verification # Check that uv reads the required-version and doesn't error uv version # Regenerate and verify no diff bash ci/generate_code.sh git diff --stat # should show no changes after regeneration * ISSUE opendatahub-io#3032: chore(uv): create a ./uv wrapper to run the correct version of uv 1. Pre-flight check (lines 93-100): Added explicit validation that $UV wrapper exists and is executable before the existing command -v uv check. This prevents a misleading version error if the wrapper is missing. 2. Constraints flag (lines 291-316): Changed constraints_flag from a plain string to a bash array (local -a constraints_flag=()), and expanded it as "${constraints_flag[@]}" in the pip compile invocation. This avoids word-splitting on paths containing spaces.
1 parent 3ec9d63 commit 137aa9b

11 files changed

Lines changed: 125 additions & 33 deletions

.github/workflows/build-notebooks-TEMPLATE.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,10 @@ jobs:
319319
# region Pytest image tests
320320

321321
# https://github.com/astral-sh/setup-uv
322-
- name: Install the latest version of uv
322+
- name: Install uv
323323
uses: astral-sh/setup-uv@v7
324324
with:
325-
version: "latest"
325+
version-file: uv.toml
326326
python-version: "3.14"
327327
enable-cache: true
328328
cache-dependency-glob: "uv.lock"

.github/workflows/code-quality.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ jobs:
1515
- uses: actions/checkout@v6
1616

1717
# https://github.com/astral-sh/setup-uv
18-
- name: Install the latest version of uv
18+
- name: Install uv
1919
uses: astral-sh/setup-uv@v7
2020
with:
21-
version: "latest"
21+
version-file: uv.toml
2222
python-version: "3.14"
2323
enable-cache: true
2424
cache-dependency-glob: "uv.lock"
@@ -44,10 +44,10 @@ jobs:
4444
- uses: actions/checkout@v6
4545

4646
# https://github.com/astral-sh/setup-uv
47-
- name: Install the latest version of uv
47+
- name: Install uv
4848
uses: astral-sh/setup-uv@v7
4949
with:
50-
version: "latest"
50+
version-file: uv.toml
5151
python-version: "3.14"
5252
enable-cache: true
5353
cache-dependency-glob: "uv.lock"

.github/workflows/docs.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ jobs:
1919
- uses: actions/checkout@v6
2020

2121
# https://github.com/astral-sh/setup-uv
22-
- name: Install the latest version of uv
22+
- name: Install uv
2323
uses: astral-sh/setup-uv@v7
2424
with:
25-
version: "latest"
25+
version-file: uv.toml
2626
python-version: "3.14"
2727
enable-cache: true
2828
cache-dependency-glob: "uv.lock"

.github/workflows/piplock-renewal.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ jobs:
7575
python-version: '3.12'
7676

7777
- name: Install uv
78-
run: pip install "uv==0.9.27"
78+
uses: astral-sh/setup-uv@v7
79+
with:
80+
version-file: uv.toml
7981

8082
- name: Run make refresh-lock-files
8183
run: |

.github/workflows/security.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ jobs:
1616
security-events: write
1717
steps:
1818

19+
- name: Checkout code
20+
uses: actions/checkout@v6
21+
1922
# https://github.com/astral-sh/setup-uv
20-
- name: Install the latest version of uv
23+
- name: Install uv
2124
uses: astral-sh/setup-uv@v7
2225
with:
23-
version: "latest"
26+
version-file: uv.toml
2427
activate-environment: false
2528
ignore-empty-workdir: true
2629
enable-cache: false
2730

28-
- name: Checkout code
29-
uses: actions/checkout@v6
30-
3131
# Trivy does not support pylock.toml https://github.com/aquasecurity/trivy/discussions/9408
3232
- run: find . -name pyproject.toml -execdir uv lock \;
3333

.pre-commit-config.yaml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@
22
# https://github.com/pre-commit/pre-commit-hooks?tab=readme-ov-file#hooks-available
33
repos:
44
# https://docs.astral.sh/uv/guides/integration/pre-commit/
5-
- repo: https://github.com/astral-sh/uv-pre-commit
6-
rev: 0.9.29
5+
# Using a local hook instead of uv-pre-commit so it goes through ./uv,
6+
# which handles version mismatches without requiring the exact system uv.
7+
- repo: local
78
hooks:
89
- id: uv-lock
10+
name: uv-lock
11+
entry: ./uv lock --locked
12+
language: system
13+
files: '(^uv\.lock$|^pyproject\.toml$|^uv\.toml$)'
14+
pass_filenames: false
915
# https://github.com/astral-sh/ruff-pre-commit
1016
- repo: https://github.com/astral-sh/ruff-pre-commit
11-
rev: v0.15.0
17+
rev: v0.15.4
1218
hooks:
13-
- id: ruff
19+
- id: ruff-check
1420
types_or: [python, pyi]
1521
args: [--fix]
1622
files: 'ci/.*|tests/.*'
@@ -25,7 +31,7 @@ repos:
2531
- id: pyright
2632
name: Run Pyright on all files
2733
# entry: /bin/bash -c 'find. -name "*.py" | xargs pyright --pythonversion 3.12'
28-
entry: uv run pyright --pythonversion 3.14
34+
entry: ./uv run pyright --pythonversion 3.14
2935
pass_filenames: true
3036
types_or: [python, pyi]
3137
language: system

README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,49 @@ Note: To ensure the GitHub Action runs successfully, users must add a `GH_ACCESS
6262

6363
#### Prepare Python + uv + pytest env
6464

65+
This project pins its uv version in `uv.toml` (`required-version`).
66+
Use the `./uv` wrapper script at the repo root — it reads the pinned
67+
version and runs it via `uvx`, so your system uv version doesn't matter:
68+
6569
```shell
6670
# Linux
6771
sudo dnf install python3.14
6872
pip install --user uv
69-
# MacOS
73+
# macOS
7074
brew install [email protected] uv
7175

72-
uv venv --python $(which python3.14)
73-
uv sync --locked
76+
./uv venv --python $(which python3.14)
77+
./uv sync --locked
7478
```
7579

80+
<details>
81+
<summary>Alternatives to <code>./uv</code></summary>
82+
83+
The `./uv` wrapper is the recommended way, but you can also
84+
(replace `0.10.6` below with the version from `uv.toml`):
85+
86+
- **Use `uvx` directly** with an explicit version:
87+
```shell
88+
uvx [email protected] sync --locked
89+
```
90+
- **Use `uv tool run`** (equivalent, longer form):
91+
```shell
92+
uv tool run [email protected] sync --locked
93+
```
94+
- **Install the exact version** so `uv` works directly:
95+
```shell
96+
# Standalone installer (any OS)
97+
curl -LsSf https://astral.sh/uv/0.10.6/install.sh | sh
98+
# Or with pip
99+
pip install uv==0.10.6
100+
```
101+
102+
If your system uv matches the pinned version, you can use `uv` directly —
103+
`required-version` in `uv.toml` will let it through. If it doesn't match,
104+
uv exits with a clear error telling you which version is required.
105+
106+
</details>
107+
76108
#### Running Python selftests in Pytest
77109
By completing configuration in previous section, you are able to run any tests that don't need to start a container using following command:
78110

@@ -106,15 +138,15 @@ sudo dnf install podman
106138
systemctl --user start podman.service
107139
systemctl --user status podman.service
108140
systemctl --user status podman.socket
109-
DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock uv run pytest tests/containers -m 'not openshift and not cuda and not rocm' --image quay.io/opendatahub/workbench-images@sha256:e98d19df346e7abb1fa3053f6d41f0d1fa9bab39e49b4cb90b510ca33452c2e4
141+
DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock ./uv run pytest tests/containers -m 'not openshift and not cuda and not rocm' --image quay.io/opendatahub/workbench-images@sha256:e98d19df346e7abb1fa3053f6d41f0d1fa9bab39e49b4cb90b510ca33452c2e4
110142
111143
# Mac OS
112144
brew install podman
113145
podman machine init
114146
podman machine set --rootful=false
115147
sudo podman-mac-helper install
116148
podman machine start
117-
uv run pytest tests/containers -m 'not openshift' --image quay.io/opendatahub/workbench-images@sha256:e98d19df346e7abb1fa3053f6d41f0d1fa9bab39e49b4cb90b510ca33452c2e4
149+
./uv run pytest tests/containers -m 'not openshift' --image quay.io/opendatahub/workbench-images@sha256:e98d19df346e7abb1fa3053f6d41f0d1fa9bab39e49b4cb90b510ca33452c2e4
118150
```
119151

120152
When using lima on macOS, it might be useful to give yourself access to rootful podman socket

ci/generate_code.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#!/usr/bin/env bash
22
set -Eeuxo pipefail
33

4-
uv --version || pip install "uv==0.9.6"
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
56

6-
uv run scripts/dockerfile_fragments.py
7-
uv run manifests/tools/generate_kustomization.py
7+
uv --version || pip install "uv==0.10.6"
8+
9+
"${REPO_ROOT}/uv" run scripts/dockerfile_fragments.py
10+
"${REPO_ROOT}/uv" run manifests/tools/generate_kustomization.py
811
bash scripts/pylocks_generator.sh

scripts/pylocks_generator.sh

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ MAIN_DIRS=("jupyter" "runtimes" "rstudio" "codeserver")
5252
# CVE constraints file - applied to all lock file generations
5353
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5454
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
55+
UV="${ROOT_DIR}/uv"
5556
CVE_CONSTRAINTS_FILE="$ROOT_DIR/dependencies/cve-constraints.txt"
5657

5758
# ----------------------------
@@ -89,13 +90,18 @@ read_conf_value() {
8990
# ----------------------------
9091
# PRE-FLIGHT CHECK
9192
# ----------------------------
93+
if [[ ! -x "$UV" ]]; then
94+
error "Expected uv wrapper at '$UV' but it is missing or not executable."
95+
exit 1
96+
fi
97+
9298
if ! command -v uv &>/dev/null; then
9399
error "uv command not found. Please install uv: https://github.com/astral-sh/uv"
94100
exit 1
95101
fi
96102

97103
UV_MIN_VERSION="0.4.0"
98-
UV_VERSION=$(uv --version 2>/dev/null | awk '{print $2}' || echo "0.0.0")
104+
UV_VERSION=$("$UV" --version 2>/dev/null | awk '{print $2}' || echo "0.0.0")
99105

100106
version_ge() {
101107
[ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
@@ -273,7 +279,9 @@ for TARGET_DIR in "${TARGET_DIRS[@]}"; do
273279
echo "➡️ Generating $(uppercase "$flavor") lock file..."
274280
fi
275281

276-
# The behavior has changed in uv 0.9.17 (https://github.com/astral-sh/uv/pull/16956)
282+
# Tag filtering was added in uv 0.9.16 (https://github.com/astral-sh/uv/pull/16956)
283+
# but bypassed in --universal mode. uv 0.10.5 (https://github.com/astral-sh/uv/pull/18081)
284+
# now filters wheels by requires-python and marker disjointness even in --universal mode.
277285
# Documentation at https://docs.astral.sh/uv/reference/cli/#uv-pip-compile--python-platform says that
278286
# `--python-platform linux` is alias for `x86_64-unknown-linux-gnu`; we cannot use this to get a multiarch pylock
279287
# Let's use --universal temporarily, and in the future we can switch to using uv.lock
@@ -285,17 +293,17 @@ for TARGET_DIR in "${TARGET_DIRS[@]}"; do
285293
# Build constraints flag if CVE constraints file exists
286294
# Use relative path to avoid absolute paths in pylock.toml headers
287295
# (which would differ between CI and local environments)
288-
local constraints_flag=""
296+
local -a constraints_flag=()
289297
if [[ -f "$CVE_CONSTRAINTS_FILE" ]]; then
290298
local relative_constraints
291299
# Use Python for cross-platform relative path computation (realpath --relative-to is GNU-only)
292300
relative_constraints=$(python3 -c "import os; print(os.path.relpath('$CVE_CONSTRAINTS_FILE', '$PWD'))")
293-
constraints_flag="--constraints=$relative_constraints"
301+
constraints_flag=(--constraints "$relative_constraints")
294302
fi
295303

296304
set +e
297305
# shellcheck disable=SC2086
298-
uv pip compile pyproject.toml \
306+
"$UV" pip compile pyproject.toml \
299307
--output-file "$output" \
300308
--format pylock.toml \
301309
--generate-hashes \
@@ -309,7 +317,7 @@ for TARGET_DIR in "${TARGET_DIRS[@]}"; do
309317
--no-emit-package odh-notebooks-meta-runtime-datascience-deps \
310318
--no-emit-package odh-notebooks-meta-workbench-datascience-deps \
311319
$UPGRADE_FLAG \
312-
$constraints_flag \
320+
"${constraints_flag[@]}" \
313321
$index
314322
local status=$?
315323
set -e

uv

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env -S bash --norc --noprofile
2+
# ./uv — run the project-pinned version of uv.
3+
#
4+
# Reads required-version from uv.toml and delegates via `uv tool run`.
5+
# This avoids version mismatch errors when your system uv (e.g. Homebrew)
6+
# differs from the version pinned for this project.
7+
#
8+
# Usage:
9+
# ./uv sync
10+
# ./uv run pytest
11+
# ./uv pip compile ...
12+
#
13+
# The pinned version is cached by uvx after the first run.
14+
# Parsing uses bash =~ instead of forking sed to avoid a subprocess.
15+
#
16+
# Bash with built-in regex is ~3x faster than Python for this task (hyperfine,
17+
# 50 runs): bash+builtin 18.7ms, bash+sed 25.0ms, python 55.4ms.
18+
set -Eeuo pipefail
19+
20+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21+
22+
while IFS= read -r line; do
23+
if [[ "$line" =~ ^required-version\ *=\ *\"==([^\"]*)\" ]]; then
24+
version="${BASH_REMATCH[1]}"
25+
break
26+
fi
27+
done < "${SCRIPT_DIR}/uv.toml"
28+
29+
if [[ -z "${version:-}" ]]; then
30+
echo "error: could not read required-version from ${SCRIPT_DIR}/uv.toml" >&2
31+
exit 1
32+
fi
33+
34+
# Fast path: use the system uv directly if it already matches the pinned version
35+
if current=$(uv --version 2>/dev/null) && [[ "$current" == "uv $version" || "$current" == "uv $version "* ]]; then
36+
exec uv "$@"
37+
fi
38+
39+
# Slow path: run the pinned version via uvx (downloaded and cached on first use)
40+
exec uv tool run "uv@${version}" "$@"

0 commit comments

Comments
 (0)