Skip to content

Commit a7acbb9

Browse files
committed
update commit latest action to install uv and dependencies so it works with out fail
1 parent 715a38f commit a7acbb9

4 files changed

Lines changed: 184 additions & 82 deletions

File tree

.github/workflows/update-commit-latest-env.yaml

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ name: Update commit-latest.env on params-latest.env change
1010
- main
1111
- rhoai-*
1212
paths:
13-
- 'manifests/base/params-latest.env'
13+
- 'manifests/odh/base/params-latest.env'
14+
- 'manifests/rhoai/base/params-latest.env'
1415

1516
jobs:
1617
sync-commit:
@@ -27,21 +28,28 @@ jobs:
2728
token: ${{ secrets.GITHUB_TOKEN }}
2829
persist-credentials: true
2930

30-
- name: Update manifests/base/commit-latest.env
31+
- name: Setup uv
32+
uses: ./.github/actions/setup-uv
33+
34+
- name: Install dependencies
35+
run: uv sync --locked
36+
37+
- name: Update commit-latest.env (ODH and RHOAI)
3138
id: update_env
32-
run: |
33-
python3 scripts/update-commit-latest-env.py
39+
run: uv run python scripts/update-commit-latest-env.py
3440

3541
- name: Commit and push changes
3642
run: |
3743
git config --global user.name 'github-actions[bot]'
3844
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
3945
40-
# Check for changes before committing
41-
if ! git diff --quiet manifests/base/commit-latest.env; then
42-
git add manifests/base/commit-latest.env
46+
if ! git diff --quiet \
47+
manifests/odh/base/commit-latest.env \
48+
manifests/rhoai/base/commit-latest.env
49+
then
50+
git add manifests/odh/base/commit-latest.env manifests/rhoai/base/commit-latest.env
4351
git commit -m "ci: update commit SHAs for image digests changes"
4452
git push
4553
else
46-
echo "No effective changes in manifests/base/commit-latest.env, skipping commit."
54+
echo "No effective changes in commit-latest.env files, skipping commit."
4755
fi
Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
odh-pipeline-runtime-datascience-cpu-py312-ubi9-commit-n=3184b80
2-
odh-pipeline-runtime-minimal-cpu-py312-ubi9-commit-n=f404c27
3-
odh-pipeline-runtime-pytorch-cuda-py312-ubi9-commit-n=3184b80
4-
odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=f404c27
5-
odh-pipeline-runtime-pytorch-rocm-py312-ubi9-commit-n=3184b80
6-
odh-pipeline-runtime-tensorflow-cuda-py312-ubi9-commit-n=3184b80
7-
odh-pipeline-runtime-tensorflow-rocm-py312-ubi9-commit-n=3184b80
8-
odh-workbench-codeserver-datascience-cpu-py312-ubi9-commit-n=d1196ba
9-
odh-workbench-jupyter-datascience-cpu-py312-ubi9-commit-n=3184b80
10-
odh-workbench-jupyter-minimal-cpu-py312-ubi9-commit-n=38b370d
11-
odh-workbench-jupyter-minimal-cuda-py312-ubi9-commit-n=38b370d
12-
odh-workbench-jupyter-minimal-rocm-py312-ubi9-commit-n=38b370d
13-
odh-workbench-jupyter-pytorch-cuda-py312-ubi9-commit-n=3184b80
14-
odh-workbench-jupyter-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=f404c27
15-
odh-workbench-jupyter-pytorch-rocm-py312-ubi9-commit-n=3184b80
16-
odh-workbench-jupyter-tensorflow-cuda-py312-ubi9-commit-n=3184b80
17-
odh-workbench-jupyter-tensorflow-rocm-py312-ubi9-commit-n=3184b80
18-
odh-workbench-jupyter-trustyai-cpu-py312-ubi9-commit-n=3184b80
19-
odh-workbench-rstudio-minimal-cpu-py312-c9s-commit-n=f404c27
20-
odh-workbench-rstudio-minimal-cuda-py312-c9s-commit-n=f404c27
1+
odh-pipeline-runtime-datascience-cpu-py312-ubi9-commit-n=7f2342d
2+
odh-pipeline-runtime-minimal-cpu-py312-ubi9-commit-n=7f2342d
3+
odh-pipeline-runtime-pytorch-cuda-py312-ubi9-commit-n=7f2342d
4+
odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=7f2342d
5+
odh-pipeline-runtime-pytorch-rocm-py312-ubi9-commit-n=335acea
6+
odh-pipeline-runtime-tensorflow-cuda-py312-ubi9-commit-n=7f2342d
7+
odh-pipeline-runtime-tensorflow-rocm-py312-ubi9-commit-n=7f2342d
8+
odh-workbench-codeserver-datascience-cpu-py312-ubi9-commit-n=c2e4c8a
9+
odh-workbench-jupyter-datascience-cpu-py312-ubi9-commit-n=53f3326
10+
odh-workbench-jupyter-minimal-cpu-py312-ubi9-commit-n=ca892c2
11+
odh-workbench-jupyter-minimal-cuda-py312-ubi9-commit-n=db82a7c
12+
odh-workbench-jupyter-minimal-rocm-py312-ubi9-commit-n=db82a7c
13+
odh-workbench-jupyter-pytorch-cuda-py312-ubi9-commit-n=7f2342d
14+
odh-workbench-jupyter-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=ca892c2
15+
odh-workbench-jupyter-pytorch-rocm-py312-ubi9-commit-n=7f2342d
16+
odh-workbench-jupyter-tensorflow-cuda-py312-ubi9-commit-n=7f2342d
17+
odh-workbench-jupyter-tensorflow-rocm-py312-ubi9-commit-n=7f2342d
18+
odh-workbench-jupyter-trustyai-cpu-py312-ubi9-commit-n=7f2342d
19+
odh-workbench-rstudio-minimal-cpu-py312-c9s-commit-n=53f3326
20+
odh-workbench-rstudio-minimal-cuda-py312-c9s-commit-n=ca892c2
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
odh-workbench-jupyter-minimal-cpu-py312-ubi9-commit-n=d3137ca
2-
odh-workbench-jupyter-minimal-cuda-py312-ubi9-commit-n=d3137ca
3-
odh-workbench-jupyter-minimal-rocm-py312-ubi9-commit-n=bff12e2
4-
odh-workbench-jupyter-datascience-cpu-py312-ubi9-commit-n=d3137ca
5-
odh-workbench-jupyter-pytorch-cuda-py312-ubi9-commit-n=8e73cac
6-
odh-workbench-jupyter-pytorch-rocm-py312-ubi9-commit-n=d3137ca
7-
odh-workbench-jupyter-tensorflow-cuda-py312-ubi9-commit-n=8e73cac
8-
odh-workbench-jupyter-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=8e73cac
9-
odh-workbench-jupyter-trustyai-cpu-py312-ubi9-commit-n=06da715
10-
odh-workbench-codeserver-datascience-cpu-py312-ubi9-commit-n=06703a3
11-
odh-pipeline-runtime-minimal-cpu-py312-ubi9-commit-n=06703a3
12-
odh-pipeline-runtime-datascience-cpu-py312-ubi9-commit-n=8a0af91
13-
odh-pipeline-runtime-pytorch-cuda-py312-ubi9-commit-n=8e73cac
14-
odh-pipeline-runtime-pytorch-rocm-py312-ubi9-commit-n=8e73cac
15-
odh-pipeline-runtime-tensorflow-cuda-py312-ubi9-commit-n=8e73cac
16-
odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=8e73cac
17-
odh-workbench-jupyter-tensorflow-rocm-py312-ubi9-commit-n=8e73cac
18-
odh-pipeline-runtime-tensorflow-rocm-py312-ubi9-commit-n=93c0810
1+
odh-pipeline-runtime-datascience-cpu-py312-ubi9-commit-n=7f2342d
2+
odh-pipeline-runtime-minimal-cpu-py312-ubi9-commit-n=7f2342d
3+
odh-pipeline-runtime-pytorch-cuda-py312-ubi9-commit-n=7f2342d
4+
odh-pipeline-runtime-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=7f2342d
5+
odh-pipeline-runtime-pytorch-rocm-py312-ubi9-commit-n=335acea
6+
odh-pipeline-runtime-tensorflow-cuda-py312-ubi9-commit-n=7f2342d
7+
odh-pipeline-runtime-tensorflow-rocm-py312-ubi9-commit-n=7f2342d
8+
odh-workbench-codeserver-datascience-cpu-py312-ubi9-commit-n=c2e4c8a
9+
odh-workbench-jupyter-datascience-cpu-py312-ubi9-commit-n=53f3326
10+
odh-workbench-jupyter-minimal-cpu-py312-ubi9-commit-n=ca892c2
11+
odh-workbench-jupyter-minimal-cuda-py312-ubi9-commit-n=db82a7c
12+
odh-workbench-jupyter-minimal-rocm-py312-ubi9-commit-n=db82a7c
13+
odh-workbench-jupyter-pytorch-cuda-py312-ubi9-commit-n=7f2342d
14+
odh-workbench-jupyter-pytorch-llmcompressor-cuda-py312-ubi9-commit-n=ca892c2
15+
odh-workbench-jupyter-pytorch-rocm-py312-ubi9-commit-n=7f2342d
16+
odh-workbench-jupyter-tensorflow-cuda-py312-ubi9-commit-n=7f2342d
17+
odh-workbench-jupyter-tensorflow-rocm-py312-ubi9-commit-n=7f2342d
18+
odh-workbench-jupyter-trustyai-cpu-py312-ubi9-commit-n=7f2342d
Lines changed: 130 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
from __future__ import annotations
33

