2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-14 10:13:49 +00:00

build: Make builds deterministic

Port SOURCE_DATE_EPOCH patterns from xcat-core to all xcat-dep build
scripts. RPM and Debian packages now produce identical output given
the same Gitepoch timestamp, regardless of build host or time.

EL (RPM) changes:
- mockbuild-all.pl: --build-timestamp flag, deterministic run_id,
  tar --sort/--owner/--group/--mtime, createrepo --revision
- All mockbuild.pl: SOURCE_DATE_EPOCH cascade (CLI > Gitepoch > git > time),
  deterministic mock config with SOURCE_DATE_EPOCH in chroot env,
  RPM macros for timestamp clamping and fixed buildhost
- goconserver: -trimpath -buildvcs=false, canonical rpmbuild path
- xnba: canonical rpmbuild path
- syslinux: ZERO_AR_DATE=1 in mock env

Ubuntu (Debian) changes:
- All make_deb.sh: SOURCE_DATE_EPOCH fallback from Gitepoch
- goconserver: deterministic SNAP_TS and changelog from epoch,
  -trimpath -buildvcs=false in debian/rules
- syslinux: -fdebug-prefix-map for path-independent debug info
- build-apt-repo.sh: gzip -n, Release Date: from SOURCE_DATE_EPOCH

