@@ -30,6 +30,7 @@ import shutil
3030import subprocess
3131import sys
3232import time
33+ from typing import Optional , Tuple
3334
3435# local imports
3536import 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