Skip to content

chore(l1,l2): make workspace crates publishable to crates.io#6896

Merged
ilitteri merged 5 commits into
mainfrom
publish-crates-to-crates-io
Jun 19, 2026
Merged

chore(l1,l2): make workspace crates publishable to crates.io#6896
ilitteri merged 5 commits into
mainfrom
publish-crates-to-crates-io

Conversation

@ilitteri

@ilitteri ilitteri commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Make the ethrex library crates consumable from crates.io. The immediate goal is to publish ethrex-common, ethrex-l2-common, ethrex-rpc, ethrex-l2-rpc, and ethrex-sdk. crates.io requires every non-dev dependency in a crate's closure to already be published, so this prepares the full 16-crate transitive closure of those five and adds a workflow that publishes them in dependency order.

The crates were not publishable as-is:

  • No [package].description (crates.io rejects a publish without one).
  • Inter-crate dependencies declared as bare paths with no version requirement (cargo publish rejects path-only deps).
  • ethrex-blockchain carried orphaned optional dependencies on ethrex-guest-program / ethrex-prover, which can never reach crates.io (git deps).
  • ethrex-rpc embedded a workspace-root file via include_str!, which a published tarball cannot contain.

Description

  • Metadata. Add a description to all 16 publish-set crates; add a shared repository via [workspace.package] (inherited with repository.workspace = true).
  • Versioned deps. Add version = "17.0.0" to the publish-set [workspace.dependencies] entries and to the three inline path deps (ethrex-vm → ethrex-levm, ethrex-blockchain → ethrex-metrics, and ethrex-sdk → ethrex-sdk-contract-utils in both [dependencies] and [build-dependencies]). The path is kept for local builds; the version is used when publishing.
  • Remove the unpublishable edge. Delete the orphaned optional ethrex-guest-program / ethrex-prover dependencies from ethrex-blockchain — no [features] entry wired them and no source used them; they were the only link from the publish set to the git-backed prover crates. Cargo.lock drops exactly those two edges.
  • Gate RPC test scaffolding. ethrex-rpc's pub mod test_utils did a cross-crate include_str! of the workspace-root genesis fixture, which a published tarball can't contain. Gate it behind a new test-utils feature (off by default) via cfg(any(test, feature = "test-utils")), so the default published build excludes test_utils and the include_str! is never compiled. In-workspace test consumers (ethrex-l2-rpc, ethrex-test) enable ethrex-rpc/test-utils through a [dev-dependencies] entry (stripped on publish), reading the fixture from the workspace root as before. No fixture is duplicated.
  • Publish workflow. Add .github/workflows/publish.yml: publishes the 16 crates with cargo publish -p <crate> in dependency order on release: released (final releases only — vX.Y.Z-rc.W pre-releases are skipped, since crates.io versions are immutable and an RC must not claim the version before testing), idempotent on "already exists", with a workflow_dispatch dry-run path and a least-privilege permissions: contents: read block.
  • Release docs. Document the crates.io publishing step in docs/developers/release-process.md (trigger, pre-release exclusion, one-time CRATES_IO_TOKEN/crates-release-prod/name-ownership setup, dry-run, troubleshooting).

Publish order: rlp, crypto, sdk-contract-utils, trie, common, metrics, levm, l2-common, storage, vm, storage-rollup, blockchain, p2p, rpc, l2-rpc, sdk.

Before the first publish (org setup, not in this PR): a CRATES_IO_TOKEN repo secret, a crates-release-prod GitHub environment, and claiming the 16 crate names on crates.io (all currently unregistered).

Validation

  • cargo package --no-verify --list passes for all 16 (no missing-description warnings, no unversioned-path-dep errors). Confirmed ethrex-crypto's assembly .s files ship in its tarball, and ethrex-rpc packages cleanly with test_utils excluded from the default build (no fixture needed).
  • cargo check passes for ethrex-rpc (default build, test_utils excluded), ethrex-rpc --tests, ethrex-l2-rpc --tests, and ethrex-test --tests (the feature-gated test_utils resolves for all test consumers). Publish set + binary also compile.
  • cargo fmt --all --check and actionlint are clean.

Checklist

  • Updated STORE_SCHEMA_VERSION (crates/storage/lib.rs) if the PR includes breaking changes to the Store requiring a re-sync. — N/A, no Store changes.

