Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9a2d6da
chore: refactor reproducible builds
MoeMahhouk Jun 16, 2025
10a3b59
Add release-reproducible CI workflow
MoeMahhouk Jun 16, 2025
a4348b2
Add reproducible-build workflow to catch regressions
MoeMahhouk Jun 16, 2025
608caab
fixing linting issues
MoeMahhouk Jun 16, 2025
d93242f
refine the reproducible build and test arm github runner
MoeMahhouk Jun 24, 2025
febc138
make release-reproducible verify reproducibility
MoeMahhouk Jun 30, 2025
19f2a0f
add label trigger for reproducible builds
MoeMahhouk Jun 30, 2025
966ee01
Merge branch 'unstable' into unstable
chong-he Jul 1, 2025
f20ef0c
Merge branch 'unstable' into unstable
MoeMahhouk Oct 6, 2025
5674e5a
Merge branch 'unstable' into unstable
chong-he Nov 2, 2025
bddc4f7
update rust version for github actions
MoeMahhouk Nov 3, 2025
5e21162
addressing PR feedback
MoeMahhouk Nov 5, 2025
dbedf06
remove the release summary from docker-reproducible.yml
MoeMahhouk Nov 6, 2025
f4c1e46
removed exposed ports from the Dockerfile.reproducible
MoeMahhouk Nov 6, 2025
2bcc784
add stable/unstable pushes to the docker-reproducible workflow
MoeMahhouk Nov 6, 2025
e838bfd
remove reproducible-builds workflow
MoeMahhouk Nov 6, 2025
89847bf
Merge branch 'unstable' into unstable
MoeMahhouk Nov 6, 2025
19768d6
remove unnecessary is_tag checks
MoeMahhouk Nov 6, 2025
3431d03
manual workflow trigger is just for testing purposes only
MoeMahhouk Nov 6, 2025
12c0574
Add reproducibility build for jemalloc-sys
MoeMahhouk Nov 6, 2025
5613f2a
chore: remove unnecessary echo
MoeMahhouk Nov 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions .github/workflows/release-reproducible.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: release-reproducible

on:
push:
tags:
- v*
workflow_dispatch:
inputs:
dry_run:
description: >-
Enable dry run mode (builds images but skips push to registry)
type: boolean
default: false

env:
DOCKER_REPRODUCIBLE_IMAGE_NAME: >-
${{ github.repository_owner }}/lighthouse-reproducible
DOCKER_PASSWORD: ${{ secrets.DH_KEY }}
DOCKER_USERNAME: ${{ secrets.DH_ORG }}

jobs:
extract-version:
name: extract version
runs-on: ubuntu-latest
steps:
- name: Extract version
run: >-
echo "VERSION=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
id: extract_version
outputs:
VERSION: ${{ steps.extract_version.outputs.VERSION }}

build-reproducible:
name: build and push reproducible images
runs-on: ubuntu-latest
needs: extract-version
strategy:
matrix:
arch: [amd64, arm64]
include:
- arch: amd64
rust_target: x86_64-unknown-linux-gnu
rust_image: >-
rust:1.86-bullseye@sha256:1110399f568f1dbe838e58f15b4162d899cb95f450f5f0ffa739614f3a4c32f1
platform: linux/amd64
- arch: arm64
rust_target: aarch64-unknown-linux-gnu
rust_image: >-
rust:1.86-bullseye@sha256:36053eabadeb701e3e0406610a2ce72ccfa10b7828963cd08cffdcf660518b27
platform: linux/arm64
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
if: ${{ github.event.inputs.dry_run != 'true' }}
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}

- name: Build reproducible image (${{ matrix.arch }})
uses: docker/build-push-action@v6
env:
IMAGE_BASE: ${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}
VERSION: ${{ needs.extract-version.outputs.VERSION }}
ARCH: ${{ matrix.arch }}
DOCKER_BUILD_RECORD_UPLOAD: false
with:
context: .
file: ./Dockerfile.reproducible
platforms: ${{ matrix.platform }}
push: ${{ github.event.inputs.dry_run != 'true' }}
tags: ${{ env.IMAGE_BASE }}:${{ env.VERSION }}-${{ env.ARCH }}
build-args: |
RUST_TARGET=${{ matrix.rust_target }}
RUST_IMAGE=${{ matrix.rust_image }}
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
provenance: false

create-manifest:
name: create multi-arch manifest
runs-on: ubuntu-latest
needs: [extract-version, build-reproducible]
if: ${{ github.event.inputs.dry_run != 'true' }}
steps:
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}

