From 3338344343fb04f6519e98d5999fc96a4f62398b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Wed, 29 Jul 2026 13:11:49 +0200 Subject: [PATCH 1/8] imgtestlib: add bootc args for gen-manifest Change the function for generating manifests in tests to add options for generation of bootc images manifest. --- test/scripts/imgtestlib/core.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/scripts/imgtestlib/core.py b/test/scripts/imgtestlib/core.py index 931c8d13e4..dd37f69f65 100644 --- a/test/scripts/imgtestlib/core.py +++ b/test/scripts/imgtestlib/core.py @@ -101,7 +101,8 @@ def check_config_names(): def gen_manifests(outputdir, config_list=None, distros=None, arches=None, images=None, - commits=False, flatpaks=False, skip_no_config=False): + commits=False, flatpaks=False, skip_no_config=False, bootc_refs=None, + bootc_remote=False, bootc_installer_ref=None): # pylint: disable=too-many-arguments,too-many-positional-arguments cmd = ["go", "run", "./cmd/gen-manifests", "--cache", os.path.join(TEST_CACHE_ROOT, "rpmmd"), @@ -115,6 +116,12 @@ def gen_manifests(outputdir, config_list=None, distros=None, arches=None, images cmd.extend(["--arches", ",".join(arches)]) if images: cmd.extend(["--types", ",".join(images)]) + if bootc_refs: + cmd.extend(["--bootc-refs", ",".join(bootc_refs)]) + if bootc_remote: + cmd.append("--bootc-remote") + if bootc_installer_ref: + cmd.extend(["--bootc-installer-ref", bootc_installer_ref]) if commits: cmd.append("--commits") if flatpaks: From 25532c4abb628ee56d4a81e8ff462f4856e898bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Wed, 5 Aug 2026 10:33:53 +0200 Subject: [PATCH 2/8] test: resolve bootc builds from bootc sources Introduce bootc sources under directory test/data/bootcrefs and resolve container refs from them. --- test/data/bootcrefs/centos-10.json | 14 ++++ test/scripts/imgtestlib/__init__.py | 2 +- test/scripts/imgtestlib/bootcsource.py | 48 +++++++++++ test/scripts/test_imgtestlib.py | 108 +++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 test/data/bootcrefs/centos-10.json create mode 100644 test/scripts/imgtestlib/bootcsource.py diff --git a/test/data/bootcrefs/centos-10.json b/test/data/bootcrefs/centos-10.json new file mode 100644 index 0000000000..7af1daad7a --- /dev/null +++ b/test/data/bootcrefs/centos-10.json @@ -0,0 +1,14 @@ +{ + "aarch64": { + "ref": "quay.io/centos-bootc/centos-bootc:stream10" + }, + "ppc64le": { + "ref": "quay.io/centos-bootc/centos-bootc:stream10" + }, + "s390x": { + "ref": "quay.io/centos-bootc/centos-bootc:stream10" + }, + "x86_64": { + "ref": "quay.io/centos-bootc/centos-bootc:stream10" + } +} diff --git a/test/scripts/imgtestlib/__init__.py b/test/scripts/imgtestlib/__init__.py index 5ad4b175b5..6f8f1a0b3a 100644 --- a/test/scripts/imgtestlib/__init__.py +++ b/test/scripts/imgtestlib/__init__.py @@ -1 +1 @@ -from . import boot, build, cache, core, gitlab, run, testenv, vm +from . import boot, bootcsource, build, cache, core, gitlab, run, testenv, vm diff --git a/test/scripts/imgtestlib/bootcsource.py b/test/scripts/imgtestlib/bootcsource.py new file mode 100644 index 0000000000..68735247ba --- /dev/null +++ b/test/scripts/imgtestlib/bootcsource.py @@ -0,0 +1,48 @@ +import json +import os + +BOOTCREFS_PATH = "./test/data/bootcrefs" +DISK_IMAGE_TYPES = {"qcow2", "ami", "raw", "gce", "vhd", "vmdk", "ova"} +INSTALLER_IMAGE_TYPES = {"bootc-generic-iso", "bootc-installer"} + + +def load_bootc_source(source_name): + path = os.path.join(BOOTCREFS_PATH, source_name + ".json") + with open(path, encoding="utf-8") as source_file: + return json.load(source_file) + + +def list_bootc_source_arches(source_name): + return sorted(load_bootc_source(source_name).keys()) + + +def resolve_bootc_source(source_name, arch): + data = load_bootc_source(source_name) + + try: + entry = data[arch] + except KeyError as exc: + raise KeyError(f"bootc source {source_name} does not define arch {arch}") from exc + + if not isinstance(entry, dict): + raise TypeError(f"bootc source {source_name} entry for arch {arch} must be an object") + + return entry + + +def resolve_ref_from_entry(entry, source_name, arch, image_type=None): + derived_refs = entry.get("derived_refs", {}) + if image_type in DISK_IMAGE_TYPES: + return derived_refs.get("disk") + if image_type in INSTALLER_IMAGE_TYPES: + return derived_refs.get("installer") + + ref = entry.get("ref") + if not isinstance(ref, str) or not ref: + raise ValueError(f"bootc source {source_name} entry for arch {arch} must define a non-empty 'ref' string") + return ref + + +def resolve_bootc_source_ref(source_name, arch, image_type=None): + entry = resolve_bootc_source(source_name, arch) + return resolve_ref_from_entry(entry, source_name, arch, image_type=image_type) diff --git a/test/scripts/test_imgtestlib.py b/test/scripts/test_imgtestlib.py index 114e542938..c71b7c2dc2 100644 --- a/test/scripts/test_imgtestlib.py +++ b/test/scripts/test_imgtestlib.py @@ -35,6 +35,114 @@ def test_runcmd_env(): assert stderr == b"" +def test_config_to_cli_args_bootc(): + config = { + "name": "bootc-empty", + "blueprint": {}, + "options": { + "bootc": { + "ref": "quay.io/example/bootc:latest", + "build_ref": "quay.io/example/toolbox:latest", + "installer_payload_ref": "quay.io/example/payload:latest", + "use_remote_container_source": True, + }, + }, + } + + bootc = testlib.build.resolve_bootc_options(config, "centos-10", "x86_64") + args = testlib.build.config_to_cli_args(config, bootc) + + assert any(arg.startswith("--blueprint=") for arg in args) + assert "--bootc-ref=quay.io/example/bootc:latest" in args + assert "--bootc-build-ref=quay.io/example/toolbox:latest" in args + assert "--bootc-installer-payload-ref=quay.io/example/payload:latest" in args + assert "--bootc-pull-container" in args + + +def test_resolve_bootc_options_from_bootcrefs(): + config = { + "name": "bootc-empty", + "blueprint": {}, + "options": { + "bootc": { + "use_remote_container_source": True, + }, + }, + } + + bootc = testlib.build.resolve_bootc_options(config, "bootc-centos-10", "x86_64") + + assert bootc["ref"] == "quay.io/centos-bootc/centos-bootc:stream10" + assert bootc["use_remote_container_source"] is True + + +def test_resolve_bootc_options_prefers_config_over_bootcrefs(): + config = { + "name": "bootc-empty", + "blueprint": {}, + "options": { + "bootc": { + "ref": "quay.io/example/bootc:latest", + }, + }, + } + + bootc = testlib.build.resolve_bootc_options(config, "bootc-centos-10", "x86_64", image_type="qcow2") + + assert bootc["ref"] == "quay.io/example/bootc:latest" + + +def test_resolve_bootc_source(): + entry = testlib.bootcsource.resolve_bootc_source("centos-10", "x86_64") + assert entry["ref"] == "quay.io/centos-bootc/centos-bootc:stream10" + + +def test_list_bootc_source_arches(): + arches = testlib.bootcsource.list_bootc_source_arches("centos-10") + assert arches == ["aarch64", "ppc64le", "s390x", "x86_64"] + + +def test_resolve_bootc_source_rejects_string_entry(tmp_path, monkeypatch): + source_dir = tmp_path / "bootcrefs" + source_dir.mkdir() + (source_dir / "bad.json").write_text('{"x86_64": "quay.io/example/bootc:latest"}', encoding="utf-8") + monkeypatch.setattr(testlib.bootcsource, "BOOTCREFS_PATH", os.fspath(source_dir)) + + with pytest.raises(TypeError, match="must be an object"): + testlib.bootcsource.resolve_bootc_source("bad", "x86_64") + + +def test_resolve_bootc_source_requires_ref(tmp_path, monkeypatch): + source_dir = tmp_path / "bootcrefs" + source_dir.mkdir() + (source_dir / "bad.json").write_text('{"x86_64": {"build_ref": "quay.io/example/toolbox:latest"}}', + encoding="utf-8") + monkeypatch.setattr(testlib.bootcsource, "BOOTCREFS_PATH", os.fspath(source_dir)) + + with pytest.raises(ValueError, match="must define a non-empty 'ref' string"): + testlib.bootcsource.resolve_bootc_source_ref("bad", "x86_64") + + +def test_resolve_ref_from_entry_returns_derived_refs(): + entry = { + "ref": "quay.io/example/base:latest", + "derived_refs": { + "disk": "quay.io/example/disk:latest", + "installer": "quay.io/example/installer:latest", + }, + } + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "qcow2") == "quay.io/example/disk:latest" + assert testlib.bootcsource.resolve_ref_from_entry( + entry, "src", "x86_64", "bootc-installer") == "quay.io/example/installer:latest" + + +def test_resolve_ref_from_entry_no_derived_refs(): + entry = {"ref": "quay.io/example/base:latest"} + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "qcow2") is None + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "bootc-installer") is None + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64") == "quay.io/example/base:latest" + + def test_read_seed(): # check that it's read without error - no need to test the value itself seed_env = testlib.testenv.rng_seed_env() From 859deb867f2681fe02155abe87edeb149b39167a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Thu, 13 Aug 2026 13:24:21 +0200 Subject: [PATCH 3/8] test: add rhel-10.json for gitlab bootc tests Add tests for rhel 10 which, compared to centos-10 also uses derived_refs in order to test also installer and disk images. --- test/data/bootcrefs/rhel-10.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/data/bootcrefs/rhel-10.json diff --git a/test/data/bootcrefs/rhel-10.json b/test/data/bootcrefs/rhel-10.json new file mode 100644 index 0000000000..002f2bfc49 --- /dev/null +++ b/test/data/bootcrefs/rhel-10.json @@ -0,0 +1,22 @@ +{ + "aarch64": { + "ref": "registry.redhat.io/rhel10/rhel-bootc:latest", + "derived_refs": { + "disk": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-qcow2:latest", + "installer": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-installer:latest", + "ec2": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-ec2:latest", + "azure": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-azure:latest", + "gcp": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-gcp:latest" + } + }, + "x86_64": { + "ref": "registry.redhat.io/rhel10/rhel-bootc:latest", + "derived_refs": { + "disk": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-qcow2:latest", + "installer": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-installer:latest", + "ec2": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-ec2:latest", + "azure": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-azure:latest", + "gcp": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-gcp:latest" + } + } +} From c0c52e10c6b865a44f76a7a521071640cf1cedb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Fri, 31 Jul 2026 14:49:55 +0200 Subject: [PATCH 4/8] imgtestlib/build: turn config bootc into cli args This commit adds bootc-related argument in order to allow building bootc-based images in tests when using build_image function. --- test/scripts/imgtestlib/build.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/test/scripts/imgtestlib/build.py b/test/scripts/imgtestlib/build.py index 02a2fba466..654979a14e 100644 --- a/test/scripts/imgtestlib/build.py +++ b/test/scripts/imgtestlib/build.py @@ -3,12 +3,24 @@ import tempfile from typing import Dict, List +from .bootcsource import resolve_bootc_source, resolve_bootc_source_ref from .gitlab import log_section from .run import runcmd, runcmd_nc from .testenv import get_host_distro, get_osbuild_commit, rng_seed_env -def config_to_cli_args(config: dict) -> List[str]: +def resolve_bootc_options(config: dict, distro: str, arch: str, image_type: str = None) -> dict: + bootc = dict(config.get("options", {}).get("bootc", {})) + if not distro.startswith("bootc-"): + return bootc + + source_name = distro.removeprefix("bootc-") + source_data = dict(resolve_bootc_source(source_name, arch)) + source_data["ref"] = resolve_bootc_source_ref(source_name, arch, image_type=image_type) + return {**source_data, **bootc} + + +def config_to_cli_args(config: dict, bootc: dict) -> List[str]: args: List[str] = [] blueprint = config.get("blueprint", {}) @@ -25,7 +37,10 @@ def config_to_cli_args(config: dict) -> List[str]: if parent := ostree.get("parent"): args.append(f"--ostree-parent={parent}") - bootc = options.get("bootc", {}) + if ref := bootc.get("ref"): + args.append(f"--bootc-ref={ref}") + if build_ref := bootc.get("build_ref"): + args.append(f"--bootc-build-ref={build_ref}") if payload_ref := bootc.get("installer_payload_ref"): args.append(f"--bootc-installer-payload-ref={payload_ref}") if bootc.get("use_remote_container_source"): @@ -47,6 +62,7 @@ def build_image(distro, arch, image_type, config_path): config = json.load(config_file) config_name = config["name"] + bootc = resolve_bootc_options(config, distro, arch, image_type=image_type) build_name = gen_build_name(distro, arch, image_type, config_name) build_dir = os.path.join("build", build_name) @@ -59,7 +75,6 @@ def build_image(distro, arch, image_type, config_path): seed = rng_seed_env()["OSBUILD_TESTING_RNG_SEED"] cmd = [ "sudo", "-E", "./bin/image-builder", "build", image_type, - "--distro", distro, "--arch", arch, "--force-repo-dir", "test/data/repositories", "--output-dir", build_dir, @@ -68,7 +83,10 @@ def build_image(distro, arch, image_type, config_path): "--ignore-warnings", "--seed", str(seed), ] - cmd.extend(config_to_cli_args(config)) + # bootc builds derive the distro from the container and reject --distro. + if not bootc.get("ref"): + cmd.extend(["--distro", distro]) + cmd.extend(config_to_cli_args(config, bootc)) runcmd_nc(cmd) print("โœ… Build finished!!") From ad2bbde718071152f7452d35f68c34c5214ced50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Wed, 5 Aug 2026 16:24:24 +0200 Subject: [PATCH 5/8] gitlab: add bootc disk build + boot-test pipelines Wire centos-10 bootc image CI via generate-bootc-config pipelines for local container builds. This commit also fixes collecting bootc jobs when --bootc-refs is set without --distros. --- .gitlab-ci.yml | 102 +++++++++++++ cmd/gen-manifests/main.go | 9 +- test/configs/bootc-remote-with-payload.json | 2 +- test/configs/bootc-with-payload.json | 2 +- test/data/bootcrefs/rhel-10.json | 12 +- ...ootc_generic_iso-bootc_remote_with_payload | 2 +- ...rch64-bootc_generic_iso-bootc_with_payload | 2 +- ...-bootc_installer-bootc_remote_with_payload | 2 +- ...aarch64-bootc_installer-bootc_with_payload | 2 +- ...ootc_generic_iso-bootc_remote_with_payload | 2 +- ...86_64-bootc_generic_iso-bootc_with_payload | 2 +- ...-bootc_installer-bootc_remote_with_payload | 2 +- ...-x86_64-bootc_installer-bootc_with_payload | 2 +- test/scripts/generate-bootc-config | 134 ++++++++++++++++++ test/scripts/generate-gitlab-ci | 51 +++++++ test/scripts/imgtestlib/bootcsource.py | 4 +- test/scripts/imgtestlib/build.py | 4 +- test/scripts/imgtestlib/core.py | 13 ++ test/scripts/test_imgtestlib.py | 24 ++++ 19 files changed, 353 insertions(+), 20 deletions(-) create mode 100755 test/scripts/generate-bootc-config diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 49ed5d04cd..4b807c485a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2030,6 +2030,108 @@ fail: needs: - "generate-ostree-build-config: [rhel-9.9, x86_64]" +"generate-bootc-config: [centos-10, aarch64]": + stage: gen + extends: .terraform + variables: + RUNNER: aws/fedora-44-aarch64 + INTERNAL_NETWORK: "false" + script: + - sudo -E ./test/scripts/setup-osbuild-repo + - sudo ./test/scripts/install-dependencies + - ./test/scripts/generate-bootc-config --bootc-source centos-10 --arch aarch64 boot-config.yml + artifacts: + paths: + - boot-config.yml + + +"generate-bootc-config: [centos-10, x86_64]": + stage: gen + extends: .terraform + variables: + RUNNER: aws/fedora-44-x86_64 + INTERNAL_NETWORK: "false" + script: + - sudo -E ./test/scripts/setup-osbuild-repo + - sudo ./test/scripts/install-dependencies + - ./test/scripts/generate-bootc-config --bootc-source centos-10 --arch x86_64 boot-config.yml + artifacts: + paths: + - boot-config.yml + + +"generate-bootc-config: [rhel-10, aarch64]": + stage: gen + extends: .terraform + variables: + RUNNER: aws/fedora-44-aarch64 + INTERNAL_NETWORK: "false" + script: + - sudo -E ./test/scripts/setup-osbuild-repo + - sudo ./test/scripts/install-dependencies + - ./test/scripts/generate-bootc-config --bootc-source rhel-10 --arch aarch64 boot-config.yml + artifacts: + paths: + - boot-config.yml + + +"generate-bootc-config: [rhel-10, x86_64]": + stage: gen + extends: .terraform + variables: + RUNNER: aws/fedora-44-x86_64 + INTERNAL_NETWORK: "false" + script: + - sudo -E ./test/scripts/setup-osbuild-repo + - sudo ./test/scripts/install-dependencies + - ./test/scripts/generate-bootc-config --bootc-source rhel-10 --arch x86_64 boot-config.yml + artifacts: + paths: + - boot-config.yml + +"bootc-build-trigger: [centos-10, aarch64]": + stage: build + trigger: + include: + - artifact: boot-config.yml + job: "generate-bootc-config: [centos-10, aarch64]" + strategy: depend + needs: + - "generate-bootc-config: [centos-10, aarch64]" + + +"bootc-build-trigger: [centos-10, x86_64]": + stage: build + trigger: + include: + - artifact: boot-config.yml + job: "generate-bootc-config: [centos-10, x86_64]" + strategy: depend + needs: + - "generate-bootc-config: [centos-10, x86_64]" + + +"bootc-build-trigger: [rhel-10, aarch64]": + stage: build + trigger: + include: + - artifact: boot-config.yml + job: "generate-bootc-config: [rhel-10, aarch64]" + strategy: depend + needs: + - "generate-bootc-config: [rhel-10, aarch64]" + + +"bootc-build-trigger: [rhel-10, x86_64]": + stage: build + trigger: + include: + - artifact: boot-config.yml + job: "generate-bootc-config: [rhel-10, x86_64]" + strategy: depend + needs: + - "generate-bootc-config: [rhel-10, x86_64]" + "generate-manifests: [centos-10, ppc64le]": stage: gen extends: .terraform diff --git a/cmd/gen-manifests/main.go b/cmd/gen-manifests/main.go index 942dca3d93..93ad8c93c1 100644 --- a/cmd/gen-manifests/main.go +++ b/cmd/gen-manifests/main.go @@ -536,7 +536,12 @@ func main() { fmt.Fprintln(os.Stderr, "Collecting jobs") - distros, invalidDistros := distros.ResolveArgValues(testedRepoRegistry.ListDistros()) + // With --bootc-refs and no --distros, skip loop to get repositories + repoDistros := testedRepoRegistry.ListDistros() + if len(distros) == 0 && len(bootcRefs) > 0 { + repoDistros = nil + } + distros, invalidDistros := distros.ResolveArgValues(repoDistros) if len(invalidDistros) > 0 { fmt.Fprintf(os.Stderr, "WARNING: invalid distro names: [%s]\n", strings.Join(invalidDistros, ",")) } @@ -639,11 +644,13 @@ func main() { } } + arches, _ := arches.ResolveArgValues(distribution.ListArches()) for _, archName := range arches { archi, err := distribution.GetArch(archName) if err != nil { panic(err) } + imgTypes, _ := imgTypes.ResolveArgValues(archi.ListImageTypes()) for _, imgTypeName := range imgTypes { imgType, err := archi.GetImageType(imgTypeName) if err != nil { diff --git a/test/configs/bootc-remote-with-payload.json b/test/configs/bootc-remote-with-payload.json index 829f6964a9..16aae2c257 100644 --- a/test/configs/bootc-remote-with-payload.json +++ b/test/configs/bootc-remote-with-payload.json @@ -7,7 +7,7 @@ }, "options": { "bootc": { - "installer_payload_ref": "payload-container-fake-ref", + "installer_payload_ref": "quay.io/centos-bootc/centos-bootc:stream10", "use_remote_container_source": true } } diff --git a/test/configs/bootc-with-payload.json b/test/configs/bootc-with-payload.json index 3f2519b90d..eabc3e4eb2 100644 --- a/test/configs/bootc-with-payload.json +++ b/test/configs/bootc-with-payload.json @@ -7,7 +7,7 @@ }, "options": { "bootc": { - "installer_payload_ref": "payload-container-fake-ref" + "installer_payload_ref": "quay.io/centos-bootc/centos-bootc:stream10" } } } diff --git a/test/data/bootcrefs/rhel-10.json b/test/data/bootcrefs/rhel-10.json index 002f2bfc49..ad2343f62f 100644 --- a/test/data/bootcrefs/rhel-10.json +++ b/test/data/bootcrefs/rhel-10.json @@ -4,9 +4,9 @@ "derived_refs": { "disk": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-qcow2:latest", "installer": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-installer:latest", - "ec2": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-ec2:latest", - "azure": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-azure:latest", - "gcp": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-gcp:latest" + "ami": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-ec2:latest", + "vhd": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-azure:latest", + "gce": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-gcp:latest" } }, "x86_64": { @@ -14,9 +14,9 @@ "derived_refs": { "disk": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-qcow2:latest", "installer": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-installer:latest", - "ec2": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-ec2:latest", - "azure": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-azure:latest", - "gcp": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-gcp:latest" + "ami": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-ec2:latest", + "vhd": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-azure:latest", + "gce": "quay.io/redhat-services-prod/insights-management-tenant/image-builder-bootc-foundry/rhel-10-gcp:latest" } } } diff --git a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_remote_with_payload b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_remote_with_payload index bdf605b441..da95792e29 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_remote_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_remote_with_payload @@ -1 +1 @@ -86da72d87b728c80df7913e48cb43b91304d37bdacd65a13b02988f973a61537 +cef10b14ef026c90582f80300a29b15ec7ad02de53f2dcd6e00b8502ce8e758f diff --git a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_with_payload b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_with_payload index a60a6a414b..d234159c76 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_generic_iso-bootc_with_payload @@ -1 +1 @@ -a3b1cd6a627ebcea95107f3b9722085e47822d47ef3cd16626005c56e742c035 +df81b2a62016713844168497eaa793a86b72735c92eaa7a9b43eb3ebcb00a41e diff --git a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_remote_with_payload b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_remote_with_payload index 9295a87d89..41389ee8ef 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_remote_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_remote_with_payload @@ -1 +1 @@ -c1f3cba58ad0f4c4db26116a08178c4dec77710c364f09ce263d3481022d254a +301be914a73e0e50c54787671be3b9bb057791c143ce2c8fd4a35a1f3c0a324c diff --git a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_with_payload b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_with_payload index 1377073286..b45bb24048 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-aarch64-bootc_installer-bootc_with_payload @@ -1 +1 @@ -2df8b35541e90cc3247f453d964cb4b010d6d1c803092bb88d07b6e0cf490d09 +c84eaeeddc762f1e9c49a0e82c3d4663e226d1513c9050bc4ab92ab8a8d10c1c diff --git a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_remote_with_payload b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_remote_with_payload index fdef5a0984..6ac01a446f 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_remote_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_remote_with_payload @@ -1 +1 @@ -0fa375e73b1872388e9cc1e0585f31796b9bb08b1805a7f53dfaba7d3c3b6d6a +d3c292ecc292bc9155a71c0ab8a144f270e7ea2ab5492280b051c9597e6260e5 diff --git a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_with_payload b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_with_payload index 1fd5580995..9dc5cca90b 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_generic_iso-bootc_with_payload @@ -1 +1 @@ -5064e0ceca39e60554b6edb1687089a58ac7a732b535a7da3cdec2df91ea14de +33dc409ed99bc90ac2f5952b318ee3c05273d291e8601300b69cc5c1c30b2f01 diff --git a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_remote_with_payload b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_remote_with_payload index 58fc0e7b90..cfa3e750d5 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_remote_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_remote_with_payload @@ -1 +1 @@ -6591c49b039799e422ef065c1720e6d2d9b40115d9acdc5ec5e9a121961b85d9 +89d82a0734068ec148713fb7098bf1f20fae41eb649a0f3380a5fe4270c03244 diff --git a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_with_payload b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_with_payload index d6039abc32..69147fe2d2 100644 --- a/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_with_payload +++ b/test/data/manifest-checksums/bootc_test_os_1-x86_64-bootc_installer-bootc_with_payload @@ -1 +1 @@ -44d8051c0d48d63847da74e98211885c4195f2c41f9812eaf751e174a6772bb8 +268b552e56217f8a828b6e4632366e21a8161cf43b747fc511b26b24b79d46ca diff --git a/test/scripts/generate-bootc-config b/test/scripts/generate-bootc-config new file mode 100755 index 0000000000..d4409dfb47 --- /dev/null +++ b/test/scripts/generate-bootc-config @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +# +# Generates gitlab child pipelines for building bootc images. +# Each pipeline should build a single image configuration test. +import os +from tempfile import TemporaryDirectory + +import imgtestlib as testlib + +BOOTC_CONFIGS = {"bootc-empty", "bootc-with-payload"} + +JOB_TEMPLATE = """ +build/{distro}/{arch}/{image_type}/{config_name}: + stage: test + script: + - sudo -E ./test/scripts/setup-osbuild-repo + - sudo ./test/scripts/install-dependencies + - podman pull {bootc_ref} + - ./test/scripts/build-image "{distro}" "{image_type}" "{config}" + - ./test/scripts/boot-image "{image_path}" "{config}" + - sudo -E ./test/scripts/upload-results "{distro}" "{image_type}" "{config}" + extends: .terraform + tags: + - {tag} + variables: + RUNNER: {runner}-{arch} + INTERNAL_NETWORK: "{internal}" +""" + +def should_include_config(config_name: str) -> bool: + return config_name in BOOTC_CONFIGS + + +@testlib.gitlab.log_section("Generating manifests") +def generate_manifests(outputdir, arch, bootc_source, bootc_installer_ref=None): + """ + Generate bootc manifests using the default config list and return a + dictionary mapping each manifest file to the manifest data and its ID. + """ + bootc_entry = testlib.bootcsource.resolve_bootc_source(bootc_source, arch) + bootc_ref = bootc_entry["ref"] + bootc_refspec = bootc_ref + if build_ref := bootc_entry.get("build_ref"): + bootc_refspec = f"{bootc_ref}#{build_ref}" + + print(f"๐Ÿ—’๏ธ Generating bootc manifests using the default config list for {bootc_source} [{arch}]") + # Omit --distros: with --bootc-refs, gen-manifests skips the RPM/repo loop. + err = testlib.core.gen_manifests(outputdir, arches=[arch], + bootc_refs=[bootc_refspec], + bootc_installer_ref=bootc_installer_ref, + skip_no_config=True) + + # print stderr in case there were errors or warnings about skipped configurations + # but filter out the annoying ones + stderr = err.decode().splitlines() + for line in stderr: + if "No match for group package" in line: + continue + if "Failed to load consumer certs" in line: + continue + print(line) + + print("โœ… Manifest generation done!\n") + return testlib.core.read_manifests(outputdir) + + +def generate_configs(build_requests, pipeline_file, bootc_source, bootc_ref): + print(f"๐Ÿงช Generating dynamic pipelines for {len(build_requests)} builds") + expected_distro = f"bootc-{bootc_source}" + for build in build_requests: + distro = build["distro"] + arch = build["arch"] + image_type = build["image-type"] + config = build["config"] + config_name = config["name"] + + if distro != expected_distro: + print(f"๐Ÿฆ˜ Skipping {distro}/{arch}/{image_type}/{config_name}: " + f"expected distro {expected_distro} for bootc source {bootc_source}") + continue + + if not should_include_config(config_name): + print(f"๐Ÿฆ˜ Skipping {distro}/{arch}/{image_type}/{config_name}: " + f"not in bootc config set") + continue + + build_name = testlib.build.gen_build_name(distro, arch, image_type, config_name) + image_path = f"./build/{build_name}" + + runner = testlib.testenv.get_ci_runner_for(distro, arch, image_type) + tag = testlib.core.get_tag_for(runner) + + if bootc_ref is None: + print(f"๐Ÿฆ˜ Skipping {distro}/{arch}/{image_type}/{config_name}: no derived ref") + continue + + config_path = os.path.join(testlib.core.CONFIGS_PATH, config_name + ".json") + pipeline_file.write(JOB_TEMPLATE.format(distro=distro, arch=arch, image_type=image_type, + runner=runner, tag=tag, + config_name=config_name, config=config_path, + internal="true" if "rhel" in distro else "false", + image_path=image_path, bootc_ref=bootc_ref)) + print("โœ… DONE!") + + +def main(): + parser = testlib.core.bootc_clargs() + args = parser.parse_args() + + arch = args.arch + bootc_source = args.bootc_source + bootc_installer_ref = args.bootc_installer_ref + + testlib.core.check_config_names() + + bootc_ref = testlib.bootcsource.resolve_bootc_source(bootc_source, arch)["ref"] + + with TemporaryDirectory() as manifest_dir: + manifests = generate_manifests(manifest_dir, arch, bootc_source, + bootc_installer_ref=bootc_installer_ref) + build_requests = testlib.core.filter_builds(manifests, arch=arch) + + with open(args.config, "w", encoding="utf-8") as config_file: + if len(build_requests) == 0: + print("โšซ No manifest changes detected. Generating null config.") + config_file.write(testlib.core.NULL_CONFIG) + return + + config_file.write(testlib.core.BASE_CONFIG) + generate_configs(build_requests, config_file, bootc_source, bootc_ref) + + +if __name__ == "__main__": + main() diff --git a/test/scripts/generate-gitlab-ci b/test/scripts/generate-gitlab-ci index 24dbe0ad4f..35cb52afbd 100755 --- a/test/scripts/generate-gitlab-ci +++ b/test/scripts/generate-gitlab-ci @@ -10,6 +10,7 @@ ARCHITECTURES = ["x86_64", "aarch64"] MANIFEST_ONLY_ARCHES = ["ppc64le", "s390x"] RUNNER = testlib.testenv.get_common_ci_runner() OSTREE_DISTROS = ["fedora", "rhel-8", "rhel-9", "centos-9"] +BOOTC_SOURCES = ["centos-10", "rhel-10"] BASE_CONFIG = """--- @@ -113,6 +114,34 @@ OSTREE_TRIGGER_TEMPLATE = """ - "generate-ostree-build-config: [{distro}, {arch}]" """ +BOOTC_GEN_TEMPLATE = """ +"generate-bootc-config: [{bootc_source}, {arch}]": + stage: gen + extends: .terraform + variables: + RUNNER: {runner}-{arch} + INTERNAL_NETWORK: "false" + script: + - sudo -E ./test/scripts/setup-osbuild-repo + - sudo ./test/scripts/install-dependencies + - ./test/scripts/generate-bootc-config --bootc-source {bootc_source} --arch {arch} boot-config.yml + artifacts: + paths: + - boot-config.yml +""" + +BOOTC_TRIGGER_TEMPLATE = """ +"bootc-build-trigger: [{bootc_source}, {arch}]": + stage: build + trigger: + include: + - artifact: boot-config.yml + job: "generate-bootc-config: [{bootc_source}, {arch}]" + strategy: depend + needs: + - "generate-bootc-config: [{bootc_source}, {arch}]" +""" + MANIFEST_GEN_TEMPLATE = """ "generate-manifests: [{distro}, {arch}]": @@ -179,6 +208,8 @@ def main(): trigger_stage = [] ostree_gen_stage = [] ostree_trigger_stage = [] + bootc_gen_stage = [] + bootc_trigger_stage = [] for img in sort_configs(images): combo = (img["distro"], img["arch"]) if combo in combos: @@ -235,6 +266,24 @@ def main(): test_file_path=test_file_path, )) + for bootc_source in BOOTC_SOURCES: + # Build/boot only arches that are both in the bootcref and supported for + # full image CI (skip manifest-only arches like ppc64le/s390x). + bootc_arches = [ + arch for arch in testlib.bootcsource.list_bootc_source_arches(bootc_source) + if arch in ARCHITECTURES + ] + for arch in bootc_arches: + bootc_gen_stage.append(BOOTC_GEN_TEMPLATE.format( + bootc_source=bootc_source, + arch=arch, + runner=RUNNER, + )) + bootc_trigger_stage.append(BOOTC_TRIGGER_TEMPLATE.format( + bootc_source=bootc_source, + arch=arch, + )) + with open(config_path, "w", encoding="utf-8") as config_file: config_file.write(BASE_CONFIG.format(runner=RUNNER)) config_file.write(testlib.core.BASE_CONFIG) @@ -242,6 +291,8 @@ def main(): config_file.write("\n".join(trigger_stage)) config_file.write("\n".join(ostree_gen_stage)) config_file.write("\n".join(ostree_trigger_stage)) + config_file.write("\n".join(bootc_gen_stage)) + config_file.write("\n".join(bootc_trigger_stage)) config_file.write("\n".join(man_gen_stage)) diff --git a/test/scripts/imgtestlib/bootcsource.py b/test/scripts/imgtestlib/bootcsource.py index 68735247ba..48cdf09740 100644 --- a/test/scripts/imgtestlib/bootcsource.py +++ b/test/scripts/imgtestlib/bootcsource.py @@ -33,9 +33,9 @@ def resolve_bootc_source(source_name, arch): def resolve_ref_from_entry(entry, source_name, arch, image_type=None): derived_refs = entry.get("derived_refs", {}) if image_type in DISK_IMAGE_TYPES: - return derived_refs.get("disk") + return derived_refs.get(image_type) or derived_refs.get("disk") if image_type in INSTALLER_IMAGE_TYPES: - return derived_refs.get("installer") + return derived_refs.get(image_type) or derived_refs.get("installer") ref = entry.get("ref") if not isinstance(ref, str) or not ref: diff --git a/test/scripts/imgtestlib/build.py b/test/scripts/imgtestlib/build.py index 654979a14e..dc31b42893 100644 --- a/test/scripts/imgtestlib/build.py +++ b/test/scripts/imgtestlib/build.py @@ -16,7 +16,9 @@ def resolve_bootc_options(config: dict, distro: str, arch: str, image_type: str source_name = distro.removeprefix("bootc-") source_data = dict(resolve_bootc_source(source_name, arch)) - source_data["ref"] = resolve_bootc_source_ref(source_name, arch, image_type=image_type) + ref = resolve_bootc_source_ref(source_name, arch, image_type=image_type) + if ref is not None: + source_data["ref"] = ref return {**source_data, **bootc} diff --git a/test/scripts/imgtestlib/core.py b/test/scripts/imgtestlib/core.py index dd37f69f65..c6896a4fa3 100644 --- a/test/scripts/imgtestlib/core.py +++ b/test/scripts/imgtestlib/core.py @@ -281,6 +281,19 @@ def clargs(): return parser +def bootc_clargs(): + default_arch = os.uname().machine + parser = argparse.ArgumentParser() + parser.add_argument("config", type=str, help="path to write config") + parser.add_argument("--bootc-source", type=str, required=True, + help="bootc source name (test/data/bootcrefs/.json)") + parser.add_argument("--arch", type=str, default=default_arch, + help="architecture to generate configs for (defaults to host architecture)") + parser.add_argument("--bootc-installer-ref", type=str, default=None, + help="installer payload container ref for manifest generation") + return parser + + def is_manifest_list(data): """Inspect a manifest determine if it's a multi-image manifest-list.""" media_type = data.get("mediaType") diff --git a/test/scripts/test_imgtestlib.py b/test/scripts/test_imgtestlib.py index c71b7c2dc2..a97eeb0d4d 100644 --- a/test/scripts/test_imgtestlib.py +++ b/test/scripts/test_imgtestlib.py @@ -132,6 +132,30 @@ def test_resolve_ref_from_entry_returns_derived_refs(): }, } assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "qcow2") == "quay.io/example/disk:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "raw") == "quay.io/example/disk:latest" + assert testlib.bootcsource.resolve_ref_from_entry( + entry, "src", "x86_64", "bootc-installer") == "quay.io/example/installer:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "ami") == "quay.io/example/disk:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "gce") == "quay.io/example/disk:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "vhd") == "quay.io/example/disk:latest" + + +def test_resolve_ref_from_entry_cloud_specific_derived_refs(): + entry = { + "ref": "registry.redhat.io/rhel10/rhel-bootc:latest", + "derived_refs": { + "disk": "quay.io/example/disk:latest", + "installer": "quay.io/example/installer:latest", + "ami": "quay.io/example/ami:latest", + "vhd": "quay.io/example/vhd:latest", + "gce": "quay.io/example/gce:latest", + }, + } + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "ami") == "quay.io/example/ami:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "gce") == "quay.io/example/gce:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "vhd") == "quay.io/example/vhd:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "qcow2") == "quay.io/example/disk:latest" + assert testlib.bootcsource.resolve_ref_from_entry(entry, "src", "x86_64", "raw") == "quay.io/example/disk:latest" assert testlib.bootcsource.resolve_ref_from_entry( entry, "src", "x86_64", "bootc-installer") == "quay.io/example/installer:latest" From 3f960266d026df9422adbfea31af59404be7f48c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Thu, 6 Aug 2026 11:35:02 +0200 Subject: [PATCH 6/8] imgtestlib: run gen-manifests for bootc as sudo Bootc requires rootful podman mount, this commit prepends sudo to the command "go run ./cmd/gen-manifests". --- test/scripts/imgtestlib/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/scripts/imgtestlib/core.py b/test/scripts/imgtestlib/core.py index c6896a4fa3..649585fb3c 100644 --- a/test/scripts/imgtestlib/core.py +++ b/test/scripts/imgtestlib/core.py @@ -128,6 +128,9 @@ def gen_manifests(outputdir, config_list=None, distros=None, arches=None, images cmd.append("--flatpaks") if skip_no_config: cmd.append("--skip-noconfig") + # Bootc needs rootful "podman mount". + if bootc_refs and os.geteuid() != 0: + cmd = ["sudo", *cmd] env = rng_seed_env() env["GOPROXY"] = "https://proxy.golang.org,direct" print("โŒจ๏ธ" + " ".join(cmd) + " ENV: " + str(env)) From 58aa2b3a623fb2909b044d30d1ee1159a7fe3614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Fri, 7 Aug 2026 15:00:42 +0200 Subject: [PATCH 7/8] gitlab: disable anaconda and pxe bootc tests Skip known-broken bootc image types in generate-bootc-config, so gen-manifests can succeed and the bootc pipeline can be generated. --- test/scripts/generate-bootc-config | 103 ++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 30 deletions(-) diff --git a/test/scripts/generate-bootc-config b/test/scripts/generate-bootc-config index d4409dfb47..375b647637 100755 --- a/test/scripts/generate-bootc-config +++ b/test/scripts/generate-bootc-config @@ -9,6 +9,28 @@ import imgtestlib as testlib BOOTC_CONFIGS = {"bootc-empty", "bootc-with-payload"} +BOOTC_IMAGE_TYPES = [ + "qcow2", + "ami", + "raw", + "gce", + "vhd", + "vmdk", + "ova", + "anaconda-iso", + "bootc-generic-iso", + "bootc-installer", + "pxe-tar-xz", +] + +# TODO: Fix these and remove the skips; they break gen-manifests today: +# * anaconda-iso โ€” depsolve fails: InvalidRequest: No 'repos' or 'root_dir' +# * pxe-tar-xz โ€” initramfs requires ostree, dmsquash-live and livenet modules +BOOTC_SKIP_IMAGE_TYPES = { + "anaconda-iso", + "pxe-tar-xz", +} + JOB_TEMPLATE = """ build/{distro}/{arch}/{image_type}/{config_name}: stage: test @@ -27,8 +49,31 @@ build/{distro}/{arch}/{image_type}/{config_name}: INTERNAL_NETWORK: "{internal}" """ -def should_include_config(config_name: str) -> bool: - return config_name in BOOTC_CONFIGS + +def should_skip(config_name, image_type): + return image_type in BOOTC_SKIP_IMAGE_TYPES or config_name not in BOOTC_CONFIGS + + +def group_image_types_by_ref(bootc_source, arch): + """Group active image types by their resolved bootc refspec. + + Loads the bootc source entry once and resolves refs in-memory to avoid re-reading the source file. + """ + groups = {} + bootc_entry = testlib.bootcsource.resolve_bootc_source(bootc_source, arch) + base_ref = bootc_entry.get("ref") + build_ref = bootc_entry.get("build_ref") + for image_type in BOOTC_IMAGE_TYPES: + if image_type in BOOTC_SKIP_IMAGE_TYPES: + continue + ref = testlib.bootcsource.resolve_ref_from_entry(bootc_entry, bootc_source, arch, image_type=image_type) + if ref is None: + continue + refspec = ref + if ref == base_ref and build_ref: + refspec = f"{ref}#{build_ref}" + groups.setdefault(refspec, []).append(image_type) + return groups @testlib.gitlab.log_section("Generating manifests") @@ -37,36 +82,33 @@ def generate_manifests(outputdir, arch, bootc_source, bootc_installer_ref=None): Generate bootc manifests using the default config list and return a dictionary mapping each manifest file to the manifest data and its ID. """ - bootc_entry = testlib.bootcsource.resolve_bootc_source(bootc_source, arch) - bootc_ref = bootc_entry["ref"] - bootc_refspec = bootc_ref - if build_ref := bootc_entry.get("build_ref"): - bootc_refspec = f"{bootc_ref}#{build_ref}" - print(f"๐Ÿ—’๏ธ Generating bootc manifests using the default config list for {bootc_source} [{arch}]") - # Omit --distros: with --bootc-refs, gen-manifests skips the RPM/repo loop. - err = testlib.core.gen_manifests(outputdir, arches=[arch], - bootc_refs=[bootc_refspec], - bootc_installer_ref=bootc_installer_ref, - skip_no_config=True) - - # print stderr in case there were errors or warnings about skipped configurations - # but filter out the annoying ones - stderr = err.decode().splitlines() - for line in stderr: - if "No match for group package" in line: - continue - if "Failed to load consumer certs" in line: - continue - print(line) + + for bootc_refspec, image_types in group_image_types_by_ref(bootc_source, arch).items(): + print(f" using {bootc_refspec} for image types: {', '.join(image_types)}") + err = testlib.core.gen_manifests(outputdir, arches=[arch], + images=image_types, + bootc_refs=[bootc_refspec], + bootc_installer_ref=bootc_installer_ref, + skip_no_config=True) + + # print stderr in case there were errors or warnings about skipped configurations + stderr = err.decode().splitlines() + for line in stderr: + if "No match for group package" in line: + continue + if "Failed to load consumer certs" in line: + continue + print(line) print("โœ… Manifest generation done!\n") return testlib.core.read_manifests(outputdir) -def generate_configs(build_requests, pipeline_file, bootc_source, bootc_ref): +def generate_configs(build_requests, pipeline_file, bootc_source): print(f"๐Ÿงช Generating dynamic pipelines for {len(build_requests)} builds") expected_distro = f"bootc-{bootc_source}" + bootc_entries = {} # cache to avoid re-reading the bootc source file for each build for build in build_requests: distro = build["distro"] arch = build["arch"] @@ -79,9 +121,8 @@ def generate_configs(build_requests, pipeline_file, bootc_source, bootc_ref): f"expected distro {expected_distro} for bootc source {bootc_source}") continue - if not should_include_config(config_name): - print(f"๐Ÿฆ˜ Skipping {distro}/{arch}/{image_type}/{config_name}: " - f"not in bootc config set") + if should_skip(config_name, image_type): + print(f"๐Ÿฆ˜ Skipping {distro}/{arch}/{image_type}/{config_name}") continue build_name = testlib.build.gen_build_name(distro, arch, image_type, config_name) @@ -89,6 +130,10 @@ def generate_configs(build_requests, pipeline_file, bootc_source, bootc_ref): runner = testlib.testenv.get_ci_runner_for(distro, arch, image_type) tag = testlib.core.get_tag_for(runner) + if arch not in bootc_entries: + bootc_entries[arch] = testlib.bootcsource.resolve_bootc_source(bootc_source, arch) + bootc_ref = testlib.bootcsource.resolve_ref_from_entry(bootc_entries[arch], bootc_source, arch, + image_type=image_type) if bootc_ref is None: print(f"๐Ÿฆ˜ Skipping {distro}/{arch}/{image_type}/{config_name}: no derived ref") @@ -113,8 +158,6 @@ def main(): testlib.core.check_config_names() - bootc_ref = testlib.bootcsource.resolve_bootc_source(bootc_source, arch)["ref"] - with TemporaryDirectory() as manifest_dir: manifests = generate_manifests(manifest_dir, arch, bootc_source, bootc_installer_ref=bootc_installer_ref) @@ -127,7 +170,7 @@ def main(): return config_file.write(testlib.core.BASE_CONFIG) - generate_configs(build_requests, config_file, bootc_source, bootc_ref) + generate_configs(build_requests, config_file, bootc_source) if __name__ == "__main__": From ab4596a3d795b540334b34b49f1b38f786dfc856 Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Tue, 11 Aug 2026 12:12:33 +0200 Subject: [PATCH 8/8] test: boot bootc images as uefi-preferred These are generally hybrid but can't be described nor inspected for their boot mode; hardcode any bootc-type to be hybrid. Signed-off-by: Simon de Vlieger --- test/scripts/imgtestlib/boot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/scripts/imgtestlib/boot.py b/test/scripts/imgtestlib/boot.py index db12c976a8..0c6077a6fc 100644 --- a/test/scripts/imgtestlib/boot.py +++ b/test/scripts/imgtestlib/boot.py @@ -466,6 +466,10 @@ def cmd_boot_aws(distro, arch, image_type, image_name, privkey, pubkey, image_pa # for image-builder if distro == "rhel-7.9" and image_type == "ec2": boot_mode = "legacy-bios" + # bootc images aren't defined as distros so we can't request the boot mode and we + # have no inspection capabilities, try to boot as hybrid + elif distro.startswith("bootc"): + boot_mode = "uefi-preferred" else: # otherwise ask for the boot mode boot_mode = get_boot_mode(distro, arch, image_type)