diff --git a/.github/workflows/repro.yaml b/.github/workflows/repro.yaml new file mode 100644 index 000000000000..d4be4d9f83fa --- /dev/null +++ b/.github/workflows/repro.yaml @@ -0,0 +1,183 @@ +--- +name: Reproducible Build Verification +on: + schedule: + - cron: "0 0 * * *" # Daily at midnight UTC + pull_request: + types: [opened, synchronize] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build-base-image: + name: Build jammy Docker Image + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build base image + run: | + echo "Building base image for jammy" + docker run --rm -v $(pwd):/build ubuntu:jammy \ + bash -c "apt-get update && apt-get install -y debootstrap && debootstrap jammy /build/jammy" + tar -C jammy -c . | docker import - jammy + echo "Verifying jammy base image:" + docker run jammy cat /etc/lsb-release + + - name: Build builder image + run: | + echo "Building CL repro jammy:" + docker build -t cl-repro-jammy - < contrib/reprobuild/Dockerfile.jammy + + - name: Save Docker image + run: | + mkdir -p docker-images + docker save cl-repro-jammy | gzip > docker-images/cl-repro-jammy.tar.gz + + - name: Upload Docker image + uses: actions/upload-artifact@v4 + with: + name: docker-images + path: docker-images/ + retention-days: 1 + + build-ubuntu: + name: Build CLN on Ubuntu + runs-on: ubuntu-latest + timeout-minutes: 120 + needs: build-base-image + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download Docker image + uses: actions/download-artifact@v4 + with: + name: docker-images + path: docker-images/ + + - name: Load Docker image + run: | + docker load --input docker-images/cl-repro-jammy.tar.gz + + - name: Create release directory + run: | + mkdir -p release + + - name: Build with jammy + run: | + docker run --rm -v $(pwd):/repo cl-repro-jammy bash -c "git clone /repo . && uv sync --all-extras --all-groups && uv run tools/repro-build.sh --force-mtime=2000-01-01 && cp *.xz /repo/release/" + + + - name: Generate checksums + run: | + cd release + sha256sum *.xz > SHA256SUMS-ubuntu + cat SHA256SUMS-ubuntu + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ubuntu-artifacts + path: release/ + retention-days: 1 + + build-debian: + name: Build CLN on Debian + runs-on: debian-latest + timeout-minutes: 120 + needs: build-base-image + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download Docker image + uses: actions/download-artifact@v4 + with: + name: docker-images + path: docker-images/ + + - name: Load Docker image + run: | + docker load --input docker-images/cl-repro-jammy.tar.gz + + - name: Create release directory + run: | + mkdir -p release + + - name: Build with jammy + run: | + docker run --rm -v $(pwd):/repo cl-repro-jammy bash -c "git clone /repo . && uv sync --all-extras --all-groups && uv run tools/repro-build.sh --force-mtime=2000-01-01 && cp *.xz /repo/release/" + + - name: Generate checksums + run: | + cd release + sha256sum *.xz > SHA256SUMS-debian + cat SHA256SUMS-debian + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: debian-artifacts + path: release/ + retention-days: 1 + + verify-reproducibility: + name: Verify Reproducibility + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: [build-ubuntu, build-debian] + if: always() + steps: + - name: Download Ubuntu artifacts + uses: actions/download-artifact@v4 + with: + name: ubuntu-artifacts + path: ubuntu-release/ + + - name: Download Debian artifacts + uses: actions/download-artifact@v4 + with: + name: debian-artifacts + path: debian-release/ + + - name: Compare artifacts + run: | + echo "=== Ubuntu checksums ===" + cat ubuntu-release/SHA256SUMS-ubuntu || echo "Ubuntu checksums not found" + echo "" + echo "=== Debian checksums ===" + cat debian-release/SHA256SUMS-debian || echo "Debian checksums not found" + echo "" + echo "=== Comparing binary reproducibility ===" + + # Extract just the hashes and filenames for comparison + if [ -f ubuntu-release/SHA256SUMS-ubuntu ] && [ -f debian-release/SHA256SUMS-debian ]; then + # Get just the hash and filename parts, sort them for comparison + ubuntu_hashes=$(grep -v SHA256SUMS ubuntu-release/SHA256SUMS-ubuntu | sort || true) + debian_hashes=$(grep -v SHA256SUMS debian-release/SHA256SUMS-debian | sort || true) + + if [ -z "$ubuntu_hashes" ] || [ -z "$debian_hashes" ]; then + echo "Checksums are empty, builds may have failed" + exit 1 + fi + + # Compare the hashes + if diff <(echo "$ubuntu_hashes") <(echo "$debian_hashes"); then + echo "✓ Builds are reproducible! Checksums match between Ubuntu and Debian runners." + exit 0 + else + echo "✗ Builds are NOT reproducible. Checksums differ between Ubuntu and Debian runners." + exit 1 + fi + else + echo "Could not find checksum files from both builds" + exit 1 + fi diff --git a/.github/workflows/repro.yml b/.github/workflows/repro.yml deleted file mode 100644 index 8bcc0c5603b0..000000000000 --- a/.github/workflows/repro.yml +++ /dev/null @@ -1,113 +0,0 @@ ---- -# https://docs.corelightning.org/docs/repro -name: Repro Build Nightly -on: - # 05:00 Berlin, 03:00 UTC, 23:00 New York, 20:00 Los Angeles - schedule: - - cron: "0 3 * * *" - workflow_dispatch: - -jobs: - ubuntu: - name: "Ubuntu repro build: ${{ matrix.version }}" - runs-on: ubuntu-22.04 - strategy: - fail-fast: false # Let each build finish. - matrix: - version: ['focal', 'jammy', 'noble'] - steps: - - name: Git checkout - uses: actions/checkout@v4 - - - name: Build environment setup - ${{ matrix.version }} - run: | - echo "Building base image for ${{ matrix.version }}" - echo "STEP=Build environment setup" >> "$GITHUB_ENV" - sudo docker run --rm -v $(pwd):/build ubuntu:${{ matrix.version }} bash -c "\ - apt-get update && \ - apt-get install -y debootstrap && \ - debootstrap ${{ matrix.version }} /build/${{ matrix.version }}" - sudo tar -C ${{ matrix.version }} -c . | docker import - ${{ matrix.version }} - - - name: Builder image setup - ${{ matrix.version }} - run: | - echo "STEP=Builder image setup" >> "$GITHUB_ENV" - docker build -t cl-repro-${{ matrix.version }} - < contrib/reprobuild/Dockerfile.${{ matrix.version }} - - - name: Build reproducible image and store Git state - ${{ matrix.version }} - run: | - echo "STEP=Build reproducible image and store Git state" >> "$GITHUB_ENV" - - # Create release directory. - mkdir $GITHUB_WORKSPACE/release - - # Perform the repro build. - docker run --name cl-build -v $GITHUB_WORKSPACE:/repo -e FORCE_MTIME=$(date +%F) -t cl-repro-${{ matrix.version }} - - # Commit the image in order to inspect the build later. - docker commit cl-build cl-repro - - # Inspect the version. - docker run --rm -v $GITHUB_WORKSPACE:/repo -t cl-repro bash -c "make version > /repo/release/version.txt" - - # Inspect the Git tree state. - docker run --rm -v $GITHUB_WORKSPACE:/repo -t cl-repro bash -c "\ - git --no-pager status > /repo/release/git.log && \ - git --no-pager diff >> /repo/release/git.log" - - # Change permissions on the release files for access by the runner environment. - sudo chown -R runner $GITHUB_WORKSPACE/release - - - name: Assert clean version - ${{ matrix.version }} - run: | - echo "STEP=Assert clean version" >> "$GITHUB_ENV" - echo 'Version:' - cat release/version.txt - echo -e - - releasefile=$(ls release/clightning-*) - echo 'Release file:' - ls -al release/clightning-* - echo -e - if [ -n "$(cat release/version.txt | sed -n '/-modded/p')" ] || \ - [ -n "$(echo $releasefile | sed -n '/-modded/p')" ]; then - echo "Git Status and Diff:" - cat release/git.log - echo -e - echo 'Error: release modded / dirty tree.' - exit 1 - else - echo 'Success! Clean release.' - fi - - - name: Upload release artifact - ${{ matrix.version }} - uses: actions/upload-artifact@v4 - with: - name: release-${{ matrix.version }} - path: release - retention-days: 3 # Automatically delete after 3 days - - - name: Send email on failure - if: ${{ failure() }} - uses: dawidd6/action-send-mail@v3 - with: - server_address: smtp.gmail.com - server_port: 587 - username: ${{ secrets.EMAIL_USERNAME }} - password: ${{ secrets.EMAIL_PASSWORD }} - from: ${{ secrets.EMAIL_USERNAME }} - to: ${{ vars.DISTRIBUTION_LIST }} - subject: "CI Failure: Step ${{ env.STEP }} failed for distro ${{ matrix.version }}" - convert_markdown: true - html_body: | - - -

