From a5f670a4cca79efb30d96b1f3c87eda12f0520cd Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Tue, 1 Sep 2026 16:34:11 -0300 Subject: [PATCH] refactor(build): share the buildinfo stamp between the two builders Both builders wrote the same six fields beside a published repository -- VERSION, RELEASE, BUILD_TIME, BUILD_MACHINE, COMMIT_ID, COMMIT_ID_LONG -- each assembling them by hand, and each shelling out to hostname to do it. builddebs.pl also ran git rev-parse a second time to get the commit, although it had already derived the same value into $GITINFO earlier in the file. It now uses that. The filenames and the time formats stay as they were. deploy.sh copies builddebs.pl's buildinfo verbatim and cluster-test.pl parses buildrpms.pl's buildinfo.txt, so both are a contract with consumers outside this repository; the shared helper takes the format from its caller rather than picking one. Confirmed byte for byte that each builder's stamp is unchanged. Covered by tests: the field names and their order, the seven-character short commit, and that the two callers' formats still differ. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- BuildUtils.pm | 22 ++++++++++++++++++++++ builddebs.pl | 16 ++++------------ buildrpms.pl | 16 ++++------------ xCAT-test/unit/build_utils.t | 25 +++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/BuildUtils.pm b/BuildUtils.pm index c455b8c00..c76200adc 100644 --- a/BuildUtils.pm +++ b/BuildUtils.pm @@ -33,6 +33,7 @@ our @EXPORT_OK = qw( backup_file restore_file sh usage read_file write_file rewrite_file + buildinfo_text ); # Both builders echo the commands they run under --verbose. Set once, after @@ -42,6 +43,27 @@ our $VERBOSE = 0; # The xCAT-probe helpers. xcat-probe reuses functions shipped by xCAT; they are COPIED # rather than symlinked because a symlink does not survive packaging, and rather than # maintained twice because they would drift. Both builders stage them the same way. +# The stamp both builders write beside a published repository. deploy.sh copies +# the file verbatim and cluster-test.pl parses it, so the field names and their +# order are a contract; each builder passes its own time format and writes to +# its own filename, which are part of that contract too. +sub buildinfo_text { + my (%args) = @_; + my $commit = $args{commit} // 'unknown'; + my $host = $args{host}; + unless (defined $host) { + $host = `hostname 2>/dev/null` || 'unknown'; + chomp $host; + } + return join('', map { "$_\n" } + "VERSION=$args{version}", + "RELEASE=$args{release}", + "BUILD_TIME=" . strftime($args{time_format}, gmtime($args{epoch})), + "BUILD_MACHINE=$host", + "COMMIT_ID=" . substr($commit, 0, 7), + "COMMIT_ID_LONG=$commit"); +} + # Whole-file read and write. Deliberately plain open/close rather than # File::Slurper, so that loading this module does not oblige a deb build to # install a module it otherwise does not need. diff --git a/builddebs.pl b/builddebs.pl index 74a7b96d5..e4da5aaf5 100755 --- a/builddebs.pl +++ b/builddebs.pl @@ -36,7 +36,7 @@ use BuildUtils qw( pin_control_version rewrite_changelog_header reprepro_distributions reprepro_options lock_id_for take_build_lock sh_quote - sh usage read_file write_file rewrite_file + sh usage read_file write_file rewrite_file buildinfo_text ); # The xcat-core packages that ship as debs. xCAT-openbmc-py, xCAT-rmc and xCAT-release @@ -338,17 +338,9 @@ SCRIPT close $m; chmod 0775, "$repodir/mklocalrepo.sh"; - my $commit = `git -C @{[ sh_quote($ROOT) ]} rev-parse HEAD 2>/dev/null` || 'unknown'; - chomp $commit; - my $host = `hostname 2>/dev/null` || 'unknown'; chomp $host; - open my $b, '>', "$repodir/buildinfo" or die "Cannot write buildinfo: $!\n"; - print {$b} "VERSION=$VERSION\n", - "RELEASE=$RELEASE\n", - "BUILD_TIME=@{[ strftime('%a %b %d %H:%M:%S %Y', gmtime($EPOCH)) ]}\n", - "BUILD_MACHINE=$host\n", - "COMMIT_ID=@{[ substr($commit, 0, 7) ]}\n", - "COMMIT_ID_LONG=$commit\n"; - close $b; + write_file("$repodir/buildinfo", buildinfo_text( + version => $VERSION, release => $RELEASE, epoch => $EPOCH, + commit => $GITINFO, time_format => '%a %b %d %H:%M:%S %Y')); return; } diff --git a/buildrpms.pl b/buildrpms.pl index 9057bed41..519d2f3a0 100755 --- a/buildrpms.pl +++ b/buildrpms.pl @@ -43,7 +43,7 @@ use File::Slurper qw(read_text write_text); use File::Temp qw(tempdir tempfile); use FindBin qw($Bin); use lib $Bin; -use BuildUtils qw(git_revision source_date_epoch sh usage); +use BuildUtils qw(git_revision source_date_epoch sh usage buildinfo_text); use Fcntl qw(:flock); # per-target build lock (concurrency guard; see main()) use Getopt::Long qw(GetOptions); use POSIX qw(strftime); @@ -811,17 +811,9 @@ EOF2 chmod 0775, "$repodir/mklocalrepo.sh"; # BUILD_TIME from SOURCE_DATE_EPOCH keeps buildinfo reproducible across rebuilds. - my $build_time = strftime("%a %b %e %H:%M:%S %Z %Y", gmtime($SOURCE_DATE_EPOCH)); - my $build_machine = `hostname`; chomp $build_machine; - my $commit_short = substr($GITINFO, 0, 7); - write_text("$repodir/buildinfo.txt", <<"EOF"); -VERSION=$VERSION -RELEASE=$RELEASE -BUILD_TIME=$build_time -BUILD_MACHINE=$build_machine -COMMIT_ID=$commit_short -COMMIT_ID_LONG=$GITINFO -EOF + write_text("$repodir/buildinfo.txt", buildinfo_text( + version => $VERSION, release => $RELEASE, epoch => $SOURCE_DATE_EPOCH, + commit => $GITINFO, time_format => "%a %b %e %H:%M:%S %Z %Y")); } # Assemble the flat MULTI-ARCH core from per-arch build outputs and sign it, in the upstream diff --git a/xCAT-test/unit/build_utils.t b/xCAT-test/unit/build_utils.t index cdccd4142..35a86d111 100644 --- a/xCAT-test/unit/build_utils.t +++ b/xCAT-test/unit/build_utils.t @@ -308,6 +308,31 @@ is( git_revision( git => sub { "\n" }, read_file => sub { " \n" } ), isnt( git_revision( git => sub { '' }, read_file => sub { '' } ), '', 'the one thing it must never return is empty' ); +# ------------------------------------------------------- buildinfo_text -- +# deploy.sh copies this file verbatim and cluster-test.pl parses it, so the +# field names and their order are a contract, not a presentation choice. +{ + my $text = BuildUtils::buildinfo_text( + version => '2.18.1', release => 'snap1', epoch => 0, + commit => 'abcdef1234567890', host => 'builder', + time_format => '%Y-%m-%d', + ); + is_deeply( [ map { (split /=/, $_, 2)[0] } split /\n/, $text ], + [qw(VERSION RELEASE BUILD_TIME BUILD_MACHINE COMMIT_ID COMMIT_ID_LONG)], + 'the fields appear in the order the consumers expect' ); + like( $text, qr/^COMMIT_ID=abcdef1\n/m, 'the short commit is seven characters' ); + like( $text, qr/^COMMIT_ID_LONG=abcdef1234567890\n/m, 'and the long one is whole' ); + like( $text, qr/^BUILD_TIME=1970-01-01\n/m, + "the caller's own time format is used" ); + + # The two builders stamp different formats, and both are consumed. + my %common = (version => '1', release => '2', epoch => 0, + commit => 'c', host => 'h'); + isnt( BuildUtils::buildinfo_text(%common, time_format => '%a %b %d %H:%M:%S %Y'), + BuildUtils::buildinfo_text(%common, time_format => '%a %b %e %H:%M:%S %Z %Y'), + 'each builder keeps the format its own consumers parse' ); +} + # -------------------------------------------------- whole-file helpers -- { my $dir = tempdir(CLEANUP => 1);