From 61fd2b97ab6e3d6c68ab69692ae39f18180ac5ad Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:02:05 -0700 Subject: [PATCH 1/3] ci: automate crates.io + GitHub release publishing via release-plz Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish-crates.yml | 85 +++++++++ RELEASING.md | 42 ++++ release-plz.toml | 274 +++++++++++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 .github/workflows/publish-crates.yml create mode 100644 RELEASING.md create mode 100644 release-plz.toml diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml new file mode 100644 index 0000000000..22f2796fcf --- /dev/null +++ b/.github/workflows/publish-crates.yml @@ -0,0 +1,85 @@ +name: Publish Crates + +on: + push: + branches: [staging] + paths: + - Cargo.toml + - '**/Cargo.toml' + - release-plz.toml + - .github/workflows/publish-crates.yml + workflow_dispatch: + inputs: + dry_run: + description: Check what release-plz would publish without publishing or tagging. + type: boolean + default: false + +concurrency: + group: publish-crates-staging + cancel-in-progress: false + +env: + CARGO_NET_GIT_FETCH_WITH_CLI: true + +jobs: + dry-run: + name: Dry Run + if: github.event_name == 'workflow_dispatch' && inputs.dry_run + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Dry Run release-plz + uses: release-plz/action@v0.5 + with: + command: release + dry_run: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish: + name: Publish + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run) + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Require Staging For Manual Publish + if: github.event_name == 'workflow_dispatch' + shell: bash + run: | + if [ "${{ github.ref_name }}" != "staging" ]; then + echo "::error::Manual crate publishing must run from staging." + exit 1 + fi + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Publish With release-plz + uses: release-plz/action@v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000000..03db4ff558 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,42 @@ +# Releasing + +snarkVM releases are crates.io first. Merging a workspace version-bump PR into `staging` runs `.github/workflows/publish-crates.yml`, publishes unpublished lockstep snarkVM crate versions to crates.io, and creates one GitHub tag and release named `v{version}` from the umbrella `snarkvm` crate. + +Do not publish normal releases with manual `cargo publish`. The workflow uses crates.io Trusted Publishing through GitHub OIDC, so it does not require a long-lived crates.io token. + +This flow is based on ProvableHQ/leo's release-plz publishing flow, with Leo's binary release artifact dispatch removed. + +## Normal Release Flow + +1. Open a PR that bumps the workspace version across all lockstep snarkVM crates. +2. Confirm the version bump is consistent across the lockstep crate set. +3. Merge the PR to `staging`. +4. Let `.github/workflows/publish-crates.yml` publish the crates and create the single `v{version}` tag and GitHub release. + +The `snarkvm-testchain-generator` binary helper is not part of the lockstep release set. + +## Dry Run + +Use the Actions tab to run `Publish Crates` manually with `dry_run` set to `true`. + +The dry run checks what release-plz would publish and reports the planned single tag without publishing crates, creating tags, or creating GitHub releases. + +## Trusted Publishing Setup + +Trusted Publishing is a one-time crates.io setup for each published crate. Configure it for `snarkvm` and each already-published `snarkvm-*` crate: + +1. Open the crate on crates.io. +2. Go to Settings, then Trusted Publishing. +3. Select Add. +4. Set Owner to `ProvableHQ`. +5. Set Repository to `snarkVM`. +6. Set Workflow filename to `publish-crates.yml`. +7. Leave Environment blank. + +This setup is inherently per crate and should be scripted across the crate set. It does not mean release-plz creates per-crate tags at release time; `release-plz.toml` keeps GitHub tags and releases on the umbrella `snarkvm` crate only. + +## New Crate Bootstrap + +Trusted Publishing cannot reserve a brand-new crate name. A new `snarkvm-*` crate needs one initial manual `cargo publish` with a crates.io token before Trusted Publishing can be enabled for that crate. + +After the first publish and Trusted Publishing setup, the normal workflow takes over for later versions. diff --git a/release-plz.toml b/release-plz.toml new file mode 100644 index 0000000000..7b8de7df90 --- /dev/null +++ b/release-plz.toml @@ -0,0 +1,274 @@ +[workspace] +# release-plz defaults to one tag/release per package in a multi-crate workspace. +# snarkVM is lockstep-versioned, so disable tags/releases workspace-wide and +# re-enable them only on the umbrella `snarkvm` crate to get exactly one tag +# and one GitHub release per workspace version. +git_tag_enable = false +git_release_enable = false + +# `version_group` is a package-level release-plz key in action v0.5. +# Assign every lockstep crate to the same group so they always bump together. +[[package]] +name = "snarkvm" +version_group = "snarkvm" +git_tag_enable = true +git_tag_name = "v{{ version }}" +git_release_enable = true +git_release_name = "v{{ version }}" + +[[package]] +name = "snarkvm-algorithms" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-algorithms-cuda" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-account" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-algorithms" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-collections" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-environment" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-environment-witness" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-network" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-program" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-address" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-boolean" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-field" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-group" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-integers" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-scalar" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-circuit-types-string" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-account" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-algorithms" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-collections" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-network" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-network-environment" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-program" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-address" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-boolean" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-field" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-group" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-integers" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-scalar" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-console-types-string" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-curves" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-fields" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-authority" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-block" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-committee" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal-batch-certificate" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal-batch-header" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal-data" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal-subdag" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal-transmission" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-narwhal-transmission-id" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-puzzle" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-puzzle-epoch" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-query" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-store" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-ledger-test-helpers" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-metrics" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-parameters" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-synthesizer" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-synthesizer-error" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-synthesizer-process" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-synthesizer-program" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-synthesizer-snark" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-utilities" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-utilities-derives" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-wasm" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-slipstream-plugin-interface" +version_group = "snarkvm" + +[[package]] +name = "snarkvm-slipstream-plugin-manager" +version_group = "snarkvm" + +# This 0.1.0 binary helper is not part of the lockstep release set. +[[package]] +name = "snarkvm-testchain-generator" +release = false From 93b183e4cb56624fe581a5850793d2d9a6232858 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:08:04 -0700 Subject: [PATCH 2/3] ci: grant pull-requests:read so release-plz can resolve associated PRs Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish-crates.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml index 22f2796fcf..3ed9cb1cfe 100644 --- a/.github/workflows/publish-crates.yml +++ b/.github/workflows/publish-crates.yml @@ -29,6 +29,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + pull-requests: read steps: - name: Checkout uses: actions/checkout@v4 @@ -55,6 +56,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + pull-requests: read id-token: write steps: - name: Checkout From dc565bba409d7f7b5f7d5999eb858197550dc0ec Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Tue, 16 Jun 2026 00:15:43 -0700 Subject: [PATCH 3/3] ci: install lld for publish verification; document tag-collision caveat cargo publish runs verification builds that honor .cargo/config.toml, which forces -fuse-ld=lld on x86_64-unknown-linux-gnu, so the publish runner needs lld installed (matching benchmarks.yml). Also document that release-plz skips a version whose v{version} tag already exists. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/publish-crates.yml | 6 ++++++ RELEASING.md | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml index 3ed9cb1cfe..2e3bb06d04 100644 --- a/.github/workflows/publish-crates.yml +++ b/.github/workflows/publish-crates.yml @@ -79,6 +79,12 @@ jobs: with: toolchain: stable + - name: Install lld + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y lld + - name: Publish With release-plz uses: release-plz/action@v0.5 with: diff --git a/RELEASING.md b/RELEASING.md index 03db4ff558..69683c7370 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -35,6 +35,10 @@ Trusted Publishing is a one-time crates.io setup for each published crate. Confi This setup is inherently per crate and should be scripted across the crate set. It does not mean release-plz creates per-crate tags at release time; `release-plz.toml` keeps GitHub tags and releases on the umbrella `snarkvm` crate only. +## Tag Collisions + +`release-plz release` treats an existing `v{version}` git tag as already released and will skip publishing the umbrella `snarkvm` crate and creating its release for that version, even if crates.io has not yet received it. Before enabling this flow, make sure the next workspace version does not already have a matching upstream tag. If a stale or pre-created `v{version}` tag exists for a version that was never published, remove or migrate it first. + ## New Crate Bootstrap Trusted Publishing cannot reserve a brand-new crate name. A new `snarkvm-*` crate needs one initial manual `cargo publish` with a crates.io token before Trusted Publishing can be enabled for that crate.