- name: Create and push multi-arch manifest
run: |
IMAGE_NAME=${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}
VERSION=${{ needs.extract-version.outputs.VERSION }}
# Create manifest for version tag
docker manifest create \
${IMAGE_NAME}:${VERSION} \
${IMAGE_NAME}:${VERSION}-amd64 \
${IMAGE_NAME}:${VERSION}-arm64
docker manifest push ${IMAGE_NAME}:${VERSION}
# Create manifest for latest tag
docker manifest create \
${IMAGE_NAME}:latest \
${IMAGE_NAME}:${VERSION}-amd64 \
${IMAGE_NAME}:${VERSION}-arm64
docker manifest push ${IMAGE_NAME}:latest
dry-run-summary:
name: dry run summary
runs-on: ubuntu-latest
needs: [build-reproducible, extract-version]
if: ${{ github.event.inputs.dry_run == 'true' }}
steps:
- name: Summarize dry run
run: |
IMAGE_NAME=${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}
VERSION=${{ needs.extract-version.outputs.VERSION }}
echo "## 🧪 Reproducible Build Dry Run Summary"
echo ""
echo "✅ Successfully completed dry run for version ${VERSION}"
echo ""
echo "### What would happen in a real release:"
echo "- Multi-arch reproducible Docker images would be built"
echo "- Images would be pushed to Docker Hub as:"
echo " - \`${IMAGE_NAME}:${VERSION}\`"
echo " - \`${IMAGE_NAME}:latest\`"
echo ""
echo "### Architectures built:"
echo "- linux/amd64 (x86_64-unknown-linux-gnu)"
echo "- linux/arm64 (aarch64-unknown-linux-gnu)"
echo ""
echo "### Next Steps"
echo "To perform a real release, push a git tag"
echo "(e.g., \`git tag v4.6.0 && git push origin v4.6.0\`)"
188 changes: 188 additions & 0 deletions .github/workflows/reproducible-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: reproducible-build

on:
workflow_dispatch: {}
schedule:
- cron: "0 1 */2 * *"
pull_request:
paths:
- "Makefile"
- "Dockerfile.reproducible"
- ".github/workflows/reproducible-build.yml"
- "Cargo.toml"
- "Cargo.lock"

jobs:
build-x86_64:
name: test reproducible builds (x86_64)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-unknown-linux-gnu

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev cmake
- name: Install cargo-cache
run: cargo install cargo-cache

- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
key: reproducible-build-x86_64

