Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 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
b946c49
Add necessary setup for reproducible debian packaging
MoeMahhouk Jun 17, 2025
d58a9c1
add CI workflow to build and upload deb packages as assets
MoeMahhouk Jun 17, 2025
af4c59b
merge the deb packaging workflow into release.yml to avoid conflicts
MoeMahhouk Jun 18, 2025
211063a
refinements and following packaging best practices
MoeMahhouk Jun 18, 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
c772026
Update lighthouse.service
MoeMahhouk Oct 6, 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
8740bcd
Remove redundant PROFILE in Makefile
michaelsproul Nov 27, 2025
a602f3e
Merge branch 'sigp:unstable' into unstable
MoeMahhouk Dec 1, 2025
9a5b9b0
Add necessary setup for reproducible debian packaging
MoeMahhouk Jun 17, 2025
3a9a1d5
add CI workflow to build and upload deb packages as assets
MoeMahhouk Jun 17, 2025
a93f8bc
merge the deb packaging workflow into release.yml to avoid conflicts
MoeMahhouk Jun 18, 2025
246a159
refinements and following packaging best practices
MoeMahhouk Jun 18, 2025
33c73f5
Merge branch 'deb-packaging' of https://github.com/MoeMahhouk/lightho…
MoeMahhouk Dec 1, 2025
a3436b9
remove merge conflict duplicates and fix lint issues
MoeMahhouk Dec 1, 2025
e09a200
remove testing script
MoeMahhouk Dec 1, 2025
e3dfb6f
lint cleanup
MoeMahhouk Dec 1, 2025
b42041b
chore: remove unused makefile target
MoeMahhouk Dec 1, 2025
16fc7f5
remove unnecessary scripts
MoeMahhouk Dec 2, 2025
f399faf
chore: extract release reproducible binaries in their own workflow
MoeMahhouk Dec 2, 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
17 changes: 17 additions & 0 deletions .github/workflows/docker-reproducible.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,23 @@ jobs:
IMAGE_TAG="${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:${VERSION}-${{ matrix.arch }}"
docker push "$IMAGE_TAG"

- name: Extract artifacts for release
run: |
# Extract binary and .deb from verified image
docker create --name extract-${{ matrix.arch }} lighthouse-verify-2-${{ matrix.arch }}
docker cp extract-${{ matrix.arch }}:/lighthouse ./lighthouse-${{ matrix.arch }}
docker cp extract-${{ matrix.arch }}:/lighthouse.deb ./lighthouse-${{ matrix.arch }}.deb
docker rm extract-${{ matrix.arch }}

- name: Upload reproducible artifacts
uses: actions/upload-artifact@v4
with:
name: reproducible-artifacts-${{ matrix.arch }}
path: |
lighthouse-${{ matrix.arch }}
lighthouse-${{ matrix.arch }}.deb
retention-days: 1

- name: Clean up local images
run: |
docker rmi lighthouse-verify-2-${{ matrix.arch }} || true
Expand Down
126 changes: 126 additions & 0 deletions .github/workflows/release-reproducible.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# This workflow signs and publishes reproducible artifacts
# It triggers when either Release Suite or docker-reproducible completes
# But only proceeds when BOTH have completed successfully for the same tag

name: release-reproducible

on:
workflow_run:
workflows: [Release Suite, docker-reproducible]
types: [completed]

