From 5690d5f90a10d708f3d73a334acb9b27f6dbca32 Mon Sep 17 00:00:00 2001 From: Sam Fowler Date: Mon, 9 Mar 2026 17:14:39 +1000 Subject: [PATCH] csmock: add experimental --hermetic-build option Using this requires setting up the lockfile and local repo earlier using regular mock, as described here: https://github.com/rpm-software-management/mock/blob/main/docs/feature-hermetic-builds.md This requires that the required scanners are either provided by the host or present in the buildroot. For instance `-t gcc` will work only if `gcc` is a build dependency. `-t cppcheck` will work if the `--use-host-cppcheck` param is used. PSSECAUT-1524 --- csmock/csmock | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/csmock/csmock b/csmock/csmock index efb96e0..e9c2718 100755 --- a/csmock/csmock +++ b/csmock/csmock @@ -30,6 +30,7 @@ import shutil import subprocess import sys import time +from typing import Optional, Tuple # local imports import csmock.common.util @@ -203,6 +204,8 @@ class MockWrapper: self.results = results self.mock_profile = props.mock_profile self.mock_root_override = props.mock_root_override + self.hermetic_build = props.hermetic_build + self.srpm = props.srpm self.pid = os.getpid() self.scrub_done = props.skip_mock_init self.init_done = props.skip_mock_init @@ -282,6 +285,17 @@ echo \"$self_pid\" > \"$lock_file\"'" \ "--disable-plugin=yum_cache"] self.def_cmd += [f"--config-opts=root={self.mock_root_override}"] + if self.hermetic_build is not None: + (lockfile, repo_dir) = self.hermetic_build + + hermetic_cmd = [mock, "--hermetic-build", lockfile, repo_dir, "--short-circuit=prep", "-N", self.srpm] + ec = self.results.exec_cmd(hermetic_cmd) + if ec != 0: + self.results.error("failed to set up hermetic chroot", ec=ec) + + # provide the offline repo for subsequent mock commands + self.def_cmd += [f"--config-opts=offline_local_repository={repo_dir}"] + return self def __exit__(self, exc_type, exc_val, exc_tb): @@ -403,7 +417,8 @@ echo \"$self_pid\" > \"$lock_file\"'" \ self.init_done = True # run `mock --calculate-build-dependencies` - srpm_deps_ok = srpm is None or self.install_deps(srpm) + # skip for hermetic builds (deps should be calculated prior) + srpm_deps_ok = srpm is None or self.hermetic_build is not None or self.install_deps(srpm) if not srpm_deps_ok and not try_only: srpm_base = os.path.basename(srpm) self.results.error(f"failed to install build dependencies of {srpm_base}", ec=ec_by_scrub) @@ -422,6 +437,13 @@ echo \"$self_pid\" > \"$lock_file\"'" \ if not missing_deps: # no misssing dependencies return srpm_deps_ok + + if self.hermetic_build and missing_deps: + self.results.print_with_ts( + f"WARN: Proceeding with hermetic build despite missing deps: {strlist_to_shell_cmd(missing_deps)}" + ) + return srpm_deps_ok + if try_only: return False @@ -476,7 +498,7 @@ class ScanProps: self.shell_cmd_to_build = None self.srpm = None self.base_srpm = None - self.mock_profile = None + self.mock_profile: Optional[str] = None self.base_mock_profile = None self.mock_root_override = None self.any_tool = False @@ -486,6 +508,7 @@ class ScanProps: self.imp_csgrep_filters = [] self.cswrap_path = None self.kfp_git_url = None + self.hermetic_build: Optional[Tuple] = None def enable_cswrap(self): if self.cswrap_enabled: @@ -862,6 +885,14 @@ exceeds the specified limit (defaults to 1024).") help='override the build root directory for mock (disables yum and root cache)' ) + parser.add_argument( + "--hermetic-build", + nargs=2, + metavar=("LOCKFILE", "REPO_DIRECTORY"), + help="perform a hermetic (fully offline) build using a pre-generated " + "lockfile and offline RPM repository (see mock --hermetic-build)", + ) + # --skip-patches, --diff-patches, and --shell-cmd are mutually exclusive group = parser.add_mutually_exclusive_group() group.add_argument( @@ -996,6 +1027,17 @@ exceeds the specified limit (defaults to 1024).") props.mock_root_override = args.mock_root_override + if args.hermetic_build is not None: + (lockfile, repo_dir) = args.hermetic_build + require_file(parser, lockfile) + if not os.path.isdir(repo_dir): + parser.error(f"not a directory: {repo_dir}") + if not os.path.isdir(os.path.join(repo_dir, "repodata")): + parser.error(f"repo directory missing repodata/: {repo_dir}") + props.hermetic_build = (os.path.realpath(lockfile), os.path.realpath(repo_dir)) + props.mock_profile = "hermetic-build" + props.skip_mock_init = True + # append the list of packages to install specified on command-line for pkg in args.install: props.install_pkgs += pkg.split()