Prepare the 16-crate transitive closure of ethrex-common, ethrex-l2-common,
ethrex-rpc, ethrex-l2-rpc, and ethrex-sdk for publication to crates.io, and add
a release workflow that publishes them in dependency order.

- Add a description to every publish-set crate's [package] (crates.io requires
  it) and a shared repository via [workspace.package].
- Add version = "17.0.0" to the publish-set [workspace.dependencies] entries and
  to the three inline path deps (ethrex-vm -> ethrex-levm, ethrex-blockchain ->
  ethrex-metrics, ethrex-sdk -> ethrex-sdk-contract-utils in both [dependencies]
  and [build-dependencies]). The path is kept for local builds; the version is
  used on publish.
- Remove the orphaned optional ethrex-guest-program / ethrex-prover dependencies
  from ethrex-blockchain. No feature wired them and no source used them; they
  were the only link from the publish set to the git-backed prover crates.
- Vendor the L1 genesis test fixture into ethrex-rpc and repoint the include_str!
  calls in test_utils.rs, so the published tarball is self-contained
  (test_utils is a public module compiled into the library).
- Add .github/workflows/publish.yml: publishes the 16 crates with cargo publish
  in dependency order on release, idempotent across re-runs, with a
  workflow_dispatch dry-run.
@github-actions github-actions Bot added L1 Ethereum client L2 Rollup client labels Jun 19, 2026
Comment thread .github/workflows/publish.yml Fixed
@ilitteri ilitteri marked this pull request as ready for review June 19, 2026 20:14
@ilitteri ilitteri requested review from a team as code owners June 19, 2026 20:14
Copilot AI review requested due to automatic review settings June 19, 2026 20:14
@ethrex-project-sync ethrex-project-sync Bot moved this to In Review in ethrex_l1 Jun 19, 2026
@github-actions

Copy link
Copy Markdown

🤖 Kimi Code Review

Review Summary

The PR prepares the workspace for crates.io publishing by adding versions, metadata, and a release workflow. The changes are structurally sound but require one fix to ensure test fixtures are included in the package.


Critical Issues

1. Missing include for test fixtures (crates/networking/rpc)
The moved genesis file (fixtures/l1_genesis.json) is referenced via include_str! in test_utils.rs, but Cargo does not include arbitrary directories like fixtures/ in the package by default. This will cause cargo publish to fail with a "file not found" error.

Fix: Add to crates/networking/rpc/Cargo.toml:

include = ["src/**/*", "fixtures/**/*", "Cargo.toml", "README.md"]

Or move the file to src/fixtures/ which is included by default.


Suggestions

2. Fragile error detection in publish workflow (.github/workflows/publish.yml:49)
The grep -q "already exists" check relies on the exact wording of Cargo's error message, which may change between versions. Consider using cargo search to check existence beforehand, or match on the specific exit code if Cargo provides one for this case.

3. Verify unpublished workspace members
Confirm that ethrex-config, ethrex-l2, ethrex-prover, ethrex-guest-program, and ethrex (the binary crate) are not dependencies of any crate being published. Published crates cannot depend on unpublished path-only crates. From the diff, this appears correct, but worth double-checking before running the workflow.


Correctness & Security

  • Workflow security: Good use of pinned action versions (actions/checkout@11bd719...) and environment protection (environment: crates-release-prod).
  • Token handling: Correct use of secrets.CRATES_IO_TOKEN with restricted workflow permissions.
  • Topological ordering: The hardcoded crate order in the publish script correctly respects the dependency graph (e.g., ethrex-rlp before ethrex-common, ethrex-crypto before ethrex-trie, etc.).
  • Version consistency: Using a unified 17.0.0 version across all published crates is valid for a monorepo release strategy.

Code Quality

  • Descriptions: All added descriptions are accurate and follow crates.io conventions.
  • Path+version dependencies: Correctly specified as { path = "...", version = "17.0.0" } to support both local development and publishing.
  • Fixture relocation: Moving the genesis file from ../../../fixtures/ into the crate directory is the correct fix for packaging.

Nits

  • Sleep duration: The 20-second sleep between publishes is reasonable for registry propagation, though index refresh times can vary. Consider making this configurable via workflow input if retries are added later.
  • Dry run: The --no-verify flag in dry-run mode is appropriate to skip unnecessary builds when just listing contents.

Action Required: Address Item 1 (fixture inclusion) before merging to ensure the publish workflow succeeds.


Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown

Benchmark Results Comparison