4+
import argparse
45
import asyncio
56
import json
67
import pathlib
@@ -13,9 +14,95 @@
1314
from ci.logging_config import configure_logging
1415

1516
PROJECT_ROOT = pathlib.Path(__file__).parent.parent
17+
ODH_MANIFEST_DIR = PROJECT_ROOT / "manifests" / "odh" / "base"
18+
RHOAI_MANIFEST_DIR = PROJECT_ROOT / "manifests" / "rhoai" / "base"
1619

1720
log = structlog.get_logger()
1821

22+
DUMMY_IMAGE = "dummy"
23+
24+
25+
def parse_args() -> argparse.Namespace:
26+
parser = argparse.ArgumentParser(
27+
description="Fill commit-latest.env with short vcs-ref values from image labels for params-latest.env."
28+
)
29+
parser.add_argument(
30+
"--odh-dir",
31+
type=pathlib.Path,
32+
default=ODH_MANIFEST_DIR,
33+
help=(
34+
"ODH directory containing params-latest.env (used as URL source of truth when "
35+
f"RHOAI params use '{DUMMY_IMAGE}') (default: manifests/odh/base under repo root)"
36+
),
37+
)
38+
parser.add_argument(
39+
"--rhoai-dir",
40+
type=pathlib.Path,
41+
default=RHOAI_MANIFEST_DIR,
42+
help="RHOAI manifest directory (default: manifests/rhoai/base under repo root).",
43+
)
44+
parser.add_argument(
45+
"--only",
46+
choices=("both", "odh", "rhoai"),
47+
default="both",
48+
help="Which commit-latest.env file(s) to write (default: both).",
49+
)
50+
return parser.parse_args()
51+
52+
53+
def resolve_manifest_dir(raw: pathlib.Path) -> pathlib.Path:
54+
return raw if raw.is_absolute() else (PROJECT_ROOT / raw).resolve()
55+
56+
57+
def load_params_env(path: pathlib.Path) -> list[tuple[str, str]]:
58+
with open(path, "rt") as file:
59+
return [
60+
(parts[0].strip(), parts[1].strip())
61+
for line in file
62+
if line.strip() and not line.strip().startswith("#")
63+
for parts in [line.strip().split("=", 1)]
64+
if len(parts) == 2
65+
]
66+
67+
68+
def resolve_rhoai_urls(
69+
rhoai_pairs: list[tuple[str, str]],
70+
odh_by_var: dict[str, str],
71+
) -> list[tuple[str, str]]:
72+
"""Replace RHOAI dummy placeholders with the same variable's image URL from ODH params."""
73+
out: list[tuple[str, str]] = []
74+
for var, raw in rhoai_pairs:
75+
if raw.strip().lower() == DUMMY_IMAGE:
76+
if var not in odh_by_var:
77+
log.error("No ODH params-latest entry to resolve RHOAI dummy image", variable=var)
78+
sys.exit(1)
79+
url = odh_by_var[var]
80+
if url.strip().lower() == DUMMY_IMAGE:
81+
log.error("ODH params-latest still has dummy for variable", variable=var)
82+
sys.exit(1)
83+
out.append((var, url))
84+
else:
85+
out.append((var, raw))
86+
return out
87+
88+
89+
def write_commit_latest(
90+
var_url_pairs: list[tuple[str, str]],
91+
vcs_by_url: dict[str, str | None],
92+
commit_latest_path: pathlib.Path,
93+
) -> None:
94+
lines: list[tuple[str, str]] = []
95+
for var, url in var_url_pairs:
96+
vcs = vcs_by_url[url]
97+
if vcs is None:
98+
log.error("Missing vcs-ref for image URL", url=url, variable=var)
99+
sys.exit(1)
100+
lines.append((re.sub(r"-n$", "-commit-n", var), vcs[:7]))
101+
102+
with open(commit_latest_path, "wt") as file:
103+
for key, short in sorted(lines):
104+
print(key, short, file=file, sep="=")
105+
19106

