Skip to main content

Execution Client Workflows

Execution client packages (Geth, Reth, Nethermind, Besu, Erigon) use four GitHub Actions workflows to ensure proper testing and release automation.

Workflow Overview

WorkflowTriggerPurpose
auto_check.ymlScheduled (every 4h)Check for upstream updates
sync.ymlScheduled daily + manualKeep execution client synced
sync-test.ymlPull requests + manualTest package changes
release.ymlPush to main + manualRelease with attestation test

1. Auto Check for Upstream Updates (auto_check.yml)

Same as standard packages - checks for upstream updates and creates PRs when new versions are available.

name: Bump upstream version

on:
schedule:
- cron: "00 */4 * * *"
push:
branches:
- "master"

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npx @dappnode/dappnodesdk github-action bump-upstream
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PINATA_API_KEY: ${{ secrets.PINATA_API_KEY }}
PINATA_SECRET_API_KEY: ${{ secrets.PINATA_SECRET_API_KEY }}

2. Sync Production (sync.yml)

This workflow keeps the execution client synced on the self-hosted runner. By maintaining a synced state, other workflows (sync-test, release) complete much faster.

Workflow File

name: Execution Client Sync Production

on:
schedule:
- cron: "0 4 * * *"
workflow_dispatch:
inputs:
consensus_client:
description: "Consensus Client"
required: true
type: choice
options: [lodestar, teku, prysm, nimbus, lighthouse]

jobs:
sync:
runs-on: staking-test-hoodi
steps:
- name: Run sync
run: |
docker run --rm --pull=always --network dncore_network \
-v /var/run/docker.sock:/var/run/docker.sock \
-e MODE=sync \
-e EXECUTION_CLIENT='geth' \
-e CONSENSUS_CLIENT=${{ github.event.inputs.consensus_client }} \
-e NETWORK=hoodi \
ghcr.io/dappnode/staker-test-util/test-runner:latest

Features

  • Scheduled daily: Runs at 4 AM UTC to keep the client synced
  • Manual trigger: Can be triggered manually with any consensus client
  • Uses test-runner image: The ghcr.io/dappnode/staker-test-util/test-runner container handles the sync logic
  • Network access: Connects to the Dappnode's dncore_network to communicate with other services

3. Sync Test (sync-test.yml)

Triggered on PRs to test package changes. Builds the package, uploads it to the local IPFS node, and runs a sync test with the new version.

Workflow File

name: Execution Client Sync Test

on:
workflow_dispatch:
inputs:
consensus_client:
description: "Consensus Client"
required: true
type: choice
options: [lodestar, teku, prysm, nimbus, lighthouse]
pull_request:
branches:
- "main"
paths-ignore:
- "README.md"

jobs:
build:
runs-on: staking-test-hoodi
name: Build
outputs:
ipfs_hash: ${{ steps.extract_hash.outputs.ipfs_hash }}
steps:
- uses: actions/checkout@v6
- name: Build and upload
run: npx @dappnode/dappnodesdk build --provider=http://$(docker inspect DAppNodeCore-ipfs.dnp.dappnode.eth --format '{{.NetworkSettings.Networks.dncore_network.IPAddress}}'):5001 --variant=hoodi
- name: Extract IPFS hash from releases.json
id: extract_hash
run: |
# Get the last key's hash from releases.json
IPFS_HASH=$(jq -r 'to_entries | last | .value.hash' package_variants/hoodi/releases.json)
echo "ipfs_hash=$IPFS_HASH" >> $GITHUB_OUTPUT
echo "Extracted IPFS hash: $IPFS_HASH"

sync-test:
runs-on: staking-test-hoodi
name: Execution Client Sync Test
needs: [build]
steps:
- name: Run sync
run: |
docker run --rm --pull=always --network dncore_network \
-v /var/run/docker.sock:/var/run/docker.sock \
-e MODE=sync \
-e EXECUTION_CLIENT='geth' \
-e IPFS_HASH=${{ needs.build.outputs.ipfs_hash }} \
-e CONSENSUS_CLIENT=${{ github.event.inputs.consensus_client }} \
-e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
-e GITHUB_REPOSITORY=${{ github.repository }} \
-e GITHUB_PR_NUMBER=${{ github.event.pull_request.number }} \
-e GITHUB_RUN_ID=${{ github.run_id }} \
-e GITHUB_SERVER_URL=${{ github.server_url }} \
ghcr.io/dappnode/staker-test-util/test-runner:latest