jobs:
check-both-workflows:
name: verify both workflows completed
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
outputs:
should_proceed: ${{ steps.check.outputs.should_proceed }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Check if both workflows completed successfully
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Get the tag/branch from the triggering workflow
TAG="${{ github.event.workflow_run.head_branch }}"

# Only proceed for version tags
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
echo "Not a version tag, skipping"
echo "should_proceed=false" >> $GITHUB_OUTPUT
exit 0
fi

echo "Checking workflows for tag: $TAG"
echo "version=$TAG" >> $GITHUB_OUTPUT

# Check Release Suite status
RELEASE_SUCCESS=$(gh api /repos/${{ github.repository }}/actions/workflows/release.yml/runs \
--jq ".workflow_runs[] | select(.head_branch == \"$TAG\" and .conclusion == \"success\") | .id" \
| head -1)

# Check docker-reproducible status
DOCKER_SUCCESS=$(gh api /repos/${{ github.repository }}/actions/workflows/docker-reproducible.yml/runs \
--jq ".workflow_runs[] | select(.head_branch == \"$TAG\" and .conclusion == \"success\") | .id" \
| head -1)

if [[ -n "$RELEASE_SUCCESS" ]] && [[ -n "$DOCKER_SUCCESS" ]]; then
echo "Both workflows completed successfully for $TAG"
echo " - Release Suite: run $RELEASE_SUCCESS"
echo " - docker-reproducible: run $DOCKER_SUCCESS"
echo "should_proceed=true" >> $GITHUB_OUTPUT
else
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block does not actually wait for the completion of another workflow that this workflow depends on, it checks the status and exits if not ready.
As per GH docs when multiple WFs specified in workflows: [Release Suite, docker-reproducible] only one needs to complete, the other could still be running when release-reproducible is triggered. So we need to actually wait for the completion periodically checking the status of a WF.

echo "Waiting for both workflows to complete for $TAG"
[[ -z "$RELEASE_SUCCESS" ]] && echo " - Release Suite: not completed"
[[ -z "$DOCKER_SUCCESS" ]] && echo " - docker-reproducible: not completed"
echo "should_proceed=false" >> $GITHUB_OUTPUT
fi

sign-and-publish:
name: sign and publish reproducible artifacts
needs: check-both-workflows
if: needs.check-both-workflows.outputs.should_proceed == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
strategy:
matrix:
arch: [amd64, arm64]
include:
- arch: amd64
rust_target: x86_64-unknown-linux-gnu
- arch: arm64
rust_target: aarch64-unknown-linux-gnu
steps:
- name: Download reproducible artifacts
uses: dawidd6/action-download-artifact@v6
with:
workflow: docker-reproducible.yml
name: reproducible-artifacts-${{ matrix.arch }}
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ needs.check-both-workflows.outputs.version }}

- name: Prepare artifacts for signing
run: |
VERSION=${{ needs.check-both-workflows.outputs.version }}
ARCH_SHORT=$(echo "${{ matrix.rust_target }}" | cut -d'-' -f1)

# Rename binary and create tarball
mv lighthouse-${{ matrix.arch }} lighthouse-reproducible-${VERSION}-${{ matrix.rust_target }}
tar -czf lighthouse-reproducible-${VERSION}-${{ matrix.rust_target }}.tar.gz \
lighthouse-reproducible-${VERSION}-${{ matrix.rust_target }} --remove-files

# Rename Debian package
mv lighthouse-${{ matrix.arch }}.deb lighthouse-${VERSION}-${ARCH_SHORT}-reproducible.deb

- name: Sign artifacts with GPG
env:
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
export GPG_TTY=$(tty)
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import

VERSION=${{ needs.check-both-workflows.outputs.version }}
ARCH_SHORT=$(echo "${{ matrix.rust_target }}" | cut -d'-' -f1)

# Sign binary tarball
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab \
lighthouse-reproducible-${VERSION}-${{ matrix.rust_target }}.tar.gz

# Sign Debian package
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab \
lighthouse-${VERSION}-${ARCH_SHORT}-reproducible.deb

- name: Upload reproducible artifacts to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION=${{ needs.check-both-workflows.outputs.version }}

# Upload all signed artifacts and their signatures
gh release upload ${VERSION} \
lighthouse-reproducible-*.tar.gz* \
lighthouse-*-reproducible.deb*
15 changes: 11 additions & 4 deletions Dockerfile.reproducible
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ ARG RUST_TARGET="x86_64-unknown-linux-gnu"
COPY ./ /app
WORKDIR /app

# Build the project with the reproducible settings
RUN make build-reproducible
# Build the project with reproducible settings and create Debian package
RUN make build-reproducible RUST_TARGET=${RUST_TARGET} && \
make deb-cargo RUST_TARGET=${RUST_TARGET}