20107
async def get_image_vcs_ref(image_url: str, semaphore: asyncio.Semaphore) -> tuple[str, str | None]:
21108
"""
@@ -30,10 +117,8 @@ async def get_image_vcs_ref(image_url: str, semaphore: asyncio.Semaphore) -> tup
30117
A tuple containing the original image_url and the value of the 'vcs-ref'
31118
label if found, otherwise None.
32119
"""
33-
# Using 'docker://' prefix is required for skopeo to identify the transport.
34120
full_image_url = f"docker://{image_url}"
35121

36-
# Use 'inspect --config' which is much faster as it only fetches the config blob.
37122
command = ["skopeo", "inspect", "--override-os=linux", "--override-arch=amd64", "--retry-times=5", "--config", full_image_url]
38123

39124
log.info(f"Starting config inspection for: {image_url}")
@@ -42,17 +127,14 @@ async def get_image_vcs_ref(image_url: str, semaphore: asyncio.Semaphore) -> tup
42127
try:
43128
async with semaphore:
44129
log.info(f"Semaphore acquired, starting skopeo inspect for: {image_url}")
45-
# Create an asynchronous subprocess
46130
process = await asyncio.create_subprocess_exec(
47131
*command,
48132
stdout=asyncio.subprocess.PIPE,
49-
stderr=asyncio.subprocess.PIPE
133+
stderr=asyncio.subprocess.PIPE,
50134
)
51-
# Wait for the command to complete and capture output
52135
stdout, stderr = await process.communicate()
53136
returncode = process.returncode
54137

