From c7cefdee721978173b2e1451ee310de6985de013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Tue, 28 Apr 2026 14:19:40 +0200 Subject: [PATCH 1/3] builder: use --with-rpmlist for ibcli Adds a flag --with-rpmlist to show rpmlist in the output of the image-builder build in koji. Co-authored-by: Simon de Vlieger Signed-off-by: Simon de Vlieger --- plugin/builder/image_builder.py | 25 +++++++++++++++++++++++++ test/unit/test_builder.py | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/plugin/builder/image_builder.py b/plugin/builder/image_builder.py index 116b4dc..14dfadc 100644 --- a/plugin/builder/image_builder.py +++ b/plugin/builder/image_builder.py @@ -109,6 +109,28 @@ def target_repo(topdir, target_info, repo_info): return f"{repo}/$arch" +def load_rpmlist_from_output(output_dir): + """Load *.rpmlist.json files written by image-builder-cli --with-rpmlist. + """ + + rpmlist = [] + if not os.path.isdir(output_dir): + return rpmlist + + for root, _, files in os.walk(output_dir): + for file in files: + if not file.endswith(".rpmlist.json"): + continue + path = os.path.join(root, file) + try: + with open(path, "r", encoding="utf-8") as f: + rpmlist.extend(json.load(f)) + except (OSError, UnicodeError, json.JSONDecodeError) as e: + logger.warning("skipped rpmlist %s: %s", path, e) + + return rpmlist + + class ImageBuilderBuildTask(BuildImageTask): """Spawns imageBuilderBuildArch tasks.""" @@ -421,6 +443,7 @@ def handler( [ "--with-sbom", "--with-manifest", + "--with-rpmlist", ] ) @@ -504,6 +527,8 @@ def handler( self.uploadFile(os.path.join(root, file), remoteName=file) data["files"].append(file) + data["rpmlist"] = load_rpmlist_from_output(output) + broot.expire() return data diff --git a/test/unit/test_builder.py b/test/unit/test_builder.py index 6c4ae3e..9496ad3 100644 --- a/test/unit/test_builder.py +++ b/test/unit/test_builder.py @@ -53,6 +53,7 @@ def test_build_arch_task(koji_mock_kojid): "//repos/f42-build/1/$arch", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--output-dir", "/builddir/output", "--output-name", @@ -101,6 +102,7 @@ def test_build_arch_task_with_repos(koji_mock_kojid): "c/x86_64/d", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--output-dir", "/builddir/output", "--output-name", @@ -147,6 +149,7 @@ def test_build_arch_task_multiple_types(koji_mock_kojid): "//repos/f42-build/1/$arch", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--output-dir", "/builddir/output", "--output-name", @@ -168,6 +171,7 @@ def test_build_arch_task_multiple_types(koji_mock_kojid): "//repos/f42-build/1/$arch", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--output-dir", "/builddir/output", "--output-name", @@ -220,6 +224,7 @@ def test_build_arch_task_ostree(koji_mock_kojid): "//repos/f42-build/1/$arch", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--ostree-url", "https://kojipkgs.fedoraproject.org/compose/iot/repo/", "--ostree-ref", @@ -297,6 +302,7 @@ def test_build_arch_task_seed(koji_mock_kojid): "//repos/f42-build/1/$arch", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--seed", "1234", "--output-dir", "/builddir/output", @@ -347,6 +353,7 @@ def test_build_arch_task_preview(koji_mock_kojid): "//repos/f42-build/1/$arch", "--with-sbom", "--with-manifest", + "--with-rpmlist", "--preview", "false", "--output-dir", "/builddir/output", @@ -357,3 +364,22 @@ def test_build_arch_task_preview(koji_mock_kojid): ] +def test_load_rpmlist_from_output_missing(koji_mock_kojid, tmp_path): + import plugin.builder.image_builder as builder + + missing = tmp_path / "not-a-dir" + assert builder.load_rpmlist_from_output(str(missing)) == [] + + +def test_load_rpmlist_from_output(koji_mock_kojid, tmp_path): + import plugin.builder.image_builder as builder + + out = tmp_path / "output" + out.mkdir() + (out / "img.rpmlist.json").write_text( + '[{"name":"mpfr","version":"4.1.0","release":"10.el9","epoch":0,"arch":"x86_64","buildtime":1770637270,"size":331788,"payloadhash":"11c1d6b33b7e64ddc40faf45b949618c829bd2e3d3661132417e4c8aee6ab0fd"}]', + encoding="utf-8", + ) + assert builder.load_rpmlist_from_output(str(out)) == [ + {"name":"mpfr","version":"4.1.0","release":"10.el9","epoch":0,"arch":"x86_64","buildtime":1770637270,"size":331788,"payloadhash":"11c1d6b33b7e64ddc40faf45b949618c829bd2e3d3661132417e4c8aee6ab0fd"}, + ] From 52b47e962d524ed957a069cc10ccc558b36a53db Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Thu, 7 May 2026 10:52:48 +0200 Subject: [PATCH 2/3] build: mark external RPMs When RPMs come from external repositories they need to be marked as such. The `BuildRoot` has a helper method to achieve this. Let's apply it. Signed-off-by: Simon de Vlieger --- plugin/builder/image_builder.py | 4 +++- test/conftest.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugin/builder/image_builder.py b/plugin/builder/image_builder.py index 14dfadc..024addf 100644 --- a/plugin/builder/image_builder.py +++ b/plugin/builder/image_builder.py @@ -527,7 +527,9 @@ def handler( self.uploadFile(os.path.join(root, file), remoteName=file) data["files"].append(file) - data["rpmlist"] = load_rpmlist_from_output(output) + rpmlist = load_rpmlist_from_output(output) + broot.markExternalRPMs(rpmlist) + data["rpmlist"] = rpmlist broot.expire() diff --git a/test/conftest.py b/test/conftest.py index 3fd4c0c..0266052 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -32,6 +32,9 @@ def mock(self, args): return 0 + def markExternalRPMs(self, args, **kwargs): + pass + @pytest.fixture def koji_mock_kojid(mocker, tmpdir): From 42808d789f92d0b3af7c2c1584841a1944d16c34 Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Thu, 7 May 2026 11:04:06 +0200 Subject: [PATCH 3/3] builder: rpmlist only when non-scratch Only attach the RPM list when this is a non-scratch build. This is identical to the behavior for the Kiwi plugin. Signed-off-by: Simon de Vlieger --- plugin/builder/image_builder.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugin/builder/image_builder.py b/plugin/builder/image_builder.py index 024addf..e0591d4 100644 --- a/plugin/builder/image_builder.py +++ b/plugin/builder/image_builder.py @@ -527,9 +527,11 @@ def handler( self.uploadFile(os.path.join(root, file), remoteName=file) data["files"].append(file) - rpmlist = load_rpmlist_from_output(output) - broot.markExternalRPMs(rpmlist) - data["rpmlist"] = rpmlist + # Only do the hdrlist when this is a non-scratch build + if not self.opts.get("scratch"): + rpmlist = load_rpmlist_from_output(output) + broot.markExternalRPMs(rpmlist) + data["rpmlist"] = rpmlist broot.expire()