From 77c11bace94ac31d208a7d53b837248c196e941b Mon Sep 17 00:00:00 2001 From: Lars Erik Wik Date: Mon, 1 Jun 2026 15:28:33 +0200 Subject: [PATCH] Make the source and package tarballs reproducible Two builds of the same source tree now produce byte-identical tarballs, following GNU tar's reproducibility guidance: * Select tar-pax in configure.ac so $(am__tar) emits --format=posix, giving stable, version-independent header encoding for both "make dist" and "make tar-package". * Expand the exported TAR_OPTIONS: --sort=name for stable member order, --numeric-owner / --owner=0 / --group=0 to drop buildslave identity, --mode=go+u,go-w for deterministic permissions, and the --pax-option flags to keep tar's PID out of header names and omit atime/ctime (leaving the archive in the ustar subset). * In dist-hook, normalize directory permissions to 755 and, when SOURCE_DATE_EPOCH is set, clamp every mtime to it. * In tar-package, clamp staged file mtimes to SOURCE_DATE_EPOCH and pass -n to gzip so the gzip header carries no timestamp. Ticket: ENT-14061 Signed-off-by: Lars Erik Wik --- Makefile.am | 36 ++++++++++++++++++++++++++++++++---- configure.ac | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index ab9e6c0c24..f3ee26b4cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,13 +9,38 @@ masterfilesdir=$(prefix)/masterfiles EXTRA_DIST = README.md inventory/README.md lib/README.md CONTRIBUTING.md LICENSE CFVERSION modules/promises -# Do not reveal usernames of the buildslave -TAR_OPTIONS = --owner=0 --group=0 +# Normalize tar header fields so two builds of the same source tree produce a +# byte-identical tarball, following the GNU tar reproducibility guidance: +# https://www.gnu.org/software/tar/manual/html_section/Reproducibility.html +# --format=posix stable, version-independent header encoding (configure.ac +# selects tar-pax so $(am__tar) emits posix) +# --pax-option=... keep tar's PID out of extended-header names and omit +# atime/ctime, leaving the archive in the ustar subset +# --sort=name stable member order +# --numeric-owner do not record buildslave user/group names +# --owner=0 --group=0 deterministic ownership +# --mode=go+u,go-w deterministic permissions +# mtime clamping (the manual's --clamp-mtime --mtime) is handled by the +# touch -d @$$SOURCE_DATE_EPOCH calls in dist-hook and tar-package below. +TAR_OPTIONS = \ + --format=posix \ + --pax-option=exthdr.name=%d/PaxHeaders/%f \ + --pax-option=delete=atime,delete=ctime \ + --sort=name \ + --numeric-owner --owner=0 --group=0 \ + --mode=go+u,go-w export TAR_OPTIONS -# Store the permissions properly in the tarball for acceptance tests to succeed +# Store the permissions properly in the tarball for acceptance tests to succeed. +# Also normalize directory permissions (which would otherwise be affected by the +# builder's umask). When SOURCE_DATE_EPOCH is set, clamp every mtime to it so +# the "make dist" source tarball is reproducible. dist-hook: find $(distdir) -name '*.cf*' | xargs chmod go-w + find $(distdir) -type d -exec chmod 755 {} + + if [ -n "$$SOURCE_DATE_EPOCH" ]; then \ + find $(distdir) -exec touch -d @$$SOURCE_DATE_EPOCH {} + ; \ + fi tar-package: pkgdir=`mktemp -d` && export pkgdir && \ @@ -24,8 +49,11 @@ tar-package: $(MAKE) prefix=$$pkgdir install && \ ( cd $$pkgdir && \ find . -name '*.cf*' | xargs -n1 chmod go-w && \ + if [ -n "$$SOURCE_DATE_EPOCH" ]; then \ + find . -exec touch -d @$$SOURCE_DATE_EPOCH {} + ; \ + fi && \ tardir=. && $(am__tar) | \ - GZIP=$(GZIP_ENV) gzip -c \ + GZIP=$(GZIP_ENV) gzip -nc \ > "$$origdir"/$(PACKAGE)-$(VERSION)-$(RELEASE).pkg.tar.gz \ ) ; \ [ x$$pkgdir != x ] && rm -rf $$pkgdir diff --git a/configure.ac b/configure.ac index 47965d313b..07077a5df7 100644 --- a/configure.ac +++ b/configure.ac @@ -39,7 +39,7 @@ m4_undefine([cfrelease]) AC_CANONICAL_TARGET -_AM_SET_OPTION([tar-ustar]) +_AM_SET_OPTION([tar-pax]) AM_INIT_AUTOMAKE([foreign]) AM_MAINTAINER_MODE([enable])