- name: Build Lighthouse (first build)
run: |
make build-reproducible \
RUST_TARGET=x86_64-unknown-linux-gnu
cp target/x86_64-unknown-linux-gnu/release/lighthouse \
lighthouse-build-1
sha256sum lighthouse-build-1 > lighthouse-build-1.sha256
- name: Clean build artifacts and cache
run: |
make clean
cargo cache -a
rm -rf target/
- name: Build Lighthouse (second build)
run: |
make build-reproducible \
RUST_TARGET=x86_64-unknown-linux-gnu
cp target/x86_64-unknown-linux-gnu/release/lighthouse \
lighthouse-build-2
sha256sum lighthouse-build-2 > lighthouse-build-2.sha256
- name: Compare binaries
run: |
echo "=== Build 1 SHA256 ==="
cat lighthouse-build-1.sha256
echo "=== Build 2 SHA256 ==="
cat lighthouse-build-2.sha256
echo "=== Binary Comparison ==="
if cmp lighthouse-build-1 lighthouse-build-2; then
echo "✅ Binaries are identical - reproducible build PASSED"
else
echo "❌ Binaries differ - reproducible build FAILED"
exit 1
fi
- name: Upload build artifacts (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-reproducible-builds-x86_64
path: |
lighthouse-build-1
lighthouse-build-2
lighthouse-build-1.sha256
lighthouse-build-2.sha256
build-aarch64:
name: test reproducible builds (aarch64)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
with:
target: aarch64-unknown-linux-gnu

- name: Install build dependencies and cross-compilation tools
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev cmake gcc-aarch64-linux-gnu
- name: Install cargo-cache
run: cargo install cargo-cache

- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
key: reproducible-build-aarch64

- name: Build Lighthouse (first build)
run: |
make build-reproducible \
RUST_TARGET=aarch64-unknown-linux-gnu
cp target/aarch64-unknown-linux-gnu/release/lighthouse \
lighthouse-build-1-arm64
sha256sum lighthouse-build-1-arm64 > \
lighthouse-build-1-arm64.sha256
- name: Clean build artifacts and cache
run: |
make clean
cargo cache -a
rm -rf target/
- name: Build Lighthouse (second build)
run: |
make build-reproducible \
RUST_TARGET=aarch64-unknown-linux-gnu
cp target/aarch64-unknown-linux-gnu/release/lighthouse \
lighthouse-build-2-arm64
sha256sum lighthouse-build-2-arm64 > \
lighthouse-build-2-arm64.sha256
- name: Compare binaries
run: |
echo "=== Build 1 SHA256 (ARM64) ==="
cat lighthouse-build-1-arm64.sha256
echo "=== Build 2 SHA256 (ARM64) ==="
cat lighthouse-build-2-arm64.sha256
echo "=== Binary Comparison ==="
if cmp lighthouse-build-1-arm64 lighthouse-build-2-arm64; then
echo "✅ ARM64 binaries are identical - reproducible build PASSED"
else
echo "❌ ARM64 binaries differ - reproducible build FAILED"
exit 1
fi
- name: Upload build artifacts (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: failed-reproducible-builds-aarch64
path: |
lighthouse-build-1-arm64
lighthouse-build-2-arm64
lighthouse-build-1-arm64.sha256
lighthouse-build-2-arm64.sha256
summary:
name: reproducible build summary
runs-on: ubuntu-latest
needs: [build-x86_64, build-aarch64]
if: always()
steps:
- name: Report results
run: |
echo "## 🔄 Reproducible Build Test Results"
echo ""
if [[ "${{ needs.build-x86_64.result }}" == "success" ]]; then
echo "✅ **x86_64**: Reproducible builds PASSED"
else
echo "❌ **x86_64**: Reproducible builds FAILED"
fi
if [[ "${{ needs.build-aarch64.result }}" == "success" ]]; then
echo "✅ **aarch64**: Reproducible builds PASSED"
else
echo "❌ **aarch64**: Reproducible builds FAILED"
fi
echo ""
if [[ "${{ needs.build-x86_64.result }}" == "success" ]] \
&& [[ "${{ needs.build-aarch64.result }}" == "success" ]]; then
echo "🎉 **Overall**: All reproducible builds are working correctly!"
else
echo "⚠️ **Overall**: Some reproducible builds failed"
echo "Check the logs above"
exit 1
fi
7 changes: 0 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,5 @@ lto = "fat"
codegen-units = 1
incremental = false

[profile.reproducible]
inherits = "release"
debug = false
panic = "abort"
codegen-units = 1
overflow-checks = true

[patch.crates-io]
quick-protobuf = { git = "https://github.com/sigp/quick-protobuf.git", rev = "681f413312404ab6e51f0b46f39b0075c6f4ebfd" }
35 changes: 8 additions & 27 deletions Dockerfile.reproducible
Original file line number Diff line number Diff line change
@@ -1,44 +1,25 @@
# Define the Rust image as an argument with a default to x86_64 Rust 1.82 image based on Debian Bullseye
ARG RUST_IMAGE="rust:1.82-bullseye@sha256:ac7fe7b0c9429313c0fe87d3a8993998d1fe2be9e3e91b5e2ec05d3a09d87128"
ARG RUST_IMAGE="rust:1.86-bullseye@sha256:1110399f568f1dbe838e58f15b4162d899cb95f450f5f0ffa739614f3a4c32f1"

FROM ${RUST_IMAGE} AS builder

# Install specific version of the build dependencies
RUN apt-get update && apt-get install -y libclang-dev=1:11.0-51+nmu5 cmake=3.18.4-2+deb11u1

# Add target architecture argument with default value
ARG RUST_TARGET="x86_64-unknown-linux-gnu"

# Copy the project to the container
COPY . /app
COPY ./ /app
WORKDIR /app

# Get the latest commit timestamp and set SOURCE_DATE_EPOCH (default it to 0 if not passed)
ARG SOURCE_DATE=0

# Set environment variables for reproducibility
ARG RUSTFLAGS="-C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix $(pwd)=."
ENV SOURCE_DATE_EPOCH=$SOURCE_DATE \
CARGO_INCREMENTAL=0 \
LC_ALL=C \
TZ=UTC \
RUSTFLAGS="${RUSTFLAGS}"

# Set the default features if not provided
ARG FEATURES="gnosis,slasher-lmdb,slasher-mdbx,slasher-redb,jemalloc"

# Set the default profile if not provided
ARG PROFILE="reproducible"

# Build the project with the reproducible settings
RUN cargo build --bin lighthouse \
--features "${FEATURES}" \
--profile "${PROFILE}" \
--locked \
--target "${RUST_TARGET}"
RUN make build-reproducible

RUN mv /app/target/${RUST_TARGET}/${PROFILE}/lighthouse /lighthouse
# Move the binary to a standard location
RUN mv /app/target/${RUST_TARGET}/release/lighthouse /lighthouse

# Create a minimal final image with just the binary
FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a
COPY --from=builder /lighthouse /lighthouse

EXPOSE 30303 30303/udp 9001 8545 8546
ENTRYPOINT [ "/lighthouse" ]
Loading
Loading