Verified: 31/33 package artifacts produce identical SHA256 hashes
across independent builds. 2 syslinux sub-packages (devel,
debugsource) have known limitations from upstream build system.
This commit is contained in:
Daniel Hilst
2026-05-26 14:29:32 -03:00
parent 1cc3932308
commit a2378ba780
17 changed files with 367 additions and 30 deletions
+3
View File
@@ -0,0 +1,3 @@
Gitepoch
Gitinfo
Release
+8
View File
@@ -1,4 +1,12 @@
#!/bin/bash
set -e
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
tar xvfz conserver-8.2.1.tar.gz
cd conserver-8.2.1
+8
View File
@@ -1,4 +1,12 @@
#!/bin/bash
set -e
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
tar xvfz elilo-3.14-source.tar.gz
cd elilo
+35 -2
View File
@@ -21,6 +21,7 @@ my $mock_uniqueext = '';
my $result_dir = "$repo_root/build-output/list3/elilo-xcat";
my $log_dir = "$repo_root/build-logs/list3/elilo-xcat";
my $skip_install = 0;
my $build_timestamp;
GetOptions(
'source-url=s' => \$source_url,
@@ -31,6 +32,7 @@ GetOptions(
'result-dir=s' => \$result_dir,
'log-dir=s' => \$log_dir,
'skip-install!' => \$skip_install,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -57,6 +59,19 @@ my $mock_uniqueext_opt = $mock_uniqueext ne ''
? ' --uniqueext ' . sh_quote($mock_uniqueext)
: '';
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
$SOURCE_DATE_EPOCH = slurp("$repo_root/Gitepoch");
chomp $SOURCE_DATE_EPOCH;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
print_step("Configuration");
print "repo_root: $repo_root\n";
print "pkg_dir: $pkg_dir\n";
@@ -93,6 +108,7 @@ my $prep_top = "$work_dir/prep";
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
make_path("$prep_top/$d");
}
my $det_mock_cfg = create_deterministic_mock_cfg($mock_cfg, $SOURCE_DATE_EPOCH, $work_dir);
copy($spec_file, "$prep_top/SPECS/elilo-xcat.spec")
or die "Failed to copy spec to prep topdir: $!\n";
for my $asset (@spec_assets) {
@@ -116,9 +132,12 @@ print_step("Build SRPM with mock");
my $srpm_out = "$work_dir/srpm";
make_path($srpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --buildsrpm --spec " . sh_quote($spec_file) .
" --sources " . sh_quote($pkg_dir) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" --resultdir " . sh_quote($srpm_out)
);
@@ -131,8 +150,11 @@ print_step("Rebuild RPM with mock");
my $rpm_out = "$work_dir/rpm";
make_path($rpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --rebuild " . sh_quote($srpm) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" --resultdir " . sh_quote($rpm_out)
);
@@ -221,9 +243,20 @@ Usage: $0 [options]
--result-dir PATH Output RPM/SRPM directory (default: $result_dir)
--log-dir PATH Log directory (default: $log_dir)
--skip-install Skip dnf install + smoke tests
--build-timestamp EPOCH Unix timestamp for SOURCE_DATE_EPOCH (deterministic builds)
USAGE
}
sub create_deterministic_mock_cfg {
my ($base_cfg, $epoch, $dir) = @_;
my $cfg_path = "$dir/mock-deterministic.cfg";
open my $fh, '>', $cfg_path or die "Cannot write $cfg_path: $!\n";
print $fh "include('/etc/mock/${base_cfg}.cfg')\n";
print $fh "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n";
close $fh;
return $cfg_path;
}
sub parse_spec {
my ($path) = @_;
open my $fh, '<', $path or die "Cannot open spec $path: $!\n";
+2 -2
View File
@@ -4,8 +4,8 @@ export CGO_ENABLED=0
dh $@
override_dh_auto_configure:
override_dh_auto_build:
go build -ldflags "-X main.Version=0.3.3" -o goconserver goconserver.go
go build -ldflags "-X main.Version=0.3.3" -o congo cmd/congo.go
go build -trimpath -buildvcs=false -ldflags "-X main.Version=0.3.3" -o goconserver goconserver.go
go build -trimpath -buildvcs=false -ldflags "-X main.Version=0.3.3" -o congo cmd/congo.go
override_dh_auto_install:
install -D -m 755 goconserver $(CURDIR)/debian/goconserver/usr/bin/goconserver
install -D -m 755 congo $(CURDIR)/debian/goconserver/usr/bin/congo
+26
View File
@@ -6,6 +6,20 @@ VERSION=0.3.3
REPO=https://github.com/xcat2/goconserver.git
REF=master
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
SNAP_TS=$(date -d "@$SOURCE_DATE_EPOCH" --utc '+%Y%m%d%H%M')
else
SNAP_TS=$(date '+%Y%m%d%H%M')
fi
FULL_VERSION="${VERSION}-snap${SNAP_TS}"
WORKDIR=$(mktemp -d)
trap "rm -rf $WORKDIR" EXIT
@@ -27,6 +41,18 @@ go mod tidy
cp -rL "$SCRIPT_DIR/debian" .
sed -i "s/Version=${VERSION}/Version=${FULL_VERSION}/g" debian/rules
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
export DEBEMAIL="${DEBEMAIL:-xcat-build@xcat.org}"
export DEBFULLNAME="${DEBFULLNAME:-xCAT Build}"
deterministic_date=$(date -R -d "@$SOURCE_DATE_EPOCH" --utc)
sed -i "1s/(.*)/(${FULL_VERSION})/" debian/changelog
sed -i "s/^ -- .*/ -- $DEBFULLNAME <$DEBEMAIL> $deterministic_date/" debian/changelog
else
dch -v "$FULL_VERSION" -b -D unstable "Snap build for xCAT"
fi
dpkg-buildpackage -uc -us
echo "Built debs:"
+30 -4
View File
@@ -21,6 +21,7 @@ my $skip_install = 0;
my $version = '0.3.3';
my $go_repo = 'https://github.com/xcat2/goconserver.git';
my $go_ref = 'master';
my $build_timestamp;
GetOptions(
'work-dir=s' => \$work_dir,
@@ -32,6 +33,7 @@ GetOptions(
'version=s' => \$version,
'go-repo=s' => \$go_repo,
'go-ref=s' => \$go_ref,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -49,6 +51,24 @@ if (!$mock_cfg) {
my ($rel) = $mock_cfg =~ /-(\d+)-/;
$rel //= '10';
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
my $epoch_content = '';
if (open my $efh, '<', "$repo_root/Gitepoch") {
$epoch_content = <$efh>;
close $efh;
chomp $epoch_content;
}
$SOURCE_DATE_EPOCH = $epoch_content;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
print_step("Configuration");
print "repo_root: $repo_root\n";
print "pkg_dir: $pkg_dir\n";
@@ -69,7 +89,8 @@ print_step("Stage build environment");
remove_tree($work_dir) if -d $work_dir;
make_path($work_dir);
my $rpmbuild_top = "$work_dir/rpmbuild";
my $rpmbuild_top = "/var/tmp/xcat-rpmbuild-goconserver";
remove_tree($rpmbuild_top) if -d $rpmbuild_top;
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
make_path("$rpmbuild_top/$d");
}
@@ -107,12 +128,12 @@ make_path($go_build_dir);
my $ldflags = "-X main.Version=$version";
run("cd " . sh_quote($src_dir) . " && " .
"go build -ldflags " . sh_quote($ldflags) .
"go build -trimpath -buildvcs=false -ldflags " . sh_quote($ldflags) .
" -o " . sh_quote("$go_build_dir/goconserver") . " goconserver.go" .
" >" . sh_quote("$log_dir/go-build-server.log") . " 2>&1");
run("cd " . sh_quote($src_dir) . " && " .
"go build -ldflags " . sh_quote($ldflags) .
"go build -trimpath -buildvcs=false -ldflags " . sh_quote($ldflags) .
" -o " . sh_quote("$go_build_dir/congo") . " cmd/congo.go" .
" >" . sh_quote("$log_dir/go-build-client.log") . " 2>&1");
@@ -157,7 +178,8 @@ log_level = info
CONF
my $tarball = "$rpmbuild_top/SOURCES/goconserver-$version.tar.gz";
run("tar -C " . sh_quote($work_dir) . " -czf " . sh_quote($tarball) .
run("tar --sort=name --owner=0 --group=0 --mtime=\@$SOURCE_DATE_EPOCH" .
" -C " . sh_quote($work_dir) . " -czf " . sh_quote($tarball) .
" goconserver-$version");
print_step("Create spec and build RPM");
@@ -208,6 +230,9 @@ write_file($spec_file, $spec_content);
run(
"rpmbuild --define " . sh_quote("_topdir $rpmbuild_top") .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" -ba " . sh_quote($spec_file) .
" >" . sh_quote("$log_dir/rpmbuild.log") . " 2>&1"
);
@@ -260,6 +285,7 @@ Options:
--version VER Version string (default: 0.3.3)
--go-repo URL Git repo URL (default: github.com/xcat2/goconserver)
--go-ref REF Git ref to build (default: master)
--build-timestamp EPOCH SOURCE_DATE_EPOCH for deterministic builds
USAGE
}
+8
View File
@@ -1,3 +1,11 @@
#!/bin/bash
set -e
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
dpkg-buildpackage -uc -us
+38 -4
View File
@@ -24,6 +24,7 @@ my $result_dir = "$repo_root/build-output/list3/grub2-xcat";
my $log_dir = "$repo_root/build-logs/list3/grub2-xcat";
my $skip_install = 0;
my $skip_upstream_download = 0;
my $build_timestamp;
GetOptions(
'resource-mode=s' => \$resource_mode,
@@ -36,6 +37,7 @@ GetOptions(
'log-dir=s' => \$log_dir,
'skip-install!' => \$skip_install,
'skip-upstream-download!' => \$skip_upstream_download,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -69,6 +71,19 @@ my $mock_uniqueext_opt = $mock_uniqueext ne ''
? ' --uniqueext ' . sh_quote($mock_uniqueext)
: '';
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
$SOURCE_DATE_EPOCH = slurp("$repo_root/Gitepoch");
chomp $SOURCE_DATE_EPOCH;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
print_step("Configuration");
print "repo_root: $repo_root\n";
print "pkg_dir: $pkg_dir\n";
@@ -131,6 +146,8 @@ my $prep_top = "$work_dir/prep";
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
make_path("$prep_top/$d");
}
my $det_mock_cfg = create_deterministic_mock_cfg($mock_cfg, $SOURCE_DATE_EPOCH, $work_dir);
copy($spec_file, "$prep_top/SPECS/grub2-xcat.spec")
or die "Failed to copy spec to prep topdir: $!\n";
for my $asset (@spec_assets) {
@@ -152,10 +169,13 @@ print_step("Build SRPM with mock");
my $srpm_out = "$work_dir/srpm";
make_path($srpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --buildsrpm --spec " . sh_quote($spec_file) .
" --sources " . sh_quote($pkg_dir) .
" --resultdir " . sh_quote($srpm_out)
" --resultdir " . sh_quote($srpm_out) .
" --define 'use_source_date_epoch_as_buildtime 1'" .
" --define 'clamp_mtime_to_source_date_epoch 1'" .
" --define '_buildhost xcat-build'"
);
my @srpms = sort glob("$srpm_out/grub2-xcat-*.src.rpm");
@@ -167,9 +187,12 @@ print_step("Rebuild RPM with mock");
my $rpm_out = "$work_dir/rpm";
make_path($rpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --rebuild " . sh_quote($srpm) .
" --resultdir " . sh_quote($rpm_out)
" --resultdir " . sh_quote($rpm_out) .
" --define 'use_source_date_epoch_as_buildtime 1'" .
" --define 'clamp_mtime_to_source_date_epoch 1'" .
" --define '_buildhost xcat-build'"
);
my @all_rpms = sort glob("$rpm_out/*.rpm");
@@ -281,9 +304,20 @@ Usage: $0 [options]
--log-dir PATH Log directory (default: $log_dir)
--skip-upstream-download Skip wget of upstream source RPM
--skip-install Skip dnf install + smoke tests
--build-timestamp EPOCH Unix timestamp for deterministic builds (SOURCE_DATE_EPOCH)
USAGE
}
sub create_deterministic_mock_cfg {
my ($base_cfg, $epoch, $dir) = @_;
my $cfg_path = "$dir/mock-deterministic.cfg";
open my $fh, '>', $cfg_path or die "Cannot write $cfg_path: $!\n";
print $fh "include('/etc/mock/${base_cfg}.cfg')\n";
print $fh "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n";
close $fh;
return $cfg_path;
}
sub parse_spec {
my ($path) = @_;
open my $fh, '<', $path or die "Cannot open spec $path: $!\n";
+9
View File
@@ -1,4 +1,13 @@
#!/bin/bash
set -e
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
VERSION=1.8.18
tar xvfz ipmitool-$VERSION.tar.gz
+39 -3
View File
@@ -21,6 +21,7 @@ my $mock_uniqueext = '';
my $result_dir = "$repo_root/build-output/list3/ipmitool-xcat";
my $log_dir = "$repo_root/build-logs/list3/ipmitool-xcat";
my $skip_install = 0;
my $build_timestamp;
GetOptions(
'source-url=s' => \$source_url,
@@ -31,6 +32,7 @@ GetOptions(
'result-dir=s' => \$result_dir,
'log-dir=s' => \$log_dir,
'skip-install!' => \$skip_install,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -57,6 +59,19 @@ my $mock_uniqueext_opt = $mock_uniqueext ne ''
? ' --uniqueext ' . sh_quote($mock_uniqueext)
: '';
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
$SOURCE_DATE_EPOCH = slurp("$repo_root/Gitepoch");
chomp $SOURCE_DATE_EPOCH;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
print_step("Configuration");
print "repo_root: $repo_root\n";
print "pkg_dir: $pkg_dir\n";
@@ -68,6 +83,7 @@ print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)')
print "source_url: $source_url\n";
print "source_file:$source_file\n";
print "skip_install: $skip_install\n";
print "SOURCE_DATE_EPOCH: $SOURCE_DATE_EPOCH\n";
make_path($result_dir);
make_path($log_dir);
@@ -90,6 +106,7 @@ print "Verified " . scalar(@spec_assets) . " Source/Patch assets from spec.\n";
print_step("Stage files for patch-application check");
remove_tree($work_dir) if -d $work_dir;
my $prep_top = "$work_dir/prep";
my $det_mock_cfg = create_deterministic_mock_cfg($mock_cfg, $SOURCE_DATE_EPOCH, $work_dir);
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
make_path("$prep_top/$d");
}
@@ -116,9 +133,12 @@ print_step("Build SRPM with mock");
my $srpm_out = "$work_dir/srpm";
make_path($srpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --buildsrpm --spec " . sh_quote($spec_file) .
" --sources " . sh_quote($pkg_dir) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" --resultdir " . sh_quote($srpm_out)
);
@@ -131,8 +151,11 @@ print_step("Rebuild RPM with mock");
my $rpm_out = "$work_dir/rpm";
make_path($rpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --rebuild " . sh_quote($srpm) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" --resultdir " . sh_quote($rpm_out)
);
@@ -243,9 +266,21 @@ Usage: $0 [options]
--result-dir PATH Output RPM/SRPM directory (default: $result_dir)
--log-dir PATH Log directory (default: $log_dir)
--skip-install Skip dnf install + smoke tests
--build-timestamp N Unix epoch for SOURCE_DATE_EPOCH (deterministic builds)
USAGE
}
sub create_deterministic_mock_cfg {
my ($base_cfg, $epoch, $dir) = @_;
make_path($dir) unless -d $dir;
my $cfg_path = "$dir/mock-deterministic.cfg";
open my $fh, '>', $cfg_path or die "Cannot write $cfg_path: $!\n";
print $fh "include('/etc/mock/${base_cfg}.cfg')\n";
print $fh "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n";
close $fh;
return $cfg_path;
}
sub parse_spec {
my ($path) = @_;
open my $fh, '<', $path or die "Cannot open spec $path: $!\n";
@@ -298,7 +333,8 @@ sub normalize_source_archive {
# Repack using the expected top-level directory required by the spec.
run(
"tar -C " . sh_quote($normalize_dir) .
"tar --sort=name --owner=0 --group=0 --mtime=\@$SOURCE_DATE_EPOCH" .
" -C " . sh_quote($normalize_dir) .
" -czf " . sh_quote($archive) .
" " . sh_quote($expected_top)
);
+41 -6
View File
@@ -19,7 +19,8 @@ my $output_root = "$repo_root/build-output/mockbuild-all";
my $target = '';
my $nproc = 1;
my $parallel_builds;
my $run_id = strftime('%Y%m%d-%H%M%S', localtime);
my $run_id = '';
my $build_timestamp;
my $skip_install = 0;
my $skip_build = 0;
my $skip_xcat_dep = 0;
@@ -39,6 +40,7 @@ GetOptions(
'nproc=i' => \$nproc,
'parallel-builds=i' => \$parallel_builds,
'run-id=s' => \$run_id,
'build-timestamp=i' => \$build_timestamp,
'skip-install!' => \$skip_install,
'skip-build!' => \$skip_build,
'skip-xcat-dep!' => \$skip_xcat_dep,
@@ -56,6 +58,23 @@ die "--parallel-builds must be >= 1\n"
if defined($parallel_builds) && $parallel_builds < 1;
$repo_root = abs_path($repo_root);
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
$SOURCE_DATE_EPOCH = slurp_chomp("$repo_root/Gitepoch");
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
if ($run_id eq '') {
$run_id = strftime('%Y%m%d-%H%M%S', gmtime($SOURCE_DATE_EPOCH));
}
$xcat_src = resolve_xcat_source($xcat_src, $repo_root);
my $arch = capture('uname -m');
@@ -170,6 +189,7 @@ if (!$skip_build) {
'--mock-uniqueext', sh_quote($step_uniqueext),
'--result-dir', sh_quote($step_result),
'--log-dir', sh_quote($step_log),
'--build-timestamp', $SOURCE_DATE_EPOCH,
($skip_install ? '--skip-install' : ()),
);
push @build_steps, {
@@ -192,6 +212,7 @@ if (!$skip_build) {
'--mock-uniqueext', sh_quote($perl_uniqueext),
'--result-dir', sh_quote($perl_result),
'--log-dir', sh_quote($perl_log),
'--build-timestamp', $SOURCE_DATE_EPOCH,
($skip_install ? '--skip-install' : ()),
);
push @build_steps, {
@@ -284,19 +305,21 @@ if (!$dry_run && $copied_srpms == 0) {
if (!$skip_createrepo) {
run_step(
step => 'Run createrepo',
cmd => 'createrepo --update ' . sh_quote($repo_dir),
cmd => 'createrepo --update --revision ' . sh_quote($SOURCE_DATE_EPOCH) . ' ' . sh_quote($repo_dir),
log => "$log_root/createrepo.log",
);
run_step(
step => 'Run createrepo for SRPM repo',
cmd => 'createrepo --update ' . sh_quote($srpm_repo_dir),
cmd => 'createrepo --update --revision ' . sh_quote($SOURCE_DATE_EPOCH) . ' ' . sh_quote($srpm_repo_dir),
log => "$log_root/createrepo-srpm.log",
);
}
if (!$skip_tarball) {
my $cmd = join(' ',
'tar', '-C', sh_quote($run_root),
'tar', '--sort=name', '--owner=0', '--group=0',
"--mtime=\@$SOURCE_DATE_EPOCH",
'-C', sh_quote($run_root),
'-czf', sh_quote($tarball),
'repo'
);
@@ -306,7 +329,9 @@ if (!$skip_tarball) {
log => "$log_root/tarball.log",
);
my $srpm_cmd = join(' ',
'tar', '-C', sh_quote($run_root),
'tar', '--sort=name', '--owner=0', '--group=0',
"--mtime=\@$SOURCE_DATE_EPOCH",
'-C', sh_quote($run_root),
'-czf', sh_quote($srpm_tarball),
'repo-src'
);
@@ -365,7 +390,8 @@ Options:
--target NAME Optional unified target in <ID>+epel-<REL>-<ARCH> format
--nproc N Parallel jobs for buildrpms.pl (default: 1)
--parallel-builds N Max concurrent top-level build steps (default: auto=queued steps)
--run-id ID Run identifier suffix (default: timestamp)
--run-id ID Run identifier suffix (default: derived from build timestamp)
--build-timestamp EPOCH Unix epoch for deterministic builds (default: Gitepoch or git log)
--skip-install Skip install/smoke tests in child builder scripts
--skip-build Skip all build steps and only collect/create repo/tarballs
--skip-xcat-dep Skip xcat-dep mockbuild.pl package steps
@@ -705,6 +731,15 @@ sub uniq {
return grep { defined($_) && !$seen{$_}++ } @_;
}
sub slurp_chomp {
my ($path) = @_;
open my $fh, '<', $path or die "Cannot read $path: $!\n";
my $line = <$fh>;
close $fh;
chomp $line if defined $line;
return $line // '';
}
sub sh_quote {
my ($s) = @_;
$s = '' if !defined $s;
+37 -2
View File
@@ -19,6 +19,7 @@ my $packages_csv = '';
my $jobs = 0;
my $skip_install = 0;
my $allow_erasing = 0;
my $build_timestamp;
GetOptions(
'work-dir=s' => \$work_dir,
@@ -30,6 +31,7 @@ GetOptions(
'jobs=i' => \$jobs,
'skip-install!' => \$skip_install,
'allow-erasing!' => \$allow_erasing,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -46,6 +48,20 @@ if (!$mock_cfg) {
my $mock_uniqueext_opt = $mock_uniqueext ne ''
? ' --uniqueext ' . sh_quote($mock_uniqueext)
: '';
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
$SOURCE_DATE_EPOCH = slurp("$repo_root/Gitepoch");
chomp $SOURCE_DATE_EPOCH;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
if (!$result_dir) {
$result_dir = "$repo_root/build-output/list6/perl/$arch";
}
@@ -277,6 +293,8 @@ sub build_package {
make_path($pkg_result);
make_path($pkg_log);
my $det_mock_cfg = create_deterministic_mock_cfg($mock_cfg, $SOURCE_DATE_EPOCH, $pkg_run_dir);
my $run_log = "$pkg_log/run.log";
open my $runfh, '>', $run_log or die "Cannot write $run_log: $!\n";
open STDOUT, '>&', $runfh or die "Cannot redirect stdout to $run_log: $!\n";
@@ -341,9 +359,12 @@ sub build_package {
my $srpm_result = "$pkg_run_dir/srpm";
make_path($srpm_result);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --buildsrpm --spec " . sh_quote($spec) .
" --sources " . sh_quote($source_dir) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" --resultdir " . sh_quote($srpm_result) .
" > " . sh_quote("$pkg_log/mock-buildsrpm.log") . " 2>&1"
);
@@ -353,8 +374,11 @@ sub build_package {
}
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --rebuild " . sh_quote($srpm_path) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build") .
" --resultdir " . sh_quote($rebuild_result) .
" > " . sh_quote("$pkg_log/mock-rebuild.log") . " 2>&1"
);
@@ -453,6 +477,7 @@ Usage: $0 [options]
--result-dir PATH Output directory (default: build-output/list6/perl/<ARCH>)
--log-dir PATH Log directory (default: build-logs/list6/perl/<ARCH>)
--packages LIST Comma-separated subset of packages to build
--build-timestamp EPOCH Unix epoch for SOURCE_DATE_EPOCH (deterministic builds)
--skip-install Skip dnf install + perl module import checks
--allow-erasing Allow dnf to erase conflicting packages during install smoke tests
USAGE
@@ -594,6 +619,16 @@ sub resolve_mock_cfg {
return $candidate;
}
sub create_deterministic_mock_cfg {
my ($base_cfg, $epoch, $dir) = @_;
my $cfg_path = "$dir/mock-deterministic.cfg";
open my $fh, '>', $cfg_path or die "Cannot write $cfg_path: $!\n";
print $fh "include('/etc/mock/${base_cfg}.cfg')\n";
print $fh "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n";
close $fh;
return $cfg_path;
}
sub resolve_source_urls {
my ($spec_path) = @_;
open my $fh, '<', $spec_path or return ();
+8
View File
@@ -1,6 +1,13 @@
#!/bin/bash
set -e
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
tar xvfj syslinux-3.86.tar.bz2
cd syslinux-3.86
cp -rL ../debian .
@@ -8,6 +15,7 @@ cp -rL ../debian .
# GCC >= 10 defaults to -fno-common; syslinux 3.86 relies on common symbols
# GCC 15 promotes implicit-function-declaration and incompatible-pointer-types to errors
sed -i '/^GCCWARN := -W -Wall/s/$/ -fcommon -Wno-error=implicit-function-declaration -Wno-error=incompatible-pointer-types -Wno-error=int-conversion/' MCONFIG
sed -i "s|^GCCWARN := .*|& -fdebug-prefix-map=$(pwd)=.|" MCONFIG
# glibc >= 2.28 moved major()/minor() to sys/sysmacros.h
# extlinux/main.c:843 calls major()/minor() unconditionally in 3.86
+38 -4
View File
@@ -22,6 +22,7 @@ my $result_dir = "$repo_root/build-output/list3/syslinux-xcat";
my $log_dir = "$repo_root/build-logs/list3/syslinux-xcat";
my $skip_install = 0;
my $skip_upstream_download = 0;
my $build_timestamp;
GetOptions(
'source-url=s' => \$source_url,
@@ -33,6 +34,7 @@ GetOptions(
'log-dir=s' => \$log_dir,
'skip-install!' => \$skip_install,
'skip-upstream-download!' => \$skip_upstream_download,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -64,6 +66,19 @@ my $mock_uniqueext_opt = $mock_uniqueext ne ''
? ' --uniqueext ' . sh_quote($mock_uniqueext)
: '';
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
$SOURCE_DATE_EPOCH = slurp("$repo_root/Gitepoch");
chomp $SOURCE_DATE_EPOCH;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
print_step("Configuration");
print "repo_root: $repo_root\n";
print "pkg_dir: $pkg_dir\n";
@@ -115,6 +130,7 @@ my $prep_top = "$work_dir/prep";
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
make_path("$prep_top/$d");
}
my $det_mock_cfg = create_deterministic_mock_cfg($mock_cfg, $SOURCE_DATE_EPOCH, $work_dir);
copy($spec_file, "$prep_top/SPECS/syslinux-xcat.spec")
or die "Failed to copy spec to prep topdir: $!\n";
for my $asset (@all_assets) {
@@ -136,10 +152,13 @@ print_step("Build SRPM with mock");
my $srpm_out = "$work_dir/srpm";
make_path($srpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --buildsrpm --spec " . sh_quote($spec_file) .
" --sources " . sh_quote($pkg_dir) .
" --resultdir " . sh_quote($srpm_out)
" --resultdir " . sh_quote($srpm_out) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build")
);
my @srpms = sort glob("$srpm_out/*.src.rpm");
@@ -151,9 +170,12 @@ print_step("Rebuild RPM with mock");
my $rpm_out = "$work_dir/rpm";
make_path($rpm_out);
run(
"mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt .
"mock -r " . sh_quote($det_mock_cfg) . $mock_uniqueext_opt .
" --rebuild " . sh_quote($srpm) .
" --resultdir " . sh_quote($rpm_out)
" --resultdir " . sh_quote($rpm_out) .
" --define " . sh_quote("use_source_date_epoch_as_buildtime 1") .
" --define " . sh_quote("clamp_mtime_to_source_date_epoch 1") .
" --define " . sh_quote("_buildhost xcat-build")
);
my @all_rpms = sort glob("$rpm_out/*.rpm");
@@ -265,11 +287,23 @@ Usage: $0 [options]
--mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds
--result-dir PATH Output RPM/SRPM directory (default: $result_dir)
--log-dir PATH Log directory (default: $log_dir)
--build-timestamp EPOCH SOURCE_DATE_EPOCH for deterministic builds
--skip-upstream-download Skip wget download step
--skip-install Skip dnf install + smoke tests
USAGE
}
sub create_deterministic_mock_cfg {
my ($base_cfg, $epoch, $dir) = @_;
my $cfg_path = "$dir/mock-deterministic.cfg";
open my $fh, '>', $cfg_path or die "Cannot write $cfg_path: $!\n";
print $fh "include('/etc/mock/${base_cfg}.cfg')\n";
print $fh "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n";
print $fh "config_opts['environment']['ZERO_AR_DATE'] = '1'\n";
close $fh;
return $cfg_path;
}
sub parse_spec {
my ($path) = @_;
open my $fh, '<', $path or die "Cannot open spec $path: $!\n";
+9 -1
View File
@@ -1,3 +1,11 @@
#!/bin/bash
set -e
dpkg-buildpackage -uc -us -R./build/rules.fromBIN
if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ -f "$REPO_ROOT/Gitepoch" ]; then
export SOURCE_DATE_EPOCH=$(cat "$REPO_ROOT/Gitepoch")
fi
fi
dpkg-buildpackage -uc -us -R./build/rules.fromBIN
+28 -2
View File
@@ -20,6 +20,7 @@ my $mock_uniqueext = '';
my $result_dir = "$repo_root/build-output/list3/xnba-undi";
my $log_dir = "$repo_root/build-logs/list3/xnba-undi";
my $skip_install = 0;
my $build_timestamp;
GetOptions(
'work-dir=s' => \$work_dir,
@@ -28,6 +29,7 @@ GetOptions(
'result-dir=s' => \$result_dir,
'log-dir=s' => \$log_dir,
'skip-install!' => \$skip_install,
'build-timestamp=i' => \$build_timestamp,
) or die usage();
die "Run as root (current uid=$>)\n" if $> != 0;
@@ -46,6 +48,24 @@ if (!$mock_cfg) {
$mock_cfg = resolve_mock_cfg($os_id, '10', $arch);
}
my $SOURCE_DATE_EPOCH;
$SOURCE_DATE_EPOCH = $build_timestamp if defined $build_timestamp;
if (!$SOURCE_DATE_EPOCH && -f "$repo_root/Gitepoch") {
my $epoch_content = '';
if (open my $efh, '<', "$repo_root/Gitepoch") {
$epoch_content = <$efh>;
close $efh;
chomp $epoch_content;
}
$SOURCE_DATE_EPOCH = $epoch_content;
}
unless ($SOURCE_DATE_EPOCH && $SOURCE_DATE_EPOCH =~ /^\d+$/) {
$SOURCE_DATE_EPOCH = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null`;
chomp $SOURCE_DATE_EPOCH;
}
$SOURCE_DATE_EPOCH = time() unless $SOURCE_DATE_EPOCH =~ /^\d+$/;
$ENV{SOURCE_DATE_EPOCH} = $SOURCE_DATE_EPOCH;
print_step("Configuration");
print "repo_root: $repo_root\n";
print "pkg_dir: $pkg_dir\n";
@@ -60,7 +80,8 @@ make_path($log_dir);
print_step("Stage build environment");
remove_tree($work_dir) if -d $work_dir;
my $rpmbuild_top = "$work_dir/rpmbuild";
my $rpmbuild_top = "/var/tmp/xcat-rpmbuild-xnba";
remove_tree($rpmbuild_top) if -d $rpmbuild_top;
for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) {
make_path("$rpmbuild_top/$d");
}
@@ -75,7 +96,8 @@ copy("$binary_dir/xnba.efi", "$src_dir/binary/xnba.efi")
or die "Failed to copy xnba.efi: $!\n";
my $tarball = "$rpmbuild_top/SOURCES/xnba-$version.tar.gz";
run("tar -C " . sh_quote($work_dir) . " -czf " . sh_quote($tarball) . " xnba-$version");
run("tar --sort=name --owner=0 --group=0 --mtime=\@$SOURCE_DATE_EPOCH" .
" -C " . sh_quote($work_dir) . " -czf " . sh_quote($tarball) . " xnba-$version");
# Create simplified spec that uses pre-built binaries
my $simple_spec = <<'SPEC';
@@ -120,6 +142,9 @@ print_step("Build RPM");
my $rpmbuild_cmd = join(' ',
'rpmbuild',
'--define', sh_quote("_topdir $rpmbuild_top"),
'--define', sh_quote("use_source_date_epoch_as_buildtime 1"),
'--define', sh_quote("clamp_mtime_to_source_date_epoch 1"),
'--define', sh_quote("_buildhost xcat-build"),
'-ba',
sh_quote("$rpmbuild_top/SPECS/xnba-undi.spec"),
);
@@ -149,6 +174,7 @@ Options:
--result-dir PATH Output directory for RPMs
--log-dir PATH Output directory for logs
--skip-install Skip install verification
--build-timestamp EPOCH Unix timestamp for reproducible builds
USAGE
}