From 2405e07d077def9e330649415aa6e68312964c69 Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:39:21 +0100 Subject: [PATCH 1/6] Make archives created with tar more reproducible --- action.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index 06f99f4..cb850ee 100644 --- a/action.yml +++ b/action.yml @@ -189,12 +189,22 @@ runs: shell: bash working-directory: ${{ runner.temp }} run: | + # Recommendations from https://reproducible-builds.org/docs/archives/ + # TODO: set a better value for this? + SOURCE_DATE_EPOCH=0 + TAROPTS=( + --sort=name + --mtime="@${SOURCE_DATE_EPOCH}" + --owner=0 --group=0 --numeric-owner + --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime + -cf - "${BASENAME}" + ) for EXT in $ARCHIVE_FORMATS ; do ARCHIVENAME=$BASENAME$EXT echo "Creating $ARCHIVENAME ..." case $EXT in - .tar.gz) tar cf - "$BASENAME" | gzip -9c > "$ASSETS/$ARCHIVENAME" ;; - .tar.bz2) tar cf - "$BASENAME" | bzip2 -9c > "$ASSETS/$ARCHIVENAME" ;; + .tar.gz) tar "${TAROPTS[@]}" | gzip -9c > "$ASSETS/$ARCHIVENAME" ;; + .tar.bz2) tar "${TAROPTS[@]}" | bzip2 -9c > "$ASSETS/$ARCHIVENAME" ;; .zip) zip -r9 --quiet "$ASSETS/$ARCHIVENAME" "$BASENAME" ;; *) echo "::error::Unsupported archive format $EXT" From 674c0ed277584068dd23236ca459aedf624fed27 Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:09:50 +0100 Subject: [PATCH 2/6] Overhaul reproducible builds, also do .tar.bz2 and .zip, add tests --- .github/workflows/CI.yml | 47 ++++++++++++++++++++++++++++++++++++++++ action.yml | 24 ++++++++++++-------- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e9e2ac7..6779fea 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,6 +14,8 @@ jobs: strategy: fail-fast: false matrix: + package: [ "gap-actions/ActionTestGAPDocPackage", "gap-actions/ActionTestOldDocPackage" ] + affix: [ 1, 2 ] # Run each package twice, to check reproducibility include: - package: "gap-actions/ActionTestGAPDocPackage" force: true @@ -53,6 +55,51 @@ jobs: with: dry-run: true force: ${{ matrix.force }} + affix: "-${{ matrix.affix }}" + + check-reproducibility: + name: "Check if builds are reproducible" + runs-on: ubuntu-latest + needs: test-action + strategy: + fail-fast: false + matrix: + package: [ "ActionTestGAPDocPackage", "ActionTestOldDocPackage" ] + steps: + + - name: "Download releases" + uses: actions/download-artifact@v8 + with: + pattern: "Release_assets_for_${{ matrix.package }}*" + path: ${{ runner.temp }} + + - name: "Compare releases" + shell: bash + working-directory: ${{ runner.temp }} + run: | + # Move downloaded assets + # The .zip is not an extension but part of the directory name + mv Release_assets_for_*-1.zip v1 + mv Release_assets_for_*-2.zip v2 + + for EXT in .tar.gz .tar.bz2 .zip; do + for file1 in v1/*"$EXT"; do + file2="v2/$(basename "$file1")" + + # Calculate sha256 checksums + chk1=$(sha256sum "$file1" | cut -d' ' -f1) + chk2=$(sha256sum "$file2" | cut -d' ' -f1) + + if [[ "$chk1" != "$chk2" ]]; then + echo "::error::Checksum mismatch for ${EXT} archives" + echo "v1: $chk1" + echo "v2: $chk2" + exit 1 + else + echo "${EXT} archives are reproducible" + fi + done + done test-scripts: name: "Test the scripts used by this action" diff --git a/action.yml b/action.yml index c0d1b28..f06a953 100644 --- a/action.yml +++ b/action.yml @@ -14,6 +14,10 @@ inputs: description: "Body text for the new GitHub release that will appear on the package's GitHub Releases page" required: false default: "Release of ${{ github.event.repository.name }}" + affix: + description: "UNDOCUMENTED. Required for testing to ensure unique artifact names" + required: false + default: "" runs: using: "composite" @@ -159,7 +163,7 @@ runs: - name: "Run autogen.sh" shell: bash working-directory: ${{ runner.temp }}/${{ env.BASENAME }} - run : | + run: | if [ -x autogen.sh ] ; then sudo apt-get install autoconf automake build-essential --yes echo "Generating build system files" @@ -170,7 +174,7 @@ runs: - name: "Check for symlinks" shell: bash working-directory: ${{ runner.temp }}/${{ env.BASENAME }} - run : | + run: | $GITHUB_ACTION_PATH/scripts/check_symlinks.sh || { echo "::error::Symlinks detected" exit 1 @@ -187,14 +191,16 @@ runs: - name: "Create archives" shell: bash + env: + SOURCE_DATE_EPOCH: 0 working-directory: ${{ runner.temp }} run: | - # Recommendations from https://reproducible-builds.org/docs/archives/ - # TODO: set a better value for this? - SOURCE_DATE_EPOCH=0 + # Note that we change file modification times / sorting / ... to obtain reproducible builds + # Recommendations taken from https://reproducible-builds.org/docs/archives/ + find $BASENAME -newermt "@${SOURCE_DATE_EPOCH}" -print0 | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + # Tar options TAROPTS=( --sort=name - --mtime="@${SOURCE_DATE_EPOCH}" --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime -cf - "${BASENAME}" @@ -205,7 +211,7 @@ runs: case $EXT in .tar.gz) tar "${TAROPTS[@]}" | gzip -9c > "$ASSETS/$ARCHIVENAME" ;; .tar.bz2) tar "${TAROPTS[@]}" | bzip2 -9c > "$ASSETS/$ARCHIVENAME" ;; - .zip) zip -r9 --quiet "$ASSETS/$ARCHIVENAME" "$BASENAME" ;; + .zip) find "$BASENAME" -print0 | LC_ALL=C sort -z | tr '\0' '\n' | zip -X -r9 "$ASSETS/$ARCHIVENAME" -@ ;; *) echo "::error::Unsupported archive format $EXT" exit 1 @@ -239,13 +245,13 @@ runs: - name: "Copy body-text" if: ${{ inputs.dry-run == 'true' }} shell: bash - run: echo "${{ inputs.body-text }}" > $RUNNER_TEMP/Release_text_for_${PKGNAME}_${VERSION}.md + run: echo "${{ inputs.body-text }}" > $RUNNER_TEMP/Release_text_for_${PKGNAME}_${VERSION}${{ inputs.affix }}.md - name: "Upload the release assets" uses: actions/upload-artifact@v7 if: ${{ inputs.dry-run == 'true' }} with: - name: "Release_assets_for_${{ env.PKGNAME }}_${{ env.VERSION }}.zip" + name: "Release_assets_for_${{ env.PKGNAME }}_${{ env.VERSION }}${{ inputs.affix }}.zip" archive: true path: ${{ env.ASSETS }}/* if-no-files-found: error From a134863f8e78b043c08da9dc31460868897453a5 Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:28:34 +0100 Subject: [PATCH 3/6] Set SOURCE_DATE_EPOCH to package release date --- .github/workflows/CI.yml | 9 +++++++-- action.yml | 8 +++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6779fea..b31d30e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -81,7 +81,8 @@ jobs: # The .zip is not an extension but part of the directory name mv Release_assets_for_*-1.zip v1 mv Release_assets_for_*-2.zip v2 - + + error_found=false for EXT in .tar.gz .tar.bz2 .zip; do for file1 in v1/*"$EXT"; do file2="v2/$(basename "$file1")" @@ -94,13 +95,17 @@ jobs: echo "::error::Checksum mismatch for ${EXT} archives" echo "v1: $chk1" echo "v2: $chk2" - exit 1 + error_found=true else echo "${EXT} archives are reproducible" fi done done + if ${error_found}; then + exit 1 + fi + test-scripts: name: "Test the scripts used by this action" runs-on: ubuntu-slim diff --git a/action.yml b/action.yml index f06a953..1d2c8df 100644 --- a/action.yml +++ b/action.yml @@ -191,13 +191,15 @@ runs: - name: "Create archives" shell: bash - env: - SOURCE_DATE_EPOCH: 0 working-directory: ${{ runner.temp }} run: | + # Get release date + DATEFORMAT=$(echo "${DATE}" | awk -F/ '{print $3"-"$2"-"$1}') + SOURCE_DATE_EPOCH=$(date -d "$DATEFORMAT" +%s) + # Note that we change file modification times / sorting / ... to obtain reproducible builds # Recommendations taken from https://reproducible-builds.org/docs/archives/ - find $BASENAME -newermt "@${SOURCE_DATE_EPOCH}" -print0 | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + find $BASENAME -print0 | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" # Tar options TAROPTS=( --sort=name From dd7baa2075afb11e0e079dfcafe767b509eae9d0 Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Thu, 5 Mar 2026 11:29:22 +0100 Subject: [PATCH 4/6] Reinstate --quiet flag when zipping --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 1d2c8df..fb16bec 100644 --- a/action.yml +++ b/action.yml @@ -213,7 +213,7 @@ runs: case $EXT in .tar.gz) tar "${TAROPTS[@]}" | gzip -9c > "$ASSETS/$ARCHIVENAME" ;; .tar.bz2) tar "${TAROPTS[@]}" | bzip2 -9c > "$ASSETS/$ARCHIVENAME" ;; - .zip) find "$BASENAME" -print0 | LC_ALL=C sort -z | tr '\0' '\n' | zip -X -r9 "$ASSETS/$ARCHIVENAME" -@ ;; + .zip) find "$BASENAME" -print0 | LC_ALL=C sort -z | tr '\0' '\n' | zip -X -r9 --quiet "$ASSETS/$ARCHIVENAME" -@ ;; *) echo "::error::Unsupported archive format $EXT" exit 1 From 8a1e8ffebb127c3d5a529f77687dffc84914c489 Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:06:59 +0100 Subject: [PATCH 5/6] Update action.yml Co-authored-by: Max Horn --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index fb16bec..42eef26 100644 --- a/action.yml +++ b/action.yml @@ -15,7 +15,7 @@ inputs: required: false default: "Release of ${{ github.event.repository.name }}" affix: - description: "UNDOCUMENTED. Required for testing to ensure unique artifact names" + description: "UNDOCUMENTED, FOR INTERNAL USE ONLY. Required for testing to ensure unique artifact names" required: false default: "" From 1eb56dce955d89e1b7e0b427dcd124cafc79bd3e Mon Sep 17 00:00:00 2001 From: stertooy <5571903+stertooy@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:34:51 +0100 Subject: [PATCH 6/6] Replace `affix` input by `_affix` --- .github/workflows/CI.yml | 2 +- action.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b31d30e..6ad0d46 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -55,7 +55,7 @@ jobs: with: dry-run: true force: ${{ matrix.force }} - affix: "-${{ matrix.affix }}" + _affix: "-${{ matrix.affix }}" check-reproducibility: name: "Check if builds are reproducible" diff --git a/action.yml b/action.yml index 42eef26..4abe1da 100644 --- a/action.yml +++ b/action.yml @@ -14,7 +14,7 @@ inputs: description: "Body text for the new GitHub release that will appear on the package's GitHub Releases page" required: false default: "Release of ${{ github.event.repository.name }}" - affix: + _affix: description: "UNDOCUMENTED, FOR INTERNAL USE ONLY. Required for testing to ensure unique artifact names" required: false default: "" @@ -247,13 +247,13 @@ runs: - name: "Copy body-text" if: ${{ inputs.dry-run == 'true' }} shell: bash - run: echo "${{ inputs.body-text }}" > $RUNNER_TEMP/Release_text_for_${PKGNAME}_${VERSION}${{ inputs.affix }}.md + run: echo "${{ inputs.body-text }}" > $RUNNER_TEMP/Release_text_for_${PKGNAME}_${VERSION}${{ inputs._affix }}.md - name: "Upload the release assets" uses: actions/upload-artifact@v7 if: ${{ inputs.dry-run == 'true' }} with: - name: "Release_assets_for_${{ env.PKGNAME }}_${{ env.VERSION }}${{ inputs.affix }}.zip" + name: "Release_assets_for_${{ env.PKGNAME }}_${{ env.VERSION }}${{ inputs._affix }}.zip" archive: true path: ${{ env.ASSETS }}/* if-no-files-found: error