From 8b98ae9539b43341c069e27c046496df6aa146c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:39:20 -0300 Subject: [PATCH 1/8] fix(xcat-dep): carry the x86 boot loaders in the riscv64 deb sections The riscv64 sections of debs-manifest.conf listed grub2-xcat only. A riscv64 management node serves the x86 nodes of a mixed cluster, so its repository must carry syslinux-xcat, elilo-xcat and xnba-undi, as the ppc64el sections already require. List the three in every riscv64 section. They are Architecture: all, built once on amd64 and assembled into every index, so the build phase is unchanged and the publish gate now verifies the riscv64 index carries them. --- debs-manifest.conf | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/debs-manifest.conf b/debs-manifest.conf index 4f8cd8c..004dbfc 100644 --- a/debs-manifest.conf +++ b/debs-manifest.conf @@ -53,11 +53,10 @@ # fails the whole run (zero tolerance; concern #4). The four supported codenames (focal jammy noble # resolute) each get an amd64, a ppc64el and a riscv64 section. # -# riscv64 differs from ppc64el in two ways. The x86 boot loaders (syslinux-xcat, elilo-xcat, -# xnba-undi) are NOT listed: a riscv64 node netboots UEFI grub2, exactly as on the EL side, so -# demanding them would gate on packages that node can never use. And xcat-genesis-base is not -# listed: riscv64 Genesis is the OpenEmbedded one, published once into the shared pool, not the -# legacy per-arch netboot image. +# riscv64 lists the x86 boot loaders like ppc64el does: a riscv64 management node serves the +# x86 nodes of a mixed cluster, so its repository must carry them. It differs from ppc64el in one +# way: xcat-genesis-base is not listed, because riscv64 Genesis is the OpenEmbedded one, published +# once into the shared pool, not the legacy per-arch netboot image. # ============================ focal (ubuntu20.04) ============================ [focal-amd64] @@ -84,7 +83,10 @@ xcat-genesis-base=2.* ipmitool-xcat=1.8.18-5 conserver-xcat=8.2.1-1 goconserver=0.3.3-snap* +syslinux-xcat=3.86-2 grub2-xcat=2.12-2 +elilo-xcat=3.14-6 +xnba-undi=1.21.1-1 # ============================ jammy (ubuntu22.04) ============================ [jammy-amd64] @@ -111,7 +113,10 @@ xcat-genesis-base=2.* ipmitool-xcat=1.8.18-5 conserver-xcat=8.2.1-1 goconserver=0.3.3-snap* +syslinux-xcat=3.86-2 grub2-xcat=2.12-2 +elilo-xcat=3.14-6 +xnba-undi=1.21.1-1 # ============================ noble (ubuntu24.04) ============================ [noble-amd64] @@ -138,7 +143,10 @@ xcat-genesis-base=2.* ipmitool-xcat=1.8.18-5 conserver-xcat=8.2.1-1 goconserver=0.3.3-snap* +syslinux-xcat=3.86-2 grub2-xcat=2.12-2 +elilo-xcat=3.14-6 +xnba-undi=1.21.1-1 # ============================ resolute (ubuntu26.04) ========================= [resolute-amd64] @@ -165,7 +173,10 @@ xcat-genesis-base=2.* ipmitool-xcat=1.8.18-5 conserver-xcat=8.2.1-1 goconserver=0.3.3-snap* +syslinux-xcat=3.86-2 grub2-xcat=2.12-2 +elilo-xcat=3.14-6 +xnba-undi=1.21.1-1 # [shared] is NOT a build target. It describes the ONE pool the OpenEmbedded Genesis release is # published into (pool/main/xcat-genesis-openembedded), which every suite indexes and which no From aef1d117d588f96480e4c8d081cb34e22b9720a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:39:20 -0300 Subject: [PATCH 2/8] test(xcat-dep): require the boot loaders on the riscv64 deb sections The manifest consistency check covered the amd64 and ppc64el sections. It now covers the riscv64 sections too, so the 4 boot components must be listed there. 12 assertions fail against the previous manifest. --- t/sbuild-all.t | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/sbuild-all.t b/t/sbuild-all.t index 8b0f513..acbc304 100644 --- a/t/sbuild-all.t +++ b/t/sbuild-all.t @@ -328,8 +328,8 @@ SKIP: { my %m = read_manifest("$RealBin/../debs-manifest.conf"); # Not every section is a build target: [shared] describes the ONE pool the OpenEmbedded Genesis # release is published into, which no builder produces. Target sections are -. - my @targets = grep { /^[a-z]+-(?:amd64|ppc64el)$/ } sort keys %m; - cmp_ok(scalar(@targets), '>=', 8, 'manifest has all 8 codename x arch target sections'); + my @targets = grep { /^[a-z]+-(?:amd64|ppc64el|riscv64)$/ } sort keys %m; + cmp_ok(scalar(@targets), '>=', 12, 'manifest has all 12 codename x arch target sections'); ok(!grep({ $_ eq 'shared' } @targets), 'the shared-pool section is not treated as a build target'); # goconserver is a compiled dep built for EVERY target (both arches, all codenames). @@ -338,11 +338,11 @@ SKIP: { or diag("missing goconserver in: @miss_go"); # The noarch boot components (syslinux-xcat, grub2-xcat, elilo-xcat, xnba-undi) are Architecture:all - # single-producer (built ONCE on amd64) but REQUIRED-PRESENT on EVERY target incl. ppc64el, so the - # gate verifies the ppc repo actually carries them (matches the EL manifest + the 2.16 ppc dep repo; - # a ppc MN needs them for netboot). It is the BUILD PHASE -- not the manifest -- that avoids - # rebuilding them on ppc (build_one_codename skips an Architecture:all package on non-amd64; see the - # control_binary_arch test below). + # single-producer (built ONCE on amd64) but REQUIRED-PRESENT on EVERY target incl. ppc64el and + # riscv64, so the gate verifies those repos actually carry them (matches the EL manifest + the 2.16 + # ppc dep repo; a ppc or riscv64 MN serves the x86 nodes of a mixed cluster). It is the BUILD PHASE + # -- not the manifest -- that avoids rebuilding them off amd64 (build_one_codename skips an + # Architecture:all package on non-amd64; see the control_binary_arch test below). for my $t (@targets) { for my $boot (qw(syslinux-xcat grub2-xcat elilo-xcat xnba-undi)) { ok(exists $m{$t}{$boot}, "$boot required-present on $t (arch:all, verified on every arch)"); From 2b49e07c28f28d91e347b64168bcd9ea7fb6bc14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:39:20 -0300 Subject: [PATCH 3/8] fix(mockbuild-all): build the x86 boot loaders for the riscv64 target The forcearch riscv64 profile built grub2-xcat but not elilo-xcat, syslinux-xcat or xnba-undi, and the rocky-10-riscv64-xcat cell did not list them, so a riscv64 management node could not serve the x86 nodes of a mixed cluster, unlike a ppc64le one. Add the three to the profile and the cell, at the pins the EL10 ppc64le cell uses. They are noarch and are built in the native x86_64 chroot, like grub2-xcat. syslinux-xcat is marked noarch in the builder table, as its spec declares, so the forcearch target does not try it in the emulated chroot, where its ExclusiveArch excludes it. The target is now cross-built on x86_64 only, which the mock config states: a native riscv64 host could not build syslinux-xcat either. --- mock-configs/rocky-10-riscv64-xcat.cfg | 4 +++- mockbuild-all.pl | 19 +++++++++++-------- packages-manifest.conf | 13 ++++++++----- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/mock-configs/rocky-10-riscv64-xcat.cfg b/mock-configs/rocky-10-riscv64-xcat.cfg index 686ea67..2c591f5 100644 --- a/mock-configs/rocky-10-riscv64-xcat.cfg +++ b/mock-configs/rocky-10-riscv64-xcat.cfg @@ -4,11 +4,13 @@ # used from the x86_64 hosts that produce the xcat-dep repos; this config includes the same # template and adds forcearch. There is no EPEL for riscv64, so nothing EPEL-only is reachable # from this chroot: mockbuild-all.pl builds the EPEL-only perl deps of xCAT for this target. +# The host is x86_64 only: the noarch boot loaders the target ships build in the native +# rocky-10-x86_64 chroot, and syslinux builds on x86 and ppc64le alone. # See BUILD.md ("riscv64") for the host prerequisites and how mockbuild-all.pl uses it. include('templates/rocky-10.tpl') config_opts['root'] = 'rocky-10-riscv64-xcat' config_opts['description'] = 'Rocky Linux 10 riscv64 (xCAT dependency build, forcearch)' config_opts['target_arch'] = 'riscv64' -config_opts['legal_host_arches'] = ('x86_64', 'riscv64') +config_opts['legal_host_arches'] = ('x86_64',) config_opts['forcearch'] = 'riscv64' diff --git a/mockbuild-all.pl b/mockbuild-all.pl index 93926fe..cfd532c 100755 --- a/mockbuild-all.pl +++ b/mockbuild-all.pl @@ -396,17 +396,19 @@ my @build_targets = $target # What a target builds. The mock-core-configs targets (+epel--) build every # dep natively on the host arch. The forcearch targets shipped in mock-configs/ cross-build -# another arch that has no EPEL: the x86-only bootloaders are not built for it, the EPEL-only -# perl deps of xCAT are (mockbuild-perl-packages.pl --epel-gap), and the noarch deps are built -# in the native, EPEL-free chroot of the same release (the rpms are identical for every arch -# and an emulated build is an order of magnitude slower). See BUILD.md ("riscv64"). +# another arch that has no EPEL: the EPEL-only perl deps of xCAT are built for it +# (mockbuild-perl-packages.pl --epel-gap), and the noarch deps, the x86 boot loaders among them, +# are built in the native, EPEL-free chroot of the same release (the rpms are identical for +# every arch and an emulated build is an order of magnitude slower). See BUILD.md ("riscv64"). my %forcearch_targets = ( 'rocky-10-riscv64-xcat' => { rel => 10, arch => 'riscv64', - noarch_cfg => "rocky-10-$host_arch", - dep_builders => [qw(grub2-xcat ipmitool-xcat goconserver conserver-xcat)], - required => [qw(ipmitool-xcat grub2-xcat perl-IO-Stty perl-HTTP-Async perl-Net-HTTPS-NB)], + # x86_64 only, as the mock config admits: syslinux-xcat builds on x86 and ppc64le alone. + noarch_cfg => 'rocky-10-x86_64', + dep_builders => [qw(elilo-xcat grub2-xcat ipmitool-xcat syslinux-xcat goconserver conserver-xcat xnba-undi)], + required => [qw(ipmitool-xcat syslinux-xcat grub2-xcat xnba-undi + perl-IO-Stty perl-HTTP-Async perl-Net-HTTPS-NB)], }, ); @@ -532,11 +534,12 @@ if (!$skip_build && !$dry_run && -d $run_root) { # committed artifacts (an x86 UNDI ROM / the grub2 resource tarball) with no arch-specific build # step, so ppc builds them the same as x86 -- no cross-arch import. A forcearch target builds # only the builders its profile lists; the noarch ones run in the profile's native chroot. +# syslinux-xcat is noarch too, and its spec builds on x86 and ppc64le only. my @dep_builders = ( { name => 'elilo-xcat', script => "$repo_root/elilo/mockbuild.pl", noarch => 1 }, { name => 'grub2-xcat', script => "$repo_root/grub2-xcat/mockbuild.pl", noarch => 1 }, { name => 'ipmitool-xcat', script => "$repo_root/ipmitool/mockbuild.pl" }, - { name => 'syslinux-xcat', script => "$repo_root/syslinux/mockbuild.pl" }, + { name => 'syslinux-xcat', script => "$repo_root/syslinux/mockbuild.pl", noarch => 1 }, { name => 'goconserver', script => "$repo_root/goconserver/mockbuild.pl" }, { name => 'conserver-xcat', script => "$repo_root/conserver/mockbuild.pl" }, { name => 'xnba-undi', script => "$repo_root/xnba/mockbuild.pl", noarch => 1 }, diff --git a/packages-manifest.conf b/packages-manifest.conf index d97d07a..382b016 100644 --- a/packages-manifest.conf +++ b/packages-manifest.conf @@ -143,18 +143,21 @@ perl-Sys-Virt=11.10.0 xCAT-genesis-base=>= 2:2.18.0 # rocky-10-riscv64-xcat is the forcearch (cross-built) EL10 riscv64 target. It differs from the -# EPEL-fed EL10 sections above in two ways (see BUILD.md, "riscv64"): -# - the x86-only boot components (elilo-xcat, syslinux-xcat, xnba-undi) are not built for it; -# - riscv64 has no EPEL, so the perl deps EL10 otherwise takes from EPEL are built here too -# (perl-Crypt-Blowfish ... perl-Path-Class below). perl-Path-Class is a build dep of -# perl-Crypt-SSLeay only. +# EPEL-fed EL10 sections above in one way (see BUILD.md, "riscv64"): riscv64 has no EPEL, so the +# perl deps EL10 otherwise takes from EPEL are built here too (perl-Crypt-Blowfish ... +# perl-Path-Class below). perl-Path-Class is a build dep of perl-Crypt-SSLeay only. The x86 boot +# loaders (elilo-xcat, syslinux-xcat, xnba-undi) are noarch and are listed like on ppc64le: a +# riscv64 management node serves the x86 nodes of a mixed cluster. # The per-EL perl set is the EL10 one, so perl-HTML-Form is absent here as well. # xCAT-genesis-base is deliberately not listed: the riscv64 repo is built with --skip-genesis. [rocky-10-riscv64-xcat] conserver-xcat=8.2.1 +elilo-xcat=3.14 goconserver=>= 0.3.3-snap202011021058 grub2-xcat=1.0 ipmitool-xcat=>= 1.8.18-4 +syslinux-xcat=>= 6.03-1 +xnba-undi=>= 1.21.1-1 perl-Crypt-SSLeay=0.72 perl-HTTP-Async=>= 0.30-3 perl-IO-Stty=>= 0.04-5 From 76ec6e081a9a13c3c54a494c08f8d27955582b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:39:21 -0300 Subject: [PATCH 4/8] test(mockbuild-all): pin the riscv64 boot loaders to the ppc64le pins The riscv64 cell must carry elilo-xcat, grub2-xcat, syslinux-xcat and xnba-undi at the pins of the EL10 ppc64le cell. 3 assertions fail against the previous manifest. --- t/mockbuild-all.t | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/t/mockbuild-all.t b/t/mockbuild-all.t index 7511ca9..5891533 100644 --- a/t/mockbuild-all.t +++ b/t/mockbuild-all.t @@ -247,6 +247,15 @@ is(rpm_release(tempdir(CLEANUP => 1), 'nonexistent-pkg'), undef, 'rpm_release is my @missing = grep { !exists $m{$_}{'conserver-xcat'} } @targets; is_deeply(\@missing, [], 'conserver-xcat is present in every manifest target section') or diag("missing conserver-xcat in: @missing"); + + # The forcearch riscv64 target carries the noarch boot components the ppc64le EL10 target + # carries, at the same pins: a riscv64 MN serves the x86 nodes of a mixed cluster too. + my ($ppc) = grep { /^[a-z+]+-10-ppc64le$/ } @targets; + ok(defined $ppc, 'an EL10 ppc64le target section exists to compare against') or $ppc = ''; + for my $boot (qw(elilo-xcat grub2-xcat syslinux-xcat xnba-undi)) { + is($m{'rocky-10-riscv64-xcat'}{$boot}, $m{$ppc}{$boot}, + "$boot pinned in the riscv64 target as in the EL10 ppc64le target"); + } } # ---- bump_dep_release_suffix: stamps xcat-dep specs, prunes nested xcat-core, idempotent -------- From 030259d7f6044c32e7fc419daaca0f5ccf59580d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:53:21 -0300 Subject: [PATCH 5/8] fix(mockbuild-all): keep rpms of another architecture out of a target's cell collect_rpms copied every binary rpm a builder produced. The syslinux builder also produces syslinux, syslinux-extlinux and their debug rpms for the chroot it runs in, so a forcearch target that builds the noarch boot loaders in the native x86_64 chroot would have published x86_64 rpms in the riscv64 cell. The completeness gate checks names and pins only, so it would have passed. Keep an rpm only when it is noarch or carries the cell's architecture, read from the rpm header. The rule lives in MockBuildUtils as rpm_in_cell. --- MockBuildUtils.pm | 28 +++++++++++++++++++++++++++- mockbuild-all.pl | 14 +++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/MockBuildUtils.pm b/MockBuildUtils.pm index aa9065b..a03c2f7 100644 --- a/MockBuildUtils.pm +++ b/MockBuildUtils.pm @@ -20,7 +20,7 @@ our @EXPORT_OK = qw( parse_evr evr_cmp evr_constraint_ok parse_pin rpmkeys_checksig_problem 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 + build_mock_uniqueext rpm_arch rpm_in_cell ); # install_deps_packages($os_id): the host packages mockbuild-all.pl needs to run at all, for the @@ -568,6 +568,32 @@ sub bump_dep_release_suffix { # 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. +# rpm_arch($rpm): the architecture of an rpm. The header decides when the file can be read, so a +# renamed file does not pass for another architecture; a bare file name falls back to its suffix. +sub rpm_arch { + my ($rpm) = @_; + return unless defined $rpm; + if (-f $rpm) { + my $arch = `rpm -qp --qf '%{ARCH}' ${\ sh_quote($rpm)} 2>/dev/null`; + chomp $arch; + return $arch if $arch ne ''; + } + my ($arch) = $rpm =~ /\.([A-Za-z0-9_]+)\.rpm$/; + return $arch; +} + +# rpm_in_cell($rpm, $target_arch): whether an rpm belongs in the repository cell of $target_arch. +# A noarch builder run in another architecture's chroot (the x86 boot loaders for riscv64) can +# emit that chroot's native rpms beside the noarch one; only noarch and the cell's own +# architecture are kept. +sub rpm_in_cell { + my ($rpm, $target_arch) = @_; + my $arch = rpm_arch($rpm); + return 0 unless defined $arch && defined $target_arch; + return 1 if $arch eq 'noarch'; + return $arch eq $target_arch ? 1 : 0; +} + sub build_mock_uniqueext { my ($run, $seq, $label) = @_; diff --git a/mockbuild-all.pl b/mockbuild-all.pl index cfd532c..6f97fd0 100755 --- a/mockbuild-all.pl +++ b/mockbuild-all.pl @@ -15,7 +15,7 @@ use Parallel::ForkManager; use POSIX qw(strftime); use FindBin qw($RealBin); use lib $RealBin, "$RealBin/lib"; -use MockBuildUtils qw(sh_quote print_step version_matches required_pkgs +use MockBuildUtils qw(sh_quote print_step version_matches required_pkgs rpm_in_cell install_deps_packages install_deps_command missing_perl_modules read_manifest verify_repo_packages verify_repo_signature verify_rpm_signatures rpm_version rpm_release rpm_sigmd5 restamp_release_line @@ -848,11 +848,13 @@ print_step('Collect RPM artifacts'); print "collection roots:\n"; print " $_\n" for @collect_roots; -my ($copied, $skipped_src, $missing_roots) = collect_rpms( +my ($copied, $skipped_src, $missing_roots, $skipped_foreign) = collect_rpms( roots => \@collect_roots, dest_dir => $repo_dir, + arch => $arch, dry_run => $dry_run, ); +print "skipped $skipped_foreign rpm(s) of another architecture\n" if $skipped_foreign; # Assert on what this run BUILT, before the Genesis release is added: the release is # installed from a verified directory rather than built here, so counting it first would @@ -1970,11 +1972,13 @@ sub collect_rpms { my (%args) = @_; my $roots = $args{roots} // []; my $dest = $args{dest_dir} // die "collect_rpms missing dest_dir\n"; + my $cell_arch = $args{arch} // die "collect_rpms missing arch\n"; my $is_dry = $args{dry_run} ? 1 : 0; my %seen; my $copied = 0; my $skipped_src = 0; + my $skipped_foreign = 0; my $missing_roots = 0; for my $root (@{$roots}) { @@ -2002,6 +2006,10 @@ sub collect_rpms { my $base = basename($rpm); next if $genesis_release && $base =~ /^xCAT-genesis-openembedded-/; + if (!rpm_in_cell($rpm, $cell_arch)) { + $skipped_foreign++; + next; + } next if $seen{$base}++; if ($is_dry) { print "DRY-RUN copy: $rpm -> $dest/$base\n"; @@ -2014,7 +2022,7 @@ sub collect_rpms { } } - return ($copied, $skipped_src, $missing_roots); + return ($copied, $skipped_src, $missing_roots, $skipped_foreign); } sub collect_srpms { From a17df594c95fa8a7d833ce4c9172fec0807e9329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:53:55 -0300 Subject: [PATCH 6/8] test(mockbuild-all): cover the per-cell rpm architecture rule --- t/mockbuild-all.t | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/t/mockbuild-all.t b/t/mockbuild-all.t index 5891533..6adbe95 100644 --- a/t/mockbuild-all.t +++ b/t/mockbuild-all.t @@ -12,6 +12,7 @@ use File::Path qw(make_path); use File::Basename qw(basename); use MockBuildUtils qw(install_deps_packages install_deps_command missing_perl_modules required_pkgs version_matches rpm_sigmd5 rpm_version rpm_release rpm_is_signed + rpm_arch rpm_in_cell restamp_release_line cross_copy_genesis finalize_xcat_dep read_manifest verify_repo_packages verify_repo_signature verify_rpm_signatures parse_evr evr_constraint_ok parse_pin rpmkeys_checksig_problem @@ -258,6 +259,51 @@ is(rpm_release(tempdir(CLEANUP => 1), 'nonexistent-pkg'), undef, 'rpm_release is } } +# ---- rpm_in_cell: only noarch and the cell's own architecture reach a target's repository ------ +# A noarch builder run in another architecture's chroot (the x86 boot loaders for riscv64 build in +# rocky-10-x86_64) can emit that chroot's native rpms beside the noarch one, as syslinux does. +{ + is(rpm_arch('syslinux-xcat-6.03-1.noarch.rpm'), 'noarch', 'rpm_arch reads noarch'); + is(rpm_arch('syslinux-extlinux-6.03-1.x86_64.rpm'), 'x86_64', 'rpm_arch reads x86_64'); + is(rpm_arch('ipmitool-xcat-1.8.18-4.el10.riscv64.rpm'), 'riscv64', 'rpm_arch reads riscv64'); + is(rpm_arch('not-an-rpm.txt'), undef, 'rpm_arch is undef for a non-rpm name'); + ok( rpm_in_cell('syslinux-xcat-6.03-1.noarch.rpm', 'riscv64'), 'noarch belongs in the riscv64 cell'); + ok( rpm_in_cell('ipmitool-xcat-1.8.18-4.el10.riscv64.rpm', 'riscv64'), 'a riscv64 rpm belongs in the riscv64 cell'); + ok(!rpm_in_cell('syslinux-extlinux-6.03-1.x86_64.rpm', 'riscv64'), 'an x86_64 rpm does not belong in the riscv64 cell'); + ok(!rpm_in_cell('syslinux-debuginfo-6.03-1.x86_64.rpm', 'riscv64'), '... nor its debuginfo'); + ok( rpm_in_cell('syslinux-extlinux-6.03-1.x86_64.rpm', 'x86_64'), 'the same rpm belongs in the x86_64 cell'); + ok(!rpm_in_cell('syslinux-xcat-6.03-1.noarch.rpm', undef), 'no target arch -> not kept (fail-safe)'); + + # With a real file the header decides, so a renamed rpm does not pass for another architecture. + SKIP: { + skip 'rpmbuild not available', 4 if system('command -v rpmbuild >/dev/null 2>&1') != 0; + my $tmp = tempdir(CLEANUP => 1); + my $build = sub { + my ($name, $buildarch) = @_; + my $spec = "$tmp/$name.spec"; + open my $fh, '>', $spec or die; + print $fh "Name: $name\nVersion: 1.0\nRelease: 1\nSummary: fixture\nLicense: EPL\n" + . ($buildarch ? "BuildArch: $buildarch\n" : '') + . "%description\nfixture\n%install\nmkdir -p %{buildroot}/opt/t\necho x > %{buildroot}/opt/t/$name\n%files\n/opt/t/$name\n"; + close $fh; + system("rpmbuild -bb --quiet --define '_topdir $tmp/rpmb-$name' --define '_rpmdir $tmp/out-$name' '$spec' >/dev/null 2>&1") == 0 + or die "rpmbuild failed for $name"; + my ($rpm) = glob("$tmp/out-$name/*/$name-*.rpm"); + return $rpm; + }; + my $host = `uname -m`; chomp $host; + my $noarch = $build->('cell-noarch', 'noarch'); + my $native = $build->('cell-native', undef); + my $disguised_native = "$tmp/cell-native-1.0-1.noarch.rpm"; + my $disguised_noarch = "$tmp/cell-noarch-1.0-1.$host.rpm"; + require File::Copy; File::Copy::copy($native, $disguised_native) or die; File::Copy::copy($noarch, $disguised_noarch) or die; + is(rpm_arch($disguised_native), $host, 'the header names the architecture of a native rpm renamed as noarch'); + ok(!rpm_in_cell($disguised_native, 'riscv64') || $host eq 'riscv64', '... so it does not enter the riscv64 cell'); + is(rpm_arch($disguised_noarch), 'noarch', 'the header names noarch for a noarch rpm renamed as native'); + ok( rpm_in_cell($disguised_noarch, 'riscv64'), '... so it enters the riscv64 cell'); + } +} + # ---- bump_dep_release_suffix: stamps xcat-dep specs, prunes nested xcat-core, idempotent -------- # Reviewer asked for a test on this path. It walks a tree, stamps the first Release: line of every # xcat-dep spec, prunes a nested xcat-core/ checkout, and is idempotent on a re-run. From a7c122b11393c83e0169f2f50b6f73e9fcd21540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:53:55 -0300 Subject: [PATCH 7/8] fix(mockbuild-all): scrub the chroot a noarch step built in A noarch builder of a forcearch target runs in the native chroot of the release, but its cleanup step was registered against the target configuration. The step scrubbed a chroot that did not exist and left the native bootstrap behind on every run, one per noarch step. Compute the configuration once per step and scrub the same one it built in. --- mockbuild-all.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mockbuild-all.pl b/mockbuild-all.pl index 6f97fd0..f512e24 100755 --- a/mockbuild-all.pl +++ b/mockbuild-all.pl @@ -651,9 +651,10 @@ if (!$skip_build) { my $step_result = "$build_root/$name"; my $step_log = "$log_root/$name"; my $step_uniqueext = build_mock_uniqueext($run_id, ++$build_step_seq, $name); + my $mock_cfg = $builder->{noarch} ? $profile->{noarch_cfg} : $target; my $cmd = join(' ', 'perl', shell_quote($script), - '--mock-cfg', shell_quote($builder->{noarch} ? $profile->{noarch_cfg} : $target), + '--mock-cfg', shell_quote($mock_cfg), ($profile->{forcearch} && !$builder->{noarch} ? ('--target-arch', shell_quote($arch)) : ()), '--mock-uniqueext', shell_quote($step_uniqueext), '--result-dir', shell_quote($step_result), @@ -676,7 +677,7 @@ if (!$skip_build) { cmd => $cmd, timeout => $step_timeout, log => "$log_root/$name/run.log", - scrub_cfg => $target, + scrub_cfg => $mock_cfg, scrub_uniqueext => $step_uniqueext, }; push @collect_roots, $step_result; From fbf7a5be2ab45592318faec81a00a31b3a104983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:39:21 -0300 Subject: [PATCH 8/8] docs(BUILD): riscv64 repositories carry the x86 boot loaders --- BUILD.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BUILD.md b/BUILD.md index a4572f6..42ece60 100644 --- a/BUILD.md +++ b/BUILD.md @@ -453,7 +453,7 @@ missing, and then builds the `[rocky-10-riscv64-xcat]` section of `packages-mani | goconserver | cross-compiled on the host (`GOARCH=riscv64`), packaged with `rpmbuild --target riscv64` | | grub2-xcat (noarch) | built in the native, EPEL-free `rocky-10-x86_64` chroot | | perl list6 + EPEL gap (`--epel-gap`) | `mockbuild-perl-packages.pl --target-arch riscv64 --noarch-mock-cfg rocky-10-x86_64 --epel-gap`: XS modules in the riscv64 chroot, noarch modules in the native chroot | -| elilo-xcat, syslinux-xcat, xnba-undi | not built (x86 bootloaders) | +| elilo-xcat, syslinux-xcat, xnba-undi (noarch) | built in the native `rocky-10-x86_64` chroot, like grub2-xcat: a riscv64 management node serves the x86 nodes of a mixed cluster. The target is cross-built on x86_64 only, as its mock config states | There is no EPEL for riscv64, so the perl deps of xCAT that EL10 otherwise takes from EPEL are built here as well (`--epel-gap` in `mockbuild-perl-packages.pl`: perl-Crypt-Blowfish, @@ -610,8 +610,8 @@ Codename ↔ version (the single supported set — `BuildUtils` is the source of manifest). `build_one_codename` **skips** an `Architecture:all` package on any non-amd64 arch (detected via `control_binary_arch`), so ppc64el and riscv64 build only the genuinely arch-specific compiled deps (`ipmitool-xcat`, `conserver-xcat`, `goconserver`) yet still verify the - boot components they need. The riscv64 sections require `grub2-xcat` only: the x86 loaders - (`syslinux-xcat`, `elilo-xcat`, `xnba-undi`) are not part of a riscv64 repository. + boot components they need. The riscv64 sections require the same four boot components as + ppc64el: a riscv64 management node serves the x86 nodes of a mixed cluster. - **Fail-hard.** Any required chroot / package / artifact failure, or any version-pin mismatch, fails the whole run non-zero. - **Genesis keeps its maintained packaging.** A native `xcat-genesis-base` deb is INGESTED as-is when @@ -677,7 +677,7 @@ needs no `--mirror`. | ipmitool-xcat, conserver-xcat | `dpkg-buildpackage` in the emulated riscv64 chroot | | goconserver | same chroot, compiled by the Go toolchain the chroot installs for riscv64 | | grub2-xcat (`Architecture:all`) | built once on amd64 and assembled into the riscv64 index; listed in the riscv64 manifest sections as required-present, because a riscv64 management node needs it to netboot | -| syslinux-xcat, elilo-xcat, xnba-undi | not built and not required (x86 loaders) | +| syslinux-xcat, elilo-xcat, xnba-undi (`Architecture:all`) | built once on amd64 and assembled into the riscv64 index; required-present like grub2-xcat, because a riscv64 management node serves the x86 nodes of a mixed cluster | | xcat-genesis-base | not built: no riscv64 section names it, and the build skips the step when the manifest does not ask for it, so `--skip-genesis` is unnecessary here | The riscv64 ipmitool-xcat deb is installed into the chroot that built it and