2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-12 04:26:25 +00:00

feat(xcat-dep): --install-deps makes a build host able to run the script

A CD run died at compile time inside XCAT::BuildUtils because xcat-master-ub was
missing File::Slurper: "Can't locate File/Slurper.pm in @INC", in the middle of a
build. It was fixed by hand, so the next unprovisioned host fails the same way
and the documented install line can drift from what the code actually loads.

--install-deps installs this host's prerequisites and exits: the sbuild/schroot
toolchain plus the modules. It then LOADS each module and fails naming any that
is still missing, rather than trusting apt's exit code.

That probe earned its place immediately: the first list named libipc-cmd-perl,
which does not exist on Ubuntu -- IPC::Cmd is core there -- and apt failed the
whole install over it. The package is gone from the list and the module is
asserted by loading instead, with a test that pins both halves of that reasoning.

The list and the command are pure functions in BuildUtils, so the decision is
unit-tested and the side effect stays in the caller. Run on xcat-master-ub and
xcat-master-ub-ppc: both report every module present.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-27 15:30:47 -03:00
parent a35343f60e
commit 5bb9cfa2b2
4 changed files with 105 additions and 1 deletions
+15
View File
@@ -402,6 +402,21 @@ Codename ↔ version (the single supported set — `BuildUtils` is the source of
- **`t/verify-repo.t`** — end-to-end tests of the repo gate against fixture apt trees (missing
secondary arch, arch:all-only index, unsigned repo, missing manifest section).
## Prerequisites (once per build host, as root)
```bash
./sbuild-all.pl --install-deps
```
Installs the sbuild/schroot toolchain and the Perl modules the script loads, then **loads** each one
and fails if any is still missing. That last step is the point: a missing module surfaces otherwise
as a compile-time abort inside `XCAT::BuildUtils`, mid-run — which is how `File::Slurper` being
absent on `xcat-master-ub` took a CD run down. `IPC::Cmd` is deliberately not in the package list:
it is core on Debian/Ubuntu, no such package exists, and naming one fails the whole install; the
probe is what asserts it is usable.
The per-codename sbuild chroots are separate host state — see `ci/mk-dep-chroots.sh`.
## Usage (per arch, as root on the matching build host)
Run `sbuild-all.pl` on the build host for the arch you are building (amd64 on the x86 Ubuntu host,
+33
View File
@@ -22,6 +22,7 @@ use Digest::MD5;
use MIME::Base64 qw(encode_base64);
our @EXPORT_OK = qw(
install_deps_packages install_deps_command missing_perl_modules
sh_quote print_step
version_matches required_pkgs read_manifest standard_options
verify_repo_packages verify_repo_signature verify_repo_arches
@@ -54,6 +55,38 @@ sub version_to_codename { my ($v) = @_; return $VERSION_TO_CODENAME{$v // ''}; }
# Shared / format-agnostic helpers (identical semantics to MockBuildUtils.pm; see migration note).
# ---------------------------------------------------------------------------------------------------
# install_deps_packages(): the host packages sbuild-all.pl needs to run at all. Kept as data beside
# the code that needs them: the failure mode is a build host provisioned by hand, and a missing perl
# module surfaces as a compile-time abort in the middle of a CD run rather than as a clear message
# (xcat-master-ub had no File::Slurper, which is how this was found).
sub install_deps_packages {
# NOTE: no libipc-cmd-perl -- IPC::Cmd is CORE on Debian/Ubuntu (it ships in perl-modules) and
# no such package exists, so naming it fails the whole install. That it is present is asserted
# by the module probe, not by installing a package.
return qw(perl libfile-slurper-perl libparallel-forkmanager-perl
sbuild schroot debootstrap apt-utils dpkg-dev devscripts equivs quilt fakeroot
build-essential reprepro gnupg rsync wget git);
}
# install_deps_command(): the argv that installs them, non-interactively.
sub install_deps_command {
return ('apt-get', 'install', '-y', '--no-install-recommends', install_deps_packages());
}
# missing_perl_modules(@modules): those that cannot be loaded, in order. --install-deps proves the
# modules by LOADING them, rather than trusting the package manager's exit code.
sub missing_perl_modules {
my (@modules) = @_;
my @missing;
for my $m (@modules) {
my $file = $m;
$file =~ s{::}{/}g;
$file .= '.pm';
eval { require $file; 1 } or push @missing, $m;
}
return @missing;
}
# sh_quote: single-quote a string for safe use in a shell command.
sub sh_quote {
my ($s) = @_;
+30
View File
@@ -46,6 +46,7 @@ use lib $RealBin, "$RealBin/lib";
# it, including the builds that never pass --genesis-release. Its subs are therefore called
# fully-qualified.
use BuildUtils qw(sh_quote print_step version_matches required_pkgs read_manifest standard_options
install_deps_packages install_deps_command missing_perl_modules
verify_repo_packages verify_repo_signature verify_repo_arches
parse_packages_index parse_release_architectures resolve_present_names
index_has_native_arch control_binary_arch skip_arch_all_on
@@ -73,6 +74,7 @@ my $build_number;
# (4 per host). N caps it to N; 1 forces serial.
my $parallel_targets = 0;
my ($skip_build, $skip_install, $skip_genesis, $skip_xcat_dep) = (0,0,0,0);
my $install_deps = 0;
my ($skip_createrepo, $skip_tarball) = (0,0);
my $dry_run = 0;
# --publish: run the FINALIZATION phase (assemble + sign + gate + tarball). See the "Publishing"
@@ -176,6 +178,7 @@ $spec{'genesis-deb=s'} = \@genesis_debs;
$spec{'genesis-rpm=s'} = \$genesis_rpm;
$spec{'genesis-rpm-ppc=s'} = \$genesis_rpm_ppc;
$spec{'require-ppc-genesis!'} = \$require_ppc_genesis;
$spec{'install-deps!'} = \$install_deps; # make this host able to run at all, then exit
$spec{'publish!'} = \$publish; # run the finalization (assemble+sign+gate+tarball)
$spec{'publish-lock-wait=i'} = \$PUBLISH_LOCK_WAIT; # seconds to queue behind another publisher
$spec{'expect-arch=s'} = \@expect_arch; # repeatable; each value may be a space/comma list
@@ -188,6 +191,27 @@ $spec{'man'} = sub { pod2usage(-verbose => 2, -exitval => 0);
GetOptions(%spec) or pod2usage(-verbose => 1, -exitval => 2);
# --install-deps: make THIS host able to run the script, then exit. It comes first because
# everything below assumes the toolchain is present, and a host that lacks it would fail with a
# compile-time error inside a module rather than a message it can act on -- which is how
# File::Slurper being absent on xcat-master-ub aborted a CD run mid-build. The modules are proven by
# LOADING them, not by trusting apt's exit code. Run once per build host, as root.
if ($install_deps) {
die "FATAL: --install-deps must run as root (uid=$>)\n" if $> != 0;
my @cmd = install_deps_command();
print_step('Install build prerequisites');
print " " . join(' ', @cmd) . "\n";
local $ENV{DEBIAN_FRONTEND} = 'noninteractive';
system('apt-get', 'update', '-q') == 0 or die "FATAL: apt-get update failed\n";
system(@cmd) == 0 or die "FATAL: " . join(' ', @cmd) . " failed\n";
my @modules = qw(File::Slurper IPC::Cmd Parallel::ForkManager Digest::MD5);
my @missing = missing_perl_modules(@modules);
die "FATAL: still missing after install: " . join(', ', @missing) . "\n" if @missing;
print " perl modules present: " . join(', ', @modules) . "\n";
print " host is ready\n";
exit 0;
}
# ---------------------------------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------------------------------
@@ -1485,6 +1509,12 @@ the default gives 8 concurrent build streams for a 4-codename matrix (4 per host
Skip the corresponding phase(s). C<--skip-build --skip-genesis> is the finalization run (it publishes
by default; see C<--publish>). C<--skip-createrepo> forces "do not publish" and always wins.
=item B<--install-deps>
Install this host's build prerequisites (the sbuild/schroot toolchain and the Perl modules the
script loads), verify each module now loads, then exit. Run once per build host, as root. Use
alone.
=item B<--publish-lock-wait> C<seconds>
How long to queue behind another publisher before failing (default 1800). Publishing takes one
+27 -1
View File
@@ -12,7 +12,8 @@ use lib "$RealBin/..";
use File::Temp qw(tempdir);
use File::Path qw(make_path);
use File::Basename qw(basename);
use BuildUtils qw(required_pkgs version_matches read_manifest standard_options
use BuildUtils qw(install_deps_packages install_deps_command missing_perl_modules
required_pkgs version_matches read_manifest standard_options
verify_repo_packages verify_repo_signature verify_repo_arches
parse_packages_index parse_release_architectures resolve_present_names
index_has_native_arch control_binary_arch skip_arch_all_on
@@ -592,4 +593,29 @@ STUB
}
}
# ---- --install-deps: the host prerequisites (the modules are what actually break a run) ----------
# A CD run died at compile time inside XCAT::BuildUtils because this builder lacked File::Slurper,
# so the list must carry every module the script loads, and the mode must PROVE them by loading
# rather than trusting apt's exit code.
{
my @pkgs = install_deps_packages();
for my $need (qw(libfile-slurper-perl libparallel-forkmanager-perl sbuild schroot apt-utils dpkg-dev)) {
ok(scalar(grep { $_ eq $need } @pkgs), "prerequisites include $need");
}
my @cmd = install_deps_command();
is($cmd[0], 'apt-get', 'installs with apt-get');
ok(scalar(grep { $_ eq '-y' } @cmd), '... non-interactively');
ok(scalar(grep { $_ eq '--no-install-recommends' } @cmd), '... without recommends');
# IPC::Cmd is CORE on Debian/Ubuntu -- there is no package to name, and naming one fails the
# whole install. The probe is what asserts it is usable.
ok(!scalar(grep { /ipc-cmd/ } @pkgs), 'no libipc-cmd-perl: IPC::Cmd is core on Debian/Ubuntu');
is_deeply([ missing_perl_modules('IPC::Cmd') ], [], '... and it loads');
is_deeply([ missing_perl_modules('Digest::MD5') ], [],
'missing_perl_modules: a loadable module is not reported');
is_deeply([ missing_perl_modules('No::Such::Module::Here') ], ['No::Such::Module::Here'],
'missing_perl_modules: an absent module is reported');
}
done_testing;