2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-12 12:36:23 +00:00
Files
xcat-dep/genesis-openembedded/package
T
2026-08-24 23:25:15 -03:00

280 lines
10 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";
}
copy("$FindBin::Bin/activate", "$source_root/activate")
or die "Cannot copy the Genesis activation helper: $!\n";
chmod(0755, "$source_root/activate")
or die "Cannot set the Genesis activation helper mode: $!\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";
my $helper = "$root/usr/libexec/xcat/genesis-openembedded-activate-$architecture";
make_path("$root/DEBIAN", $image, $doc, dirname($helper), "$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";
copy("$source_root/activate", $helper)
or die "Cannot stage DEB activation helper: $!\n";
chmod(0755, $helper) or die "Cannot set activation helper 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_binary(
"$root/DEBIAN/postinst",
"#!/bin/sh\n"
. "/usr/libexec/xcat/genesis-openembedded-activate-$architecture $architecture\n"
. "exit 0\n",
);
chmod(0755, "$root/DEBIAN/postinst")
or die "Cannot set post-installation script mode: $!\n";
write_binary(
"$root/DEBIAN/postrm",
"#!/bin/sh\n"
. "case \"\$1\" in\n"
. " remove|purge)\n"
. " host_root=\$(stat -c '%i %d' / 2>/dev/null) || host_root=\n"
. " init_root=\$(stat -c '%i %d' /proc/1/root/. 2>/dev/null) || init_root=\n"
. " if [ -f /proc/cmdline ] && [ -n \"\$host_root\" ] && [ \"\$host_root\" = \"\$init_root\" ] && [ -x /opt/xcat/sbin/mknb ]; then\n"
. " /opt/xcat/sbin/mknb $architecture --remove-openembedded || :\n"
. " /opt/xcat/sbin/mknb $architecture || :\n"
. " fi\n"
. " ;;\n"
. "esac\n"
. "exit 0\n",
);
chmod(0755, "$root/DEBIAN/postrm")
or die "Cannot set post-removal script mode: $!\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
}