No significant difference was registered for any benchmark run.

Detailed Results

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
main_revm_BubbleSort 2.950 ± 0.035 2.917 3.038 1.14 ± 0.01
main_levm_BubbleSort 2.601 ± 0.070 2.564 2.788 1.01 ± 0.03
pr_revm_BubbleSort 2.967 ± 0.026 2.934 3.012 1.15 ± 0.01
pr_levm_BubbleSort 2.579 ± 0.006 2.570 2.586 1.00

Benchmark Results: ERC20Approval

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Approval 978.6 ± 11.8 968.7 1000.7 1.01 ± 0.02
main_levm_ERC20Approval 972.9 ± 22.7 957.5 1031.9 1.01 ± 0.03
pr_revm_ERC20Approval 981.4 ± 5.9 973.2 993.0 1.02 ± 0.01
pr_levm_ERC20Approval 965.6 ± 8.4 955.7 986.4 1.00

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 132.7 ± 2.7 131.3 140.3 1.00
main_levm_ERC20Mint 148.5 ± 1.4 147.4 152.1 1.12 ± 0.03
pr_revm_ERC20Mint 134.1 ± 2.2 132.1 139.7 1.01 ± 0.03
pr_levm_ERC20Mint 147.7 ± 1.9 146.1 152.6 1.11 ± 0.03

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 231.4 ± 1.1 230.3 234.0 1.00
main_levm_ERC20Transfer 239.0 ± 5.5 234.0 252.7 1.03 ± 0.02
pr_revm_ERC20Transfer 233.5 ± 2.5 231.0 237.6 1.01 ± 0.01
pr_levm_ERC20Transfer 235.7 ± 1.0 234.0 236.9 1.02 ± 0.01

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 222.9 ± 2.2 221.1 228.5 1.00
main_levm_Factorial 275.7 ± 60.3 250.3 438.2 1.24 ± 0.27
pr_revm_Factorial 225.7 ± 5.5 221.3 236.4 1.01 ± 0.03
pr_levm_Factorial 251.9 ± 1.7 249.4 254.2 1.13 ± 0.01

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.648 ± 0.023 1.608 1.678 1.00 ± 0.02
main_levm_FactorialRecursive 9.087 ± 0.032 9.038 9.138 5.53 ± 0.08
pr_revm_FactorialRecursive 1.643 ± 0.024 1.614 1.684 1.00
pr_levm_FactorialRecursive 9.065 ± 0.042 9.011 9.150 5.52 ± 0.08

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 198.1 ± 1.3 196.1 201.0 1.00
main_levm_Fibonacci 212.2 ± 3.1 207.9 216.8 1.07 ± 0.02
pr_revm_Fibonacci 198.5 ± 1.0 197.2 200.6 1.00 ± 0.01
pr_levm_Fibonacci 210.9 ± 2.2 208.2 215.3 1.06 ± 0.01

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 878.9 ± 8.4 866.9 894.6 1.29 ± 0.02
main_levm_FibonacciRecursive 681.3 ± 9.7 670.3 700.0 1.00
pr_revm_FibonacciRecursive 876.1 ± 14.1 858.9 906.3 1.29 ± 0.03
pr_levm_FibonacciRecursive 681.8 ± 7.8 674.1 699.4 1.00 ± 0.02

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 8.5 ± 0.1 8.4 8.8 1.00 ± 0.02
main_levm_ManyHashes 9.2 ± 0.2 9.0 9.8 1.08 ± 0.03
pr_revm_ManyHashes 8.5 ± 0.1 8.4 8.7 1.00
pr_levm_ManyHashes 9.1 ± 0.1 9.0 9.3 1.08 ± 0.02

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 262.2 ± 21.0 250.0 318.4 1.39 ± 0.11
main_levm_MstoreBench 189.3 ± 1.1 187.2 190.5 1.00
pr_revm_MstoreBench 251.8 ± 1.1 250.0 253.8 1.33 ± 0.01
pr_levm_MstoreBench 192.1 ± 6.8 187.9 207.6 1.01 ± 0.04

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 289.6 ± 1.3 288.1 291.6 1.22 ± 0.02
main_levm_Push 246.2 ± 19.9 235.0 295.5 1.04 ± 0.09
pr_revm_Push 291.1 ± 3.2 288.8 299.5 1.23 ± 0.02
pr_levm_Push 237.1 ± 3.3 234.6 244.2 1.00

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 167.5 ± 3.8 163.3 176.5 1.70 ± 0.04
main_levm_SstoreBench_no_opt 98.6 ± 0.8 97.8 100.5 1.00
pr_revm_SstoreBench_no_opt 167.9 ± 2.2 163.1 171.8 1.70 ± 0.03
pr_levm_SstoreBench_no_opt 98.7 ± 1.8 97.8 103.9 1.00 ± 0.02

