mirror of
https://github.com/xcat2/xcat-dep.git
synced 2026-09-12 12:36:23 +00:00
2fe5f186a1
%_buildtime is not an rpm macro: built with it alone and no SOURCE_DATE_EPOCH, rpm 4.14.3, 4.16.1.3 and 4.19.1.1 all stamped the current time. The build time and build host the release verifier checks come from SOURCE_DATE_EPOCH with use_source_date_epoch_as_buildtime, and from _buildhost -- both honoured on every one of those versions and on rpm 6.0.2, where the package also builds and verifies unchanged.
247 lines
8.8 KiB
Perl
Executable File
247 lines
8.8 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Cwd qw(abs_path);
|
|
use File::Basename qw(basename dirname);
|
|
use File::Copy qw(copy);
|
|
use File::Path qw(make_path remove_tree);
|
|
use File::Temp qw(tempdir);
|
|
use FindBin;
|
|
use Getopt::Long qw(GetOptions);
|
|
use lib "$FindBin::Bin/../lib";
|
|
use XCAT::BuildUtils qw(
|
|
capture_command
|
|
digest_manifest
|
|
relative_files
|
|
require_command
|
|
run_command
|
|
write_binary
|
|
);
|
|
use XCAT::GenesisRelease qw(
|
|
deb_package_name
|
|
rpm_package_name
|
|
validate_architecture
|
|
validate_export
|
|
);
|
|
|
|
my ($architecture, $export_dir, $output_dir, $version, $release, $revision);
|
|
my $source_date_epoch;
|
|
my $format = 'all';
|
|
my $rpm_top;
|
|
|
|
END {
|
|
eval { remove_tree($rpm_top) }
|
|
if defined($rpm_top) && -d $rpm_top && !-l $rpm_top;
|
|
}
|
|
|
|
GetOptions(
|
|
'architecture=s' => \$architecture,
|
|
'export-dir=s' => \$export_dir,
|
|
'output-dir=s' => \$output_dir,
|
|
'version=s' => \$version,
|
|
'release=s' => \$release,
|
|
'revision=s' => \$revision,
|
|
'source-date-epoch=i' => \$source_date_epoch,
|
|
'format=s' => \$format,
|
|
) or die usage();
|
|
|
|
for my $required (
|
|
[ architecture => $architecture ],
|
|
[ 'export-dir' => $export_dir ],
|
|
[ 'output-dir' => $output_dir ],
|
|
[ version => $version ],
|
|
[ release => $release ],
|
|
[ revision => $revision ],
|
|
[ 'source-date-epoch' => $source_date_epoch ],
|
|
)
|
|
{
|
|
die "Missing --$required->[0]\n" unless defined($required->[1]) && $required->[1] ne '';
|
|
}
|
|
|
|
validate_architecture($architecture);
|
|
die "Invalid version: $version\n" unless $version =~ /^\d+(?:\.\d+){1,3}$/;
|
|
die "Invalid release: $release\n" unless $release =~ /^[A-Za-z0-9][A-Za-z0-9.+~]*$/;
|
|
die "Invalid xcat-core revision: $revision\n" unless $revision =~ /^[0-9a-f]{40}$/;
|
|
die "Invalid source date epoch\n" unless $source_date_epoch =~ /^\d+$/;
|
|
die "Unsupported package format: $format\n"
|
|
unless $format eq 'all' || $format eq 'rpm' || $format eq 'deb';
|
|
umask(0022);
|
|
|
|
$export_dir = abs_path($export_dir) or die "Cannot resolve export directory\n";
|
|
validate_export($export_dir, $architecture);
|
|
|
|
my $output_parent = dirname($output_dir);
|
|
make_path($output_parent) unless -d $output_parent;
|
|
$output_parent = abs_path($output_parent) or die "Cannot resolve output parent\n";
|
|
$output_dir = "$output_parent/" . basename($output_dir);
|
|
die "Output already exists: $output_dir\n" if -e $output_dir || -l $output_dir;
|
|
chdir($output_parent) or die "Cannot use output parent $output_parent: $!\n";
|
|
|
|
my $work = tempdir('xcat-genesis-package.XXXXXX', TMPDIR => 1, CLEANUP => 1);
|
|
my $staging = tempdir('.xcat-genesis-output.XXXXXX', DIR => $output_parent, CLEANUP => 1);
|
|
my $rpm_name = rpm_package_name($architecture);
|
|
my $deb_name = deb_package_name($architecture);
|
|
my $source_name = "$rpm_name-$version";
|
|
my $source_root = "$work/$source_name";
|
|
make_path("$source_root/image");
|
|
|
|
for my $entry (_flat_files($export_dir)) {
|
|
copy("$export_dir/$entry", "$source_root/image/$entry")
|
|
or die "Cannot copy export file $entry: $!\n";
|
|
}
|
|
write_binary("$source_root/xcat-core-revision", "$revision\n");
|
|
|
|
local $ENV{SOURCE_DATE_EPOCH} = $source_date_epoch;
|
|
|
|
if ($format eq 'all' || $format eq 'rpm') {
|
|
require_command('rpmbuild');
|
|
_build_rpm($work, $staging, $source_root, $source_name, $rpm_name);
|
|
}
|
|
if ($format eq 'all' || $format eq 'deb') {
|
|
require_command('dpkg-deb');
|
|
_build_deb($work, $staging, $source_root, $deb_name);
|
|
}
|
|
|
|
chmod(0755, $staging) or die "Cannot set output mode: $!\n";
|
|
rename($staging, $output_dir) or die "Cannot publish $output_dir: $!\n";
|
|
print "Packaged Genesis $architecture in $output_dir\n";
|
|
|
|
sub _build_rpm {
|
|
my ($work, $staging, $source_root, $source_name, $package_name) = @_;
|
|
_require_gnu_tar();
|
|
require_command('gzip');
|
|
$rpm_top = tempdir('xcat-genesis-rpmbuild.XXXXXX', DIR => '/var/tmp', CLEANUP => 0);
|
|
make_path(map { "$rpm_top/$_" } qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS));
|
|
make_path("$staging/rpm", "$staging/srpm");
|
|
|
|
my $archive = "$rpm_top/SOURCES/$source_name.tar.gz";
|
|
run_command(
|
|
'tar', '--sort=name', '--owner=0', '--group=0', '--numeric-owner',
|
|
"--mtime=\@$source_date_epoch", '--use-compress-program=gzip -n',
|
|
'-cf', $archive, '-C', dirname($source_root), basename($source_root),
|
|
);
|
|
my $spec = "$FindBin::Bin/rpm/xCAT-genesis-openembedded.spec";
|
|
copy($spec, "$rpm_top/SPECS/xCAT-genesis-openembedded.spec")
|
|
or die "Cannot stage RPM spec: $!\n";
|
|
run_command(
|
|
'rpmbuild', '-ba', '--nodeps',
|
|
'--define', "_topdir $rpm_top",
|
|
'--define', "genesis_arch $architecture",
|
|
'--define', "version $version",
|
|
'--define', "release $release",
|
|
# SOURCE_DATE_EPOCH is exported above; rpm turns it into the build time only
|
|
# when asked, and takes the build host from _buildhost. Both are honoured from
|
|
# rpm 4.14 through 6, which is what makes the identity verify-release expects
|
|
# reproducible on every builder.
|
|
'--define', '_buildhost xcat-release',
|
|
'--define', 'use_source_date_epoch_as_buildtime 1',
|
|
'--define', 'clamp_mtime_to_source_date_epoch 1',
|
|
'--define', '_binary_payload w9.gzdio',
|
|
'--define', '_source_payload w9.gzdio',
|
|
"$rpm_top/SPECS/xCAT-genesis-openembedded.spec",
|
|
);
|
|
|
|
my $binary = "$rpm_top/RPMS/noarch/$package_name-$version-$release.noarch.rpm";
|
|
my $source = "$rpm_top/SRPMS/$package_name-$version-$release.src.rpm";
|
|
die "RPM build did not produce $binary\n" unless -f $binary;
|
|
die "RPM build did not produce $source\n" unless -f $source;
|
|
copy($binary, "$staging/rpm/" . basename($binary)) or die "Cannot collect RPM: $!\n";
|
|
copy($source, "$staging/srpm/" . basename($source)) or die "Cannot collect SRPM: $!\n";
|
|
remove_tree($rpm_top);
|
|
undef($rpm_top);
|
|
}
|
|
|
|
sub _build_deb {
|
|
my ($work, $staging, $source_root, $package_name) = @_;
|
|
my $root = "$work/deb-root";
|
|
my $image = "$root/opt/xcat/share/xcat/netboot/genesis-openembedded/$architecture";
|
|
my $doc = "$root/usr/share/doc/$package_name";
|
|
make_path("$root/DEBIAN", $image, $doc, "$staging/deb");
|
|
|
|
for my $entry (_flat_files("$source_root/image")) {
|
|
copy("$source_root/image/$entry", "$image/$entry")
|
|
or die "Cannot stage DEB image file $entry: $!\n";
|
|
chmod(0644, "$image/$entry") or die "Cannot set mode on $entry: $!\n";
|
|
}
|
|
copy("$source_root/xcat-core-revision", "$doc/xcat-core-revision")
|
|
or die "Cannot stage DEB revision: $!\n";
|
|
chmod(0644, "$doc/xcat-core-revision") or die "Cannot set revision mode: $!\n";
|
|
|
|
my $installed_kib = _tree_bytes($root);
|
|
$installed_kib = int(($installed_kib + 1023) / 1024);
|
|
write_binary(
|
|
"$root/DEBIAN/control",
|
|
"Package: $package_name\n"
|
|
. "Version: $version-$release\n"
|
|
. "Section: admin\nPriority: optional\nArchitecture: all\n"
|
|
. "Installed-Size: $installed_kib\n"
|
|
. "Maintainer: xCAT <xcat-user\@lists.sourceforge.net>\n"
|
|
. "Description: xCAT Genesis netboot image\n"
|
|
. " The OpenEmbedded Genesis image used for node discovery, inventory,\n"
|
|
. " and service actions.\n",
|
|
);
|
|
_write_deb_md5sums($root);
|
|
_normalize_mtime($root, $source_date_epoch);
|
|
|
|
my $deb = "$staging/deb/${package_name}_${version}-${release}_all.deb";
|
|
run_command('dpkg-deb', '--root-owner-group', '-Zgzip', '-z9', '--build', $root, $deb);
|
|
die "DEB build did not produce $deb\n" unless -f $deb;
|
|
}
|
|
|
|
sub _write_deb_md5sums {
|
|
my ($root) = @_;
|
|
my @files = grep { $_ !~ m{\ADEBIAN/} } relative_files($root);
|
|
write_binary(
|
|
"$root/DEBIAN/md5sums",
|
|
digest_manifest($root, 'md5', @files),
|
|
);
|
|
}
|
|
|
|
sub _normalize_mtime {
|
|
my ($root, $epoch) = @_;
|
|
require File::Find;
|
|
File::Find::find(
|
|
{
|
|
no_chdir => 1,
|
|
wanted => sub {
|
|
utime($epoch, $epoch, $File::Find::name)
|
|
or die "Cannot set timestamp on $File::Find::name: $!\n";
|
|
},
|
|
},
|
|
$root,
|
|
);
|
|
}
|
|
|
|
sub _flat_files {
|
|
my ($directory) = @_;
|
|
my @entries = relative_files($directory);
|
|
for my $entry (@entries) {
|
|
die "Expected a flat directory: $directory\n" if $entry =~ m{/};
|
|
}
|
|
return @entries;
|
|
}
|
|
|
|
sub _tree_bytes {
|
|
my ($directory) = @_;
|
|
my $bytes = 0;
|
|
$bytes += -s "$directory/$_" for relative_files($directory);
|
|
return $bytes;
|
|
}
|
|
|
|
sub _require_gnu_tar {
|
|
require_command('tar');
|
|
my $version_line = capture_command('tar', '--version');
|
|
die "GNU tar is required to build Genesis source packages\n"
|
|
unless $version_line =~ /^tar \(GNU tar\)/;
|
|
}
|
|
|
|
sub usage {
|
|
return <<'USAGE';
|
|
Usage: package --architecture ARCH --export-dir DIR --output-dir DIR
|
|
--version VERSION --release RELEASE --revision COMMIT
|
|
--source-date-epoch EPOCH [--format all|rpm|deb]
|
|
USAGE
|
|
}
|