mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-05 04:27:55 +00:00
feat(build): add a source-only mode to buildrpms.pl
buildrpms.pl already produces a source rpm for every package on every run -- buildall() is createmockconfig -> buildsources -> buildspkgs (mock --buildsrpm) -> buildpkgs (mock --rebuild). Source-only is that sequence without the last step, so it belongs here rather than in a parallel implementation: the spec, the staged sources and the mock root are identical either way, and anything built beside them can drift from what a real build does. --source-only stops after buildspkgs. Two things downstream had to learn about it, and both are about not publishing something untrue: - index_repo no longer re-indexes the binary directory. Running createrepo_c over a directory with no binaries in it would replace working metadata with metadata for an empty repository -- a repo that resolves nothing. The srpm index is still regenerated. - write_repo_metadata_dir emits nothing. The .repo file and buildinfo describe an installable binary repository, which this mode does not produce. --source-only with --merge-core-repos is refused: one builds packages, the other assembles per-arch trees that are already built. buildrpms.pl cannot be loaded by a test -- it runs mkdir, git and read_text at file scope -- so the two routines whose behaviour changed are lifted out with a regex and eval'd into a scratch package with their collaborators stubbed, per the code standard, with BAIL_OUT if the extraction stops matching. The CLI contract is exercised by running the real program. Verified by mutation: removing the index_repo guard reddens 2 of the 7 assertions. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
+39
-1
@@ -195,12 +195,18 @@ GetOptions(
|
||||
"output-dir=s" => \$opts{output_dir},
|
||||
"input-core-repos=s{1,}" => \@cli_input_core_repos,
|
||||
"repo-baseurl=s" => \$opts{repo_baseurl},
|
||||
"source-only" => \$opts{source_only},
|
||||
) or usage();
|
||||
|
||||
# --package REPLACES the default set (build exactly what was asked), so
|
||||
# `--package xCAT-genesis-base` builds only genesis-base for the dep pipeline.
|
||||
# The full default set is built on every arch (x86_64 and ppc64le alike), so each
|
||||
# arch produces a complete, self-contained xcat-core repo.
|
||||
# --source-only produces source rpms and nothing else. It is a build mode, so it has
|
||||
# nothing to assemble and must not be confused with the deploy-time merge.
|
||||
usage(message => "--source-only and --merge-core-repos are different modes; pass one")
|
||||
if $opts{source_only} && $opts{merge_core_repos};
|
||||
|
||||
$opts{packages} = \@cli_packages if @cli_packages;
|
||||
|
||||
# --native-only: build just the arch-native packages (@NATIVE_PACKAGES). Used on a
|
||||
@@ -560,6 +566,11 @@ sub buildall {
|
||||
createmockconfig($pkg, $target);
|
||||
buildsources($pkg, $target);
|
||||
buildspkgs($pkg, $target);
|
||||
# --source-only stops here: buildspkgs has produced the src.rpm, and the binary
|
||||
# rebuild is the only thing buildpkgs does. Everything upstream of this point --
|
||||
# the spec, the staged sources, the mock root -- is identical either way, which
|
||||
# is why source-only belongs here rather than in a parallel script.
|
||||
return if $opts{source_only};
|
||||
buildpkgs($pkg, $target);
|
||||
}
|
||||
|
||||
@@ -700,7 +711,12 @@ sub index_repo {
|
||||
# ships neither). The canonical src.rpm lives in SRPMS/.
|
||||
unlink($_) for glob("$repodir/*.src.rpm"), glob("$repodir/*.log"),
|
||||
glob("$repodir/SRPMS/*.log");
|
||||
createrepo_dir($repodir, "--excludes 'SRPMS/*' --excludes '*.src.rpm'");
|
||||
# In source-only mode no binaries were built, so re-indexing the binary dir would
|
||||
# replace good metadata with metadata for an empty repo -- publishing a repo that
|
||||
# resolves nothing. Leave it exactly as the last binary build left it and index
|
||||
# only the srpms.
|
||||
createrepo_dir($repodir, "--excludes 'SRPMS/*' --excludes '*.src.rpm'")
|
||||
unless $opts{source_only};
|
||||
createrepo_dir("$repodir/SRPMS") if -d "$repodir/SRPMS";
|
||||
}
|
||||
|
||||
@@ -777,6 +793,10 @@ sub write_repo_metadata {
|
||||
sub write_repo_metadata_dir {
|
||||
my ($repodir) = @_;
|
||||
return unless -d $repodir;
|
||||
# The .repo file and buildinfo describe an installable binary repository. A
|
||||
# source-only run produced none, so emitting them would advertise packages that
|
||||
# are not there.
|
||||
return if $opts{source_only};
|
||||
|
||||
# Shipped baseurl points at xcat.org (--repo-baseurl overrides it per family, e.g. the
|
||||
# sles/apt layout); mklocalrepo.sh rewrites baseurl/gpgkey to file:// at deploy time.
|
||||
@@ -1128,6 +1148,24 @@ Default: all host CPUs.
|
||||
|
||||
Rebuild artifacts even if output files already exist.
|
||||
|
||||
=item B<--source-only>
|
||||
|
||||
Build source RPMs and stop. Every step up to and including C<mock --buildsrpm> runs
|
||||
normally, so the srpms are the same ones a full build would produce; only the binary
|
||||
C<--rebuild> is skipped.
|
||||
|
||||
./buildrpms.pl --target alma+epel-9-x86_64 --source-only
|
||||
|
||||
The srpms land in C<dist/E<lt>targetE<gt>/rpms/SRPMS/> and that index is regenerated.
|
||||
The binary metadata under C<dist/E<lt>targetE<gt>/rpms/> is deliberately left as the
|
||||
last binary build wrote it: re-indexing a directory with no binaries in it would
|
||||
replace working metadata with metadata for an empty repository. For the same reason
|
||||
no C<.repo> file or buildinfo is emitted, since both describe an installable binary
|
||||
repo that this mode does not produce. With C<--gpg-sign> the srpms are signed.
|
||||
|
||||
Cannot be combined with C<--merge-core-repos>, which assembles already-built
|
||||
per-arch binary trees.
|
||||
|
||||
=item B<--release>=I<STRING>
|
||||
|
||||
Override the auto-generated C<snapYYYYMMDDHHMM> release string. xCAT packages
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env perl
|
||||
# buildrpms.pl --source-only: build the source rpms and stop.
|
||||
#
|
||||
# buildrpms.pl cannot be loaded -- it runs mkdir/git/read_text at file scope and
|
||||
# expects a working tree -- so the routines whose behaviour changed are lifted out
|
||||
# with a regex and eval'd into a scratch package with their collaborators stubbed,
|
||||
# per the repo's code standard. The CLI contract is exercised by running the real
|
||||
# program.
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Cwd qw(getcwd);
|
||||
use File::Slurper qw(read_text);
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use Test::More;
|
||||
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
|
||||
my $builder = repo_path('buildrpms.pl');
|
||||
plan skip_all => 'buildrpms.pl not found' unless -r $builder;
|
||||
|
||||
my $source = read_text($builder);
|
||||
|
||||
# ---------------------------------------------------------------- extraction --
|
||||
# Lift the two routines that decide what a source-only run publishes. BAIL_OUT
|
||||
# rather than skip: if the extraction stops matching, this file would silently
|
||||
# cover nothing.
|
||||
my %routine;
|
||||
for my $name (qw(index_repo write_repo_metadata_dir)) {
|
||||
my ($body) = $source =~ /\n(sub \Q$name\E \{.*?\n\})\n/s;
|
||||
BAIL_OUT("could not extract $name from buildrpms.pl") unless $body;
|
||||
$routine{$name} = $body;
|
||||
}
|
||||
|
||||
our @CREATEREPO;
|
||||
our @METADATA_WRITTEN;
|
||||
|
||||
{
|
||||
package Scratch;
|
||||
no warnings 'redefine';
|
||||
# Collaborators the lifted code calls. Each records instead of acting.
|
||||
sub createrepo_dir { push @main::CREATEREPO, $_[0]; }
|
||||
}
|
||||
|
||||
# %opts lives in the scratch package and is set directly. Aliasing it to a hash in
|
||||
# main and localising that does NOT work: local swaps the container, so the lifted
|
||||
# code keeps reading the hash the glob pointed at before.
|
||||
my $harness = join "\n",
|
||||
'package Scratch;',
|
||||
'use strict; use warnings;',
|
||||
'our %opts;',
|
||||
'sub say { }',
|
||||
# write_repo_metadata_dir does real work past the guard; stop it there so the
|
||||
# test observes the guard and nothing else.
|
||||
$routine{index_repo},
|
||||
($routine{write_repo_metadata_dir} =~ s/(return if \$opts\{source_only\};).*\n\}\z/$1\n push \@main::METADATA_WRITTEN, \$repodir;\n return 1;\n}/sr),
|
||||
'1;';
|
||||
|
||||
eval $harness or BAIL_OUT("could not evaluate the extracted routines: $@");
|
||||
|
||||
sub run_index {
|
||||
my (%args) = @_;
|
||||
%Scratch::opts = (source_only => $args{source_only});
|
||||
local @CREATEREPO = ();
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
mkdir File::Spec->catdir($dir, 'SRPMS');
|
||||
Scratch::index_repo($dir);
|
||||
return [map { $_ eq $dir ? 'BINARY' : 'SRPMS' } @CREATEREPO];
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------ behaviour --
|
||||
is_deeply( run_index(source_only => 0), ['BINARY', 'SRPMS'],
|
||||
'a normal run indexes the binary repository and the srpms' );
|
||||
|
||||
is_deeply( run_index(source_only => 1), ['SRPMS'],
|
||||
'a source-only run indexes only the srpms' );
|
||||
|
||||
# The reason for the guard: re-indexing a directory with no binaries in it would
|
||||
# replace working metadata with metadata for an empty repository.
|
||||
ok( !grep({ $_ eq 'BINARY' } @{ run_index(source_only => 1) }),
|
||||
'a source-only run leaves the binary metadata alone' );
|
||||
|
||||
{
|
||||
%Scratch::opts = (source_only => 1);
|
||||
local @METADATA_WRITTEN = ();
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
Scratch::write_repo_metadata_dir($dir);
|
||||
is_deeply( \@METADATA_WRITTEN, [],
|
||||
'a source-only run emits no .repo file for packages it did not build' );
|
||||
}
|
||||
{
|
||||
%Scratch::opts = (source_only => 0);
|
||||
local @METADATA_WRITTEN = ();
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
Scratch::write_repo_metadata_dir($dir);
|
||||
is_deeply( \@METADATA_WRITTEN, [$dir],
|
||||
'a normal run still emits the repository metadata' );
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------- the CLI --
|
||||
# Run the real program. --source-only and --merge-core-repos are different modes:
|
||||
# one builds, the other assembles trees that are already built.
|
||||
my $cwd = getcwd();
|
||||
chdir repo_path('.') or BAIL_OUT("cannot chdir to the repository root: $!");
|
||||
my $out = qx($^X buildrpms.pl --source-only --merge-core-repos 2>&1);
|
||||
my $rc = $? >> 8;
|
||||
chdir $cwd;
|
||||
|
||||
isnt( $rc, 0, 'combining --source-only with --merge-core-repos is refused' );
|
||||
like( $out, qr/--source-only and --merge-core-repos/,
|
||||
'and the refusal names both options' );
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user