@github-actions

Copy link
Copy Markdown

🤖 Claude Code Review

Now I have a complete view of the diff. Let me write up the review.


PR Review: chore(l1,l2): make workspace crates publishable to crates.io

Overview

This PR adds all the metadata, versioned dependency entries, and a publish CI workflow needed to release 16 crates to crates.io. The main changes are: metadata additions across Cargo.toml files, version pinning of intra-workspace deps, removal of orphaned optional dependencies from ethrex-blockchain, vendoring of an include_str!-referenced genesis fixture into the RPC crate, and the new publish workflow.


Correctness

Version consistency — no central source of truth

version = "17.0.0" is now written in at least 16 Cargo.toml files (workspace root and several inline path deps). There is no version.workspace = true usage; each entry is a standalone literal. A version bump requires a grep-and-replace across every one of these files, and there is no check that prevents them from drifting apart. If ethrex-rlp is patched to 17.0.1 but its consumers in [workspace.dependencies] still say version = "17.0.0", the build stays green locally (path wins), but the published tarball for those consumers will resolve against 17.0.0 from crates.io, silently pulling an older version. Consider either documenting this footgun or consolidating with a workspace-level [package] version inherited by all publish-set crates.

Vendored genesis file — silent drift risk

crates/networking/rpc/fixtures/l1_genesis.json is stated to be a "byte-identical copy" of fixtures/genesis/l1.json, but there is no CI assertion enforcing this. If the canonical file is updated for new test configuration, the vendored copy silently diverges, meaning ethrex-rpc's tests (compiled into the library tarball) will silently run against stale genesis data. A diff -q step in the existing test CI job, or a #[test] that checks a hash of the embedded string, would catch this.


Workflow (publish.yml)

Fragile "already exists" detection (publish.yml, line 64)

if echo "$OUTPUT" | grep -q "already exists"; then

This relies on the precise phrasing of crates.io's error message. If crates.io ever changes the message (e.g. "crate already published" or "version already uploaded"), the guard breaks and the workflow fails on an idempotent re-run instead of skipping. Matching against the exit code returned by cargo publish when the specific "already uploaded" condition occurs is more robust, or at minimum the grep pattern should be anchored loosely enough to survive minor rewording.

sleep 20 is a heuristic with no fallback

The comment acknowledges this is a best-effort propagation buffer. Under registry load, 20 seconds may not be enough for a newly indexed crate to be visible to the next cargo publish that depends on it, causing a spurious failure mid-sequence. In practice this is unlikely, but consider bumping to sleep 30 or adding a retry loop for the next publish step rather than a blind sleep.

DRY_RUN evaluation is correct but non-obvious

${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }} — when triggered by release: published, inputs.dry_run is undefined and the whole expression evaluates to false. The shell guard [ "${DRY_RUN:-false}" = "true" ] handles this correctly. No bug, but worth a one-line comment for the next reader.

Positive: Actions pinned by SHA — both actions/checkout and actions-rust-lang/setup-rust-toolchain are pinned to full commit hashes. Good supply-chain hygiene.

No cache step — publishing 16 crates sequentially without build caching may take a long time for a cold runner. Not a correctness issue, but a cache: true in the rust toolchain setup action would speed up re-runs significantly.


Dependency hygiene

Removal of ethrex-guest-program / ethrex-prover — the PR correctly identifies these as orphaned (no [features] entry, no source usage). The Cargo.lock update confirms exactly those two edges are dropped. Clean removal.

ethrex-config not in the publish setethrex-config retains no version field. This is intentional per the PR description. Worth confirming that none of the 16 published crates takes ethrex-config as a non-dev, non-optional dependency, or cargo publish would fail on that crate with an unversioned path dep.


Minor / Style

ethrex-sdk-contract-utils description (contract_utils/Cargo.toml):

"Solidity contract compilation and git dependency helpers for the ethrex Ethereum execution client"

