Repository navigation
ci: add a release pipeline with release candidates, binaries and promotion - #674
MegaRedHand wants to merge 7 commits into
Conversation
🤖 Codex Code ReviewFound 2 actionable issues; the Rust-side version override itself looks sound and low risk for consensus logic.
Looks Good
If you want, I can turn these into ready-to-post GitHub review comments. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR focusing on the areas you specified. This appears to be a release infrastructure PR with workflow changes, Dockerfile updates, and version string handling. Let me analyze each component. SummaryThe PR implements a proper release pipeline with candidate → promotion flow, multi-arch Docker images, release binaries, and runtime channel override for version strings. The core consensus code changes are minimal (only version reporting). Detailed Review1. GitHub Workflows
|
🤖 Claude Code ReviewReview of PR 674: release candidate and promotion pipelineI read the diff only and did not run any of the workflows. Overall the design is sound. Promoting by re-stamping the tested image rather than rebuilding it is a good choice. The Worth addressing
Minor
Looks good
Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Kimi Code ReviewI'll review this PR, which introduces a comprehensive release pipeline with Docker image promotion, binary releases, and attestation workflows. Let me analyze the changes across all files. SummaryThis PR implements a full release management system: automated builds for Detailed Findings1. GitHub Workflows —
|
| Priority | Issue | Location |
|---|---|---|
| Blocking | actions/checkout@v6 does not exist |
All workflow files |
| Blocking | actions/upload-artifact@v7 does not exist |
release.yaml:275 |
| Blocking | actions/download-artifact@v8 does not exist |
release.yaml:386 |
| High | Verify actions/attest@v4 exists |
release.yaml:450, release_promote.yaml:176 |
Security Observations
-
Attestation scope: The
attest-imagejob only runs for candidates (candidate != ''), not formainbuilds. This is intentional —mainbuilds areunstableand not attested as release artifacts. Good. -
OIDC permissions: The
id-token: writepermission is narrowly scoped to jobs that need it (attest-image,github-release,promote). Good principle of least privilege. -
Registry authentication: Uses
secrets.GITHUB_TOKENwithpackages: writescoped to specific jobs. Correct. -
Promotion integrity: The re-stamp build uses
FROM $IMAGE:$RC_TAGwithout digest verification. Consider adding an integrity check:docker buildx imagetools inspect "$IMAGE:$RC_TAG" --format '{{.Manifest.Digest}}'
and logging it, or even pinning the
FROMwith digest. However, since candidates are immutable tags, this is acceptable.
Recommendations
-
Fix action versions immediately — this PR will break all workflows.
-
Add whitespace trimming for
ETHLAMBDA_CHANNEL:.map(|s| s.trim()) .filter(|channel| !channel.is_empty())
-
Consider stricter regex in
docker_publish.yamlfor version tags to matchrelease.yamlexactly. -
Add timeout to
release_promote.yamlpolling loop to prevent infinite hangs ifseqfails (defensive, thoughset -euo pipefailhelps). -
Document the
vprefix handling —release.yamlstripsvfrom tags for image tags, butdocker_publish.yamlallowsv?prefix. Ensure consistency.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
🤖 Codex Code ReviewOverall, the Rust changes look low-risk and do not touch consensus-critical paths like fork choice, attestation validation, justification/finalization, state transition, XMSS verification, or SSZ logic. I did not find a correctness or memory-safety bug in the code shown.
A couple of review notes:
If you want, I can also do a second pass focused just on the workflow changes in Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Claude Code ReviewReview of PR 674: release pipeline (RCs, binaries, promotion)The design is sound. Promoting by re-stamping Points worth addressing
Minor
Not verified: the workflows haven't run on GitHub (as the PR notes). The first Automated review by Claude (Anthropic) · sonnet · custom prompt |
…ntime The release pipeline promotes a tested release candidate by re-stamping its image instead of rebuilding it, so the binary that ships is the one that was tested. That binary has its candidate channel (`rc.N`) baked in at build time; reading the channel from `ETHLAMBDA_CHANNEL` when set lets the promoted image report `stable` without touching the executable. An empty value is ignored, so a blank `-e ETHLAMBDA_CHANNEL=` cannot drop the channel from the string. `CLIENT_VERSION` becomes `client_version()`, built once on first use, since a `concat!` constant cannot read the environment.
ethlambda shipped only hand-tagged Docker images, with no versioned releases, no binaries, and `latest` moved by hand. This adopts ethrex's release flow: - release.yaml: a `vX.Y.Z-rc.N` tag publishes the `X.Y.Z-rc.N` image, builds Linux (x86_64, aarch64) and macOS (aarch64) binaries, and opens a GitHub pre-release with them and a conventional-commit changelog. Pushes to main keep publishing `unstable` and `sha-<7>`, moved here from docker_publish.yaml so one workflow owns every automatic tag. A manual run rehearses the whole candidate path without publishing. - release_promote.yaml: renaming the candidate's tag to `vX.Y.Z` and unticking pre-release, in one edit, re-publishes the tested images as `X.Y.Z` and `latest` with `ETHLAMBDA_CHANNEL=stable`, then checks `latest` moved and the image reports `-stable-`. A `released` tripwire fails if the promotion never fired; a manual run recovers it. - docker_publish.yaml: manual only, for devnet tags. It refuses the tags the release workflows own, so two writers never race over `latest`. Two guards ethrex lacks: a tag whose `X.Y.Z` differs from the Cargo version is refused, as is promoting a candidate to a different version, since the binary reports its Cargo version rather than the tag either way. A tag checkout is detached, so vergen would report the branch as "HEAD". The Dockerfile and the binary jobs now pass the channel to vergen through VERGEN_GIT_BRANCH, which build.rs watches. RELEASE.md documents the procedure.
The binaries and the changelog only ran on a release-candidate tag or a manual run, so a change that broke them surfaced on the next tag, long after the commit that caused it. Main pushes now build both too, publishing neither: the binaries stay workflow artifacts and the changelog goes to the run summary.
The manual run built the chosen branch as a branch, so it never reached what only a tag runs: the tag-versus-Cargo version check, the `rc.N` channel, and the pre-release. Now that main pushes build the binaries, it rehearsed nothing a push to main does not. It now takes an `rc_tag` input and runs that tag's path on the chosen ref, publishing nothing: images are built but not pushed, and the pre-release job writes the notes and assets it would publish to the run summary and a `dry-run-release-assets` artifact. Running it on `release/vX.Y.Z` before tagging catches a bad version bump or a broken asset step without cutting a candidate.
Release assets were published with only a SHA256SUMS beside them, which proves integrity against the release page itself and nothing more. They are now verifiable two independent ways: - minisign, as ethrex does: every asset, SHA256SUMS included, gets a `.minisig` whose trusted comment names the asset, tag and commit. The script checks the secret key derives to the committed .github/minisign.pub and verifies each signature against it, so a key rotated in one place only fails the run instead of shipping signatures nobody can check. The canonical repository refuses to publish unsigned; a fork without a key publishes unsigned with a warning. - GitHub build provenance attestations: the binaries and the candidate image on tag push, and the promoted image at promotion, since re-stamping it makes a new manifest the candidate's attestation does not cover. Each is verified with `gh attestation verify` before the run goes green. A dry run signs with the real key (marked DRY-RUN in the trusted comment) so a broken key or script fails before a tag does, but skips attesting, since an attestation is public the moment it is made. The OIDC and attestation permissions are granted only to the jobs that publish a release asset, not to the per-push image jobs. The key itself is not part of this change: RELEASE.md describes creating it and setting the two secrets, and until that is done a candidate tag fails at signing.
…ed channels Step 4 says to test a candidate on the current devnet, but not which checks or hosts that means, so each release would be tested differently. A TODO marks where that checklist goes until it is written. The new distribution section records why crates.io is out of reach (the workspace depends on leanvm from git, which crates.io rejects) and that apt and Homebrew are future work, so the question does not have to be re-investigated each release.
minisign was added on the premise that it matched ethrex, but ethrex signs only its zkVM guest ELFs and verification keys, because eth-act's zkEVM guest handbook requires those to ship as signed assets. Its binaries and images are unsigned. Nothing consumes a minisign signature of ethlambda's binaries, and the build provenance attestations already let a user verify a download, without a long-lived key to generate, store, rotate, and set up before the first release candidate can be cut.
e39a248 to
613c1ec
Compare
🗒️ Description / Motivation
ethlambda has no versioned releases: images are tagged by hand, there are no
binaries, and
latestis moved manually. This adds a release pipeline modeled onethrex's:
cut a release candidate from a tag, test it, then promote it to the final version
without rebuilding, so what ships is what was tested. Binaries and images carry
GitHub build provenance attestations.
What Changed
.github/workflows/release.yaml(new)unstable+sha-<7>(and-shadowtwins), moved here fromdocker_publish.yaml. It also builds the binaries and the changelog without publishing them, so a broken release path fails on the commit that broke it.vX.Y.Z-rc.Ntag: publishes and attests theX.Y.Z-rc.Nimage, builds and attestsethlambda-{linux-x86_64,linux-aarch64,macos-aarch64}, and opens a pre-release withSHA256SUMSand a conventional-commit changelog. Manual run: a dry run of anrc_taginput on the chosen ref, i.e. everything a push of that tag would do (version check, release notes), publishing nothing. The assets are kept as a workflow artifact..github/workflows/release_promote.yaml(new)X.Y.Z+latestviaFROM <rc>/ENV ETHLAMBDA_CHANNEL=stable, then attests the new image. Verifies thatlatestmoved, that the image reports-stable-, and that its attestation verifies. Areleasedtripwire fails if promotion never fired; a manual dispatch recovers..github/workflows/docker_publish.yamldevnetX. Refuseslatest,unstable,sha-*and version tags, which the release workflows own.bin/ethlambda/src/version.rsCLIENT_VERSIONconst →client_version(), which lets a non-emptyETHLAMBDA_CHANNELreplace the baked channel.Dockerfile,bin/ethlambda/build.rsGIT_BRANCHto vergen asVERGEN_GIT_BRANCH(a tag checkout is detached and would reportHEAD); add theorg.opencontainers.image.versionlabel.RELEASE.md,README.mdNot carried over from ethrex: L2/GPU builds and zkVM guests (no equivalent here), minisign (ethrex signs only its zkVM guest artifacts, which eth-act's guest handbook requires; nothing requires it here, and attestations need no key), crates.io (impossible while
leanvmis a git dependency), and apt and Homebrew (future work; noted inRELEASE.md).Correctness / Behavior Guarantees
ETHLAMBDA_CHANNELis set and non-empty.latestnow means "latest release" rather than "current devnet";devnetXtags stay manual.X.Y.Zdiffers from[workspace.package].versionfails the run, and promoting a candidate to a different version is refused.gh attestation verify, before the pre-release is created or the promotion goes green.github-releaseholdscontents: write. The OIDC and attestation permissions are granted only to the jobs that publish a release asset, not to the per-push image jobs. The third-party changelog action runs in a read-only job.rc.Nafter promotion (same as ethrex).Tests Added / Run
version::tests: override replaces the channel; unset or empty keeps it.VERGEN_GIT_BRANCH=rc.7and checked--versionwithETHLAMBDA_CHANNELunset /stable/ empty →rc.7/stable/rc.7.actionlinton all workflows.workflow_dispatchonly works once the workflow is onmain, so the first full exercise is after merge: a main push builds everything, and a manual run withrc_tag: v0.1.0-rc.1dry-runs the release.✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleanmake test— onlycargo test -p ethlambda --bin ethlambda(the Rust change is confined to the binary)