Skip to content

Commit eec89ae

Browse files
committed
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. Resolves: https://redhat.atlassian.net/browse/PSSECAUT-1524
1 parent f3007a6 commit eec89ae

1 file changed

Lines changed: 50 additions & 3 deletions

File tree

csmock/csmock

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import shutil
3030
import subprocess
3131
import sys
3232
import time
33+
from typing import Optional, Tuple
3334

3435
# local imports
3536
import csmock.common.util
@@ -203,6 +204,7 @@ class MockWrapper:
203204
self.results = results
204205
self.mock_profile = props.mock_profile
205206
self.mock_root_override = props.mock_root_override
207+
self.hermetic_build = props.hermetic_build
206208
self.pid = os.getpid()
207209
self.scrub_done = props.skip_mock_init
208210
self.init_done = props.skip_mock_init
@@ -268,7 +270,7 @@ echo \"$self_pid\" > \"$lock_file\"'" \
268270
else:
269271
# fallback to any mock in $PATH (e.g. /usr/local/bin/mock)
270272
mock = "mock"
271-
self.def_cmd = [mock, "-r", self.mock_profile]
273+
self.def_cmd = [mock]
272274

273275
# make csmock work in case the 'tmpfs' plug-in is enabled
274276
# (see <https://bugzilla.redhat.com/1190100> for details)
@@ -284,6 +286,21 @@ echo \"$self_pid\" > \"$lock_file\"'" \
284286

285287
return self
286288

289+
def setup_chroot(self, srpm):
290+
"""Set up the mock chroot, including hermetic build setup if requested."""
291+
if self.hermetic_build is not None:
292+
(lockfile, repo_dir) = self.hermetic_build
293+
ec = self.exec_mock_cmd(["--hermetic-build", lockfile, repo_dir,
294+
"--short-circuit=prep", "--rpmbuild-opts=--noprep",
295+
"-N", srpm], quiet=False)
296+
if ec != 0:
297+
self.results.error("failed to set up hermetic chroot", ec=ec)
298+
self.def_cmd += [f"--config-opts=offline_local_repository={repo_dir}"]
299+
300+
# -r is added here (rather than in __enter__) because mock's
301+
# --hermetic-build is incompatible with -r and must run first
302+
self.def_cmd += ["-r", self.mock_profile]
303+
287304
def __exit__(self, exc_type, exc_val, exc_tb):
288305
if not self.skip_clean:
289306
# clean up mock chroot
@@ -403,7 +420,8 @@ echo \"$self_pid\" > \"$lock_file\"'" \
403420
self.init_done = True
404421

405422
# run `mock --calculate-build-dependencies`
406-
srpm_deps_ok = srpm is None or self.install_deps(srpm)
423+
# skip for hermetic builds (deps should be calculated prior)
424+
srpm_deps_ok = srpm is None or self.hermetic_build is not None or self.install_deps(srpm)
407425
if not srpm_deps_ok and not try_only:
408426
srpm_base = os.path.basename(srpm)
409427
self.results.error(f"failed to install build dependencies of {srpm_base}", ec=ec_by_scrub)
@@ -422,6 +440,13 @@ echo \"$self_pid\" > \"$lock_file\"'" \
422440
if not missing_deps:
423441
# no misssing dependencies
424442
return srpm_deps_ok
443+
444+
if self.hermetic_build and missing_deps:
445+
self.results.print_with_ts(
446+
f"WARN: Proceeding with hermetic build despite missing deps: {strlist_to_shell_cmd(missing_deps)}"
447+
)
448+
return srpm_deps_ok
449+
425450
if try_only:
426451
return False
427452

@@ -476,7 +501,7 @@ class ScanProps:
476501
self.shell_cmd_to_build = None
477502
self.srpm = None
478503
self.base_srpm = None
479-
self.mock_profile = None
504+
self.mock_profile: Optional[str] = None
480505
self.base_mock_profile = None
481506
self.mock_root_override = None
482507
self.any_tool = False
@@ -486,6 +511,7 @@ class ScanProps:
486511
self.imp_csgrep_filters = []
487512
self.cswrap_path = None
488513
self.kfp_git_url = None
514+
self.hermetic_build: Optional[Tuple] = None
489515

490516
def enable_cswrap(self):
491517
if self.cswrap_enabled:
@@ -862,6 +888,14 @@ exceeds the specified limit (defaults to 1024).")
862888
help='override the build root directory for mock (disables yum and root cache)'
863889
)
864890

891+
parser.add_argument(
892+
"--hermetic-build",
893+
nargs=2,
894+
metavar=("LOCKFILE", "REPO_DIRECTORY"),
895+
help="perform a hermetic (fully offline) build using a pre-generated "
896+
"lockfile and offline RPM repository (see mock --hermetic-build)",
897+
)
898+
865899
# --skip-patches, --diff-patches, and --shell-cmd are mutually exclusive
866900
group = parser.add_mutually_exclusive_group()
867901
group.add_argument(
@@ -996,6 +1030,17 @@ exceeds the specified limit (defaults to 1024).")
9961030

9971031
props.mock_root_override = args.mock_root_override
9981032

1033+
if args.hermetic_build is not None:
1034+
(lockfile, repo_dir) = args.hermetic_build
1035+
require_file(parser, lockfile)
1036+
if not os.path.isdir(repo_dir):
1037+
parser.error(f"not a directory: {repo_dir}")
1038+
if not os.path.isdir(os.path.join(repo_dir, "repodata")):
1039+
parser.error(f"repo directory missing repodata/: {repo_dir}")
1040+
props.hermetic_build = (os.path.realpath(lockfile), os.path.realpath(repo_dir))
1041+
props.mock_profile = "hermetic-build"
1042+
props.skip_mock_init = True
1043+
9991044
# append the list of packages to install specified on command-line
10001045
for pkg in args.install:
10011046
props.install_pkgs += pkg.split()
@@ -1106,6 +1151,8 @@ the package. Use --tools or --all-tools to enable them!\n", ec=0)
11061151
props.run_hooks(results, "pre-mock", results, props)
11071152

11081153
with MockWrapper(results, props) as mock:
1154+
mock.setup_chroot(props.srpm)
1155+
11091156
if srpm_dup is not None:
11101157
# first rebuild the given SRPM (some deps might be required even for the rebuild)
11111158
mock.init_and_install(srpm_dup, props.install_pkgs, try_only=True)

0 commit comments

Comments
 (0)