From b8a90e626f77099554bb726c29ebdc6aae4828b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Tue, 21 Apr 2026 17:40:03 +0200 Subject: [PATCH 1/2] main: provide --with-rpmlist to output a json file Using this flag enables outputing a json file with rpmlist. This enables a list of rpms to be displayed in koji. --- cmd/image-builder/main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/image-builder/main.go b/cmd/image-builder/main.go index 486c50b9..1657fc04 100644 --- a/cmd/image-builder/main.go +++ b/cmd/image-builder/main.go @@ -179,6 +179,10 @@ func cmdManifestWrapper(pbar progress.ProgressBar, cmd *cobra.Command, args []st if err != nil { return nil, err } + withRPMList, err := cmd.Flags().GetBool("with-rpmlist") + if err != nil { + return nil, err + } ignoreWarnings, err := cmd.Flags().GetBool("ignore-warnings") if err != nil { return nil, err @@ -351,6 +355,7 @@ func cmdManifestWrapper(pbar progress.ProgressBar, cmd *cobra.Command, args []st BootcInstallerPayloadRef: bootcInstallerPayloadRef, BootcOmitDefaultKernelArgs: bootcOmitDefaultKernelArgs, WithSBOM: withSBOM, + WithRPMList: withRPMList, IgnoreWarnings: ignoreWarnings, Subscription: subscription, Preview: preview, @@ -627,6 +632,7 @@ operating systems like Fedora, CentOS and RHEL with easy customizations support. manifestCmd.Flags().Bool("bootc-no-default-kernel-args", false, `don't use the default kernel arguments`) manifestCmd.Flags().Bool("use-librepo", true, `use librepo to download packages (disable if you use old versions of osbuild)`) manifestCmd.Flags().Bool("with-sbom", false, `export SPDX SBOM document`) + manifestCmd.Flags().Bool("with-rpmlist", false, `export RPM list as JSON`) manifestCmd.Flags().Bool("ignore-warnings", false, `ignore warnings during manifest generation`) manifestCmd.Flags().String("registrations", "", `filename of a registrations file with e.g. subscription details`) manifestCmd.Flags().String("rpmmd-cache", "", `osbuild directory to cache rpm metadata`) From 70372df6ce0a5e02f780cf64d5e5232488a5a82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Wed, 22 Apr 2026 12:15:52 +0200 Subject: [PATCH 2/2] cmd/image-builder: add rpmlist for manifestgen Add creation if an rpmlist in a json file as an output of the manifestgen. This can later be used by koji in order to show rpmlist in a format it expects. --- cmd/image-builder/manifest.go | 13 +++++++++++-- test/test_build.py | 24 ++++++++++++++++++++++++ test/test_manifest.py | 22 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/cmd/image-builder/manifest.go b/cmd/image-builder/manifest.go index f7120f0d..283492d1 100644 --- a/cmd/image-builder/manifest.go +++ b/cmd/image-builder/manifest.go @@ -34,13 +34,14 @@ type manifestOptions struct { Subscription *subscription.ImageOptions RpmDownloader osbuild.RpmDownloader WithSBOM bool + WithRPMList bool IgnoreWarnings bool Preview *bool ForceRepos []string } -func sbomWriter(outputDir, filename string, content io.Reader) error { +func fileWriter(outputDir, filename string, content io.Reader) error { p := filepath.Join(outputDir, filename) if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { return err @@ -69,7 +70,7 @@ func generateManifest(repoDir string, extraRepos []string, img *imagefilter.Resu outputDir := basenameFor(img, opts.OutputDir) manifestGenOpts.SBOMWriter = func(filename string, content io.Reader, docType sbom.StandardType) error { filename = fmt.Sprintf("%s.%s", basenameFor(img, opts.OutputFilename), strings.SplitN(filename, ".", 2)[1]) - return sbomWriter(outputDir, filename, content) + return fileWriter(outputDir, filename, content) } } if len(opts.ForceRepos) > 0 { @@ -83,6 +84,14 @@ func generateManifest(repoDir string, extraRepos []string, img *imagefilter.Resu manifestGenOpts.WarningsOutput = os.Stderr } + if opts.WithRPMList { + outputDir := basenameFor(img, opts.OutputDir) + manifestGenOpts.RPMListWriter = func(filename string, content io.Reader) error { + filename = fmt.Sprintf("%s.%s", basenameFor(img, opts.OutputFilename), filename) + return fileWriter(outputDir, filename, content) + } + } + mg, err := manifestgen.New(repos, manifestGenOpts) if err != nil { return err diff --git a/test/test_build.py b/test/test_build.py index 306542cd..ff0f81aa 100644 --- a/test/test_build.py +++ b/test/test_build.py @@ -1,3 +1,4 @@ +import json import os import pathlib import platform @@ -63,6 +64,29 @@ def test_build_build_generates_manifest(tmp_path, build_container, shared_store) assert image_manifest_path.exists() +@pytest.mark.skipif(os.getuid() != 0, reason="needs root") +def test_build_generates_rpmlist(tmp_path, build_container, shared_store): + output_dir = tmp_path / "output" + output_dir.mkdir() + subprocess.check_call(podman_run + [ + "-v", f"{output_dir}:/output", + "-v", f"{shared_store}:{OSBUILD_STORE_CONTAINER_PATH}", + build_container, + "build", + "qcow2", + "--distro", "centos-9", + "--with-rpmlist", + ], stdout=subprocess.DEVNULL) + arch = platform.machine() + fn = f"centos-9-qcow2-{arch}/centos-9-qcow2-{arch}.rpmlist.json" + rpmlist_path = output_dir / fn + assert rpmlist_path.exists() + rpmlist = json.loads(rpmlist_path.read_text()) + assert isinstance(rpmlist, list) + names = [p["name"] for p in rpmlist] + assert "glibc" in names, f"missing glibc in {rpmlist_path}" + + # pylint: disable=too-many-arguments @pytest.mark.parametrize("progress,needle,forbidden", [ ("verbose", "osbuild-stdout-output", "[|]"), diff --git a/test/test_manifest.py b/test/test_manifest.py index 3aa1463f..d744ca14 100644 --- a/test/test_manifest.py +++ b/test/test_manifest.py @@ -37,6 +37,28 @@ def test_manifest_generates_sbom(tmp_path, build_container): assert "glibc" in [s["name"] for s in sbom_json["packages"]], f"missing glibc in {sbom_json}" +@pytest.mark.skipif(os.getuid() != 0, reason="needs root") +def test_manifest_generates_rpmlist(tmp_path, build_container): + output_dir = tmp_path / "output" + output_dir.mkdir() + subprocess.check_call(podman_run + [ + "-v", f"{output_dir}:/output", + build_container, + "manifest", + "minimal-raw", + "--distro", "centos-9", + "--with-rpmlist", + ], stdout=subprocess.DEVNULL) + arch = platform.machine() + fn = f"centos-9-minimal-raw-{arch}/centos-9-minimal-raw-{arch}.rpmlist.json" + rpmlist_path = output_dir / fn + assert rpmlist_path.exists() + rpmlist = json.loads(rpmlist_path.read_text()) + assert isinstance(rpmlist, list) + names = [p["name"] for p in rpmlist] + assert "glibc" in names, f"missing glibc in {rpmlist_path}" + + @pytest.mark.parametrize("use_seed_arg", [False, True]) @pytest.mark.skipif(os.getuid() != 0, reason="needs root") def test_manifest_seeded_is_the_same(build_container, use_seed_arg):