Release Process
Versioning and release workflow
Where the version lives
The release version has a single source of truth in the root Cargo.toml:
- [workspace.metadata.workspaces].version — authoritative version. The release workflow and tooling read it (e.g. cargo metadata | jq -r '.metadata.workspaces.version').
- [workspace.package].version stays "0.0.0" as a placeholder; cargo-workspaces uses the metadata version when publishing.
Cutting a release
Bump the version under [workspace.metadata.workspaces], merge (e.g. release PR), then the CI workflow:
- Reads the version from cargo metadata (no sed or in-repo string replacement).
- Builds binaries, publishes crates (cargo ws publish), and creates the GitHub release with that version.
Binaries, libraries, and types
- Binaries (merod, meroctl) — displayed version comes from build-time env vars in each crate’s build.rs (MEROD_VERSION, MEROCTL_VERSION), derived from workspace metadata plus git describe/commit.
- Libraries that need a release string (e.g. bundle minRuntimeVersion) expose CALIMERO_RELEASE_VERSION from build.rs, sourced from [workspace.metadata.workspaces].version.
- The Version type and related protocol types live in calimero_primitives::version.
Example metadata bump
version = "0.11.0-rc.7" # current release candidate; was 0.11.0-rc.6
CI workflow
Pushes to master run the main CI pipeline. The release workflow reads the version from root Cargo.toml workspace metadata (cargo metadata), builds merod and meroctl binaries for Linux and macOS, publishes workspace crates with cargo-workspaces, and creates a GitHub release attaching those artifacts.
Clippy gate: The CI checks step runs clippy across the full workspace with warnings treated as errors:
This covers all targets (binaries, libraries, tests, examples) and includes the feature-gated testing harness. Any clippy or rustc warning in any target fails CI. Run this command locally before pushing to avoid failures.
Docker images
The merod image is published to ghcr.io/calimero-network/merod. Images are tagged with the release version; edge tracks the latest master build.
Changelog
Release notes are driven by CHANGELOG.md at the repository root. It follows the Keep a Changelog format and should be updated in the same PR that bumps the workspace version.
The [Unreleased] compare link base tracks the current tip: 0.11.0-rc.7...HEAD. A new [0.11.0-rc.7] diff entry (0.11.0-rc.6...0.11.0-rc.7) was added for this release candidate, along with a [#2855] pull-request reference.
ABI contract check workflow
A dedicated workflow at .github/workflows/abi-contract.yml automates cross-repo ABI compatibility checks in CI.
Triggers
- Push to
master. - Pull requests that touch ABI-related paths (path filters defined in the workflow).
What the job does
- Checks out the core repo. The checkout
reffalls back frompull_request.head.shatogithub.shaso both PR and push events resolve correctly. - Checks out
mero-devtools-jsat the ref controlled by theDEVTOOLS_REFvariable (defaults tomain). - Builds
abi-codegen. - Runs the contract check script against the checked-out devtools.
Status and pinning
The job is non-required and is expected to show red until the matching schema-sync lands in mero-devtools-js. Once the downstream schema update is merged, bump DEVTOOLS_REF from main to a release tag to pin enforcement and prevent future regressions from an uncontrolled main tip.
# Example: pin to a specific devtools release DEVTOOLS_REF: v1.4.0
Purpose
Without this workflow, ABI regressions were only discovered after mero-devtools-js vendored new generated types. The check now runs on every relevant PR so breakage is caught before merging rather than after the downstream tool picks up the change.
E2E op-store completeness reporting (C3 Stage 0)
A CI step named Report unified op-store completeness (C3 Stage 0, observe-only) runs inside the end-to-end workflow after Docker logs are collected. It makes known structural incompleteness in the op-store visible per scenario rather than silent.
What the step does
- Greps
docker-logs/for two marker strings:op_store_incomplete— signals gaps in op-store coverage.op_store_gate_unavailable— signals that the gate-load mechanism itself could not be reached.
- Classifies the result into one of three outcomes:
- Gaps found: emits a
::notice::annotation with the count of incomplete markers. - Gate-load failures only (no gap markers): emits an
UNVERIFIEDnotice — the step explicitly does not claim the store is clean. - No markers of either kind: prints a confirmed-clean message.
- Gaps found: emits a
Observe-only behaviour
The step never fails the job. It is purely informational at C3 Stage 0, giving each subsequent stage a visible baseline to track. The intent is that the marker count falls to zero across stages, at which point the step becomes a hard gate at C3 Stage 4.
Why it was added
Previously, op-store incompleteness was silent in CI and only surfaced after downstream tooling consumed the data. Surfacing the markers per scenario on every relevant run allows regressions and progress to be observed incrementally without blocking merges during the remediation period.
# Markers the step looks for in docker-logs/ op_store_incomplete op_store_gate_unavailable