From 3cf6509bd2adf7fad6ad588a5394e897c97855e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:03:36 +0200 Subject: [PATCH 01/22] test: add Schutzfile and update-schutzfile-images script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Schutzfile pins the images library commit that the boot test infrastructure uses. The update script fetches the latest commit from GitHub and updates the ref, mirroring the pattern from the images library's update-schutzfile-osbuild. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- .gitignore | 1 + Schutzfile | 9 +++++ test/update-schutzfile-images | 63 +++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 Schutzfile create mode 100755 test/update-schutzfile-images diff --git a/.gitignore b/.gitignore index b829135..a6b4c3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /*.local.sh /*.local.yaml +github_pr_body.txt diff --git a/Schutzfile b/Schutzfile new file mode 100644 index 0000000..ad55097 --- /dev/null +++ b/Schutzfile @@ -0,0 +1,9 @@ +{ + "common": { + "dependencies": { + "images": { + "ref": "86fa680b70efb2c1d6360abbc021a097fb5e3da8" + } + } + } +} diff --git a/test/update-schutzfile-images b/test/update-schutzfile-images new file mode 100755 index 0000000..2e9f932 --- /dev/null +++ b/test/update-schutzfile-images @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +import json +import os +import sys +import urllib.request + +IMAGES_REPO = "osbuild/images" +SCHUTZFILE = "Schutzfile" + + +def images_main_commit_id(): + token = os.environ.get("GITHUB_TOKEN") + req = urllib.request.Request( + f"https://api.github.com/repos/{IMAGES_REPO}/commits/main" + ) + req.add_header("Accept", "application/vnd.github+json") + if token: + req.add_header("Authorization", f"Bearer {token}") + try: + with urllib.request.urlopen(req, timeout=30) as resp: + body = resp.read() + except urllib.error.HTTPError as http_error: + print(http_error) + sys.exit(1) + + data = json.loads(body) + return data["sha"] + + +def update_images_ref(new_ref): + with open(SCHUTZFILE, encoding="utf-8") as f: + data = json.load(f) + + old_ref = data.get("common", {}).get("dependencies", {}).get("images", {}).get("ref") + if old_ref == new_ref: + return False + + data["common"]["dependencies"]["images"]["ref"] = new_ref + + with open(SCHUTZFILE, encoding="utf-8", mode="w") as f: + json.dump(data, f, indent=" ") + f.write("\n") + + with open("github_pr_body.txt", encoding="utf-8", mode="w") as pr_body: + pr_body.write("Updating images library ref to current `main`\n\n") + pr_body.write( + f"Changes: https://github.com/{IMAGES_REPO}/compare/{old_ref}...{new_ref}\n" + ) + + return True + + +def main(): + commit_id = images_main_commit_id() + print(f"{IMAGES_REPO} main commit ID: {commit_id}") + if update_images_ref(commit_id): + print("Schutzfile updated") + else: + print("Already up to date") + + +if __name__ == "__main__": + main() From e9d3ed2a9d693c24ef007fce4c7d3e20a1385057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:11:54 +0200 Subject: [PATCH 02/22] test: add setup.sh for CI environment preparation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clone the images library at the Schutzfile-pinned ref, then delegate to its setup-osbuild-repo (which auto-detects the host distro and reads the per-distro osbuild commit from the images library's own Schutzfile) and install-dependencies scripts. The images repo URL is overridable via IMAGES_REPO_URL for cross-testing with draft PRs in the images library. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- .gitignore | 1 + test/setup.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100755 test/setup.sh diff --git a/.gitignore b/.gitignore index a6b4c3e..bd4790a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /*.local.sh /*.local.yaml github_pr_body.txt +_images/ diff --git a/test/setup.sh b/test/setup.sh new file mode 100755 index 0000000..cf5eff7 --- /dev/null +++ b/test/setup.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +IMAGES_DIR="${REPO_ROOT}/_images" + +IMAGES_REPO_URL="${IMAGES_REPO_URL:-https://github.com/osbuild/images.git}" + +# Read the images library ref from Schutzfile +IMAGES_REF=$(jq -r '.common.dependencies.images.ref' "${REPO_ROOT}/Schutzfile") + +echo "Images library ref: ${IMAGES_REF}" +echo "Images repo URL: ${IMAGES_REPO_URL}" + +# Clone the images library at the pinned ref +rm -rf "${IMAGES_DIR}" +echo "Cloning images library at ${IMAGES_REF}" +git clone "${IMAGES_REPO_URL}" "${IMAGES_DIR}" +git -C "${IMAGES_DIR}" checkout "${IMAGES_REF}" + +# Set up the osbuild RPM repository. The images library's setup-osbuild-repo +# auto-detects the host distro from /etc/os-release and reads the matching +# per-distro or common osbuild commit from its own Schutzfile. +sudo "${IMAGES_DIR}/test/scripts/setup-osbuild-repo" + +# Install all dependencies using the images library's install script +sudo "${IMAGES_DIR}/test/scripts/install-dependencies" From 6bfd38b131855ea9b4555dad4950df8b320a9b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:23:03 +0200 Subject: [PATCH 03/22] test: add get-container.sh to build container images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build a container from a Containerfile into local podman storage at quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/${CONTAINERFILE}:latest, which is consistent with the location for prod container images. Using the same tag is important to make the installer images work out of the box. This script is isolated from build.sh so the container source can be swapped to a registry pull without changing the build logic. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- test/get-container.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100755 test/get-container.sh diff --git a/test/get-container.sh b/test/get-container.sh new file mode 100755 index 0000000..566fb1b --- /dev/null +++ b/test/get-container.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -euo pipefail + +CONTAINERFILE="${1:?Usage: get-container.sh }" + +IMAGE_NAME="quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/${CONTAINERFILE}:latest" + +echo "Building container image: ${IMAGE_NAME}" >&2 +echo "Containerfile: ${CONTAINERFILE}" >&2 + +sudo podman build -t "${IMAGE_NAME}" -f "${CONTAINERFILE}" . >&2 + +echo "Container image built: ${IMAGE_NAME}" >&2 + +# Print the container ref for downstream scripts to capture +echo "${IMAGE_NAME}" From 0b5b73fe860f0dc42022fa7dd23d6ad178d53c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:25:13 +0200 Subject: [PATCH 04/22] test: add build.sh to create disk images from containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build a disk image using the images library's cmd/build tool. Create the build config JSON (with optional installer payload ref), invoke cmd/build with the bootc container ref, and write info.json into the output directory for boot-image to consume. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- .gitignore | 1 + test/build.sh | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100755 test/build.sh diff --git a/.gitignore b/.gitignore index bd4790a..135995b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /*.local.yaml github_pr_body.txt _images/ +build/ diff --git a/test/build.sh b/test/build.sh new file mode 100755 index 0000000..2197a54 --- /dev/null +++ b/test/build.sh @@ -0,0 +1,101 @@ +#!/bin/bash +set -euo pipefail + +CONTAINER_IMAGE="${1:?Usage: build.sh [payload-container-image]}" +IMAGE_TYPE="${2:?Missing image-type argument}" +ARCH="${3:?Missing arch argument}" +DISTRO_ID="${4:?Missing distro-id argument}" +PAYLOAD_CONTAINER_IMAGE="${5:-}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +IMAGES_DIR="${REPO_ROOT}/_images" +BUILD_DIR="${REPO_ROOT}/build" +CONFIG_FILE="${BUILD_DIR}/config.json" + +mkdir -p "${BUILD_DIR}" + +# Build the config JSON. Start with the base, optionally inject the payload ref. +cat > "${CONFIG_FILE}" < "${CONFIG_FILE}" +fi + +echo "Build config:" +cat "${CONFIG_FILE}" + +echo "" +echo "Building disk image:" +echo " Container: ${CONTAINER_IMAGE}" +echo " Image type: ${IMAGE_TYPE}" +echo " Architecture: ${ARCH}" +echo " Distro ID: ${DISTRO_ID}" +if [ -n "${PAYLOAD_CONTAINER_IMAGE}" ]; then + echo " Payload container: ${PAYLOAD_CONTAINER_IMAGE}" +fi + +# Build the disk image using the images library's cmd/build. +# Run from the images directory so Go can find the module (go.mod). +# sudo is required because cmd/build uses 'podman mount' which needs root. +(cd "${IMAGES_DIR}" && sudo go run ./cmd/build \ + --bootc-ref "${CONTAINER_IMAGE}" \ + --arch "${ARCH}" \ + --type "${IMAGE_TYPE}" \ + --config "${CONFIG_FILE}" \ + --output "${BUILD_DIR}") + +# sudo go run produces root-owned output; reclaim ownership so the rest of the +# pipeline (and boot.sh) can access the artifacts without sudo. +sudo chown -R "$(id -u):$(id -g)" "${BUILD_DIR}" + +# Discover the build output directory (single subdirectory under build/) +BUILD_OUTPUT_DIR=$(find "${BUILD_DIR}" -mindepth 1 -maxdepth 1 -type d ! -name ".*" | head -1) + +if [ -z "${BUILD_OUTPUT_DIR}" ]; then + echo "ERROR: No build output directory found under ${BUILD_DIR}/" + exit 1 +fi + +echo "Build output directory: ${BUILD_OUTPUT_DIR}" + +# Try to extract the embedded kickstart from the container image. Installer +# images ship one at /usr/share/anaconda/interactive-defaults.ks with +# the payload directive. Non-installer images won't have this file, so failures +# are silently ignored. +KS_FILENAME="embedded.ks" +# shellcheck disable=SC2024 +sudo podman run --rm "${CONTAINER_IMAGE}" \ + cat /usr/share/anaconda/interactive-defaults.ks \ + > "${BUILD_OUTPUT_DIR}/${KS_FILENAME}" 2>/dev/null || true + +# Write info.json for boot-image. +cat > "${BUILD_OUTPUT_DIR}/info.json" < "${BUILD_OUTPUT_DIR}/info.json" +fi + +# Copy the build config into the output directory for boot.sh +cp "${CONFIG_FILE}" "${BUILD_OUTPUT_DIR}/config.json" + +echo "Build complete: ${BUILD_OUTPUT_DIR}" From 5d177598031e7a6d41d7084add6f4dcdfdb47331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:26:02 +0200 Subject: [PATCH 05/22] test: add boot.sh to boot and validate disk images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discover the build output directory and config, then delegate to the images library's boot-image script. boot-image reads info.json to determine the boot method (QEMU, ISO, cloud) and runs check-host-config for post-boot validation. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- test/boot.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100755 test/boot.sh diff --git a/test/boot.sh b/test/boot.sh new file mode 100755 index 0000000..f1efd4a --- /dev/null +++ b/test/boot.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +IMAGES_DIR="${REPO_ROOT}/_images" +BUILD_DIR="${REPO_ROOT}/build" + +# Discover the build output subdirectory +BUILD_OUTPUT_DIR=$(find "${BUILD_DIR}" -mindepth 1 -maxdepth 1 -type d ! -name ".*" | head -1) + +if [ -z "${BUILD_OUTPUT_DIR}" ]; then + echo "ERROR: No build output directory found under ${BUILD_DIR}" + exit 1 +fi + +# Find the build config in the output directory +CONFIG_FILE="${BUILD_OUTPUT_DIR}/config.json" +if [ ! -f "${CONFIG_FILE}" ]; then + echo "ERROR: Build config not found at ${CONFIG_FILE}" + exit 1 +fi + +echo "Booting image from: ${BUILD_OUTPUT_DIR}" +echo "Build config: ${CONFIG_FILE}" + +# Boot the image and run validation checks. +# Run from the images directory so Go can find the module (go.mod) when +# boot-image builds cmd/check-host-config. +(cd "${IMAGES_DIR}" && ./test/scripts/boot-image "${BUILD_OUTPUT_DIR}" "${CONFIG_FILE}") From b053e54d7da1d9d1aaee9a0eb71223052be23145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 19:50:15 +0200 Subject: [PATCH 06/22] schutzbot: add scripts and files needed for CI setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add scripts that update GitHub PR with the GitLab CI pipeline status and unregister a RHEL host. Also add the file with terraform ref and also team SSH keys, which get configured as trusted on the CI runner. These files were taken from the osbuild/images and osbuild/osbuild-composer repositories and amended for the use in this repo. Signed-off-by: Tomáš Hozza --- schutzbot/team_ssh_keys.txt | 11 ++++++++ schutzbot/terraform | 1 + schutzbot/unregister.sh | 22 +++++++++++++++ schutzbot/update_github_status.sh | 45 +++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 schutzbot/team_ssh_keys.txt create mode 100644 schutzbot/terraform create mode 100755 schutzbot/unregister.sh create mode 100755 schutzbot/update_github_status.sh diff --git a/schutzbot/team_ssh_keys.txt b/schutzbot/team_ssh_keys.txt new file mode 100644 index 0000000..70b6ba0 --- /dev/null +++ b/schutzbot/team_ssh_keys.txt @@ -0,0 +1,11 @@ +# SSH keys from members of the osbuild team that are used in CI. +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPB1jFl4p6FTBixHT6wOk6X8nj/Z7eoPNQE/M0wK485K obudai@redhat.com +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAw6IgsAlMJQlOtJXvtlY1racZPntLiy4+iDwrPMCgbYbsylY5TI2S4JCzC3OsnOF/abozKOhTrX04KOSOPkG8iZjBEUsMX4rQXtdViyec8pAdKOimzN9tdlfC2joW8jPlr/wpKMnMRCQmNDUZIOl1ujyTeY592JE8sj9TTqyc+fk= bcl@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAYU2wzSk9r1l3iOwsvaJXCsfQIUga3xzShZJAM1zHv akoutsou-R@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM+4pso8s0M0hKFW6XoEvM6loZp0C7D9ZlmwXQbhxyV0 akoutsou-i@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINPExjjH74MOM6wrXEpRUg6I0dtRdAV3bAUY+u7WMc2G sanne@redhat.com +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDjNynFZPCEPVDyOB2yzrww5kxwK6MAb1D0GN5yP8y/iw+gtx+Hj3CqojHMTa/9r3q3R1TMgCITdvzAiKylbx/owV8bgXS1p8je2KirWx3o/Dy80AYsas2F+sodm5/FOz6LvcUZw2vZiVs1wp8dz7ak+pm6Xg7xa7511xO4T/HStzNUE/XSPYmC9LNJ+uVWTiCjTWlZxp1JcDVfO7k69F60u8D42e1Ty60IeNeJItX/o8FUjB/rMAAJRpjFpd/uyfPTWamjNoVzrB7chFxaemg2Nf8na6PHLAx8Gcxz2fdnnsg+M5vr6z0yVYz1cc8VOhYynQm9iISvTt6bDVEbWc2T thozza@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINDRWitNwQc/YsOSC7Reeh7x57mSzcc+4+SayHHu/NCG sdevlieg-0@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKNh/u8oWHfYwr01X8G8ijSC3hPfKfLpK8MISxg2mq1O sdevlieg-1@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBCWAwAqV3weCALKWrSAAHir+oIga1TU5VL4hnjWWU2x gzuccare@redhat.com +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEhnn80ZywmjeBFFOGm+cm+5HUwm62qTVnjKlOdYFLHN lzap@redhat.com diff --git a/schutzbot/terraform b/schutzbot/terraform new file mode 100644 index 0000000..8796f25 --- /dev/null +++ b/schutzbot/terraform @@ -0,0 +1 @@ +3545c5e293c8677f8f48b9a1e4cf6131bf288c24 diff --git a/schutzbot/unregister.sh b/schutzbot/unregister.sh new file mode 100755 index 0000000..c60a1f9 --- /dev/null +++ b/schutzbot/unregister.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Colorful output. +function greenprint { + echo -e "\033[1;32m[$(date -Isecond)] ${1}\033[0m" +} +function redprint { + echo -e "\033[1;31m[$(date -Isecond)] ${1}\033[0m" +} + +if ! hash subscription-manager; then + exit 0 +fi +if ! sudo subscription-manager status; then + exit 0 +fi +if sudo subscription-manager unregister; then + greenprint "Host unregistered." + exit 0 +fi +redprint "Failed to unregister" +exit 1 diff --git a/schutzbot/update_github_status.sh b/schutzbot/update_github_status.sh new file mode 100755 index 0000000..7b4fc41 --- /dev/null +++ b/schutzbot/update_github_status.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# if a user is logged in to the runner, wait until they're done +while (( $(who -s | wc -l) > 0 )); do + echo "Waiting for user(s) to log off" + sleep 30 +done + +if [[ $1 == "start" ]]; then + GITHUB_NEW_STATE="pending" + GITHUB_NEW_DESC="I'm currently testing this commit, be patient." +elif [[ $1 == "finish" ]]; then + GITHUB_NEW_STATE="success" + GITHUB_NEW_DESC="I like this commit!" +elif [[ $1 == "update" ]]; then + if [[ $CI_JOB_STATUS == "canceled" ]]; then + GITHUB_NEW_STATE="failure" + GITHUB_NEW_DESC="Someone told me to cancel this test run." + elif [[ $CI_JOB_STATUS == "failed" ]]; then + GITHUB_NEW_STATE="failure" + GITHUB_NEW_DESC="I'm sorry, something is odd about this commit." + else + exit 0 + fi +elif [[ $1 == "fail" ]]; then + GITHUB_NEW_STATE="failure" + GITHUB_NEW_DESC="I'm sorry, something is odd about this commit." +else + echo "unknown command" + exit 1 +fi + +CONTEXT="Schutzbot on GitLab" + +# TODO: This should be amended once we start using schedule triggers +if [[ "$CI_PIPELINE_SOURCE" == "schedule" ]]; then + CONTEXT="$CONTEXT, RHEL-${RHEL_MAJOR:-}-nightly" +fi + +curl \ + -u "${SCHUTZBOT_LOGIN}" \ + -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/osbuild/bootc-foundry/statuses/${CI_COMMIT_SHA}" \ + -d '{"state":"'"${GITHUB_NEW_STATE}"'", "description": "'"${GITHUB_NEW_DESC}"'", "context": "'"${CONTEXT}"'", "target_url": "'"${CI_PIPELINE_URL}"'"}' From b3bc7ea3942b86c7e5ac78b628976c8123e979ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:35:52 +0200 Subject: [PATCH 07/22] test/generate_ci.py: add Containerfile COPY directive parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse COPY directives from Containerfiles to extract source paths for CI change detection rules. Validate each source against the filesystem to classify it as a directory (producing glob patterns) or a plain file. Support single-source and multi-source COPY commands (e.g. COPY src1 src2 /dest/). Handle ${TARGETARCH} substitution, skip heredoc COPYs, and reject COPY with options (--chmod, --from, etc.). Signed-off-by: Tomáš Hozza --- test/generate_ci.py | 54 ++++++++++ test/test_generate_ci.py | 218 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 test/generate_ci.py create mode 100644 test/test_generate_ci.py diff --git a/test/generate_ci.py b/test/generate_ci.py new file mode 100644 index 0000000..fcb03e9 --- /dev/null +++ b/test/generate_ci.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +"""Generate .gitlab-ci.yml from test/config.yml and Containerfile COPY directives.""" + +import re +from pathlib import Path + +ARCH_TO_DOCKER = { + "x86_64": "amd64", + "aarch64": "arm64", +} + +COPY_RE = re.compile(r"^COPY\s+(\S+(?:\s+\S+)+)") + + +def parse_copy_sources(lines, arch, repo_root): + """Parse COPY directives and return (sorted_dir_globs, sorted_files). + + Extracts COPY sources from Containerfile lines, substitutes ${TARGETARCH} + with the Docker arch name, and validates each source against the filesystem + rooted at repo_root. + + Returns a tuple of two sorted lists: + - directory globs: '/**/*' patterns for directory sources + - files: paths for plain file sources + + Raises ValueError for COPY with options or sources that don't exist on disk. + """ + docker_arch = ARCH_TO_DOCKER[arch] + dirs = set() + files = set() + for line in lines: + stripped = line.strip() + if stripped.startswith("COPY <<"): + continue + if stripped.startswith("COPY --"): + raise ValueError(f"COPY with options is not supported: {stripped}") + m = COPY_RE.match(stripped) + if not m: + continue + tokens = m.group(1).split() + sources = tokens[:-1] + for src in sources: + src_substituted = src.replace("${TARGETARCH}", docker_arch) + resolved = repo_root / src_substituted + if resolved.is_dir(): + top_dir = Path(src_substituted).parts[0] + dirs.add(top_dir) + elif resolved.is_file(): + files.add(src_substituted) + else: + raise ValueError( + f"COPY source does not exist: {src} (resolved to {resolved})" + ) + return sorted(f"{d}/**/*" for d in dirs), sorted(files) diff --git a/test/test_generate_ci.py b/test/test_generate_ci.py new file mode 100644 index 0000000..b604f67 --- /dev/null +++ b/test/test_generate_ci.py @@ -0,0 +1,218 @@ +import os +import sys + +import pytest + +sys.path.insert(0, os.path.dirname(__file__)) +from generate_ci import ARCH_TO_DOCKER, parse_copy_sources + + +@pytest.mark.parametrize( + "arch, expected_docker", + [ + pytest.param("x86_64", "amd64", id="x86_64"), + pytest.param("aarch64", "arm64", id="aarch64"), + ], +) +def test_arch_to_docker_mapping(arch, expected_docker): + assert ARCH_TO_DOCKER[arch] == expected_docker + + +def _setup_layout(root, dirs=None, files=None): + """Create directories and empty files under root.""" + for d in dirs or []: + (root / d).mkdir(parents=True, exist_ok=True) + for f in files or []: + p = root / f + p.parent.mkdir(parents=True, exist_ok=True) + p.touch() + + +@pytest.mark.parametrize( + "case", + [ + pytest.param( + dict( + lines=["COPY azure/etc/ /etc/"], + arch="x86_64", + layout_dirs=["azure/etc"], + layout_files=[], + expected_dirs=["azure/**/*"], + expected_files=[], + ), + id="dir_simple", + ), + pytest.param( + dict( + lines=["COPY rhel-10-azure /root/Containerfile"], + arch="x86_64", + layout_dirs=[], + layout_files=["rhel-10-azure"], + expected_dirs=[], + expected_files=["rhel-10-azure"], + ), + id="file_simple", + ), + pytest.param( + dict( + lines=["COPY config/myfile.conf /etc/myfile.conf"], + arch="x86_64", + layout_dirs=[], + layout_files=["config/myfile.conf"], + expected_dirs=[], + expected_files=["config/myfile.conf"], + ), + id="file_nested", + ), + pytest.param( + dict( + lines=["COPY < Date: Wed, 6 May 2026 12:38:57 +0200 Subject: [PATCH 08/22] test/generate_ci.py: add CI job generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build change rules from parsed COPY directives, compose CI job dictionaries with runner tags and script sections, and assemble the full GitLab CI config. Installer entries with a payload containerfile get extra container build steps and expanded change detection rules. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- test/generate_ci.py | 142 +++++++++++++++++++++ test/test_generate_ci.py | 268 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 409 insertions(+), 1 deletion(-) diff --git a/test/generate_ci.py b/test/generate_ci.py index fcb03e9..8cab232 100644 --- a/test/generate_ci.py +++ b/test/generate_ci.py @@ -52,3 +52,145 @@ def parse_copy_sources(lines, arch, repo_root): f"COPY source does not exist: {src} (resolved to {resolved})" ) return sorted(f"{d}/**/*" for d in dirs), sorted(files) + + +def build_change_rules(cntfile_name, cntfile_lines, arch, payload_cntfile_name, payload_cntfile_lines, repo_root): + """Build the list of paths for CI change detection rules.""" + sources = {cntfile_name, "test/**/*", "Schutzfile"} + if payload_cntfile_name: + sources.add(payload_cntfile_name) + + all_lines = list(cntfile_lines) + if payload_cntfile_lines: + all_lines.extend(payload_cntfile_lines) + + copy_dirs, copy_files = parse_copy_sources(all_lines, arch, repo_root) + sources.update(copy_dirs) + sources.update(copy_files) + + rules = sorted(sources) + return rules + + +def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rules, payload_cntfile_name): + """Build a single CI job dictionary.""" + if "{arch}" in runner_name: + runner = runner_name.format(arch=arch) + else: + runner = f"{runner_name}-{arch}" + script = [f"CONTAINER_REF=$(test/get-container.sh {cntfile_name})"] + + if payload_cntfile_name: + script.append(f"PAYLOAD_REF=$(test/get-container.sh {payload_cntfile_name})") + + build_cmd = f'test/build.sh "$CONTAINER_REF" {image_type} {arch} {distro_id}' + if payload_cntfile_name: + build_cmd += ' "$PAYLOAD_REF"' + script.append(build_cmd) + + script.append("test/boot.sh") + + extends = ".terraform" + if runner_name.startswith("rhos-01/"): + extends = ".terraform/openstack" + + return { + "stage": "test", + "extends": extends, + "variables": { + "RUNNER": runner, + }, + "rules": [{"changes": change_rules}], + "script": script, + } + + +def generate_ci_config(config, cntfile_cache, repo_root): + """Generate the full CI config dictionary from test config and Containerfile contents. + + cntfile_cache: dict mapping containerfile names to their lines + (used instead of reading files, so tests can provide synthetic content). + """ + ci = { + "stages": ["init", "test", "finish"], + "init": { + "stage": "init", + "interruptible": True, + "tags": ["shell"], + "script": [ + "schutzbot/update_github_status.sh start", + ] + }, + "finish": { + "stage": "finish", + "tags": ["shell"], + "script": [ + "schutzbot/update_github_status.sh finish", + ] + }, + "fail": { + "stage": "finish", + "tags": ["shell"], + "script": [ + "schutzbot/update_github_status.sh fail", + "exit 1", + ], + "when": "on_failure" + }, + ".base": { + "interruptible": True, + "variables": { + "PYTHONUNBUFFERED": "1", + }, + "before_script": [ + "cat schutzbot/team_ssh_keys.txt | tee -a ~/.ssh/authorized_keys > /dev/null", + "test/setup.sh", + ], + "after_script": [ + "schutzbot/unregister.sh || true", + ], + }, + ".terraform": { + "extends": ".base", + "tags": ["terraform"], + }, + ".terraform/openstack": { + "extends": ".base", + "tags": ["terraform/openstack"], + }, + } + + for distro_id, distro_info in config["images"].items(): + runner = distro_info["runner"] + for entry in distro_info["containerfiles"]: + cf_name = entry["containerfile"] + image_type = entry["image-type"] + payload_cf_name = entry.get("payload-containerfile") + + cf_lines = cntfile_cache[cf_name] + payload_cf_lines = None + if payload_cf_name: + payload_cf_lines = cntfile_cache[payload_cf_name] + + for arch in entry["arches"]: + change_rules = build_change_rules( + cntfile_name=cf_name, + cntfile_lines=cf_lines, + arch=arch, + payload_cntfile_name=payload_cf_name, + payload_cntfile_lines=payload_cf_lines, + repo_root=repo_root, + ) + job = build_job( + cntfile_name=cf_name, + image_type=image_type, + arch=arch, + runner_name=runner, + distro_id=distro_id, + change_rules=change_rules, + payload_cntfile_name=payload_cf_name, + ) + job_name = f"{cf_name}-{arch}" + ci[job_name] = job + + return ci diff --git a/test/test_generate_ci.py b/test/test_generate_ci.py index b604f67..0623f7d 100644 --- a/test/test_generate_ci.py +++ b/test/test_generate_ci.py @@ -4,7 +4,13 @@ import pytest sys.path.insert(0, os.path.dirname(__file__)) -from generate_ci import ARCH_TO_DOCKER, parse_copy_sources +from generate_ci import ( + ARCH_TO_DOCKER, + build_change_rules, + build_job, + generate_ci_config, + parse_copy_sources, +) @pytest.mark.parametrize( @@ -216,3 +222,263 @@ def test_parse_copy_sources_errors(tmp_path, lines, arch, error_match, layout_di _setup_layout(tmp_path, dirs=layout_dirs, files=layout_files) with pytest.raises(ValueError, match=error_match): parse_copy_sources(lines, arch, tmp_path) + + +@pytest.mark.parametrize( + "case", + [ + pytest.param( + dict( + cntfile_name="stream10-qcow2", + cntfile_lines=[ + "COPY qcow2-${TARGETARCH}/usr/ /usr/", + "COPY stream10-qcow2 /root/Containerfile", + ], + arch="x86_64", + payload_name=None, + payload_lines=None, + layout_dirs=["qcow2-amd64/usr"], + layout_files=["stream10-qcow2"], + expected=[ + "Schutzfile", + "qcow2-amd64/**/*", + "stream10-qcow2", + "test/**/*", + ], + ), + id="simple-containerfile", + ), + pytest.param( + dict( + cntfile_name="hummingbird-qcow2", + cntfile_lines=[ + "COPY qcow2-${TARGETARCH}/usr/ /usr/", + "COPY hummingbird-qcow2-${TARGETARCH}/usr/ /usr/", + "COPY hummingbird-qcow2 /root/Containerfile", + ], + arch="aarch64", + payload_name=None, + payload_lines=None, + layout_dirs=["qcow2-arm64/usr", "hummingbird-qcow2-arm64/usr"], + layout_files=["hummingbird-qcow2"], + expected=[ + "Schutzfile", + "hummingbird-qcow2", + "hummingbird-qcow2-arm64/**/*", + "qcow2-arm64/**/*", + "test/**/*", + ], + ), + id="aarch64-multiple-dirs", + ), + pytest.param( + dict( + cntfile_name="rhel-10-azure", + cntfile_lines=[ + "COPY azure/etc/ /etc/", + "COPY azure/usr/ /usr/", + "COPY azure/var/ /var/", + "COPY azure-${TARGETARCH}/usr/ /usr/", + "COPY rhel-10-azure /root/Containerfile", + ], + arch="x86_64", + payload_name=None, + payload_lines=None, + layout_dirs=["azure/etc", "azure/usr", "azure/var", "azure-amd64/usr"], + layout_files=["rhel-10-azure"], + expected=[ + "Schutzfile", + "azure-amd64/**/*", + "azure/**/*", + "rhel-10-azure", + "test/**/*", + ], + ), + id="dedup-common-dirs", + ), + pytest.param( + dict( + cntfile_name="rhel-10-installer", + cntfile_lines=[ + 'COPY < /dev/null", + "test/setup.sh", + ] + assert ci[".base"]["after_script"] == [ + "schutzbot/unregister.sh || true", + ] + assert ".terraform" in ci + assert ci[".terraform"]["extends"] == ".base" + assert ci[".terraform"]["tags"] == ["terraform"] + assert ".terraform/openstack" in ci + assert ci[".terraform/openstack"]["extends"] == ".base" + assert ci[".terraform/openstack"]["tags"] == ["terraform/openstack"] + + +def test_generate_ci_config_generates_job_per_arch(tmp_path): + _setup_single_distro_layout(tmp_path) + ci = generate_ci_config(SINGLE_DISTRO_CONFIG, SINGLE_DISTRO_CF_CACHE, tmp_path) + assert "stream10-qcow2-x86_64" in ci + assert "stream10-qcow2-aarch64" in ci From 56ea6a54a599478e54ed049900ffbad7af5dea91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:40:39 +0200 Subject: [PATCH 09/22] test/generate_ci.py: add main entry point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up main() to read config, parse all referenced Containerfiles, generate CI jobs, and emit YAML to stdout. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- test/generate_ci.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) mode change 100644 => 100755 test/generate_ci.py diff --git a/test/generate_ci.py b/test/generate_ci.py old mode 100644 new mode 100755 index 8cab232..39d0388 --- a/test/generate_ci.py +++ b/test/generate_ci.py @@ -2,6 +2,8 @@ """Generate .gitlab-ci.yml from test/config.yml and Containerfile COPY directives.""" import re +import sys +import yaml from pathlib import Path ARCH_TO_DOCKER = { @@ -194,3 +196,35 @@ def generate_ci_config(config, cntfile_cache, repo_root): ci[job_name] = job return ci + + +def main(): + repo_root = Path(__file__).resolve().parent.parent + + config_path = repo_root / "test" / "config.yml" + with open(config_path) as f: + config = yaml.safe_load(f) + + containerfile_cache = {} + for distro_info in config["images"].values(): + for entry in distro_info["containerfiles"]: + cf_name = entry["containerfile"] + if cf_name not in containerfile_cache: + containerfile_cache[cf_name] = (repo_root / cf_name).read_text().splitlines() + payload = entry.get("payload-containerfile") + if payload and payload not in containerfile_cache: + containerfile_cache[payload] = (repo_root / payload).read_text().splitlines() + + ci = generate_ci_config(config, containerfile_cache, repo_root) + + class IndentedListDumper(yaml.Dumper): + def increase_indent(self, flow=False, indentless=False): + return super().increase_indent(flow, False) + + output = yaml.dump(ci, default_flow_style=False, sort_keys=False, Dumper=IndentedListDumper, indent=2) + output = re.sub(r"\n(?=\S)", "\n\n", output) + sys.stdout.write(output) + + +if __name__ == "__main__": + main() From 896d3bb0103e40afb46dba7fce512706dea2186b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Mon, 11 May 2026 13:06:52 +0200 Subject: [PATCH 10/22] test/generate_ci.py: support per-containerfile runner override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow individual containerfile entries in config.yml to specify a runner, overriding the distro-level default. This is needed for image types like installers that require nested virtualization for boot tests. Signed-off-by: Tomáš Hozza --- test/generate_ci.py | 3 ++- test/test_generate_ci.py | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/test/generate_ci.py b/test/generate_ci.py index 39d0388..a5d4826 100755 --- a/test/generate_ci.py +++ b/test/generate_ci.py @@ -163,11 +163,12 @@ def generate_ci_config(config, cntfile_cache, repo_root): } for distro_id, distro_info in config["images"].items(): - runner = distro_info["runner"] + distro_runner = distro_info["runner"] for entry in distro_info["containerfiles"]: cf_name = entry["containerfile"] image_type = entry["image-type"] payload_cf_name = entry.get("payload-containerfile") + runner = entry.get("runner", distro_runner) cf_lines = cntfile_cache[cf_name] payload_cf_lines = None diff --git a/test/test_generate_ci.py b/test/test_generate_ci.py index 0623f7d..2481e92 100644 --- a/test/test_generate_ci.py +++ b/test/test_generate_ci.py @@ -482,3 +482,44 @@ def test_generate_ci_config_generates_job_per_arch(tmp_path): ci = generate_ci_config(SINGLE_DISTRO_CONFIG, SINGLE_DISTRO_CF_CACHE, tmp_path) assert "stream10-qcow2-x86_64" in ci assert "stream10-qcow2-aarch64" in ci + + +RUNNER_OVERRIDE_CONFIG = { + "images": { + "test-distro": { + "runner": "aws/default-runner", + "containerfiles": [ + { + "containerfile": "stream10-qcow2", + "image-type": "qcow2", + "arches": ["x86_64"], + }, + { + "containerfile": "stream10-installer", + "image-type": "bootc-generic-iso", + "payload-containerfile": "stream10-qcow2", + "runner": "aws/nested-virt", + "arches": ["x86_64"], + }, + ], + }, + }, +} +RUNNER_OVERRIDE_CF_CACHE = { + "stream10-qcow2": [ + "COPY qcow2-${TARGETARCH}/usr/ /usr/", + "COPY stream10-qcow2 /root/Containerfile", + ], + "stream10-installer": [ + 'COPY < Date: Wed, 6 May 2026 20:38:15 +0200 Subject: [PATCH 11/22] test/generate_ci.py: support subscribing test runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building RHEL derived bootc container images needs accessing RHEL repositories to install additional packages. Add a subscription-needed flag support to the test config, propagate it as a CI variable, and register the system with subscription-manager when the flag is set. Signed-off-by: Tomáš Hozza --- test/generate_ci.py | 14 ++++++++++---- test/setup.sh | 13 +++++++++++++ test/test_generate_ci.py | 5 +++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/test/generate_ci.py b/test/generate_ci.py index a5d4826..f770253 100755 --- a/test/generate_ci.py +++ b/test/generate_ci.py @@ -74,7 +74,7 @@ def build_change_rules(cntfile_name, cntfile_lines, arch, payload_cntfile_name, return rules -def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rules, payload_cntfile_name): +def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rules, payload_cntfile_name, subscription_needed): """Build a single CI job dictionary.""" if "{arch}" in runner_name: runner = runner_name.format(arch=arch) @@ -96,12 +96,17 @@ def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rul if runner_name.startswith("rhos-01/"): extends = ".terraform/openstack" + variables = { + "RUNNER": runner, + } + + if subscription_needed: + variables["SUBSCRIPTION_NEEDED"] = True + return { "stage": "test", "extends": extends, - "variables": { - "RUNNER": runner, - }, + "variables": variables, "rules": [{"changes": change_rules}], "script": script, } @@ -192,6 +197,7 @@ def generate_ci_config(config, cntfile_cache, repo_root): distro_id=distro_id, change_rules=change_rules, payload_cntfile_name=payload_cf_name, + subscription_needed=distro_info.get("subscription-needed", False), ) job_name = f"{cf_name}-{arch}" ci[job_name] = job diff --git a/test/setup.sh b/test/setup.sh index cf5eff7..6d4e08d 100755 --- a/test/setup.sh +++ b/test/setup.sh @@ -26,3 +26,16 @@ sudo "${IMAGES_DIR}/test/scripts/setup-osbuild-repo" # Install all dependencies using the images library's install script sudo "${IMAGES_DIR}/test/scripts/install-dependencies" + +# Ensure that the system is subscribed if needed +if [ "${SUBSCRIPTION_NEEDED:-false}" = "true" ]; then + if [ -z "${V2_RHN_REGISTRATION_SCRIPT:-}" ]; then + echo "ERROR: SUBSCRIPTION_NEEDED is set but V2_RHN_REGISTRATION_SCRIPT is empty" + exit 1 + fi + + echo "Registering system with Red Hat Subscription Manager" + sudo dnf install -y subscription-manager + set +x + echo "${V2_RHN_REGISTRATION_SCRIPT}" | sudo bash +fi diff --git a/test/test_generate_ci.py b/test/test_generate_ci.py index 2481e92..f564b6d 100644 --- a/test/test_generate_ci.py +++ b/test/test_generate_ci.py @@ -349,6 +349,7 @@ def test_build_change_rules(tmp_path, case): distro_id="rhel-10", change_rules=["rhel-10-ec2", "ec2-amd64/**/*", "test/**/*", "Schutzfile"], payload_name=None, + subscription_needed=False, expected={ "stage": "test", "extends": ".terraform", @@ -374,6 +375,7 @@ def test_build_change_rules(tmp_path, case): distro_id="rhel-10", change_rules=["rhel-10-qcow2", "qcow2-amd64/**/*", "test/**/*", "Schutzfile"], payload_name=None, + subscription_needed=False, expected={ "stage": "test", "extends": ".terraform/openstack", @@ -399,11 +401,13 @@ def test_build_change_rules(tmp_path, case): distro_id="rhel-10", change_rules=["rhel-10-installer", "rhel-10-qcow2", "qcow2-amd64/**/*", "test/**/*", "Schutzfile"], payload_name="rhel-10-qcow2", + subscription_needed=True, expected={ "stage": "test", "extends": ".terraform/openstack", "variables": { "RUNNER": "rhos-01/rhel-10.1-ga-x86_64-large", + "SUBSCRIPTION_NEEDED": True, }, "rules": [{"changes": ["rhel-10-installer", "rhel-10-qcow2", "qcow2-amd64/**/*", "test/**/*", "Schutzfile"]}], "script": [ @@ -427,6 +431,7 @@ def test_build_job(case): distro_id=case["distro_id"], change_rules=case["change_rules"], payload_cntfile_name=case["payload_name"], + subscription_needed=case["subscription_needed"], ) assert job == case["expected"] From fdf67d76eb5467d4bf6d0983890b3c8267fa7ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Mon, 11 May 2026 18:39:50 +0200 Subject: [PATCH 12/22] test/generate_ci.py: support Red Hat registry login for CI runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow authenticating podman with registry.redhat.io when the rh-registry-login-needed flag is set in the test config. Signed-off-by: Tomáš Hozza --- test/generate_ci.py | 6 +++++- test/setup.sh | 13 +++++++++++++ test/test_generate_ci.py | 5 +++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/generate_ci.py b/test/generate_ci.py index f770253..9f5f73e 100755 --- a/test/generate_ci.py +++ b/test/generate_ci.py @@ -74,7 +74,7 @@ def build_change_rules(cntfile_name, cntfile_lines, arch, payload_cntfile_name, return rules -def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rules, payload_cntfile_name, subscription_needed): +def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rules, payload_cntfile_name, subscription_needed, rh_registry_login_needed): """Build a single CI job dictionary.""" if "{arch}" in runner_name: runner = runner_name.format(arch=arch) @@ -103,6 +103,9 @@ def build_job(cntfile_name, image_type, arch, runner_name, distro_id, change_rul if subscription_needed: variables["SUBSCRIPTION_NEEDED"] = True + if rh_registry_login_needed: + variables["RH_REGISTRY_LOGIN_NEEDED"] = True + return { "stage": "test", "extends": extends, @@ -198,6 +201,7 @@ def generate_ci_config(config, cntfile_cache, repo_root): change_rules=change_rules, payload_cntfile_name=payload_cf_name, subscription_needed=distro_info.get("subscription-needed", False), + rh_registry_login_needed=distro_info.get("rh-registry-login-needed", False), ) job_name = f"{cf_name}-{arch}" ci[job_name] = job diff --git a/test/setup.sh b/test/setup.sh index 6d4e08d..214b8da 100755 --- a/test/setup.sh +++ b/test/setup.sh @@ -39,3 +39,16 @@ if [ "${SUBSCRIPTION_NEEDED:-false}" = "true" ]; then set +x echo "${V2_RHN_REGISTRATION_SCRIPT}" | sudo bash fi + +if [ "${RH_REGISTRY_LOGIN_NEEDED:-false}" = "true" ]; then + if [ -z "${RH_CREDS:-}" ]; then + echo "ERROR: RH_REGISTRY_LOGIN_NEEDED is set but RH_CREDS is empty" + exit 1 + fi + + echo "Logging in to registry.redhat.io" + set +x + RH_USER="${RH_CREDS%%:*}" + RH_PASS="${RH_CREDS#*:}" + sudo podman login -u "${RH_USER}" -p "${RH_PASS}" registry.redhat.io +fi diff --git a/test/test_generate_ci.py b/test/test_generate_ci.py index f564b6d..2c55814 100644 --- a/test/test_generate_ci.py +++ b/test/test_generate_ci.py @@ -350,6 +350,7 @@ def test_build_change_rules(tmp_path, case): change_rules=["rhel-10-ec2", "ec2-amd64/**/*", "test/**/*", "Schutzfile"], payload_name=None, subscription_needed=False, + rh_registry_login_needed=False, expected={ "stage": "test", "extends": ".terraform", @@ -376,6 +377,7 @@ def test_build_change_rules(tmp_path, case): change_rules=["rhel-10-qcow2", "qcow2-amd64/**/*", "test/**/*", "Schutzfile"], payload_name=None, subscription_needed=False, + rh_registry_login_needed=False, expected={ "stage": "test", "extends": ".terraform/openstack", @@ -402,12 +404,14 @@ def test_build_change_rules(tmp_path, case): change_rules=["rhel-10-installer", "rhel-10-qcow2", "qcow2-amd64/**/*", "test/**/*", "Schutzfile"], payload_name="rhel-10-qcow2", subscription_needed=True, + rh_registry_login_needed=True, expected={ "stage": "test", "extends": ".terraform/openstack", "variables": { "RUNNER": "rhos-01/rhel-10.1-ga-x86_64-large", "SUBSCRIPTION_NEEDED": True, + "RH_REGISTRY_LOGIN_NEEDED": True, }, "rules": [{"changes": ["rhel-10-installer", "rhel-10-qcow2", "qcow2-amd64/**/*", "test/**/*", "Schutzfile"]}], "script": [ @@ -432,6 +436,7 @@ def test_build_job(case): change_rules=case["change_rules"], payload_cntfile_name=case["payload_name"], subscription_needed=case["subscription_needed"], + rh_registry_login_needed=case["rh_registry_login_needed"], ) assert job == case["expected"] From a5673a8afb951b427c593e0d1158362235cd9fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:44:40 +0200 Subject: [PATCH 13/22] test: add boot test config matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the central test matrix for boot tests. Each distro maps to a list of Containerfiles with their target image type and supported architectures. The generator reads this to produce the GitLab CI configuration. The osbuild/images CI runs only on Fedora. As a consequence, it uses packages for boot-testing, which are not available on RHEL or CentOS Stream. To not complicate the setup in this repository, let's use Fedora runner as well for all distros. For the purpose of boot-testing the disk images built from derived containers, there is no downside in doing it all on Fedora. Use rhos-01 runner for boot-testing image types which require nested virtualization. Signed-off-by: Tomáš Hozza --- test/config.yml | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 test/config.yml diff --git a/test/config.yml b/test/config.yml new file mode 100644 index 0000000..e887a2d --- /dev/null +++ b/test/config.yml @@ -0,0 +1,65 @@ +images: + centos-9: + runner: aws/fedora-43 + containerfiles: + - containerfile: stream9-qcow2 + runner: rhos-01/fedora-43 + image-type: qcow2 + arches: [x86_64] + - containerfile: stream9-qcow2 + image-type: qcow2 + arches: [aarch64] + + centos-10: + runner: aws/fedora-43 + containerfiles: + - containerfile: stream10-qcow2 + runner: rhos-01/fedora-43 + image-type: qcow2 + arches: [x86_64] + - containerfile: stream10-qcow2 + image-type: qcow2 + arches: [aarch64] + - containerfile: stream10-installer + runner: rhos-01/fedora-43-{arch}-large + image-type: bootc-generic-iso + payload-containerfile: stream10-qcow2 + arches: [x86_64] + + rhel-10: + runner: aws/fedora-43 + subscription-needed: true + rh-registry-login-needed: true + containerfiles: + - containerfile: rhel-10-azure + image-type: vhd + arches: [x86_64] + - containerfile: rhel-10-ec2 + image-type: ami + arches: [x86_64, aarch64] + - containerfile: rhel-10-gcp + image-type: gce + arches: [x86_64] + - containerfile: rhel-10-installer + runner: rhos-01/fedora-43-{arch}-large + image-type: bootc-generic-iso + payload-containerfile: rhel-10-qcow2 + arches: [x86_64] + - containerfile: rhel-10-qcow2 + runner: rhos-01/fedora-43 + image-type: qcow2 + arches: [x86_64] + - containerfile: rhel-10-qcow2 + image-type: qcow2 + arches: [aarch64] + + hummingbird: + runner: aws/fedora-43 + containerfiles: + - containerfile: hummingbird-qcow2 + runner: rhos-01/fedora-43 + image-type: qcow2 + arches: [x86_64] + - containerfile: hummingbird-qcow2 + image-type: qcow2 + arches: [aarch64] From ac3d1d1bd06c0310048839639a9331771d54b6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:45:43 +0200 Subject: [PATCH 14/22] ci: add generated .gitlab-ci.yml for boot tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated from test/config.yml by test/generate_ci.py. This file is committed so it is reviewable in PRs. Regenerate with 'python3 test/generate_ci.py > .gitlab-ci.yml' after changing the config or generator. Update .yamllint.yaml to use consistent 2-space indentation for sequences, matching PyYAML's default output format. This ensures the generated .gitlab-ci.yml passes yamllint validation. Signed-off-by: Tomáš Hozza --- .gitlab-ci.yml | 294 ++++++++++++++++++++++++++++++++++++++++++++++++- .yamllint.yaml | 3 + 2 files changed, 294 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 610f8cb..79f56e3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,296 @@ ---- stages: + - init - test + - finish -no-op: +init: + stage: init + interruptible: true + tags: + - shell + script: + - schutzbot/update_github_status.sh start + +finish: + stage: finish + tags: + - shell + script: + - schutzbot/update_github_status.sh finish + +fail: + stage: finish + tags: + - shell + script: + - schutzbot/update_github_status.sh fail + - exit 1 + when: on_failure + +.base: + interruptible: true + variables: + PYTHONUNBUFFERED: '1' + before_script: + - cat schutzbot/team_ssh_keys.txt | tee -a ~/.ssh/authorized_keys > /dev/null + - test/setup.sh + after_script: + - schutzbot/unregister.sh || true + +.terraform: + extends: .base + tags: + - terraform + +.terraform/openstack: + extends: .base + tags: + - terraform/openstack + +stream9-qcow2-x86_64: + stage: test + extends: .terraform/openstack + variables: + RUNNER: rhos-01/fedora-43-x86_64 + rules: + - changes: + - Schutzfile + - qcow2-amd64/**/* + - stream9-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh stream9-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 x86_64 centos-9 + - test/boot.sh + +stream9-qcow2-aarch64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-aarch64 + rules: + - changes: + - Schutzfile + - qcow2-arm64/**/* + - stream9-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh stream9-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 aarch64 centos-9 + - test/boot.sh + +stream10-qcow2-x86_64: + stage: test + extends: .terraform/openstack + variables: + RUNNER: rhos-01/fedora-43-x86_64 + rules: + - changes: + - Schutzfile + - qcow2-amd64/**/* + - stream10-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh stream10-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 x86_64 centos-10 + - test/boot.sh + +stream10-qcow2-aarch64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-aarch64 + rules: + - changes: + - Schutzfile + - qcow2-arm64/**/* + - stream10-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh stream10-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 aarch64 centos-10 + - test/boot.sh + +stream10-installer-x86_64: + stage: test + extends: .terraform/openstack + variables: + RUNNER: rhos-01/fedora-43-x86_64-large + rules: + - changes: + - Schutzfile + - qcow2-amd64/**/* + - stream10-installer + - stream10-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh stream10-installer) + - PAYLOAD_REF=$(test/get-container.sh stream10-qcow2) + - test/build.sh "$CONTAINER_REF" bootc-generic-iso x86_64 centos-10 "$PAYLOAD_REF" + - test/boot.sh + +rhel-10-azure-x86_64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-x86_64 + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - azure-amd64/**/* + - azure/**/* + - rhel-10-azure + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-azure) + - test/build.sh "$CONTAINER_REF" vhd x86_64 rhel-10 + - test/boot.sh + +rhel-10-ec2-x86_64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-x86_64 + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - ec2-amd64/**/* + - ec2/**/* + - rhel-10-ec2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-ec2) + - test/build.sh "$CONTAINER_REF" ami x86_64 rhel-10 + - test/boot.sh + +rhel-10-ec2-aarch64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-aarch64 + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - ec2-arm64/**/* + - ec2/**/* + - rhel-10-ec2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-ec2) + - test/build.sh "$CONTAINER_REF" ami aarch64 rhel-10 + - test/boot.sh + +rhel-10-gcp-x86_64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-x86_64 + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - gcp-amd64/**/* + - gcp/**/* + - rhel-10-gcp + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-gcp) + - test/build.sh "$CONTAINER_REF" gce x86_64 rhel-10 + - test/boot.sh + +rhel-10-installer-x86_64: + stage: test + extends: .terraform/openstack + variables: + RUNNER: rhos-01/fedora-43-x86_64-large + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - qcow2-amd64/**/* + - rhel-10-installer + - rhel-10-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-installer) + - PAYLOAD_REF=$(test/get-container.sh rhel-10-qcow2) + - test/build.sh "$CONTAINER_REF" bootc-generic-iso x86_64 rhel-10 "$PAYLOAD_REF" + - test/boot.sh + +rhel-10-qcow2-x86_64: + stage: test + extends: .terraform/openstack + variables: + RUNNER: rhos-01/fedora-43-x86_64 + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - qcow2-amd64/**/* + - rhel-10-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 x86_64 rhel-10 + - test/boot.sh + +rhel-10-qcow2-aarch64: + stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-aarch64 + SUBSCRIPTION_NEEDED: true + RH_REGISTRY_LOGIN_NEEDED: true + rules: + - changes: + - Schutzfile + - qcow2-arm64/**/* + - rhel-10-qcow2 + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh rhel-10-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 aarch64 rhel-10 + - test/boot.sh + +hummingbird-qcow2-x86_64: + stage: test + extends: .terraform/openstack + variables: + RUNNER: rhos-01/fedora-43-x86_64 + rules: + - changes: + - Schutzfile + - hummingbird-qcow2 + - hummingbird-qcow2-amd64/**/* + - qcow2-amd64/**/* + - test/**/* + script: + - CONTAINER_REF=$(test/get-container.sh hummingbird-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 x86_64 hummingbird + - test/boot.sh + +hummingbird-qcow2-aarch64: stage: test + extends: .terraform + variables: + RUNNER: aws/fedora-43-aarch64 + rules: + - changes: + - Schutzfile + - hummingbird-qcow2 + - hummingbird-qcow2-arm64/**/* + - qcow2-arm64/**/* + - test/**/* script: - - echo "no-op" + - CONTAINER_REF=$(test/get-container.sh hummingbird-qcow2) + - test/build.sh "$CONTAINER_REF" qcow2 aarch64 hummingbird + - test/boot.sh diff --git a/.yamllint.yaml b/.yamllint.yaml index 9f3b20a..b30dbc0 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -6,6 +6,9 @@ ignore: | rules: document-start: disable + indentation: + spaces: 2 + indent-sequences: consistent line-length: max: 200 truthy: disable From 93a995002fdb2535c2a706d6c7edae3283dadab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:46:51 +0200 Subject: [PATCH 15/22] ci: add staleness check for generated .gitlab-ci.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate .gitlab-ci.yml on every PR to 'main', and fail if the result differs from what is committed. This catches forgotten regeneration after config or generator changes. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- .github/workflows/check-ci-config.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/check-ci-config.yml diff --git a/.github/workflows/check-ci-config.yml b/.github/workflows/check-ci-config.yml new file mode 100644 index 0000000..b3901f0 --- /dev/null +++ b/.github/workflows/check-ci-config.yml @@ -0,0 +1,20 @@ +name: Check CI config + +on: + pull_request: + branches: [main] + +jobs: + check-ci-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: pip install pyyaml + + - name: Regenerate .gitlab-ci.yml + run: python3 test/generate_ci.py > .gitlab-ci.yml.generated + + - name: Check .gitlab-ci.yml is up to date + run: diff -u .gitlab-ci.yml .gitlab-ci.yml.generated From 3830972d85d5c193049bcec2d091ce961952e637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:47:34 +0200 Subject: [PATCH 16/22] ci: add weekly images library ref update workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatically fetch the latest images library main branch commit, update the Schutzfile ref, and open a PR. The resulting PR triggers the normal GitLab CI pipeline so the updated images library version is boot-tested before merging. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- .github/workflows/update-images.yml | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/update-images.yml diff --git a/.github/workflows/update-images.yml b/.github/workflows/update-images.yml new file mode 100644 index 0000000..f3cdd01 --- /dev/null +++ b/.github/workflows/update-images.yml @@ -0,0 +1,43 @@ +name: Update images library ref + +on: + workflow_dispatch: + schedule: + - cron: "0 12 * * 0" + +jobs: + update-and-push: + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.SCHUTZBOT_GITHUB_ACCESS_TOKEN }} + GITHUB_TOKEN: ${{ secrets.SCHUTZBOT_GITHUB_ACCESS_TOKEN }} + steps: + - name: Clone + run: | + git clone --depth=1 --branch main https://github.com/$GITHUB_REPOSITORY ./src + + - name: User config + working-directory: ./src + run: | + git config user.name "schutzbot" + git config user.email "schutzbot@gmail.com" + + - name: Update Schutzfile + working-directory: ./src + run: python3 test/update-schutzfile-images + + - name: Open PR + working-directory: ./src + run: | + if git diff --exit-code; then echo "No changes"; exit 0; fi + branch="schutzfile-images-$(date -I)" + git checkout -b "${branch}" + git add Schutzfile + git commit -m "schutzfile: Update images library commit ref" + remote="https://oauth2:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}" + git push -f "${remote}" "${branch}" + gh pr create \ + --title "Update images library commit ref" \ + --body-file "github_pr_body.txt" \ + --base "main" \ + --head "${branch}" From 693233bdf78875cf94b936e81d5711e70e2b9f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 12:57:05 +0200 Subject: [PATCH 17/22] test: add Makefile targets for tests and CI generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 'make test' to run the generator unit tests and 'make generate-ci' to regenerate .gitlab-ci.yml from the test config. Assisted-by: Claude Code Signed-off-by: Tomáš Hozza --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 00bad33..24511c9 100644 --- a/Makefile +++ b/Makefile @@ -15,3 +15,11 @@ fmt: fmt-shell .PHONY: fmt-shell fmt-shell: find . -name '*.sh' -not -path '*/.git/*' -exec shfmt -w -i 4 {} \; + +.PHONY: test +test: + python3 -m pytest test/ -v + +.PHONY: generate-ci +generate-ci: + python3 test/generate_ci.py > .gitlab-ci.yml From c34e361acdd4e53b3dc36114234f720749ed42f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Tue, 12 May 2026 18:55:59 +0200 Subject: [PATCH 18/22] stream10-installer: use local container storage for bootc source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the bootc kickstart source from pulling the upstream centos-bootc image via registry to using the foundry-built stream10-qcow2 image from containers-storage, and drop the now-unnecessary --target-imgref. This is consistent with the rhel-10-installer. Signed-off-by: Tomáš Hozza --- stream10-installer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream10-installer b/stream10-installer index e70089d..c6d5037 100644 --- a/stream10-installer +++ b/stream10-installer @@ -39,7 +39,7 @@ grub2: EOT COPY <> /etc/passwd && \ From 55f002f686687ae3630ebb7b174411dae1404900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Thu, 14 May 2026 08:18:27 +0200 Subject: [PATCH 19/22] stream10-installer: add fuse-overlayfs for overlay-on-overlay support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the booted Anaconda installer deploys the bootc payload from containers-storage, the container runtime attempts to use overlayfs on a filesystem that is already overlay-backed, which the kernel does not support. Adding fuse-overlayfs provides a userspace overlay implementation that avoids this limitation. This aligns with the rhel-10-installer which already includes fuse-overlayfs. Signed-off-by: Tomáš Hozza --- stream10-installer | 1 + 1 file changed, 1 insertion(+) diff --git a/stream10-installer b/stream10-installer index c6d5037..b446ede 100644 --- a/stream10-installer +++ b/stream10-installer @@ -21,6 +21,7 @@ RUN dnf install -y \ google-noto-sans-cjk-fonts \ xorriso \ squashfs-tools \ + fuse-overlayfs \ && dnf clean all RUN mkdir -p /boot/efi \ From e63cb311e67bfe0c77cc60c27708a2c4b87347e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Tue, 12 May 2026 18:55:59 +0200 Subject: [PATCH 20/22] rhel-10-installer: fix container ref in kickstart commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the ostreecontainer --url to include the :latest tag and correct the commented-out bootc --source-imgref to use containers-storage: transport, consistent with stream10-installer. The previous local-storage:// transport does not exist. Signed-off-by: Tomáš Hozza --- rhel-10-installer | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rhel-10-installer b/rhel-10-installer index 350714b..6dd656c 100644 --- a/rhel-10-installer +++ b/rhel-10-installer @@ -63,8 +63,8 @@ EOT COPY <> /etc/passwd && \ From a50763c7be1b7ec22eb1810f3c0350456ae31bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Thu, 14 May 2026 13:34:12 +0200 Subject: [PATCH 21/22] gitignore: add Python bytecode patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomáš Hozza --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 135995b..0283850 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ github_pr_body.txt _images/ build/ +*.pyc +__pycache__/ From 1963a06eb3ab11d1a405daa6c858f21748547cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 6 May 2026 15:50:22 +0200 Subject: [PATCH 22/22] XXX: cross-test against images fork MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomáš Hozza --- Schutzfile | 2 +- test/setup.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Schutzfile b/Schutzfile index ad55097..e94b1a3 100644 --- a/Schutzfile +++ b/Schutzfile @@ -2,7 +2,7 @@ "common": { "dependencies": { "images": { - "ref": "86fa680b70efb2c1d6360abbc021a097fb5e3da8" + "ref": "HMS-10336" } } } diff --git a/test/setup.sh b/test/setup.sh index 214b8da..acbf80e 100755 --- a/test/setup.sh +++ b/test/setup.sh @@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" IMAGES_DIR="${REPO_ROOT}/_images" -IMAGES_REPO_URL="${IMAGES_REPO_URL:-https://github.com/osbuild/images.git}" +IMAGES_REPO_URL="${IMAGES_REPO_URL:-https://github.com/thozza/osbuild-images.git}" # Read the images library ref from Schutzfile IMAGES_REF=$(jq -r '.common.dependencies.images.ref' "${REPO_ROOT}/Schutzfile")