55-
# Process the results outside the semaphore block
56138
if returncode != 0:
57139
log.error(f"Skopeo command failed for {image_url} with exit code {returncode}.")
58140
if stderr:
@@ -63,11 +145,8 @@ async def get_image_vcs_ref(image_url: str, semaphore: asyncio.Semaphore) -> tup
63145
log.error(f"Skopeo command returned success but stdout was empty for {image_url}.")
64146
return image_url, None
65147

66-
# Decode and parse the JSON output from stdout
67-
# The output of 'inspect --config' is the image config JSON directly.
68148
image_config = json.loads(stdout.decode())
69149

70-
# Safely extract the 'vcs-ref' label from the config's 'Labels'
71150
vcs_ref = image_config.get("config", {}).get("Labels", {}).get("vcs-ref")
72151

73152
if vcs_ref:
@@ -81,46 +160,61 @@ async def get_image_vcs_ref(image_url: str, semaphore: asyncio.Semaphore) -> tup
81160
log.error("The 'skopeo' command was not found. Please ensure it is installed and in your PATH.")
82161
return image_url, None
83162
except json.JSONDecodeError:
84-
# This error can now also happen if stdout is None or not valid JSON
85163
log.error(f"Failed to parse skopeo output as JSON for {image_url}.")
86164
if stdout:
87165
log.debug(f"Stdout from skopeo for {image_url}: {stdout.decode(errors='replace')}")
88166
return image_url, None
89-
except Exception as e:
90-
log.error("Unexpected error while processing image", image_url=image_url, exc_info=True)
167+
except Exception:
168+
log.exception("Unexpected error while processing image", image_url=image_url)
91169
return image_url, None
92170

93171

94-
async def inspect(images_to_inspect: typing.Iterable[str]) -> list[tuple[str, str | None]]:
95-
"""
96-
Main function to orchestrate the concurrent inspection of multiple images.
97-
"""
98-
semaphore = asyncio.Semaphore(22) # Limit concurrent skopeo processes
99-
tasks = [get_image_vcs_ref(image, semaphore) for image in images_to_inspect]
100-
return await asyncio.gather(*tasks)
172+
async def inspect_urls(urls: typing.Iterable[str]) -> dict[str, str | None]:
173+
unique = list(dict.fromkeys(urls))
174+
semaphore = asyncio.Semaphore(22)
175+
tasks = [get_image_vcs_ref(url, semaphore) for url in unique]
176+
results = await asyncio.gather(*tasks)
177+
out = dict(results)
178+
if any(v is None for v in out.values()):
179+
log.error("Failed to get commit hash for one or more images; aborting.")
180+
sys.exit(1)
181+
return out
101182

102183

103-
async def main():
104-
with open(PROJECT_ROOT / "manifests/base/params-latest.env", "rt") as file:
105-
images_to_inspect: list[list[str]] = [line.strip().split('=', 1) for line in file.readlines()
106-
if line.strip() and not line.strip().startswith("#")]
184+
async def main() -> None:
185+
args = parse_args()
186+
odh_dir = resolve_manifest_dir(args.odh_dir)
187+
rhoai_dir = resolve_manifest_dir(args.rhoai_dir)
107188

108-
results = await inspect(value for _, value in images_to_inspect)
109-
if any(commit_hash is None for variable, commit_hash in results):
110-
log.error("Failed to get commit hash for some images. Quitting, please try again to try again, like.")
111-
sys.exit(1)
189+
odh_params = odh_dir / "params-latest.env"
190+
rhoai_params = rhoai_dir / "params-latest.env"
191+
192+
odh_by_var = dict(load_params_env(odh_params))
193+
194+
targets: list[tuple[pathlib.Path, list[tuple[str, str]]]] = []
195+
196+
if args.only in ("both", "odh"):
197+
odh_pairs = load_params_env(odh_params)
198+
if any(v.strip().lower() == DUMMY_IMAGE for _, v in odh_pairs):
199+
log.error("ODH params-latest.env must not use dummy image placeholders.")
200+
raise SystemExit(1)
201+
targets.append((odh_dir / "commit-latest.env", odh_pairs))
202+
203+
if args.only in ("both", "rhoai"):
204+
rhoai_pairs = load_params_env(rhoai_params)
205+
rhoai_resolved = resolve_rhoai_urls(rhoai_pairs, odh_by_var)
206+
targets.append((rhoai_dir / "commit-latest.env", rhoai_resolved))
207+
208+
all_urls: list[str] = []
209+
for _, pairs in targets:
210+
all_urls.extend(url for _, url in pairs)
112211

113-
output = []
114-
for image, result in zip(images_to_inspect, results, strict=True):
115-
variable, image_digest = image
116-
_, commit_hash = result
117-
output.append((re.sub(r'-n$', "-commit-n", variable), commit_hash[:7]))
212+
vcs_by_url = await inspect_urls(all_urls)
118213

119-
with open(PROJECT_ROOT / "manifests/base/commit-latest.env", "wt") as file:
120-
for line in sorted(output):
121-
print(*line, file=file, sep="=", end="\n")
214+
for commit_path, pairs in targets:
215+
write_commit_latest(pairs, vcs_by_url, commit_path)
122216

123217

124-
if __name__ == '__main__':
218+
if __name__ == "__main__":
125219
configure_logging()
126220
asyncio.run(main())

0 commit comments

Comments
 (0)