"git dependency helpers" will be visible on the crates.io page for a crate that has no git dependencies itself (it is published). The wording describes an internal build concern and is likely to confuse external users. Something like "Solidity contract build utilities for the ethrex L2 SDK" would be clearer.

No keywords or categories fields — not required by crates.io, but they significantly improve discoverability. Worth adding in a follow-up, especially for widely-consumed crates like ethrex-common, ethrex-rlp, and ethrex-levm.


Summary

The structural approach is sound and the described validation (cargo package --no-verify --list passing for all 16, cargo check clean) is the right bar before merging. The main actionable items are:

  • Document or mitigate the version-drift risk across the 16 hardcoded 17.0.0 entries.
  • Add a CI guard (or at minimum a documented procedure) to keep the vendored genesis in sync with the original.
  • Harden the already exists grep in the publish workflow against message format changes.
  • Verify ethrex-config is not a non-optional dependency of any publish-set crate.

Automated review by Claude (Anthropic) · sonnet · custom prompt

@github-actions

Copy link
Copy Markdown

🤖 Codex Code Review

  1. publish.yml weakens the only manual preflight path too much. cargo package --no-verify --list only lists files; it does not verify that the packaged crate still builds or that publish-time dependency metadata is correct. That means workflow_dispatch can succeed while the real release path at publish.yml still fails on exactly the class of problems this PR is trying to prevent. I’d switch the dry-run branch to cargo publish --dry-run -p "$c" or at least cargo package -p "$c" without --no-verify.

  2. test_utils.rs and test_utils.rs embed the same 49.7 KB genesis JSON twice via two separate include_str! calls. Since test_utils is compiled as a normal public module, this is unnecessary binary bloat for every ethrex-rpc build, not just tests. setup_store() should parse TEST_GENESIS instead of re-including the file.

No consensus, EVM, gas-accounting, trie, or RLP correctness issues stood out here; this PR is mostly packaging/release plumbing. I wasn’t able to run a full cargo package/publish --dry-run validation in this environment because the local Cargo/Rustup home is read-only, so the review is based on manifest/workflow inspection rather than an executed publish simulation.


Automated review by OpenAI Codex · gpt-5.4 · custom prompt

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Prepares the ethrex Rust workspace to be publishable on crates.io by adding required crate metadata, making internal dependencies publish-compatible, removing an unpublishable dependency edge, vendoring an RPC fixture into the crate, and introducing an automated publish workflow that releases crates in dependency order.

Changes:

  • Add crates.io-required metadata (description, workspace-inherited repository) across the publish set.
  • Add versions alongside path for internal dependencies so cargo publish accepts them; remove unused optional prover-related deps from ethrex-blockchain.
  • Add a GitHub Actions workflow to publish the crate set in topological (dependency) order; vendor the L1 genesis JSON fixture into ethrex-rpc.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/vm/levm/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/vm/Cargo.toml Adds crates.io metadata and makes ethrex-levm dependency publishable by adding a version.
crates/storage/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/networking/rpc/test_utils.rs Repoints include_str! to a vendored in-crate fixture path for publishable packaging.
crates/networking/rpc/fixtures/l1_genesis.json Adds the vendored L1 genesis fixture to ship within the ethrex-rpc crate tarball.
crates/networking/rpc/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/networking/p2p/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/l2/storage/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/l2/sdk/contract_utils/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/l2/sdk/Cargo.toml Adds crates.io metadata and makes ethrex-sdk-contract-utils dependency publishable by adding a version (deps and build-deps).
crates/l2/networking/rpc/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/l2/common/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/common/trie/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/common/rlp/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/common/crypto/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/common/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/blockchain/metrics/Cargo.toml Adds crates.io metadata (description, repository.workspace).
crates/blockchain/Cargo.toml Adds crates.io metadata, adds version to ethrex-metrics path dep, and removes unused optional unpublishable deps.
Cargo.toml Adds workspace.package.repository and adds versions to workspace internal dependencies for publishability.
Cargo.lock Removes the dropped prover-related dependencies from the resolved graph.
.github/workflows/publish.yml Adds a release-triggered workflow to publish the crate set to crates.io in dependency order (supports dry-run on manual dispatch).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/networking/rpc/test_utils.rs Outdated
Comment on lines 187 to 188
let genesis: &str = include_str!("fixtures/l1_genesis.json");
let genesis: Genesis = serde_json::from_str(genesis).expect("Fatal: test config is invalid");
@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR prepares 16 ethrex crates for publication to crates.io by adding required description and repository metadata, versioning inter-crate path dependencies at 17.0.0, removing unpublishable ethrex-guest-program/ethrex-prover optional edges from ethrex-blockchain, vendoring the l1_genesis.json test fixture into crates/networking/rpc/ so the ethrex-rpc tarball is self-contained, and adding a publish GitHub Actions workflow.

  • Metadata & versioning: All 16 crates in the publish set gain description and repository.workspace = true; version = "17.0.0" is added alongside each path entry in [workspace.dependencies] and the three inline path deps.
  • Fixture vendoring: crates/networking/rpc/fixtures/l1_genesis.json is a byte-identical copy of the workspace-root fixtures/genesis/l1.json; test_utils.rs is updated to reference the local copy so the published tarball is self-contained.
  • Publish workflow: .github/workflows/publish.yml iterates through crates in topological order on release: published, with an idempotency check on "already exists" and a workflow_dispatch dry-run path.

