diff --git a/elilo/mockbuild.pl b/elilo/mockbuild.pl index f8d03f4..6e4aa32 100755 --- a/elilo/mockbuild.pl +++ b/elilo/mockbuild.pl @@ -13,7 +13,6 @@ my $repo_root = abs_path("$script_dir/.."); my $pkg_dir = "$repo_root/elilo"; my $spec_file = "$pkg_dir/elilo-xcat.spec"; -my $source_url = 'https://downloads.sourceforge.net/project/elilo/elilo/elilo-3.14/elilo-3.14-all.tar.gz'; my $source_file = ''; my $work_dir = '/tmp/elilo-xcat-mockbuild'; my $mock_cfg = ''; @@ -24,7 +23,6 @@ my $skip_install = 0; my $build_timestamp; GetOptions( - 'source-url=s' => \$source_url, 'source-file=s' => \$source_file, 'work-dir=s' => \$work_dir, 'mock-cfg=s' => \$mock_cfg, @@ -38,7 +36,7 @@ GetOptions( die "Run as root (current uid=$>)\n" if $> != 0; die "Missing spec file: $spec_file\n" if !-f $spec_file; -for my $bin (qw(wget mock rpmbuild rpm dnf file bash grep)) { +for my $bin (qw(mock rpmbuild rpm dnf file bash grep)) { run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); } @@ -80,7 +78,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 "source_url: $source_url\n"; print "source_file:$source_file\n"; print "skip_install: $skip_install\n"; @@ -90,28 +87,23 @@ make_path($log_dir); print_step("Mock config check"); run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); -print_step("Prepare source archive"); -# The elilo source tarball is tracked in the repo, already normalized to an elilo/ top-level -# tree. Re-downloading + normalizing rewrites $source_path IN PLACE -- and it lives in the -# shared (NFS) checkout that BOTH arch build hosts (x86 + ppc) build against at the same time, -# so the other host's concurrent elilo build can read it mid-rewrite and get a truncated -# archive (intermittent "missing elilo top-level tree" failures). Use the tracked copy -# read-only when it is already normalized; only fetch upstream if it is absent/unnormalized. -my $have_normalized = 0; -if (-f $source_path) { - my $top = capture( - "tar -tzf " . sh_quote($source_path) . - " 2>/dev/null | grep -E '^(\\./)?elilo/' | head -n1 || true" - ); - $have_normalized = 1 if $top ne ''; -} -if ($have_normalized) { - print "Using tracked normalized source archive (no upstream fetch, no shared write): $source_path\n"; -} else { - run("wget --spider " . sh_quote($source_url)); - run("wget -O " . sh_quote($source_path) . " " . sh_quote($source_url)); - normalize_source_archive($source_path, $version, $work_dir); -} +print_step("Verify tracked source archive"); +# Source0 (elilo--source.tar.gz) is tracked in the repo, already normalized to an elilo/ +# top-level tree, and consumed directly by mock (--sources $pkg_dir below). There is nothing to +# download: the old fetch re-derived this SAME tracked file and rewrote it IN PLACE. Because the +# checkout is on a shared (NFS) mount that BOTH arch build hosts (x86 + ppc) build against at the +# same time, that in-place rewrite raced the other host's concurrent elilo build -- it could read +# the file mid-write and get a truncated archive ("missing elilo top-level tree" failures). We now +# only READ the tracked file, so concurrent builds can never race on it. Fail loudly (do NOT +# silently re-fetch) if the checkout is missing/broken -- that is repo corruption, not a fetch miss. +die "Tracked elilo source missing: $source_path (incomplete checkout?)\n" if !-f $source_path; +my $top = capture( + "tar -tzf " . sh_quote($source_path) . + " 2>/dev/null | grep -E '^(\\./)?elilo/' | head -n1 || true" +); +die "Tracked elilo source is not normalized (no elilo/ top-level tree): $source_path\n" + if $top eq ''; +print "Using tracked normalized source archive (read-only, no fetch, no shared write): $source_path\n"; print_step("Verify spec assets"); for my $asset (@spec_assets) { @@ -253,7 +245,6 @@ exit 0; sub usage { return <<"USAGE"; Usage: $0 [options] - --source-url URL Upstream tarball URL (default: $source_url) --source-file FILE Source filename stored in elilo/ (default: inferred from spec version) --work-dir PATH Temporary work dir (default: $work_dir) --mock-cfg NAME Mock config (default: +epel-10-) @@ -302,49 +293,6 @@ sub parse_spec { return ($version, @assets); } -sub normalize_source_archive { - my ($archive, $version, $work_base) = @_; - - my $has_elilo = capture( - "tar -tzf " . sh_quote($archive) . - " | grep -E '^(\\./)?elilo/' | head -n1 || true" - ); - return if $has_elilo ne ''; - - my $nested = capture( - "tar -tzf " . sh_quote($archive) . - " | grep -E '^(\\./)?elilo-$version-source\\.tar\\.gz\$' | head -n1 || true" - ); - die "Downloaded archive does not contain elilo source payload: $archive\n" - if $nested eq ''; - - my $normalize_dir = "$work_base/source-normalize"; - remove_tree($normalize_dir) if -d $normalize_dir; - make_path($normalize_dir); - - run( - "tar -xzf " . sh_quote($archive) . - " -C " . sh_quote($normalize_dir) . - " " . sh_quote($nested) - ); - - my $nested_rel = $nested; - $nested_rel =~ s{^\./}{}; - my $nested_path = "$normalize_dir/$nested_rel"; - die "Failed to extract nested source archive: $nested_path\n" - if !-f $nested_path; - - copy($nested_path, $archive) - or die "Failed to normalize source archive $archive: $!\n"; - - my $recheck = capture( - "tar -tzf " . sh_quote($archive) . - " | grep -E '^(\\./)?elilo/' | head -n1 || true" - ); - die "Normalized source archive still missing elilo top-level tree: $archive\n" - if $recheck eq ''; -} - sub print_step { my ($msg) = @_; print "\n== $msg ==\n"; diff --git a/ipmitool/mockbuild.pl b/ipmitool/mockbuild.pl index 3afcb26..dbd9241 100755 --- a/ipmitool/mockbuild.pl +++ b/ipmitool/mockbuild.pl @@ -3,7 +3,7 @@ use strict; use warnings; use Cwd qw(abs_path); -use File::Basename qw(dirname basename); +use File::Basename qw(dirname); use File::Copy qw(copy); use File::Path qw(make_path remove_tree); use Getopt::Long qw(GetOptions); @@ -13,7 +13,6 @@ my $repo_root = abs_path("$script_dir/.."); my $pkg_dir = "$repo_root/ipmitool"; my $spec_file = "$pkg_dir/ipmitool.spec"; -my $source_url = 'https://github.com/ipmitool/ipmitool/archive/refs/tags/IPMITOOL_1_8_18.tar.gz'; my $source_file = ''; my $work_dir = '/tmp/ipmitool-xcat-mockbuild'; my $mock_cfg = ''; @@ -24,7 +23,6 @@ my $skip_install = 0; my $build_timestamp; GetOptions( - 'source-url=s' => \$source_url, 'source-file=s' => \$source_file, 'work-dir=s' => \$work_dir, 'mock-cfg=s' => \$mock_cfg, @@ -38,7 +36,7 @@ GetOptions( die "Run as root (current uid=$>)\n" if $> != 0; die "Missing spec file: $spec_file\n" if !-f $spec_file; -for my $bin (qw(wget mock rpmbuild rpm dnf ldd bash)) { +for my $bin (qw(mock rpmbuild rpm dnf ldd bash)) { run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); } @@ -80,7 +78,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 "source_url: $source_url\n"; print "source_file:$source_file\n"; print "skip_install: $skip_install\n"; print "SOURCE_DATE_EPOCH: $SOURCE_DATE_EPOCH\n"; @@ -91,10 +88,22 @@ make_path($log_dir); print_step("Mock config check"); run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); -print_step("Download upstream source"); -run("wget --spider " . sh_quote($source_url)); -run("wget -O " . sh_quote($source_path) . " " . sh_quote($source_url)); -normalize_source_archive($source_path, $version, $work_dir); +print_step("Verify tracked source archive"); +# The ipmitool source (ipmitool-.tar.gz) is tracked in the repo, already normalized to the +# ipmitool-/ top-level that %setup -n expects, and consumed directly by mock (--sources +# $pkg_dir below). There is nothing to download: the old fetch re-derived this SAME tracked file +# and rewrote it IN PLACE, and the checkout is shared between the two arch build hosts building at +# once -- so the in-place rewrite raced the other host's concurrent ipmitool build, which could +# read the file mid-write and get a truncated archive. We only READ it now, so concurrent builds +# can never race on it. Fail loudly (do NOT silently re-fetch) if the checkout is missing/broken. +die "Tracked ipmitool source missing: $source_path (incomplete checkout?)\n" if !-f $source_path; +my $top = capture( + "tar -tzf " . sh_quote($source_path) . + " 2>/dev/null | grep -E '^(\\./)?ipmitool-$version/' | head -n1 || true" +); +die "Tracked ipmitool source is not the expected ipmitool-$version/ tree: $source_path\n" + if $top eq ''; +print "Using tracked source archive (read-only, no fetch, no shared write): $source_path\n"; print_step("Verify spec assets"); for my $asset (@spec_assets) { @@ -258,7 +267,6 @@ exit 0; sub usage { return <<"USAGE"; Usage: $0 [options] - --source-url URL Upstream tarball URL (default: $source_url) --source-file FILE Source filename stored in ipmitool/ (default: inferred from spec version) --work-dir PATH Temporary work dir (default: $work_dir) --mock-cfg NAME Mock config (default: +epel-10-) @@ -308,38 +316,6 @@ sub parse_spec { return ($version, @assets); } -sub normalize_source_archive { - my ($archive, $version, $work_base) = @_; - - my $normalize_dir = "$work_base/source-normalize"; - remove_tree($normalize_dir) if -d $normalize_dir; - make_path($normalize_dir); - - run("tar -xzf " . sh_quote($archive) . " -C " . sh_quote($normalize_dir)); - - my @entries = grep { $_ !~ m{/\.\.?$} } glob("$normalize_dir/*"); - die "Unexpected archive layout in $archive\n" if @entries != 1; - my $top_path = $entries[0]; - die "Unexpected non-directory top-level entry in $archive: $top_path\n" - if !-d $top_path; - - my $expected_top = "ipmitool-$version"; - my $actual_top = basename($top_path); - if ($actual_top ne $expected_top) { - my $new_path = "$normalize_dir/$expected_top"; - run("rm -rf " . sh_quote($new_path)); - run("mv " . sh_quote($top_path) . " " . sh_quote($new_path)); - } - - # Repack using the expected top-level directory required by the spec. - run( - "tar --sort=name --owner=0 --group=0 --mtime=\@$SOURCE_DATE_EPOCH" . - " -C " . sh_quote($normalize_dir) . - " -czf " . sh_quote($archive) . - " " . sh_quote($expected_top) - ); -} - sub print_step { my ($msg) = @_; print "\n== $msg ==\n"; diff --git a/syslinux/mockbuild.pl b/syslinux/mockbuild.pl index cc320c1..d7bd499 100755 --- a/syslinux/mockbuild.pl +++ b/syslinux/mockbuild.pl @@ -13,7 +13,6 @@ my $repo_root = abs_path("$script_dir/.."); my $pkg_dir = "$repo_root/syslinux"; my $spec_file = "$pkg_dir/syslinux-xcat.spec"; -my $source_url = 'https://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.xz'; my $source_file = ''; my $work_dir = '/tmp/syslinux-xcat-mockbuild'; my $mock_cfg = ''; @@ -21,11 +20,9 @@ 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 $skip_upstream_download = 0; my $build_timestamp; GetOptions( - 'source-url=s' => \$source_url, 'source-file=s' => \$source_file, 'work-dir=s' => \$work_dir, 'mock-cfg=s' => \$mock_cfg, @@ -33,14 +30,13 @@ GetOptions( 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, 'skip-install!' => \$skip_install, - 'skip-upstream-download!' => \$skip_upstream_download, 'build-timestamp=i' => \$build_timestamp, ) or die usage(); die "Run as root (current uid=$>)\n" if $> != 0; die "Missing spec file: $spec_file\n" if !-f $spec_file; -for my $bin (qw(wget mock rpmbuild rpm dnf file bash grep cut)) { +for my $bin (qw(mock rpmbuild rpm dnf file bash grep)) { run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); } @@ -90,10 +86,8 @@ print "pkg_name: $pkg_name\n"; print "version: $version\n"; print "mock_cfg: $mock_cfg\n"; print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; -print "source_url: $source_url\n"; print "source_file:$source_file\n"; print "skip_install: $skip_install\n"; -print "skip_upstream_download: $skip_upstream_download\n"; make_path($result_dir); make_path($log_dir); @@ -101,21 +95,22 @@ make_path($log_dir); print_step("Mock config check"); run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); -if (!$skip_upstream_download) { - print_step("Download upstream source"); - run("wget --spider " . sh_quote($source_url)); - run("wget -O " . sh_quote($source_path) . " " . sh_quote($source_url)); - - my $sha = capture("sha256sum " . sh_quote($source_path) . " | cut -d ' ' -f1"); - my $meta_file = "$log_dir/upstream-source.txt"; - open my $mfh, '>', $meta_file or die "Cannot write $meta_file: $!\n"; - print {$mfh} "url=$source_url\n"; - print {$mfh} "file=$source_path\n"; - print {$mfh} "sha256=$sha\n"; - close $mfh; - print "Downloaded source: $source_path\n"; - print "SHA256: $sha\n"; -} +print_step("Verify tracked source archive"); +# The syslinux source (syslinux-.tar.xz, Source0) is tracked in the repo, has the +# syslinux-/ top-level that %setup -n expects, and is consumed directly by mock (--sources +# $pkg_dir below). There is nothing to download: the old fetch re-downloaded this SAME tracked file +# and rewrote it IN PLACE, and the checkout is shared between the two arch build hosts building at +# once -- so the in-place rewrite raced the other host's concurrent syslinux build, which could +# read the file mid-write and get a truncated archive. We only READ it now, so concurrent builds +# can never race on it. Fail loudly (do NOT silently re-fetch) if the checkout is missing/broken. +die "Tracked syslinux source missing: $source_path (incomplete checkout?)\n" if !-f $source_path; +my $top = capture( + "tar -tf " . sh_quote($source_path) . + " 2>/dev/null | grep -E '^(\\./)?syslinux-' | head -n1 || true" +); +die "Tracked syslinux source is not a syslinux-*/ source tree: $source_path\n" + if $top eq ''; +print "Using tracked source archive (read-only, no fetch, no shared write): $source_path\n"; print_step("Verify spec assets"); for my $asset (@all_assets) { @@ -280,7 +275,6 @@ exit 0; sub usage { return <<"USAGE"; Usage: $0 [options] - --source-url URL Upstream tarball URL (default: $source_url) --source-file FILE Source filename stored in syslinux/ (default: inferred from spec) --work-dir PATH Temporary work dir (default: $work_dir) --mock-cfg NAME Mock config (default: +epel-10-) @@ -288,7 +282,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-upstream-download Skip wget download step --skip-install Skip dnf install + smoke tests USAGE }