From 051d01ddd345aff1238b3fcb72ee1478caa04727 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 11 Jun 2026 00:52:48 -0400 Subject: [PATCH 1/4] feat(op-reth): add StreamingFast Docker image build + release workflow Add a StreamingFast-flavored Docker build/push/release flow for op-reth, kept in .sf-suffixed files separate from upstream so upstream merges stay clean. No existing upstream CI/Dockerfile is modified. - .github/workflows/sf-release.yml: build + release jobs triggered on `*-fh*` tags and manual dispatch. Builds a linux/amd64 ghcr image from Dockerfile.sf, then (on tag) extracts the op-reth binary, pulls release notes from CHANGELOG.sf.md via sfreleaser, and publishes a GitHub release. - rust/op-reth/Dockerfile.sf: SF variant tracking op-reth/DockerfileOp, adds ARG VERSION (OCI label) and installs op-reth at /usr/local/bin. - CHANGELOG.sf.md: Keep-a-Changelog seed read by the release job. amd64-only for now: op-reth's maxperf full-node build is too heavy to reliably build on GitHub-hosted arm64 runners. --- .github/workflows/sf-release.yml | 128 +++++++++++++++++++++++++++++++ CHANGELOG.sf.md | 20 +++++ rust/op-reth/Dockerfile.sf | 71 +++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 .github/workflows/sf-release.yml create mode 100644 CHANGELOG.sf.md create mode 100644 rust/op-reth/Dockerfile.sf diff --git a/.github/workflows/sf-release.yml b/.github/workflows/sf-release.yml new file mode 100644 index 00000000000..7bc2289457d --- /dev/null +++ b/.github/workflows/sf-release.yml @@ -0,0 +1,128 @@ +# StreamingFast Docker image build, push and (on tag) release. +# +# Maintained by StreamingFast and intentionally kept separate from upstream CI +# so that upstream merges stay clean. Triggers only on `*-fh*` tags and manual +# dispatch — never on pull requests or branch pushes — so it cannot collide with +# upstream workflows and only maintainers (who push tags) can run it. +# +# Single architecture (linux/amd64): the op-reth `maxperf` full-node build is too +# heavy to reliably build on GitHub-hosted arm64 runners, so we do not produce a +# multi-arch manifest. Re-introduce a matrix + manifest-merge job here if arm64 +# becomes worthwhile. +name: Build, push and release (if tag) + +on: + push: + tags: + - "*-fh*" + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-22.04 + + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract version + id: extract-version + run: | + version="edge" + if [[ "${GITHUB_REF}" == refs/tags/* ]]; then + version=${GITHUB_REF#refs/tags/} + fi + echo "VERSION=${version}" >> "$GITHUB_OUTPUT" + + - name: Generate docker tags/labels from github build context + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # `type=sha` produces a stable `` tag that the `release` job + # below uses as a deterministic image reference to `docker cp` the + # binary out of. We build a single architecture and only on tags or + # manual dispatch, so there is no concurrent branch build to race with + # and no per-arch suffix tag is required. + tags: | + type=ref,event=tag,prefix= + type=sha,prefix= + type=edge + flavor: | + latest=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') }} + + - name: Build and push Docker image (linux/amd64) + uses: docker/build-push-action@v6 + with: + context: rust + file: ./rust/op-reth/Dockerfile.sf + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + provenance: false + build-args: | + VERSION=${{ steps.extract-version.outputs.VERSION }} + + release: + if: startsWith(github.ref, 'refs/tags/') + needs: build + runs-on: ubuntu-22.04 + + permissions: + contents: write + + steps: + - name: Docker login + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract image + id: image + run: | + echo "ID=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + + - name: Extract assets + run: | + # The --platform is not strictly needed for a single-arch image, but it + # silences the buildx warning. + docker cp $(docker create --platform=linux/amd64 ${{ steps.image.outputs.ID }}):/usr/local/bin/op-reth ./op-reth_linux_amd64 + + - name: Extract Changelog + id: changelog + run: | + curl -L https://github.com/streamingfast/sfreleaser/releases/download/v0.12.1/sfreleaser_linux_x86_64.tar.gz | tar -xz + chmod +x sfreleaser + + ./sfreleaser changelog extract-section \ + github://token:${{ github.token }}@${{ github.repository }}/$GITHUB_SHA/CHANGELOG.sf.md \ + --github-output="changelog:$GITHUB_OUTPUT" + + - name: Release + uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.changelog.outputs.changelog }} + prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + files: | + ./op-reth_linux_amd64 diff --git a/CHANGELOG.sf.md b/CHANGELOG.sf.md new file mode 100644 index 00000000000..6c10a086add --- /dev/null +++ b/CHANGELOG.sf.md @@ -0,0 +1,20 @@ +# StreamingFast op-reth Changelog + +All notable StreamingFast-specific changes to this fork are documented in this +file. It tracks only the `.sf`-suffixed StreamingFast additions (Firehose +instrumentation, Docker image, release flow) on top of upstream op-reth. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +The `sf-release.yml` workflow publishes the top-most version section here as the +GitHub release notes (via `sfreleaser changelog extract-section`), so keep the +most recent release at the top. + +## Unreleased + +### Added + +- StreamingFast Docker image build, push and release flow (`Dockerfile.sf`, + `.github/workflows/sf-release.yml`). Pushing a `*-fh*` tag builds the + Firehose-instrumented `op-reth` binary, publishes a `linux/amd64` image to + `ghcr.io`, and creates a GitHub release with the binary attached and these + notes as the body. diff --git a/rust/op-reth/Dockerfile.sf b/rust/op-reth/Dockerfile.sf new file mode 100644 index 00000000000..776392b4de3 --- /dev/null +++ b/rust/op-reth/Dockerfile.sf @@ -0,0 +1,71 @@ +# StreamingFast variant of op-reth/DockerfileOp. +# +# This file is maintained by StreamingFast and is intentionally kept separate +# from upstream's `op-reth/DockerfileOp` so that upstream merges stay clean. +# It builds the Firehose-instrumented `op-reth` binary and installs it at a +# stable path (`/usr/local/bin/op-reth`) that the `sf-release.yml` workflow can +# `docker cp` out of the image to attach as a GitHub release asset. +# +# Keep this in sync with `op-reth/DockerfileOp` to avoid drift: when the upstream +# Dockerfile changes (base image, build profile, build command), mirror the +# relevant change here. +# +# Build context is the `rust/` workspace directory (same as the upstream +# `op-reth` docker-bake target), so crate paths are rooted at `/app/op-reth`. + +FROM lukemathwalker/cargo-chef:latest-rust-1.94 AS chef +WORKDIR /app + +LABEL org.opencontainers.image.source=https://github.com/streamingfast/op-reth +LABEL org.opencontainers.image.licenses="MIT OR Apache-2.0" + +RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config +RUN cargo install cargo-auditable + +# Builds a cargo-chef plan +FROM chef AS planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json + +ARG BUILD_PROFILE=maxperf +ENV BUILD_PROFILE=$BUILD_PROFILE + +ARG RUSTFLAGS="" +ENV RUSTFLAGS="$RUSTFLAGS" + +ARG FEATURES="" +ENV FEATURES=$FEATURES + +# Version of the release being built (e.g. the pushed tag). Stamped as an OCI +# image label below. The reth binary derives its own `--version` string from the +# upstream git metadata at compile time, so this is informational only. +ARG VERSION="dev" + +# Allow cook to fail on patched crates (op-alloy-network) whose crates.io +# version conflicts with alloy 1.8. All other dependencies are still cached. +# The real build below uses the workspace [patch.crates-io] sources. +RUN cargo chef cook --profile $BUILD_PROFILE --features "$FEATURES" --recipe-path recipe.json --manifest-path /app/op-reth/bin/Cargo.toml || true + +COPY . . +RUN cargo auditable build --profile $BUILD_PROFILE --features "$FEATURES" --bin op-reth --manifest-path /app/op-reth/bin/Cargo.toml + +RUN ls -la /app/target/$BUILD_PROFILE/op-reth +RUN cp /app/target/$BUILD_PROFILE/op-reth /app/op-reth + +FROM chainguard/wolfi-base:latest AS runtime + +ARG VERSION="dev" +LABEL org.opencontainers.image.version="${VERSION}" + +RUN apk add --no-cache ca-certificates openssl libstdc++ strace + +WORKDIR /app +COPY --from=builder /app/op-reth /usr/local/bin/ +RUN chmod +x /usr/local/bin/op-reth +COPY op-reth/LICENSE-* ./ + +EXPOSE 30303 30303/udp 9001 8545 8546 7545 8551 +ENTRYPOINT ["/usr/local/bin/op-reth"] From a5bfbc34f16306827a9512f235176375d043dbb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:49:04 +0000 Subject: [PATCH 2/4] fix(ci): gate release step to tag refs --- .github/workflows/sf-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sf-release.yml b/.github/workflows/sf-release.yml index 7bc2289457d..218c4c01401 100644 --- a/.github/workflows/sf-release.yml +++ b/.github/workflows/sf-release.yml @@ -120,6 +120,7 @@ jobs: --github-output="changelog:$GITHUB_OUTPUT" - name: Release + if: github.ref_type == 'tag' uses: softprops/action-gh-release@v2 with: body: ${{ steps.changelog.outputs.changelog }} From 0e77483823b2b9decaf0b81e502c15ccb80ac72a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:52:08 +0000 Subject: [PATCH 3/4] revert(ci): remove redundant release step tag gate --- .github/workflows/sf-release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sf-release.yml b/.github/workflows/sf-release.yml index 218c4c01401..7bc2289457d 100644 --- a/.github/workflows/sf-release.yml +++ b/.github/workflows/sf-release.yml @@ -120,7 +120,6 @@ jobs: --github-output="changelog:$GITHUB_OUTPUT" - name: Release - if: github.ref_type == 'tag' uses: softprops/action-gh-release@v2 with: body: ${{ steps.changelog.outputs.changelog }} From cd43dc66bd0d054e97e1e1b379b016d24cc52f7b Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 11 Jun 2026 14:28:03 -0400 Subject: [PATCH 4/4] ci(sf-release): branch/PR triggers, ubuntu-24.04, alpha + slash-safe versions - Run on `firehose/*` branch pushes and pull requests in addition to `*-fh*` tags and manual dispatch. - Skip ghcr login/push for fork PRs (read-only token); same-repo PRs and pushes still authenticate and push. The image always builds to validate the Dockerfile. - Bump both jobs to ubuntu-24.04. - Treat `-alpha` like `-beta`/`-rc` for `latest` exclusion and prerelease. - Restore the version-suffixed `-` tag (race-safe when a tag and its branch build the same commit) and sanitize `/` -> `-` in the extracted version so slash-containing refs stay valid Docker tags. --- .github/workflows/sf-release.yml | 57 ++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/.github/workflows/sf-release.yml b/.github/workflows/sf-release.yml index 7bc2289457d..c88f62e2ad1 100644 --- a/.github/workflows/sf-release.yml +++ b/.github/workflows/sf-release.yml @@ -1,9 +1,12 @@ # StreamingFast Docker image build, push and (on tag) release. # # Maintained by StreamingFast and intentionally kept separate from upstream CI -# so that upstream merges stay clean. Triggers only on `*-fh*` tags and manual -# dispatch — never on pull requests or branch pushes — so it cannot collide with -# upstream workflows and only maintainers (who push tags) can run it. +# so that upstream merges stay clean. +# +# Triggers: `firehose/*` branch pushes, `*-fh*` tags, pull requests and manual +# dispatch. Pull requests opened from a fork get a read-only GITHUB_TOKEN, so the +# login/push steps are skipped for them (the image still builds, to validate the +# Dockerfile); same-repo ("inner") PRs and pushes authenticate and push normally. # # Single architecture (linux/amd64): the op-reth `maxperf` full-node build is too # heavy to reliably build on GitHub-hosted arm64 runners, so we do not produce a @@ -13,8 +16,11 @@ name: Build, push and release (if tag) on: push: + branches: + - "firehose/*" tags: - "*-fh*" + pull_request: workflow_dispatch: env: @@ -23,12 +29,20 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: read packages: write + env: + # Pushes (branch/tag), same-repo PRs and manual dispatch can authenticate + # to ghcr and push images. A PR opened from a fork gets a read-only + # GITHUB_TOKEN with no `packages: write`, so we must skip login and push for + # it — the build still runs to validate the Dockerfile compiles. The value + # is the string "true"/"false". + IS_EXTERNAL_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -37,6 +51,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to the Container registry + if: env.IS_EXTERNAL_PR != 'true' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -50,6 +65,10 @@ jobs: if [[ "${GITHUB_REF}" == refs/tags/* ]]; then version=${GITHUB_REF#refs/tags/} fi + # Docker tags forbid `/`; a tag (or ref) containing slashes would make + # the `-` tag invalid. Match docker/metadata-action's own + # sanitization by replacing `/` with `-`. + version=${version//\//-} echo "VERSION=${version}" >> "$GITHUB_OUTPUT" - name: Generate docker tags/labels from github build context @@ -57,17 +76,23 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - # `type=sha` produces a stable `` tag that the `release` job - # below uses as a deterministic image reference to `docker cp` the - # binary out of. We build a single architecture and only on tags or - # manual dispatch, so there is no concurrent branch build to race with - # and no per-arch suffix tag is required. + # The version-suffixed sha tag uniquely identifies the image for a given + # build, appending the extracted version (either `edge` or the tag). This + # avoids a race condition when a tag and its branch are pushed together: + # both builds run on the same commit and would otherwise push the same + # `` tag, with the last writer winning and possibly clobbering the + # tag build's version label. The `-` tag stays distinct + # (`-edge` vs `-v1.2.3-fh3.0`), and the `release` job below + # reads `-` so it always picks up the correct image. tags: | type=ref,event=tag,prefix= + type=ref,event=branch + type=ref,event=pr type=sha,prefix= + type=sha,prefix=,suffix=-${{ steps.extract-version.outputs.VERSION }} type=edge flavor: | - latest=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') }} + latest=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') && !contains(github.ref, 'alpha') }} - name: Build and push Docker image (linux/amd64) uses: docker/build-push-action@v6 @@ -75,7 +100,7 @@ jobs: context: rust file: ./rust/op-reth/Dockerfile.sf platforms: linux/amd64 - push: true + push: ${{ env.IS_EXTERNAL_PR != 'true' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} provenance: false @@ -85,7 +110,7 @@ jobs: release: if: startsWith(github.ref, 'refs/tags/') needs: build - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: write @@ -101,7 +126,11 @@ jobs: - name: Extract image id: image run: | - echo "ID=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + version=${GITHUB_REF#refs/tags/} + # Match the sanitization done in the build job's "Extract version" step + # so this ID resolves to the same `-` image tag. + version=${version//\//-} + echo "ID=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${GITHUB_SHA::7}-${version}" >> "$GITHUB_OUTPUT" - name: Extract assets run: | @@ -123,6 +152,6 @@ jobs: uses: softprops/action-gh-release@v2 with: body: ${{ steps.changelog.outputs.changelog }} - prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'rc') || contains(github.ref, 'alpha') }} files: | ./op-reth_linux_amd64