Confidence Score: 4/5

Safe to merge — the Cargo metadata changes are mechanical and correct, the publish workflow is well-structured with idempotency handling, and the test_utils.rs change only re-points include_str! paths to the new vendored copy.

The changes are largely additive (metadata, versioned deps, a new workflow file, a vendored fixture). The one ongoing maintenance risk is the duplicated genesis fixture, which has no automated sync check and could silently diverge in future PRs — but it does not break anything today. The workflow logging issues (swallowed success output, unclosed group on hard failure) are cosmetic and do not affect correctness.

.github/workflows/publish.yml and crates/networking/rpc/fixtures/l1_genesis.json (paired with fixtures/genesis/l1.json) are the two areas worth a second look before the first real publish run.

Important Files Changed

Filename Overview
.github/workflows/publish.yml New workflow publishing 16 crates in topological order; publish success output is captured but silently discarded, and ::endgroup:: is not emitted before exit 1.
crates/networking/rpc/test_utils.rs Two include_str! calls updated to reference the vendored local fixture; change is correct but creates a second copy of the genesis file that can silently drift from the workspace-root original.
crates/networking/rpc/fixtures/l1_genesis.json New vendored copy of fixtures/genesis/l1.json; content appears correct and byte-identical to the original.
Cargo.toml Adds repository to [workspace.package] and adds version = "17.0.0" to all 16 publish-set workspace dependencies alongside existing path entries; correct pattern for Cargo path+version dual-mode.
crates/blockchain/Cargo.toml Adds description, repository, and inline version for ethrex-metrics; removes orphaned optional ethrex-guest-program and ethrex-prover entries that blocked publishing.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Trigger: release/published or workflow_dispatch] --> B{DRY_RUN?}
    B -- true --> C[cargo package --no-verify --list]
    B -- false --> D[Loop over 16 crates in topo order]
    D --> E[cargo publish -p crate]
    E -- success --> F[sleep 20s]
    F --> G{More crates?}
    G -- yes --> D
    G -- no --> H[Done]
    E -- failure --> I{output contains 'already exists'?}
    I -- yes --> J[Skip, continue]
    J --> G
    I -- no --> K[exit 1]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Trigger: release/published or workflow_dispatch] --> B{DRY_RUN?}
    B -- true --> C[cargo package --no-verify --list]
    B -- false --> D[Loop over 16 crates in topo order]
    D --> E[cargo publish -p crate]
    E -- success --> F[sleep 20s]
    F --> G{More crates?}
    G -- yes --> D
    G -- no --> H[Done]
    E -- failure --> I{output contains 'already exists'?}
    I -- yes --> J[Skip, continue]
    J --> G
    I -- no --> K[exit 1]
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
.github/workflows/publish.yml:56-67
The `cargo publish` output is captured into `OUTPUT` and only echoed on failure. When publishing succeeds the log is silent, making it impossible to audit what version was uploaded or to diagnose slow runs without re-running. Printing it unconditionally before the sleep preserves the audit trail.

```suggestion
              OUTPUT=$(cargo publish -p "$c" 2>&1) || {
                echo "$OUTPUT"
                if echo "$OUTPUT" | grep -q "already exists"; then
                  echo "$c already published, skipping"
                  echo "::endgroup::"
                  continue
                fi
                exit 1
              }
              echo "$OUTPUT"
              # cargo >=1.66 blocks until the version is in the index;
              # a short settle covers any registry propagation lag.
              sleep 20
```

