diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d09e247f..a72f0f87 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,6 +10,9 @@ # - "flashbox-l1" → builds only flashbox-l1 # - "flashbox-l2" → builds only flashbox-l2 # - "flashbox-l1,flashbox-l2" → builds both +# - Reproducibility test (default: false) +# - Adds a second build on a separate runner and compares SHA256 hashes + name: Build mkosi images @@ -29,6 +32,11 @@ on: required: false default: 'flashbox-l1' type: string + reprotest: + description: 'Run reproducibility test (builds on 2 machines and compares hashes)' + required: false + default: false + type: boolean jobs: validate: @@ -122,9 +130,95 @@ jobs: - name: Generate SHA256 checksums run: | - cd build/ - TIMESTAMP=$(git show -s --format=%ct HEAD) - SHORT_SHA="${GITHUB_SHA::8}" - CHECKSUM_FILE="${{ matrix.image }}_${TIMESTAMP}_${SHORT_SHA}.sha256" - sha256sum ${{ matrix.image }}_* > "$CHECKSUM_FILE" - cat "$CHECKSUM_FILE" + sha256sum build/${{ matrix.image }}_*.efi | tee build/checksum.sha256 + + - name: Upload checksum for reprotest + if: inputs.reprotest == true + uses: actions/upload-artifact@v4 + with: + name: checksum-${{ matrix.image }}-build-1 + path: build/checksum.sha256 + retention-days: 1 + + reprotest-build: + needs: validate + if: inputs.reprotest == true + strategy: + fail-fast: false + matrix: + image: ${{ fromJSON(needs.validate.outputs.matrix) }} + name: reprotest ${{ matrix.image }} + runs-on: warp-ubuntu-latest-x64-32x + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ inputs.branch || github.ref }} + + - name: Install tools + run: | + sudo apt-get update && sudo apt-get install -y debian-archive-keyring + + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - name: Enable user namespaces + run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 + + - name: Build ${{ matrix.image }} image + run: | + umask 022 + nix develop --command mkosi --force -I images/${{ matrix.image }}.conf --image-id=${{ matrix.image }} + sudo chown -R $(id -u):$(id -g) build/ + + - name: Generate SHA256 checksum + run: | + sha256sum build/${{ matrix.image }}_*.efi | tee build/checksum.sha256 + + - name: Upload checksum + uses: actions/upload-artifact@v4 + with: + name: checksum-${{ matrix.image }}-build-2 + path: build/checksum.sha256 + retention-days: 1 + + reprotest-compare: + needs: [validate, build, reprotest-build] + if: inputs.reprotest == true + strategy: + fail-fast: false + matrix: + image: ${{ fromJSON(needs.validate.outputs.matrix) }} + name: reprotest compare ${{ matrix.image }} + runs-on: ubuntu-latest + steps: + - name: Download checksums + uses: actions/download-artifact@v4 + with: + pattern: checksum-${{ matrix.image }}-* + path: checksums/ + + - name: Compare SHA256 hashes + run: | + echo "=== Reproducibility Check for ${{ matrix.image }} ===" + echo "Build 1 (all artifacts):" + cat checksums/checksum-${{ matrix.image }}-build-1/*.sha256 + echo "Build 2 (EFI only):" + cat checksums/checksum-${{ matrix.image }}-build-2/checksum.sha256 + + # Extract only the .efi hash from build-1 to compare with build-2 + hash1=$(grep '\.efi$' checksums/checksum-${{ matrix.image }}-build-1/*.sha256 | awk '{print $1}') + hash2=$(awk '{print $1}' checksums/checksum-${{ matrix.image }}-build-2/checksum.sha256) + + echo "" + echo "EFI hash build-1: $hash1" + echo "EFI hash build-2: $hash2" + + if [ "$hash1" = "$hash2" ]; then + echo "✅ SUCCESS: ${{ matrix.image }} images are identical (reproducible build verified)" + else + echo "❌ FAILURE: ${{ matrix.image }} images differ (reproducible build failed)" + exit 1 + fi