diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e9e2ac7..6ad0d46 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,56 @@ 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 + + error_found=false + 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" + 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" diff --git a/action.yml b/action.yml index 733ff41..55b186a 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, FOR INTERNAL USE ONLY. Required for testing to ensure unique artifact names" + required: false + default: "" runs: using: "composite" @@ -162,7 +166,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" @@ -173,7 +177,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 @@ -192,13 +196,27 @@ runs: shell: bash 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 -print0 | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + # Tar options + TAROPTS=( + --sort=name + --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" ;; - .zip) zip -r9 --quiet "$ASSETS/$ARCHIVENAME" "$BASENAME" ;; + .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 --quiet "$ASSETS/$ARCHIVENAME" -@ ;; *) echo "::error::Unsupported archive format $EXT" exit 1 @@ -232,13 +250,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