# Move the binary to a standard location
RUN mv /app/target/${RUST_TARGET}/release/lighthouse /lighthouse
# Move artifacts to standard locations for easier extraction
RUN mv /app/target/${RUST_TARGET}/release/lighthouse /lighthouse && \
mv /app/target/${RUST_TARGET}/debian/*.deb /lighthouse.deb

# Artifacts stage for extracting files in CI/CD workflows
FROM scratch AS artifacts
COPY --from=builder /lighthouse /lighthouse
COPY --from=builder /lighthouse.deb /lighthouse.deb

# Create a minimal final image with just the binary
FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a
Expand Down
78 changes: 78 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,81 @@ clean:
cargo clean
make -C $(EF_TESTS) clean
make -C $(STATE_TRANSITION_VECTORS) clean

.PHONY: deb-cargo
deb-cargo: build-reproducible ## Build .deb package using cargo-deb with reproducible settings
@echo "Building .deb package with cargo-deb..."
cargo install cargo-deb@3.6.0 --locked
cd lighthouse && \
SOURCE_DATE_EPOCH=$(SOURCE_DATE) \
CARGO_INCREMENTAL=$(CARGO_INCREMENTAL_VAL) \
LC_ALL=$(LOCALE_VAL) \
TZ=$(TZ_VAL) \
cargo deb --target $(RUST_TARGET) --no-build --no-dbgsym --no-strip

@echo "Package built successfully!"
@find target/$(RUST_TARGET)/debian -name "*.deb" -exec ls -la {} \;


.PHONY: deb-cargo-x86_64
deb-cargo-x86_64: ## Build .deb for specific architectures
$(MAKE) deb-cargo RUST_TARGET=x86_64-unknown-linux-gnu

.PHONY: deb-cargo-aarch64
deb-cargo-aarch64:
$(MAKE) deb-cargo RUST_TARGET=aarch64-unknown-linux-gnu

.PHONY: deb-cargo-all
deb-cargo-all: deb-cargo-x86_64 deb-cargo-aarch64

.PHONY: install-deb-local
install-deb-local: ## Install .deb package locally for testing
@PACKAGE=$$(find target/$(RUST_TARGET)/debian -name "*.deb" | head -1); \
if [ -n "$$PACKAGE" ]; then \
echo "Installing lighthouse package: $$PACKAGE"; \
sudo dpkg -i "$$PACKAGE"; \
echo "Fixing dependencies if needed..."; \
sudo apt-get install -f; \
echo "Package installed successfully!"; \
echo ""; \
echo "The lighthouse service is now available but not started."; \
echo "Your systemd service file handles user creation declaratively."; \
echo ""; \
echo "To check service status: systemctl status lighthouse"; \
else \
echo "No .deb package found. Run 'make deb-cargo' first."; \
fi


.PHONY: remove-deb-local
remove-deb-local: ## Remove installed lighthouse package
@echo "Removing lighthouse package..."
sudo dpkg -r lighthouse || true
sudo systemctl daemon-reload || true

.PHONY: clean-deb
clean-deb: ## Clean up debian packaging artifacts
@echo "Cleaning up debian packaging artifacts..."
rm -f lighthouse_*.deb lighthouse-deb-*.deb *-diff.txt
rm -rf target/*/debian/


.PHONY: help-deb
help-deb: ## Show help for debian packaging
@echo "Clean Debian Packaging Targets:"
@echo " deb-cargo - Build .deb package with cargo-deb"
@echo " deb-cargo-x86_64 - Build x86_64 .deb package"
@echo " deb-cargo-aarch64 - Build aarch64 .deb package"
@echo " deb-cargo-all - Build all architectures"
@echo " test-deb-reproducible - Test reproducibility"
@echo " install-deb-local - Install .deb package locally"
@echo " remove-deb-local - Remove installed package"
@echo " clean-deb - Clean up packaging artifacts"
@echo ""
@echo "Prerequisites:"
@echo " - lighthouse/lighthouse.service file"
@echo " - README.md file in current directory"
@echo ""
@echo "Quick start:"
@echo " make deb-cargo - Build .deb for current RUST_TARGET"
@echo " make install-deb-local - Test the package"
23 changes: 23 additions & 0 deletions lighthouse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,26 @@ zeroize = { workspace = true }
[[test]]
name = "lighthouse_tests"
path = "tests/main.rs"

[package.metadata.deb]
maintainer = "Sigma Prime <team@sigmaprime.io>"
extended-description = """\
Lighthouse is a Rust implementation of the Ethereum beacon chain, \
built by Sigma Prime. It implements the official Ethereum 2.0 specification."""

depends = "$auto, systemd"
section = "net"
priority = "optional"
maintainer-scripts = "debian/" # This tells cargo-deb where to find scripts

# System integration
systemd-units = { enable = false, start = false }

# Assets to include in the package
assets = [
["target/release/lighthouse", "usr/bin/", "755"],
["debian/lighthouse.service", "lib/systemd/system/", "644"],
["../README.md", "usr/share/doc/lighthouse/", "644"],
]

default-features = false
16 changes: 16 additions & 0 deletions lighthouse/debian/lighthouse.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[Unit]
Description=Lighthouse Ethereum Beacon Node
After=network.target
Wants=network.target

[Service]
Type=exec
DynamicUser=yes
StateDirectory=lighthouse
ExecStart=/usr/bin/lighthouse bn \
--execution-endpoint http://localhost:8551 \
--execution-jwt-secret-key 0000000000000000000000000000000000000000000000000000000000000000
--datadir %S/lighthouse

[Install]
WantedBy=default.target
12 changes: 12 additions & 0 deletions lighthouse/debian/prerm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh
set -e

if [ "$1" = "remove" ]; then
# Stop service if running
systemctl stop lighthouse || true
systemctl disable lighthouse || true
fi

#DEBHELPER#

exit 0