### Issue 2 of 3
.github/workflows/publish.yml:63
**`::endgroup::` not emitted before `exit 1`**

When `cargo publish` fails with an error that is not "already exists", the script hits `exit 1` without first calling `echo "::endgroup::"`. In the GitHub Actions UI this leaves the log group unclosed, causing subsequent workflow output to appear nested under the failed crate's group. Adding `echo "::endgroup::"` immediately before `exit 1` keeps the log structure clean.

### Issue 3 of 3
crates/networking/rpc/test_utils.rs:50
**Vendored genesis fixture can silently drift from the workspace original**

`crates/networking/rpc/fixtures/l1_genesis.json` is a byte-identical copy of `fixtures/genesis/l1.json`. If the workspace-root file is updated (e.g. a new hardfork time or a contract address change) without also updating the vendored copy, `test_utils.rs` will compile against the stale fixture and RPC tests will run against a different genesis than the rest of the suite — with no compile error or CI failure to surface the mismatch. Consider adding a CI step (e.g. `diff -q`) that asserts the two files are identical.

Reviews (1): Last reviewed commit: "chore(l1,l2): make workspace crates publ..." | Re-trigger Greptile

Comment on lines +56 to +67
OUTPUT=$(cargo publish -p "$c" 2>&1) || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "$c already published, skipping"
echo "::endgroup::"
continue
fi
exit 1
}
# cargo >=1.66 blocks until the version is in the index;
# a short settle covers any registry propagation lag.
sleep 20

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The cargo publish output is captured into OUTPUT and only echoed on failure. When publishing succeeds the log is silent, making it impossible to audit what version was uploaded or to diagnose slow runs without re-running. Printing it unconditionally before the sleep preserves the audit trail.

Suggested change
OUTPUT=$(cargo publish -p "$c" 2>&1) || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "$c already published, skipping"
echo "::endgroup::"
continue
fi
exit 1
}
# cargo >=1.66 blocks until the version is in the index;
# a short settle covers any registry propagation lag.
sleep 20
OUTPUT=$(cargo publish -p "$c" 2>&1) || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "$c already published, skipping"
echo "::endgroup::"
continue
fi
exit 1
}
echo "$OUTPUT"
# cargo >=1.66 blocks until the version is in the index;
# a short settle covers any registry propagation lag.
sleep 20
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 56-67

Comment:
The `cargo publish` output is captured into `OUTPUT` and only echoed on failure. When publishing succeeds the log is silent, making it impossible to audit what version was uploaded or to diagnose slow runs without re-running. Printing it unconditionally before the sleep preserves the audit trail.