GitHub Workflow ${{ github.workflow }} Failed! For more details, click on the action below.

- Failure Details:
- Event: ${{ github.event_name }}
- Job: ${{ github.job }}
- Distro: ${{ matrix.version }}
- Step: ${{ env.STEP }}
- Action: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- - diff --git a/.gitmodules b/.gitmodules index 734acd9efcca..96f4f1adffad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ [submodule "daemon/jsmn"] path = external/jsmn url = https://github.com/zserge/jsmn +[submodule "libsodium"] + path = external/libsodium + url = https://github.com/jedisct1/libsodium.git [submodule "external/libbacktrace"] path = external/libbacktrace url = https://github.com/ianlancetaylor/libbacktrace.git @@ -11,3 +14,7 @@ [submodule "external/gheap"] path = external/gheap url = https://github.com/valyala/gheap +[submodule "external/lowdown"] + path = external/lowdown + url = https://github.com/kristapsdz/lowdown.git + ignore = dirty diff --git a/contrib/reprobuild/Dockerfile.focal b/contrib/reprobuild/Dockerfile.focal index 4d960aa6906b..a749be3bd903 100644 --- a/contrib/reprobuild/Dockerfile.focal +++ b/contrib/reprobuild/Dockerfile.focal @@ -26,11 +26,9 @@ RUN apt-get update \ unzip \ wget \ git \ - zip - -# Ensure correct ownership -RUN chown root:root /etc/sudoers -RUN chown root:root /usr/lib/sudo/sudoers.so + zip \ + && rm -rf /var/cache/apt/archives /var/lib/apt/lists/*. +RUN echo "ALL ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers # Download and install jq from official repository RUN wget -O /usr/local/bin/jq https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64 \ diff --git a/contrib/reprobuild/Dockerfile.jammy b/contrib/reprobuild/Dockerfile.jammy index 3f156a6f6658..f48f08ae2ad2 100644 --- a/contrib/reprobuild/Dockerfile.jammy +++ b/contrib/reprobuild/Dockerfile.jammy @@ -23,16 +23,17 @@ RUN apt-get update \ libsodium23 \ libtool \ m4 \ - sudo \ unzip \ wget \ jq \ zip -# Ensure correct ownership -RUN chown root:root /etc/sudoers -RUN chown root:root /etc/sudo.conf -RUN chown root:root /usr/libexec/sudo/sudoers.so +RUN printf 'root ALL=(ALL) ALL\n%%sudo ALL=(ALL) ALL\n' > /etc/sudoers && \ + chmod 0440 /etc/sudoers + +# Since we're running as root, create a sudo wrapper that's a no-op +RUN printf '#!/bin/sh\nexec "$@"\n' > /usr/local/bin/sudo && \ + chmod +x /usr/local/bin/sudo # Install Python3.10 (more reproducible than relying on python3-setuptools) RUN git clone https://github.com/pyenv/pyenv.git /root/.pyenv && \ @@ -61,6 +62,10 @@ RUN cd /tmp/ && \ mv bin/protoc /usr/local/bin && \ rm -rf include bin protoc-${PROTOC_VERSION}-linux-x86_64.zip +RUN wget http://ftp.ch.debian.org/debian/pool/main/l/lowdown/lowdown_2.0.2-2_amd64.deb && \ + dpkg -i lowdown_2.0.2-2_amd64.deb && \ + rm lowdown_2.0.2-2_amd64.deb + RUN mkdir /build WORKDIR /build