Features

  • Builds to local IPFS: Uses the Dappnode's IPFS node as the provider
  • IPFS hash extraction: Extracts the hash from releases.json for the test
  • PR integration: Posts a test report to the PR with results
  • GitHub context: Passes GitHub environment variables for PR commenting

PR Report

After the test completes, a report is posted to the PR including:

  • Clients used and versions (before/after install)
  • Timing measurements for each operation
  • Container error logs (if any)
  • Link to full CI logs

4. Release (release.yml)

Triggered on push to main. Runs a full attestation test before releasing the package.

Workflow File

name: "Release"

on:
workflow_dispatch:
inputs:
consensus_client:
description: "Consensus Client"
required: true
type: choice
options: [lodestar, teku, prysm, nimbus, lighthouse]
push:
branches:
- "main"
paths-ignore:
- "README.md"

jobs:
build:
runs-on: staking-test-hoodi
name: Build
outputs:
ipfs_hash: ${{ steps.extract_hash.outputs.ipfs_hash }}
steps:
- uses: actions/checkout@v6
- name: Build and upload
run: npx @dappnode/dappnodesdk build --provider=http://$(docker inspect DAppNodeCore-ipfs.dnp.dappnode.eth --format '{{.NetworkSettings.Networks.dncore_network.IPAddress}}'):5001 --variant=hoodi
- name: Extract IPFS hash from releases.json
id: extract_hash
run: |
# Get the last key's hash from releases.json
IPFS_HASH=$(jq -r 'to_entries | last | .value.hash' package_variants/hoodi/releases.json)
echo "ipfs_hash=$IPFS_HASH" >> $GITHUB_OUTPUT
echo "Extracted IPFS hash: $IPFS_HASH"

test:
name: Test
runs-on: staking-test-hoodi
needs: [build]
steps:
- uses: actions/checkout@v6
- name: Run staker test runner
run: |
docker run --rm --pull=always \
--network dncore_network \
-v /var/run/docker.sock:/var/run/docker.sock \
-e MODE=test \
-e IPFS_HASH=${{ needs.build.outputs.ipfs_hash }} \
-e CONSENSUS_CLIENT=${{ github.event.inputs.consensus_client }} \
-e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
-e GITHUB_REPOSITORY=${{ github.repository }} \
-e GITHUB_PR_NUMBER=${{ github.event.pull_request.number }} \
-e GITHUB_RUN_ID=${{ github.run_id }} \
-e GITHUB_SERVER_URL=${{ github.server_url }} \
ghcr.io/dappnode/staker-test-util/test-runner:latest

release:
name: Release
runs-on: ipfs-dev-gateway
needs: [test]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Publish
run: npx @dappnode/dappnodesdk publish patch --content_provider=http://10.200.200.7:5001 --eth_provider=https://web3.dappnode.net --timeout 2h --all-variants
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEVELOPER_ADDRESS: "0xf35960302a07022aba880dffaec2fdd64d5bf1c1"

Features

  • Proof of Attestation Test: The test job uses MODE=test which:
    1. Syncs execution and consensus clients
    2. Imports validators into web3signer
    3. Waits for validators to become live on the beacon chain
    4. Confirms successful attestation
  • Three-stage pipeline: Build → Test → Release
  • Different runners: Test runs on staking-test-hoodi, release on ipfs-dev-gateway
  • All variants: Publishes all package variants with --all-variants

Test Logs

The proof of attestation test logs include:

  • Validator beaconcha.in URL
  • Clients used and versions
  • Sync timing measurements
  • Attestation confirmation

Environment Variables

VariableDescription
MODEsync for sync only, test for full attestation test
EXECUTION_CLIENTThe execution client name (e.g., geth, reth)
CONSENSUS_CLIENTThe consensus client to pair with
IPFS_HASHThe IPFS hash of the built package to test
NETWORKThe network to use (e.g., hoodi)
GITHUB_TOKENFor PR commenting and GitHub API access
GITHUB_REPOSITORYRepository name for PR comments
GITHUB_PR_NUMBERPR number for posting reports
GITHUB_RUN_IDRun ID for linking to CI logs
GITHUB_SERVER_URLGitHub server URL for links