From 35467bdbdcc01a8ad6b42c33c935ffe5c8c74395 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 26 Aug 2025 17:05:21 -0400 Subject: [PATCH] bootc-base-imagectl: add `--lock` We want to be able to pin certain packages to certain versions during the build. Ideally, we'd be able to use `dnf versionlock` for this before calling `bootc-base-imagectl`, but that obviously doesn't work because it's not actually dnf in the backend but rpm-ostree. Add a new `--lock` which provides `dnf versionlock`-like functionality. But in practice, this is backed currently by rpm-ostree lockfiles. Both rpm-ostree lockfiles and the versionlock plugin are implemented the same way, leveraging excludes to make some packages completely invisible to the solver. So we should be able to retain this interface in the future when moving to dnf. --- bootc-base-imagectl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bootc-base-imagectl b/bootc-base-imagectl index 335e470..824d17d 100755 --- a/bootc-base-imagectl +++ b/bootc-base-imagectl @@ -11,6 +11,7 @@ import subprocess import sys import tempfile +ARCH = os.uname().machine MANIFESTDIR = 'usr/share/doc/bootc-base-imagectl/manifests' def run_build_rootfs(args): @@ -66,6 +67,21 @@ def run_build_rootfs(args): tmp_manifest.close() manifest_path = tmp_manifest.name + tmp_lockfile = None + if args.lock: + lockfile = {'packages': {}} + for nevra in args.lock: + # we support passing either a NEVRA or a NEVR + name, ev, r_or_ra = nevra.rsplit('-', 2) + evr_or_evra = f'{ev}-{r_or_ra}' + field = 'evra' if r_or_ra.endswith(('.noarch', f'.{ARCH}')) else 'evr' + lockfile['packages'][name] = {field: evr_or_evra} + + tmp_lockfile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.json', delete=False) + json.dump(lockfile, tmp_lockfile) + tmp_lockfile.close() + rpmostree_argv.append(f"--lockfile={tmp_lockfile.name}") + try: if args.cachedir != "": rpmostree_argv.append(f"--cachedir={args.cachedir}") @@ -94,6 +110,8 @@ def run_build_rootfs(args): print(f"Error executing command: {e}") sys.exit(1) finally: + if tmp_lockfile is not None: + os.unlink(tmp_lockfile.name) if tmp_manifest is not None: os.unlink(tmp_manifest.name) if tmp_ostree_repo: @@ -160,6 +178,7 @@ if __name__ == "__main__": build_rootfs.add_argument("--sysusers", help="Run systemd-sysusers instead of injecting hardcoded passwd/group entries", action='store_true') build_rootfs.add_argument("--nobody-99", help=argparse.SUPPRESS, action='store_true') build_rootfs.add_argument("--repo", help="Enable specific repositories only", action='append', default=[], metavar='REPO') + build_rootfs.add_argument("--lock", help="Lock package to specific version", action='append', default=[], metavar='NEVRA') build_rootfs.add_argument("source_root", help="Path to the source root directory used for dnf configuration (default=/)", nargs='?', default='/') build_rootfs.add_argument("target", help="Path to the target root directory that will be generated.") build_rootfs.set_defaults(func=run_build_rootfs)