diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 20e75e7..bf0eb0b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,7 +61,7 @@ get_version: stage: rootfs parallel: matrix: - - GROUP: [base, base-devel, multilib-devel] + - GROUP: [repro, base, base-devel, multilib-devel] before_script: - pacman -Syu --noconfirm git make fakechroot fakeroot - pacman -Sdd --noconfirm devtools @@ -98,7 +98,7 @@ rootfs:secure: stage: image parallel: matrix: - - GROUP: [base, base-devel, multilib-devel] + - GROUP: [repro, base, base-devel, multilib-devel] tags: - vm id_tokens: @@ -192,6 +192,12 @@ image:publish:secure: - id -u http - locale | grep -q UTF-8 +test:repro: + extends: .test + image: $CI_REGISTRY_IMAGE:repro-$CI_COMMIT_REF_SLUG + script: + - *test-script + test:base: extends: .test image: $CI_REGISTRY_IMAGE:base-$CI_COMMIT_REF_SLUG @@ -234,7 +240,7 @@ pre-release: -d "{\"full_description\": $(cat README.md | jq -sR .)}" # Upload rootfs to the Generic Packages Repository - for group in base base-devel multilib-devel; do + for group in repro base base-devel multilib-devel; do rootfs_file="${group}-${BUILD_VERSION}.tar.zst" mv "output/${group}.tar.zst" "output/${rootfs_file}" mv "output/${group}.tar.zst.SHA256" "output/${rootfs_file}.SHA256" @@ -246,7 +252,7 @@ pre-release: done # Create the Dockerfiles, commit to the release branch - for group in base base-devel multilib-devel; do + for group in repro base base-devel multilib-devel; do rootfs_file="${group}-${BUILD_VERSION}.tar.zst" ./scripts/make-dockerfile.sh "${rootfs_file}" "${group}" "output" "curl -sOJL \"${PACKAGE_REGISTRY_URL}/${rootfs_file}\"" "${group}" sed -i "/^COPY ${rootfs_file} \/$/d" output/Dockerfile.${group} @@ -258,6 +264,9 @@ pre-release: --form "branch=releases" --form "commit_message=Release ${BUILD_VERSION}" --form "actions[][action]=update" + --form "actions[][file_path]=Dockerfile.repro" + --form "actions[][content]=> library/archlinux echo "GitCommit: ${BUILD_COMMIT}" >> library/archlinux diff --git a/Makefile b/Makefile index 86d9c72..7a6361d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ OCITOOL=podman # or docker BUILDDIR=$(shell pwd)/build OUTPUTDIR=$(shell pwd)/output +ARCHIVE_SNAPSHOT=$(shell date -d "$(awk -F. '{print $1"-"$2"-"$3}' <<< "$IMAGE_VERSION") -1 day" +"%Y/%m/%d") +SOURCE_DATE_EPOCH=$(shell date -u -d "$(echo "$ARCHIVE_SNAPSHOT")" +"%s") .PHONY: clean clean: @@ -8,7 +10,7 @@ clean: .PRECIOUS: $(OUTPUTDIR)/%.tar.zst $(OUTPUTDIR)/%.tar.zst: - scripts/make-rootfs.sh $(*) $(BUILDDIR) $(OUTPUTDIR) + scripts/make-rootfs.sh $(*) $(BUILDDIR) $(OUTPUTDIR) $(ARCHIVE_SNAPSHOT) $(SOURCE_DATE_EPOCH) .PRECIOUS: $(OUTPUTDIR)/Dockerfile.% $(OUTPUTDIR)/Dockerfile.%: $(OUTPUTDIR)/%.tar.zst @@ -16,6 +18,6 @@ $(OUTPUTDIR)/Dockerfile.%: $(OUTPUTDIR)/%.tar.zst # The following is for local builds only, it is not used by the CI/CD pipeline -all: image-base image-base-devel image-multilib-devel +all: image-repro image-base image-base-devel image-multilib-devel image-%: $(OUTPUTDIR)/Dockerfile.% ${OCITOOL} build -f $(OUTPUTDIR)/Dockerfile.$(*) -t archlinux/archlinux:$(*) $(OUTPUTDIR) diff --git a/scripts/make-rootfs.sh b/scripts/make-rootfs.sh index a55f718..fc9c996 100755 --- a/scripts/make-rootfs.sh +++ b/scripts/make-rootfs.sh @@ -7,6 +7,8 @@ declare -r WRAPPER="fakechroot -- fakeroot" declare -r GROUP="$1" declare -r BUILDDIR="$2" declare -r OUTPUTDIR="$3" +declare -r ARCHIVE_SNAPSHOT="$4" +declare -rx SOURCE_DATE_EPOCH="$5" mkdir -vp "$BUILDDIR/alpm-hooks/usr/share/libalpm/hooks" find /usr/share/libalpm/hooks -exec ln -sf /dev/null "$BUILDDIR/alpm-hooks"{} \; @@ -33,18 +35,33 @@ fi cp --recursive --preserve=timestamps rootfs/* "$BUILDDIR/" ln -fs /usr/lib/os-release "$BUILDDIR/etc/os-release" +# Use archived repo snapshot from archive.archlinux.org for reproducible builds +sed -i "1iServer = https://archive.archlinux.org/repos/$ARCHIVE_SNAPSHOT/\\\$repo/os/\\\$arch" "$BUILDDIR/etc/pacman.d/mirrorlist" + $WRAPPER -- \ pacman -Sy -r "$BUILDDIR" \ --disable-sandbox-filesystem \ --noconfirm --dbpath "$BUILDDIR/var/lib/pacman" \ --config pacman.conf \ --noscriptlet \ - --hookdir "$BUILDDIR/alpm-hooks/usr/share/libalpm/hooks/" base "$GROUP" + --hookdir "$BUILDDIR/alpm-hooks/usr/share/libalpm/hooks/" base ${GROUP:+${GROUP/repro/}} +# # repro is not a package, so excluded here ^ $WRAPPER -- chroot "$BUILDDIR" update-ca-trust $WRAPPER -- chroot "$BUILDDIR" pacman-key --init $WRAPPER -- chroot "$BUILDDIR" pacman-key --populate +# Remove archived repo snapshot from the mirrorlist +sed -i '1d' "$BUILDDIR/etc/pacman.d/mirrorlist" + +if [[ "$GROUP" == "repro" ]]; + # Clear pacman keyring for reproducible builds + rm -rf "$BUILDDIR"/etc/pacman.d/gnupg/* +fi + +# Normalize mtimes +find "$BUILDDIR" -exec touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} + + # add system users $WRAPPER -- chroot "$BUILDDIR" /usr/bin/systemd-sysusers --root "/" @@ -58,6 +75,10 @@ fakeroot -- \ --numeric-owner \ --xattrs \ --acls \ + --mtime="@$SOURCE_DATE_EPOCH" \ + --clamp-mtime \ + --sort=name \ + --pax-option=delete=atime,delete=ctime \ --exclude-from=exclude \ -C "$BUILDDIR" \ -c . \