```suggestion
              OUTPUT=$(cargo publish -p "$c" 2>&1) || {
                echo "$OUTPUT"
                if echo "$OUTPUT" | grep -q "already exists"; then
                  echo "$c already published, skipping"
                  echo "::endgroup::"
                  continue
                fi
                exit 1
              }
              echo "$OUTPUT"
              # cargo >=1.66 blocks until the version is in the index;
              # a short settle covers any registry propagation lag.
              sleep 20
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

echo "::endgroup::"
continue
fi
exit 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 ::endgroup:: not emitted before exit 1

When cargo publish fails with an error that is not "already exists", the script hits exit 1 without first calling echo "::endgroup::". In the GitHub Actions UI this leaves the log group unclosed, causing subsequent workflow output to appear nested under the failed crate's group. Adding echo "::endgroup::" immediately before exit 1 keeps the log structure clean.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/publish.yml
Line: 63

Comment:
**`::endgroup::` not emitted before `exit 1`**

When `cargo publish` fails with an error that is not "already exists", the script hits `exit 1` without first calling `echo "::endgroup::"`. In the GitHub Actions UI this leaves the log group unclosed, causing subsequent workflow output to appear nested under the failed crate's group. Adding `echo "::endgroup::"` immediately before `exit 1` keeps the log structure clean.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread crates/networking/rpc/test_utils.rs Outdated
// Base price for each test transaction.
pub const BASE_PRICE_IN_WEI: u64 = 10_u64.pow(9);
pub const TEST_GENESIS: &str = include_str!("../../../fixtures/genesis/l1.json");
pub const TEST_GENESIS: &str = include_str!("fixtures/l1_genesis.json");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Vendored genesis fixture can silently drift from the workspace original

crates/networking/rpc/fixtures/l1_genesis.json is a byte-identical copy of fixtures/genesis/l1.json. If the workspace-root file is updated (e.g. a new hardfork time or a contract address change) without also updating the vendored copy, test_utils.rs will compile against the stale fixture and RPC tests will run against a different genesis than the rest of the suite — with no compile error or CI failure to surface the mismatch. Consider adding a CI step (e.g. diff -q) that asserts the two files are identical.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/networking/rpc/test_utils.rs
Line: 50

Comment:
**Vendored genesis fixture can silently drift from the workspace original**

`crates/networking/rpc/fixtures/l1_genesis.json` is a byte-identical copy of `fixtures/genesis/l1.json`. If the workspace-root file is updated (e.g. a new hardfork time or a contract address change) without also updating the vendored copy, `test_utils.rs` will compile against the stale fixture and RPC tests will run against a different genesis than the rest of the suite — with no compile error or CI failure to surface the mismatch. Consider adding a CI step (e.g. `diff -q`) that asserts the two files are identical.

How can I resolve this? If you propose a fix, please make it concise.

Add a workflow-level `permissions: contents: read` block so the publish
workflow does not run with the default broad GITHUB_TOKEN scopes. Publishing
uses CARGO_REGISTRY_TOKEN; the GITHUB_TOKEN only needs read access for checkout.
Resolves the CodeQL "workflow does not contain permissions" finding.
…vendoring a fixture

Replace the vendored crates/networking/rpc/fixtures/l1_genesis.json copy with a
test-utils Cargo feature (off by default) that gates `pub mod test_utils`.

The published default build of ethrex-rpc no longer compiles test_utils, so its
cross-crate include_str! of fixtures/genesis/l1.json is never evaluated and the
crate publishes without a vendored copy. In-workspace test consumers
(ethrex-l2-rpc, ethrex-test) enable the feature via a dev-dependency on
ethrex-rpc, so the fixture is read from the workspace root as before; the
dev-dependency is stripped from the published manifests.

- Gate `pub mod test_utils` with cfg(any(test, feature = "test-utils")).
- Add the empty test-utils feature to ethrex-rpc.
- Restore the original include_str!("../../../fixtures/genesis/l1.json") path.
- Remove the vendored crates/networking/rpc/fixtures/l1_genesis.json.
- Enable ethrex-rpc/test-utils via [dev-dependencies] in ethrex-l2-rpc and ethrex-test.
@github-actions

Copy link
Copy Markdown

Lines of code report

Total lines added: 1
Total lines removed: 0
Total lines changed: 1

Detailed view
+-------------------------------------+-------+------+
| File                                | Lines | Diff |
+-------------------------------------+-------+------+
| ethrex/crates/networking/rpc/lib.rs | 33    | +1   |
+-------------------------------------+-------+------+

ilitteri added 2 commits June 19, 2026 17:45
ef_tests-engine's engine_ctx.rs imports ethrex_rpc::test_utils in library code,
so after gating test_utils behind the test-utils feature this crate must enable
it. ef_tests-engine is not published, so enabling the feature on the normal
ethrex-rpc dependency is fine. Lives in the separate tooling/ workspace, which
is why the root-workspace check did not catch the break.
…io publishing

Trigger publish.yml on `release: released` instead of `published`. The release
process creates a `vX.Y.Z-rc.W` pre-release first, tests it, then promotes it to
a full release. `published` fires on pre-release creation, which would publish
the crates to crates.io at version X.Y.Z before testing -- and crates.io
versions are immutable, so a failed RC would burn the version. `released` fires
only when a pre-release is promoted to a full release (never on pre-release
creation), so only tested, final releases publish.

Document the crates.io publishing step in docs/developers/release-process.md:
how it triggers, that pre-releases are skipped, the one-time CRATES_IO_TOKEN /
crates-release-prod / crate-name-ownership setup, the dry-run path, and a
troubleshooting entry.
@ilitteri ilitteri enabled auto-merge June 19, 2026 21:44
@ilitteri ilitteri added this pull request to the merge queue Jun 19, 2026
Merged via the queue into main with commit 0dc4f20 Jun 19, 2026
82 of 84 checks passed
@ilitteri ilitteri deleted the publish-crates-to-crates-io branch June 19, 2026 23:12
@github-project-automation github-project-automation Bot moved this from In Review to Done in ethrex_l1 Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client L2 Rollup client

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants