Skip to content

Commit 8a8bda5

Browse files
committed
RHOAIENG-26264: fix automatic updates of commit-latest.env references
1 parent e98a63e commit 8a8bda5

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Update commit-latest.env on params-latest.env change
2+
3+
"on":
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
- rhoai-*
9+
paths:
10+
- 'manifests/base/params-latest.env'
11+
12+
jobs:
13+
sync-commit:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: write
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
with:
21+
token: ${{ secrets.GITHUB_TOKEN }}
22+
23+
- name: Update manifests/base/commit-latest.env
24+
id: update_env
25+
run: |
26+
python3 scripts/update-commit-latest-env.py
27+
28+
- name: Commit and push changes
29+
run: |
30+
git config --global user.name 'github-actions[bot]'
31+
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
32+
33+
# Check for changes before committing
34+
if ! git diff --quiet manifests/base/commit-latest.env; then
35+
git add manifests/base/commit-latest.env
36+
git commit -m "ci: update commit SHAs for image digests changes"
37+
git push
38+
else
39+
echo "No effective changes in manifests/base/commit-latest.env, skipping commit."
40+
fi

scripts/update-commit-latest-env.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import asyncio
2+
import json
3+
import logging
4+
import pathlib
5+
import re
6+
import typing
7+
8+
PROJECT_ROOT = pathlib.Path(__file__).parent.parent
9+
10+
11+
async def get_image_vcs_ref(image_url: str) -> tuple[str, str | None]:
12+
"""
13+
Asynchronously inspects a container image's configuration using skopeo
14+
and extracts the 'vcs-ref' label.
15+
16+
Args:
17+
image_url: The full URL of the image to inspect
18+
(e.g., 'quay.io/opendatahub/workbench-images@sha256:...').
19+
20+
Returns:
21+
A tuple containing the original image_url and the value of the 'vcs-ref'
22+
label if found, otherwise None.
23+
"""
24+
# Using 'docker://' prefix is required for skopeo to identify the transport.
25+
full_image_url = f"docker://{image_url}"
26+
27+
# Use 'inspect --config' which is much faster as it only fetches the config blob.
28+
command = ["skopeo", "inspect", "--config", full_image_url]
29+
30+
logging.info(f"Starting config inspection for: {image_url}")
31+
32+
try:
33+
# Create an asynchronous subprocess
34+
process = await asyncio.create_subprocess_exec(
35+
*command,
36+
stdout=asyncio.subprocess.PIPE,
37+
stderr=asyncio.subprocess.PIPE
38+
)
39+
40+
# Wait for the command to complete and capture output
41+
stdout, stderr = await process.communicate()
42+
43+
# Check for errors
44+
if process.returncode != 0:
45+
logging.error(f"Skopeo command failed for {image_url} with exit code {process.returncode}.")
46+
logging.error(f"Stderr: {stderr.decode().strip()}")
47+
return image_url, None
48+
49+
# Decode and parse the JSON output from stdout
50+
# The output of 'inspect --config' is the image config JSON directly.
51+
image_config = json.loads(stdout.decode())
52+
53+
# Safely extract the 'vcs-ref' label from the config's 'Labels'
54+
vcs_ref = image_config.get("config", {}).get("Labels", {}).get("vcs-ref")
55+
56+
if vcs_ref:
57+
logging.info(f"Successfully found 'vcs-ref' for {image_url}: {vcs_ref}")
58+
else:
59+
logging.warning(f"'vcs-ref' label not found for {image_url}.")
60+
61+
return image_url, vcs_ref
62+
63+
except FileNotFoundError:
64+
logging.error("The 'skopeo' command was not found. Please ensure it is installed and in your PATH.")
65+
return image_url, None
66+
except json.JSONDecodeError:
67+
logging.error(f"Failed to parse skopeo output as JSON for {image_url}.")
68+
return image_url, None
69+
except Exception as e:
70+
logging.error(f"An unexpected error occurred while processing {image_url}: {e}")
71+
return image_url, None
72+
73+
74+
async def inspect(images_to_inspect: typing.Iterable[str]) -> list[tuple[str, str | None]]:
75+
""
76+
"""
77+
Main function to orchestrate the concurrent inspection of multiple images.
78+
"""
79+
tasks = [get_image_vcs_ref(image) for image in images_to_inspect]
80+
return await asyncio.gather(*tasks)
81+
82+
83+
async def main():
84+
with open(PROJECT_ROOT / "manifests/base/params-latest.env", "rt") as file:
85+
images_to_inspect: list[list[str]] = [line.strip().split('=', 1) for line in file.readlines()
86+
if line.strip() and not line.strip().startswith("#")]
87+
88+
results = await inspect(value for _, value in images_to_inspect)
89+
output = []
90+
for image, result in zip(images_to_inspect, results):
91+
variable, image_digest = image
92+
_, commit_hash = result
93+
output.append((re.sub(r'-n$', "-commit-n", variable), commit_hash[:7]))
94+
95+
with open(PROJECT_ROOT / "manifests/base/commit-latest.env", "wt") as file:
96+
for line in output:
97+
print(*line, file=file, sep="=", end="\n")
98+
99+
100+
if __name__ == '__main__':
101+
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
102+
asyncio.run(main())

0 commit comments

Comments
 (0)