diff --git a/BUILD.md b/BUILD.md index cea63b1..b969225 100644 --- a/BUILD.md +++ b/BUILD.md @@ -75,12 +75,16 @@ the whole run. 1. Optional chroot cleanup (`--scrub-all-chroots`) 2. Parallel build execution — only the target's manifest packages; any failure fails the run 3. Post-build chroot scrub — reclaims each build step's mock chroot (unless `--keep-buildroots`) -4. Optional install/smoke checks inside child builders (disabled with `--skip-install`) -5. Binary RPM collection into `repo//` -6. Source RPM collection into `repo-src/` -7. `createrepo --update` on both repo trees -8. Tarball creation for both repo trees -9. Summary generation (`summary.txt`) +4. Binary RPM collection into `repo//` +5. Source RPM collection into `repo-src/` +6. `createrepo --update` on both repo trees +7. Tarball creation for both repo trees +8. Summary generation (`summary.txt`) + +The build process does **not** install any built RPM onto the build host. Installing an EL8/EL9 +package on the (single, possibly EL10) build host corrupts the host RPM database; the real +install-and-run verification happens in the CI's separate Test phase (`cluster-test.pl` boots a +matching MN and installs xCAT + the freshly built xcat-dep there). # Packages notes @@ -96,8 +100,6 @@ the whole run. Use these flags to skip specific operations: -- `--skip-install` - - Skips install/smoke checks performed by child builder scripts after RPM build. - `--skip-genesis` - Skips the `xCAT-genesis-base` build (`/buildrpms.pl --package xCAT-genesis-base`). - `--skip-xcat-dep` @@ -194,15 +196,15 @@ perl ./mockbuild-all.pl \ Notes: -- Install/smoke checks run by default inside child builders. -- Add `--skip-install` to skip those checks. +- The build never installs a built RPM onto the build host (see above); install-and-run + verification is the CI Test phase's job. - Add `--skip-genesis` to skip the `xCAT-genesis-base` build (the only step that invokes `/buildrpms.pl`). - `` is optional; when omitted it is timestamp-based. # Common Build Modes -xcat-dep repo (with install/smoke checks): +xcat-dep repo: ```bash cd @@ -212,17 +214,6 @@ perl ./mockbuild-all.pl \ --scrub-all-chroots ``` -xcat-dep repo (skip install/smoke checks): - -```bash -cd -perl ./mockbuild-all.pl \ - --repo-root \ - --xcat-source \ - --scrub-all-chroots \ - --skip-install -``` - Dependency repo without the `xCAT-genesis-base` build: ```bash @@ -231,8 +222,7 @@ perl ./mockbuild-all.pl \ --repo-root \ --xcat-source \ --scrub-all-chroots \ - --skip-genesis \ - --skip-install + --skip-genesis ``` Collection-only pass from existing build artifacts: diff --git a/MockBuildUtils.pm b/MockBuildUtils.pm index f444a44..45879d7 100644 --- a/MockBuildUtils.pm +++ b/MockBuildUtils.pm @@ -10,6 +10,7 @@ use File::Basename qw(basename); use File::Copy qw(copy); use File::Find; use Sys::Hostname; +use Digest::MD5 qw(md5_hex); our @EXPORT_OK = qw( sh_quote print_step @@ -17,6 +18,7 @@ our @EXPORT_OK = qw( verify_repo_packages verify_repo_signature rpm_version rpm_release rpm_sigmd5 rpm_is_signed restamp_release_line cross_copy_genesis finalize_xcat_dep bump_dep_release_suffix + build_mock_uniqueext ); # sh_quote: single-quote a string for safe use in a shell command. @@ -377,4 +379,41 @@ sub bump_dep_release_suffix { return $bumped; } +# build_mock_uniqueext: a mock --uniqueext UNIQUE per (run, build-step) so concurrent mock builds +# never share a chroot root (/var/lib/mock/-). $run is the per-target run id +# (e.g. "alma+epel-8-ppc64le-"), $seq orders the step, $label names the package. +# +# The run id must NOT be blindly tail-truncated. The per-target id leads with the EL/arch token, and +# for the 7-char "ppc64le" arch the EL digit is exactly what falls off the front of a keep-the-last-24 +# truncation -- so alma+epel-{8,9,10}-ppc64le all collapse to the same run part. That is catastrophic +# for goconserver, which compiles EVERY EL in the el10 chroot (build_cfg rewritten to -10-): the +# chroot NAME is then identical across the three ELs, and the uniqueext is the ONLY thing keeping +# their roots apart, so three concurrent el8/el9/el10 ppc64le goconserver builds race in one root. +# When the id is too long, keep a readable leading token AND append a short digest of the FULL id, so +# distinct ids always yield distinct uniqueext regardless of where in the string they differ. +sub build_mock_uniqueext { + my ($run, $seq, $label) = @_; + + my $run_part = defined($run) ? $run : 'run'; + $run_part =~ s/[^A-Za-z0-9_.-]+/-/g; + $run_part =~ s/^-+|-+$//g; + $run_part = 'run' if $run_part eq ''; + if (length($run_part) > 24) { + my $digest = substr(md5_hex($run_part), 0, 8); + (my $head = substr($run_part, 0, 15)) =~ s/-+$//; + $run_part = "$head-$digest"; + } + + my $label_part = defined($label) ? $label : 'step'; + $label_part =~ s/[^A-Za-z0-9_.-]+/-/g; + $label_part =~ s/^-+|-+$//g; + $label_part = 'step' if $label_part eq ''; + $label_part = substr($label_part, 0, 20) if length($label_part) > 20; + + my $idx = defined($seq) ? int($seq) : 0; + $idx = 0 if $idx < 0; + + return sprintf("mba-%02d-%s-%s", $idx, $run_part, $label_part); +} + 1; diff --git a/conserver/mockbuild.pl b/conserver/mockbuild.pl index 6d175e6..e03c959 100755 --- a/conserver/mockbuild.pl +++ b/conserver/mockbuild.pl @@ -1,11 +1,10 @@ #!/usr/bin/perl # # mockbuild.pl - build conserver-xcat (the traditional C conserver, 8.2.1) for one -# mock target and smoke-test the resulting binaries. Mirrors the other xcat-dep +# mock target. Mirrors the other xcat-dep # builders (goconserver/mockbuild.pl, ipmitool/mockbuild.pl): stage sources + spec, -# build a SRPM, `mock --rebuild` it in the target chroot, copy the RPMs to -# --result-dir, then (unless --skip-install) install into the chroot and run -# `console -V` / `conserver -V` to confirm the binaries work. +# build a SRPM, `mock --rebuild` it in the target chroot, and copy the RPMs to +# --result-dir. # # conserver is NOT part of the default mockbuild-all.pl dep set (xCAT uses goconserver), # so this builder is standalone. Usage: @@ -28,7 +27,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$script_dir/../build-output/list-conserver/conserver"; my $log_dir = "$script_dir/../build-logs/list-conserver/conserver"; -my $skip_install = 0; my $build_timestamp; GetOptions( @@ -37,7 +35,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); @@ -112,19 +109,6 @@ for my $r (@rpms) { } print "built: " . join(', ', map { basename($_) } @rpms) . "\n"; -# ---- smoke test: install into the chroot and run the binaries --------------- -unless ($skip_install) { - print "== Smoke test (install + run) ==\n"; - run("mock -r " . sh_quote($mock_cfg) . $uniq . " --install " . sh_quote($main) - . " > " . sh_quote("$log_dir/install.log") . " 2>&1"); - # console (client) and conserver (daemon) both print their version to stderr/stdout. - my $smoke = capture("mock -r " . sh_quote($mock_cfg) . $uniq - . " --chroot -- " . sh_quote('/usr/bin/console -V 2>&1; /usr/sbin/conserver -V 2>&1') - . " 2>&1"); - print " output: $smoke\n"; - die "FATAL: smoke test did not report version $version\n" unless $smoke =~ /\Q$version\E/; - print "smoke test PASSED (console/conserver report $version)\n"; -} print "DONE: conserver-xcat $version for $mock_cfg -> $result_dir\n"; # ---- helpers ---------------------------------------------------------------- @@ -143,5 +127,5 @@ sub capture { sub sh_quote { my ($s) = @_; $s =~ s/'/'\\''/g; return "'$s'"; } sub usage { return "usage: mockbuild.pl --mock-cfg [--result-dir DIR] [--work-dir DIR]\n" - . " [--log-dir DIR] [--mock-uniqueext EXT] [--skip-install]\n"; + . " [--log-dir DIR] [--mock-uniqueext EXT]\n"; } diff --git a/elilo/mockbuild.pl b/elilo/mockbuild.pl index d6d7b5e..663145d 100755 --- a/elilo/mockbuild.pl +++ b/elilo/mockbuild.pl @@ -19,7 +19,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$repo_root/build-output/list3/elilo-xcat"; my $log_dir = "$repo_root/build-logs/list3/elilo-xcat"; -my $skip_install = 0; my $build_timestamp; GetOptions( @@ -29,7 +28,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); @@ -79,7 +77,6 @@ print "log_dir: $log_dir\n"; print "mock_cfg: $mock_cfg\n"; print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; print "source_file:$source_file\n"; -print "skip_install: $skip_install\n"; make_path($result_dir); make_path($log_dir); @@ -207,37 +204,6 @@ for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { or die "Failed to copy $src to $log_dir: $!\n"; } -if (!$skip_install) { - print_step("Install RPM and run smoke tests"); - run("dnf -y install " . sh_quote($main_rpm)); - - my $efi_file = '/tftpboot/xcat/elilo-x64.efi'; - die "Missing installed EFI binary: $efi_file\n" if !-f $efi_file; - - my $file_log = "$log_dir/smoke-file.log"; - my $qf_log = "$log_dir/smoke-rpm-qf.log"; - my $rc_file = run_capture_rc("file $efi_file", $file_log); - my $rc_qf = run_capture_rc("rpm -qf $efi_file", $qf_log); - - die "Smoke check failed: file returned $rc_file\n" if $rc_file != 0; - die "Smoke check failed: rpm -qf returned $rc_qf\n" if $rc_qf != 0; - - my $file_out = slurp($file_log); - my $qf_out = slurp($qf_log); - - die "EFI file signature check failed:\n$file_out\n" - if $file_out !~ /(EFI application|PE32\+ executable)/i; - die "Installed file is not owned by elilo-xcat:\n$qf_out\n" - if $qf_out !~ /^elilo-xcat-/m; - - my $summary = "$log_dir/smoke-summary.txt"; - open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; - print {$sfh} "efi_file=$efi_file\n"; - print {$sfh} "rc_file=$rc_file\n"; - print {$sfh} "rc_qf=$rc_qf\n"; - close $sfh; -} - print_step("Completed"); print "Main RPM: $main_rpm\n"; print "Artifacts: $result_dir\n"; @@ -253,7 +219,6 @@ Usage: $0 [options] --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds --result-dir PATH Output RPM/SRPM directory (default: $result_dir) --log-dir PATH Log directory (default: $log_dir) - --skip-install Skip dnf install + smoke tests --build-timestamp EPOCH Unix timestamp for SOURCE_DATE_EPOCH (deterministic builds) USAGE } diff --git a/goconserver/mockbuild.pl b/goconserver/mockbuild.pl index c6d3ec8..d9c9630 100755 --- a/goconserver/mockbuild.pl +++ b/goconserver/mockbuild.pl @@ -17,7 +17,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$repo_root/build-output/list5/goconserver"; my $log_dir = "$repo_root/build-logs/list5/goconserver"; -my $skip_install = 0; my $version = '0.3.3'; my $go_repo = 'https://github.com/xcat2/goconserver.git'; # Immutable pin: goconserver 0.3.3 is unreleased (newest tag v0.3.2) so it lives only on master. @@ -33,7 +32,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'version=s' => \$version, 'go-repo=s' => \$go_repo, 'go-ref=s' => \$go_ref, @@ -95,7 +93,6 @@ print "arch: $arch\n"; print "version: $version\n"; print "go_ref: $go_ref\n"; print "release_suffix: " . ($release_suffix ne '' ? $release_suffix : '(none)') . "\n"; -print "skip_install: $skip_install\n"; make_path($result_dir); make_path($log_dir); @@ -288,19 +285,6 @@ for my $log (qw(build.log root.log state.log)) { system("mock -r " . sh_quote($build_cfg) . $mock_uniqueext_opt . " --scrub=chroot --scrub=bootstrap >" . sh_quote("$log_dir/mock-scrub.log") . " 2>&1"); -if (!$skip_install) { - print_step("Install and smoke test"); - my $main_rpm = $arch_rpms[0]; - run("dnf -y install " . sh_quote($main_rpm) . " >" . sh_quote("$log_dir/dnf-install.log") . " 2>&1"); - die "Missing /usr/bin/goconserver\n" if !-x '/usr/bin/goconserver'; - die "Missing /usr/bin/congo\n" if !-x '/usr/bin/congo'; - my $rc_help = run_rc("goconserver -h >" . sh_quote("$log_dir/smoke-help.log") . " 2>&1"); - die "goconserver -h failed (rc=$rc_help)\n" if $rc_help > 1; - my $rc_congo = run_rc("congo -h >" . sh_quote("$log_dir/smoke-congo.log") . " 2>&1"); - die "congo -h failed (rc=$rc_congo)\n" if $rc_congo > 1; - print "Smoke tests passed.\n"; -} - print_step("Completed"); print "Results in: $result_dir\n"; exit 0; @@ -321,7 +305,6 @@ Options: --mock-uniqueext STR Mock uniqueext (for concurrency isolation under mockbuild-all.pl) --result-dir PATH Output directory for RPMs --log-dir PATH Output directory for logs - --skip-install Skip dnf install + smoke tests --version VER Version string (default: 0.3.3) --go-repo URL Git repo URL (default: github.com/xcat2/goconserver) --go-ref REF Git ref/SHA to build (default: the pinned commit) @@ -352,13 +335,6 @@ sub run { } } -sub run_rc { - my ($cmd) = @_; - print "+ $cmd\n"; - my $rc = system($cmd); - return $rc == -1 ? 255 : ($rc >> 8); -} - sub capture { my ($cmd) = @_; my $out = `$cmd`; diff --git a/grub2-xcat/mockbuild.pl b/grub2-xcat/mockbuild.pl index a7848c9..a3d8ad9 100755 --- a/grub2-xcat/mockbuild.pl +++ b/grub2-xcat/mockbuild.pl @@ -20,7 +20,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$repo_root/build-output/list3/grub2-xcat"; my $log_dir = "$repo_root/build-logs/list3/grub2-xcat"; -my $skip_install = 0; my $build_timestamp; GetOptions( @@ -30,7 +29,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); @@ -88,7 +86,6 @@ print "result_dir: $result_dir\n"; print "log_dir: $log_dir\n"; print "mock_cfg: $mock_cfg\n"; print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; -print "skip_install: $skip_install\n"; make_path($result_dir); make_path($log_dir); @@ -218,48 +215,6 @@ for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { or die "Failed to copy $src to $log_dir: $!\n"; } -if (!$skip_install) { - print_step("Install RPM and run smoke tests"); - run("dnf -y install " . sh_quote($main_rpm)); - - my $core = '/tftpboot/boot/grub2/powerpc-ieee1275/core.elf'; - my $grub2ppc = '/tftpboot/boot/grub2/grub2.ppc'; - die "Missing installed core image: $core\n" if !-f $core; - die "Missing installed post script output: $grub2ppc\n" if !-f $grub2ppc; - - my $file_core_log = "$log_dir/smoke-file-core.log"; - my $file_ppc_log = "$log_dir/smoke-file-grub2ppc.log"; - my $qf_log = "$log_dir/smoke-rpm-qf.log"; - - my $rc_file_core = run_capture_rc("file $core", $file_core_log); - my $rc_file_ppc = run_capture_rc("file $grub2ppc", $file_ppc_log); - my $rc_qf = run_capture_rc("rpm -qf $core", $qf_log); - my $rc_cmp = run_capture_rc("cmp -s $core $grub2ppc", "$log_dir/smoke-cmp.log"); - - die "Smoke check failed: file core returned $rc_file_core\n" if $rc_file_core != 0; - die "Smoke check failed: file grub2.ppc returned $rc_file_ppc\n" if $rc_file_ppc != 0; - die "Smoke check failed: rpm -qf returned $rc_qf\n" if $rc_qf != 0; - die "Smoke check failed: core.elf and grub2.ppc differ (cmp rc=$rc_cmp)\n" if $rc_cmp != 0; - - my $core_out = slurp($file_core_log); - my $qf_out = slurp($qf_log); - - die "Core image signature check failed:\n$core_out\n" - if $core_out !~ /ELF|data/i; - die "Installed core image is not owned by grub2-xcat:\n$qf_out\n" - if $qf_out !~ /^grub2-xcat-/m; - - my $summary = "$log_dir/smoke-summary.txt"; - open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; - print {$sfh} "core=$core\n"; - print {$sfh} "grub2ppc=$grub2ppc\n"; - print {$sfh} "rc_file_core=$rc_file_core\n"; - print {$sfh} "rc_file_ppc=$rc_file_ppc\n"; - print {$sfh} "rc_qf=$rc_qf\n"; - print {$sfh} "rc_cmp=$rc_cmp\n"; - close $sfh; -} - print_step("Completed"); print "Main RPM: $main_rpm\n"; print "Artifacts: $result_dir\n"; @@ -275,7 +230,6 @@ Usage: $0 [options] --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds --result-dir PATH Output RPM/SRPM directory (default: $result_dir) --log-dir PATH Log directory (default: $log_dir) - --skip-install Skip dnf install + smoke tests --build-timestamp EPOCH Unix timestamp for deterministic builds (SOURCE_DATE_EPOCH) USAGE } diff --git a/ipmitool/mockbuild.pl b/ipmitool/mockbuild.pl index f02f2f2..ba599b2 100755 --- a/ipmitool/mockbuild.pl +++ b/ipmitool/mockbuild.pl @@ -19,7 +19,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$repo_root/build-output/list3/ipmitool-xcat"; my $log_dir = "$repo_root/build-logs/list3/ipmitool-xcat"; -my $skip_install = 0; my $build_timestamp; GetOptions( @@ -29,7 +28,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); @@ -79,7 +77,6 @@ print "log_dir: $log_dir\n"; print "mock_cfg: $mock_cfg\n"; print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; print "source_file:$source_file\n"; -print "skip_install: $skip_install\n"; print "SOURCE_DATE_EPOCH: $SOURCE_DATE_EPOCH\n"; make_path($result_dir); @@ -206,60 +203,6 @@ for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { or die "Failed to copy $src to $log_dir: $!\n"; } -if (!$skip_install) { - print_step("Install RPM and run smoke tests"); - run("dnf -y install " . sh_quote($main_rpm)); - - my $bin = '/opt/xcat/bin/ipmitool-xcat'; - die "Missing installed binary: $bin\n" if !-x $bin; - - my $help_short_log = "$log_dir/smoke-help-short.log"; - my $help_long_log = "$log_dir/smoke-help-long.log"; - my $version_log = "$log_dir/smoke-version.log"; - my $open_log = "$log_dir/smoke-open-mc-info.log"; - my $ldd_log = "$log_dir/smoke-ldd.log"; - - my $rc_help_short = run_capture_rc("$bin -h", $help_short_log); - my $rc_help_long = run_capture_rc("$bin --help", $help_long_log); - my $rc_version = run_capture_rc("$bin -V", $version_log); - my $rc_open = run_capture_rc("$bin -I open mc info", $open_log); - my $rc_ldd = run_capture_rc("ldd $bin", $ldd_log); - - die "Smoke check failed: -h returned $rc_help_short\n" if $rc_help_short != 0; - die "Smoke check failed: -V returned $rc_version\n" if $rc_version != 0; - die "Smoke check failed: ldd returned $rc_ldd\n" if $rc_ldd != 0; - - my $help_short_out = slurp($help_short_log); - my $help_long_out = slurp($help_long_log); - my $version_out = slurp($version_log); - my $open_out = slurp($open_log); - my $ldd_out = slurp($ldd_log); - - die "Short help output does not contain usage text\n" - if $help_short_out !~ /usage:/i; - die "Long help output does not contain usage text\n" - if $help_long_out !~ /usage:/i; - die "Long help returned unexpected rc=$rc_help_long (expected 0 or 1)\n" - if $rc_help_long != 0 && $rc_help_long != 1; - die "Version output missing expected version string\n" - if $version_out !~ /ipmitool-xcat version \Q$version\E/i; - die "ldd output missing libcrypto dependency\n" - if $ldd_out !~ /libcrypto/; - if ($rc_open != 0 && $open_out !~ m{Could not open device|/dev/ipmi}) { - die "IPMI probe failed with unexpected output:\n$open_out\n"; - } - - my $summary = "$log_dir/smoke-summary.txt"; - open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; - print {$sfh} "binary=$bin\n"; - print {$sfh} "rc_help_short=$rc_help_short\n"; - print {$sfh} "rc_help_long=$rc_help_long\n"; - print {$sfh} "rc_version=$rc_version\n"; - print {$sfh} "rc_open=$rc_open\n"; - print {$sfh} "rc_ldd=$rc_ldd\n"; - close $sfh; -} - print_step("Completed"); print "Main RPM: $main_rpm\n"; print "Artifacts: $result_dir\n"; @@ -275,7 +218,6 @@ Usage: $0 [options] --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds --result-dir PATH Output RPM/SRPM directory (default: $result_dir) --log-dir PATH Log directory (default: $log_dir) - --skip-install Skip dnf install + smoke tests --build-timestamp N Unix epoch for SOURCE_DATE_EPOCH (deterministic builds) USAGE } diff --git a/mockbuild-all.pl b/mockbuild-all.pl index 5956a98..0d3fd52 100755 --- a/mockbuild-all.pl +++ b/mockbuild-all.pl @@ -16,7 +16,8 @@ use lib $RealBin; use MockBuildUtils qw(sh_quote print_step version_matches required_pkgs read_manifest verify_repo_packages verify_repo_signature rpm_version rpm_release rpm_sigmd5 restamp_release_line - cross_copy_genesis finalize_xcat_dep bump_dep_release_suffix); + cross_copy_genesis finalize_xcat_dep bump_dep_release_suffix + build_mock_uniqueext); # --- Mount-namespace isolation: guard the host cgroup against mock teardown propagation ---------- # mock mounts /sys/fs/cgroup into every build chroot. On these systemd build hosts every mount is @@ -80,7 +81,6 @@ my $build_number; # newest tag is v0.3.2), so it exists only on master -- pin an immutable SHA instead of the moving # branch so the build is reproducible. Bump this deliberately when uptaking a new goconserver. my $GOCONSERVER_REF = '6166fe5ec1c5b3c20475e322a9f0e8e93c87e45f'; -my $skip_install = 0; my $skip_build = 0; my $skip_xcat_dep = 0; my $skip_perl = 0; @@ -134,7 +134,6 @@ GetOptions( 'run-id=s' => \$run_id, 'build-timestamp=i' => \$build_timestamp, 'build-number=i' => \$build_number, - 'skip-install!' => \$skip_install, 'skip-build!' => \$skip_build, 'skip-xcat-dep!' => \$skip_xcat_dep, 'skip-perl!' => \$skip_perl, @@ -443,7 +442,6 @@ print "skip_build: $skip_build\n"; print "skip_xcat_dep: $skip_xcat_dep\n"; print "skip_perl: $skip_perl\n"; print "skip_genesis: $skip_genesis\n"; -print "skip_install: $skip_install\n"; print "skip_createrepo: $skip_createrepo\n"; print "skip_tarball: $skip_tarball\n"; print "scrub_all_chroots:$scrub_all_chroots\n"; @@ -485,7 +483,6 @@ if (!$skip_build) { # host-local, run-scoped work dir so /tmp doesn't collide between runs '--work-dir', sh_quote("/tmp/mockbuild-all-$run_id/$name"), '--build-timestamp', $SOURCE_DATE_EPOCH, - ($skip_install ? '--skip-install' : ()), # goconserver generates its spec at build time (from an upstream clone), so the # in-tree spec Release bump above cannot reach it. Hand the CD suffix down so its # NVR advances per run too, and pin the clone to an immutable commit (not the moving @@ -529,7 +526,6 @@ if (!$skip_build) { # packages; the srpm-mode ones (HTML-Form, IO-Stty, Net-Telnet) build from a # committed .src.rpm, so hand the suffix down for the builder to re-stamp them. ($RELEASE_BUMP ne '' ? ('--release-suffix', sh_quote($RELEASE_BUMP)) : ()), - ($skip_install ? '--skip-install' : ()), ($keep_buildroots ? '--keep-buildroots' : ()), ); push @build_steps, { @@ -972,7 +968,6 @@ Options: 0/auto = host nproc (default: auto) --run-id ID Run identifier suffix (default: derived from build timestamp) --build-timestamp EPOCH Unix epoch for deterministic builds (default: Gitepoch or git log) - --skip-install Skip install/smoke tests in child builder scripts --skip-build Skip all build steps and only collect/create repo/tarballs --skip-xcat-dep Skip xcat-dep mockbuild.pl package steps --skip-perl Skip perl package build step @@ -1408,27 +1403,6 @@ sub resolve_mock_cfg { . "(tried /etc/mock/${os_id}+epel-${rel}-${arch}.cfg and /etc/mock/${short}+epel-${rel}-${arch}.cfg)\n"; } -sub build_mock_uniqueext { - my ($run, $seq, $label) = @_; - - my $run_part = defined($run) ? $run : 'run'; - $run_part =~ s/[^A-Za-z0-9_.-]+/-/g; - $run_part =~ s/^-+|-+$//g; - $run_part = 'run' if $run_part eq ''; - $run_part = substr($run_part, -24) if length($run_part) > 24; - - my $label_part = defined($label) ? $label : 'step'; - $label_part =~ s/[^A-Za-z0-9_.-]+/-/g; - $label_part =~ s/^-+|-+$//g; - $label_part = 'step' if $label_part eq ''; - $label_part = substr($label_part, 0, 20) if length($label_part) > 20; - - my $idx = defined($seq) ? int($seq) : 0; - $idx = 0 if $idx < 0; - - return sprintf("mba-%02d-%s-%s", $idx, $run_part, $label_part); -} - sub resolve_xcat_source { my ($requested, $root) = @_; # Prefer the sibling ../xcat-core (the real layout: source/xcat-core beside source/xcat-dep) diff --git a/mockbuild-perl-packages.pl b/mockbuild-perl-packages.pl index 531ca81..3822b3f 100755 --- a/mockbuild-perl-packages.pl +++ b/mockbuild-perl-packages.pl @@ -18,8 +18,6 @@ my $result_dir = ''; my $log_dir = ''; my $packages_csv = ''; my $jobs = 0; -my $skip_install = 0; -my $allow_erasing = 0; my $build_timestamp; # CD version bump: appended to the Release of the srpm-mode packages (HTML-Form, IO-Stty, # Net-Telnet), which build from a committed .src.rpm and so are NOT covered by mockbuild-all's @@ -35,8 +33,6 @@ GetOptions( 'log-dir=s' => \$log_dir, 'packages=s' => \$packages_csv, 'jobs=i' => \$jobs, - 'skip-install!' => \$skip_install, - 'allow-erasing!' => \$allow_erasing, 'build-timestamp=i' => \$build_timestamp, 'release-suffix=s' => \$release_suffix, ) or die usage(); @@ -170,10 +166,6 @@ $jobs = 1 if $jobs < 1; if (@packages && $jobs > scalar(@packages)) { $jobs = scalar(@packages); } -if (!$skip_install && $jobs > 1) { - print "INFO: --skip-install is disabled; forcing --jobs 1 to avoid host dnf lock contention\n"; - $jobs = 1; -} make_path($result_dir); make_path($log_dir); @@ -189,8 +181,6 @@ print "mock_cfg: $mock_cfg\n"; print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; print "packages: " . join(', ', @packages) . "\n"; print "jobs: $jobs\n"; -print "skip_install:$skip_install\n"; -print "allow_erasing:$allow_erasing\n"; print "release_suffix:" . ($release_suffix ne '' ? $release_suffix : '(none)') . "\n"; print_step("Mock config check"); @@ -230,8 +220,6 @@ for my $idx (0 .. $#packages) { mock_cfg => $mock_cfg, mock_uniqueext => $pkg_uniqueext, arch => $arch, - skip_install => $skip_install, - allow_erasing => $allow_erasing, release_suffix => $release_suffix, ); unless ($keep_buildroots) { @@ -328,8 +316,6 @@ sub build_package { my $mock_cfg = $args{mock_cfg}; my $mock_uniqueext = $args{mock_uniqueext}; my $arch = $args{arch}; - my $skip_install = $args{skip_install}; - my $allow_erasing = $args{allow_erasing}; my $release_suffix = $args{release_suffix}; my $pkg_run_dir = "$work_dir/$pkg"; @@ -506,15 +492,6 @@ sub build_package { } } - if (!$skip_install) { - my $install_cmd = "dnf -y install "; - $install_cmd .= "--allowerasing " if $allow_erasing; - run($install_cmd . sh_quote($main_rpm)); - my $module = $cfg->{module}; - my $rc_mod = run_capture_rc("perl -M$module -e 1", "$pkg_log/smoke-perl-module.log"); - die "Perl module import failed for $pkg ($module), rc=$rc_mod\n" if $rc_mod != 0; - } - $summary = "$pkg PASS main_rpm=" . basename($main_rpm); $ok = 1; }; @@ -561,8 +538,6 @@ Usage: $0 [options] --build-timestamp EPOCH Unix epoch for SOURCE_DATE_EPOCH (deterministic builds) --release-suffix STR CD bump appended to the Release of the srpm-mode packages that build from a committed .src.rpm (HTML-Form, IO-Stty, Net-Telnet) - --skip-install Skip dnf install + perl module import checks - --allow-erasing Allow dnf to erase conflicting packages during install smoke tests USAGE } diff --git a/syslinux/mockbuild.pl b/syslinux/mockbuild.pl index ca82bd8..dda65f3 100755 --- a/syslinux/mockbuild.pl +++ b/syslinux/mockbuild.pl @@ -19,7 +19,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$repo_root/build-output/list3/syslinux-xcat"; my $log_dir = "$repo_root/build-logs/list3/syslinux-xcat"; -my $skip_install = 0; my $build_timestamp; GetOptions( @@ -29,7 +28,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); @@ -87,7 +85,6 @@ print "version: $version\n"; print "mock_cfg: $mock_cfg\n"; print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; print "source_file:$source_file\n"; -print "skip_install: $skip_install\n"; make_path($result_dir); make_path($log_dir); @@ -226,48 +223,6 @@ for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { or die "Failed to copy $src to $log_dir: $!\n"; } -if (!$skip_install) { - print_step("Install RPM(s) and run smoke tests"); - run("dnf -y install " . sh_quote($xcat_rpm)); - - my $pxe_file = '/opt/xcat/share/xcat/netboot/syslinux/pxelinux.0'; - die "Missing installed PXE file: $pxe_file\n" if !-f $pxe_file; - - my $file_log = "$log_dir/smoke-file.log"; - my $qf_log = "$log_dir/smoke-rpm-qf.log"; - my $rc_file = run_capture_rc("file $pxe_file", $file_log); - my $rc_qf = run_capture_rc("rpm -qf $pxe_file", $qf_log); - - die "Smoke check failed: file returned $rc_file\n" if $rc_file != 0; - die "Smoke check failed: rpm -qf returned $rc_qf\n" if $rc_qf != 0; - - my $qf_out = slurp($qf_log); - die "Installed file is not owned by syslinux-xcat:\n$qf_out\n" - if $qf_out !~ /^syslinux-xcat-/m; - - # EL10 hosts may already carry syslinux-nonlinux, which conflicts with - # directly installing the rebuilt syslinux payload. The xcat subpackage - # is the artifact we need to smoke-test on the host; repository-level - # dependency validation happens later in the full install phase. - my $syslinux_help_log = "$log_dir/smoke-syslinux-help.log"; - if (-x '/usr/bin/syslinux') { - my $rc_help = run_capture_rc("/usr/bin/syslinux --help", $syslinux_help_log); - my $help_out = slurp($syslinux_help_log); - die "syslinux --help returned unexpected rc=$rc_help\n" - if $rc_help != 0 && $rc_help != 1; - die "syslinux --help output missing expected usage text\n" - if $help_out !~ /usage|syslinux/i; - } - - my $summary = "$log_dir/smoke-summary.txt"; - open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; - print {$sfh} "pxe_file=$pxe_file\n"; - print {$sfh} "rc_file=$rc_file\n"; - print {$sfh} "rc_qf=$rc_qf\n"; - print {$sfh} "main_rpm_available=" . ($main_rpm ? 1 : 0) . "\n"; - close $sfh; -} - print_step("Completed"); print "syslinux-xcat RPM: $xcat_rpm\n"; print "Artifacts: $result_dir\n"; @@ -284,7 +239,6 @@ Usage: $0 [options] --result-dir PATH Output RPM/SRPM directory (default: $result_dir) --log-dir PATH Log directory (default: $log_dir) --build-timestamp EPOCH SOURCE_DATE_EPOCH for deterministic builds - --skip-install Skip dnf install + smoke tests USAGE } diff --git a/t/mockbuild-all.t b/t/mockbuild-all.t index 76076c9..0d3fc83 100644 --- a/t/mockbuild-all.t +++ b/t/mockbuild-all.t @@ -12,7 +12,8 @@ use File::Path qw(make_path); use File::Basename qw(basename); use MockBuildUtils qw(required_pkgs version_matches rpm_sigmd5 rpm_version rpm_release rpm_is_signed restamp_release_line cross_copy_genesis finalize_xcat_dep read_manifest - verify_repo_packages verify_repo_signature bump_dep_release_suffix); + verify_repo_packages verify_repo_signature bump_dep_release_suffix + build_mock_uniqueext); # Run a printing sub with STDOUT muted so its progress lines do not pollute TAP. sub quiet(&) { @@ -325,4 +326,33 @@ is(rpm_release(tempdir(CLEANUP => 1), 'nonexistent-pkg'), undef, 'rpm_release is 'verify_repo_signature: mismatch reported as WRONGKEY : signed by , expected '); } +# ---- build_mock_uniqueext: distinct per target so concurrent mock roots never collide --------- +# (PR #62 review) A long (timestamp) run id must not tail-truncate away the leading EL/arch token: +# for the 7-char "ppc64le" arch that dropped the EL digit, so alma+epel-{8,9,10}-ppc64le collapsed to +# one uniqueext -- and goconserver builds all three ELs in the SAME el10 chroot, so the roots raced. +{ + my $seq = 6; my $label = 'goconserver'; + # The reproducing case: the default timestamp run id (long), folded with the per-target prefix. + my @ppc = map { build_mock_uniqueext("alma+epel-$_-ppc64le-20260821-210716", $seq, $label) } (8, 9, 10); + my %seen; $seen{$_}++ for @ppc; + is(scalar(keys %seen), 3, + 'build_mock_uniqueext: el8/el9/el10 ppc64le get DISTINCT uniqueext on a long run id (no collision)'); + like($ppc[0], qr/^mba-06-alma-epel-8-/, 'uniqueext keeps a readable leading EL/arch token'); + + # x86_64 (6-char arch) was never broken -- assert it stays distinct too. + my @x86 = map { build_mock_uniqueext("alma+epel-$_-x86_64-20260821-210716", $seq, $label) } (8, 9, 10); + my %sx; $sx{$_}++ for @x86; + is(scalar(keys %sx), 3, 'build_mock_uniqueext: el8/el9/el10 x86_64 also distinct'); + + # Short run ids (e.g. the CD "$BUILD_NUMBER") are unchanged and already distinct per target. + isnt(build_mock_uniqueext('alma+epel-8-ppc64le-104', $seq, $label), + build_mock_uniqueext('alma+epel-9-ppc64le-104', $seq, $label), + 'build_mock_uniqueext: short (build-number) run ids distinct per target'); + + # Same run id + same step -> stable (deterministic; a re-run reuses/scrubs the same root). + is(build_mock_uniqueext('alma+epel-8-ppc64le-20260821-210716', $seq, $label), + build_mock_uniqueext('alma+epel-8-ppc64le-20260821-210716', $seq, $label), + 'build_mock_uniqueext: deterministic for a given (run, seq, label)'); +} + done_testing; diff --git a/xnba/mockbuild.pl b/xnba/mockbuild.pl index 08c6084..7c1395a 100755 --- a/xnba/mockbuild.pl +++ b/xnba/mockbuild.pl @@ -19,7 +19,6 @@ my $mock_cfg = ''; my $mock_uniqueext = ''; my $result_dir = "$repo_root/build-output/list3/xnba-undi"; my $log_dir = "$repo_root/build-logs/list3/xnba-undi"; -my $skip_install = 0; my $build_timestamp; GetOptions( @@ -28,7 +27,6 @@ GetOptions( 'mock-uniqueext=s' => \$mock_uniqueext, 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, - 'skip-install!' => \$skip_install, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); @@ -73,7 +71,6 @@ print "work_dir: $work_dir\n"; print "result_dir: $result_dir\n"; print "log_dir: $log_dir\n"; print "mock_cfg: $mock_cfg\n"; -print "skip_install: $skip_install\n"; make_path($result_dir); make_path($log_dir); @@ -176,7 +173,6 @@ Options: --mock-uniqueext STR Mock uniqueext value --result-dir PATH Output directory for RPMs --log-dir PATH Output directory for logs - --skip-install Skip install verification --build-timestamp EPOCH Unix timestamp for reproducible builds USAGE }