From cdc33092401cc11543c74e10ffe1ae8d68f7e28d Mon Sep 17 00:00:00 2001 From: Maksim Kramarenko Date: Fri, 29 May 2026 12:40:06 -0400 Subject: [PATCH 1/2] ci: add release-please versioning pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The binary's `-V` output drifted from the release tag: clap reads CARGO_PKG_VERSION from [workspace.package] version in Cargo.toml, which hand-made tags never updated. role_solana substring-matches `-V` output to detect the deployed version, so the mismatch made Ansible reinstall and restart the proxy on every converge. Adopt release-please to drive versioning from conventional commits and keep Cargo.toml in sync with the tag. Versions are X.Y.Z+qn: a QN-owned semver line with a +qn build-metadata marker (precedence-neutral per semver, but identifies QN builds). release-please's default strategy preserves +qn across bumps, so no per-release Release-As is needed. - release-please-config.json / .release-please-manifest.json: rust type (Cargo.lock + CHANGELOG), generic extra-files updater for the [workspace.package] version (rust type alone doesn't update it in a virtual workspace; see release-please#1170). - .github/workflows/release-please.yml: manages the release PR, tag, and GitHub Release on pushes to main-qn. No build step — role_chain_build builds from the tag. - Remove release.yml: building is role_chain_build's job, and its GITHUB_TOKEN-created tags wouldn't have triggered it anyway. - Document the scheme and conventional-commit PR titles. Bootstrap the first managed release to 0.2.14+qn via the footer below. Release-As: 0.2.14+qn Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/PULL_REQUEST_TEMPLATE.md | 5 +++ .github/workflows/release-please.yml | 28 +++++++++++++++ .github/workflows/release.yml | 39 -------------------- .release-please-manifest.json | 3 ++ CONTRIBUTING.md | 53 +++++++++++++++++++++++++--- Cargo.toml | 2 +- release-please-config.json | 13 +++++++ 7 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/release-please.yml delete mode 100644 .github/workflows/release.yml create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3722b1fb..e989e685 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,6 +10,11 @@ follow up after merge with a cherry-pick onto a branch off `master` and open an upstream PR against `jito-labs/shredstream-proxy:master`. See CONTRIBUTING.md for the full workflow. + +PR TITLE: use a conventional-commit message (e.g. `feat: ...`, `fix: ...`, +`feat!: ...`). PRs are squash-merged and release-please reads the squash +commit (= this title) to compute the next version. A non-conventional title +ships no version bump. See "Versioning & releases" in CONTRIBUTING.md. --> ## Is this change upstreamable? diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 00000000..fec31ca7 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,28 @@ +name: release-please + +# Opens/maintains a release PR on main-qn and, on merge, creates the git tag +# (vX.Y.Z+qn) and GitHub Release. Binaries are NOT built here — role_chain_build +# builds them from the tag — so this workflow only versions, tags, and releases. +# +# The version (including the +qn build-metadata marker) lives in +# [workspace.package] version in Cargo.toml. release-please's default versioning +# strategy preserves build metadata across bumps, so +qn carries through +# automatically (0.2.14+qn -> fix -> 0.2.15+qn -> feat -> 0.3.0+qn). + +on: + push: + branches: [main-qn] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-22.04 + steps: + - uses: googleapis/release-please-action@v4 + with: + target-branch: main-qn + # Picks up release-please-config.json + .release-please-manifest.json + # from the repo root by default. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 90d70517..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: release - -on: - push: - tags: - - 'v*' - -jobs: - release: - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - lfs: true - - - name: Setup Rust - uses: ./.github/actions/setup-rust - - - name: Clippy check - run: cargo clippy --all-features --all-targets --tests -- -D warnings - - - name: Release build - run: cargo build --release --locked --bin jito-shredstream-proxy - - - name: Stage artifact - run: | - cp target/release/jito-shredstream-proxy \ - ./jito-shredstream-proxy-x86_64-unknown-linux-gnu - file ./jito-shredstream-proxy-x86_64-unknown-linux-gnu - ls -lh ./jito-shredstream-proxy-x86_64-unknown-linux-gnu - - - name: Release - uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') - with: - files: | - ./jito-shredstream-proxy-x86_64-unknown-linux-gnu diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..45f2163e --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.2.13" +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e321bf67..f48bc355 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,9 +13,11 @@ is unlikely. | **`main-qn`** | Default branch on the fork. QN's working line. | All day-to-day PRs. | Everything in `master` + QN-specific patches (CI, deploy plumbing, fleet defaults). | | **`master`** | Clean mirror of `jito-labs/shredstream-proxy:master`. | Only branches destined for upstream contribution. | Exactly what's upstream, nothing more. Refreshed via `gh repo sync`. | -The deploy pipeline (`role_chain_build`) builds release tags. Tags are -created from `main-qn`, so the resulting binary always carries the QN -patches. +Releases are managed by [release-please](https://github.com/googleapis/release-please): +merges to `main-qn` accumulate into a release PR, and merging that PR bumps the +version, tags `vX.Y.Z+qn`, and cuts a GitHub Release. The deploy pipeline +(`role_chain_build`) then builds that tag, so the resulting binary always +carries the QN patches. See [Versioning & releases](#versioning--releases). ## What's currently QN-specific @@ -26,6 +28,7 @@ As of this writing, the diff between `main-qn` and `master` is: | `proxy/src/forwarder.rs` + `proxy/src/main.rs` (metric label patch) | Adds `listen_port` and `device` tags to the `shredstream_proxy-receiver_stats` metric so multicast (`device=doublezero1`) and unicast (`device=unicast`) traffic from the same source IP can be attributed separately. Originally needed to measure DoubleZero's marginal contribution to first-arrival shred throughput. Eligible for upstream contribution. | | `.github/workflows/*.yml`, `.github/actions/setup-rust/action.yaml` | Switches CI from Jito's private self-hosted runners (`ubuntu-22.04-16c-64g-public`) to stock `ubuntu-22.04`. Replaces Docker Hub publish steps with direct `cargo build --release`. Uses `actions-rust-lang/setup-rust-toolchain` for the toolchain (reads `rust-toolchain.toml`). QN-specific — would not be appropriate upstream. | | `.github/PULL_REQUEST_TEMPLATE.md`, `CONTRIBUTING.md` | Fork-management policy (this document). QN-specific. | +| `release-please-config.json`, `.release-please-manifest.json`, `.github/workflows/release-please.yml`, `+qn` build metadata in `Cargo.toml` version | Automated, conventional-commit-driven versioning for the fork. The `+qn` build-metadata marker distinguishes QN releases from upstream. QN-specific. | When evaluating whether a new change is upstreamable, the metric label patch is a useful reference for what "broadly useful" looks like. @@ -54,7 +57,49 @@ The active CI workflows on every PR to `main-qn`: | `test` | `cargo test --all-features --locked` | | `build` | `cargo clippy --all-features --all-targets --tests -- -D warnings` and `cargo build --release --locked --bin jito-shredstream-proxy` | -Both must pass before merge. The `release` workflow only runs on `v*` tag pushes — it produces a GitHub Release asset. +Both must pass before merge. The `release-please` workflow runs on pushes to +`main-qn` (not on PRs) and only manages the release PR, tag, and GitHub Release — +it does not build binaries (`role_chain_build` does). See +[Versioning & releases](#versioning--releases). + +## Versioning & releases + +Versions are **`X.Y.Z+qn`** — a QN-owned semver line with a `+qn` +[build-metadata](https://semver.org/#spec-item-10) marker. `+qn` is +precedence-neutral per the semver spec (so `0.2.14+qn` compares equal to +`0.2.14`) but unmistakably marks a QN build. The version lives in one place, +`[workspace.package] version` in the root `Cargo.toml`; clap reads it via +`CARGO_PKG_VERSION`, so `jito-shredstream-proxy -V` prints it verbatim. Keeping +this in sync with the released tag is the whole point of the pipeline — +`role_solana` detects the deployed version by substring-matching `-V` output. + +[release-please](https://github.com/googleapis/release-please) automates the +bump from [conventional commits](https://www.conventionalcommits.org/): + +| Commit type | Bump | Example | +|---|---|---| +| `fix:` | patch | `0.2.14+qn` → `0.2.15+qn` | +| `feat:` | minor | `0.2.14+qn` → `0.3.0+qn` | +| `feat!:` / `BREAKING CHANGE:` | major | `0.2.14+qn` → `1.0.0+qn` | + +release-please's default versioning strategy preserves the `+qn` build metadata +across bumps automatically, so it never needs to be re-added. + +**Because PRs are squash-merged, the PR title must be a conventional-commit +message** — that title becomes the commit release-please reads. Non-conventional +titles are ignored (no version bump). + +The release flow: + +1. Merge PRs to `main-qn` with conventional titles. +2. release-please opens/updates a **release PR** (`chore: release X.Y.Z+qn`) that + bumps `Cargo.toml` + `Cargo.lock` and updates `CHANGELOG.md`. Review the + proposed version there; override it with a `Release-As: X.Y.Z+qn` footer in a + commit if needed. +3. Merging the release PR tags `vX.Y.Z+qn` and cuts a GitHub Release. +4. Bump `jito_shredstream_version` in `role_solana` group_vars to the new + `X.Y.Z+qn` and release `role_solana`; Drone → `role_chain_build` builds the + tag and publishes the binary. ## Branch naming diff --git a/Cargo.toml b/Cargo.toml index cfc4a9ce..d6b99a18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["examples", "jito_protos", "proxy"] resolver = "2" [workspace.package] -version = "0.2.13" +version = "0.2.13" # x-release-please-version description = "Fast path to receive shreds from Jito, forwarding to local consumers. See https://docs.jito.wtf/lowlatencytxnfeed/ for details." authors = ["Jito Team "] homepage = "https://jito.wtf/" diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..3257e1d1 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "rust", + "include-component-in-tag": false, + "packages": { + ".": { + "component": "jito-shredstream-proxy", + "extra-files": [ + { "type": "generic", "path": "Cargo.toml" } + ] + } + } +} From efce9ecdd5a87b7327cfeb8bd10bda060dc9e7c4 Mon Sep 17 00:00:00 2001 From: Maksim Kramarenko Date: Fri, 29 May 2026 13:06:16 -0400 Subject: [PATCH 2/2] ci: guard +qn build metadata against release-please regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address PR review concern that release-please might strip the +qn build metadata on bumps. Verified empirically against release-please 17.6.1 that the default strategy preserves it (fix -> 0.2.15+qn, feat -> 0.3.0+qn, feat! -> 1.0.0+qn): each VersionUpdater re-emits version.build and Version.toString() appends it. The maintainer statement cited in review (release-please#1816) is about auto-generating/incrementing a build number, not about preserving a constant marker — a different feature. Preservation isn't an officially-tested contract, so guard against a future regression instead of relying on it silently: - build.yml: assert [workspace.package] version still carries +qn; the release PR (a pull_request) fails CI loudly if it's ever stripped, rather than shipping a bare version that breaks role_solana's -V substring check. - Seed Cargo.toml + Cargo.lock + manifest at 0.2.13+qn so the marker is present from the start and the guard holds on every PR. - Document the verified behavior and the #1816 distinction. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/build.yml | 13 +++++++++++++ .github/workflows/release-please.yml | 9 +++++++-- .release-please-manifest.json | 2 +- CONTRIBUTING.md | 10 ++++++++-- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29fff893..2ef76760 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,19 @@ jobs: - name: Setup Rust uses: ./.github/actions/setup-rust + # Guard: the [workspace.package] version must keep the +qn build-metadata + # marker. role_solana detects the deployed version by substring-matching + # `-V` output; a bare X.Y.Z would fail that check and make Ansible reinstall + # the proxy on every converge. release-please's default strategy preserves + # build metadata across bumps (verified against release-please 17.x), but + # this fails loudly if a future version ever regresses and strips it. + - name: Assert version carries +qn build metadata + run: | + line="$(grep -E '^version = ".*" # x-release-please-version' Cargo.toml || true)" + echo "workspace.package $line" + echo "$line" | grep -qE '^version = "[0-9]+\.[0-9]+\.[0-9]+\+qn" # x-release-please-version' \ + || { echo "::error file=Cargo.toml::[workspace.package] version is missing the '+qn' build-metadata marker (got: ${line:-}). See CONTRIBUTING.md > Versioning & releases."; exit 1; } + - name: Clippy check run: cargo clippy --all-features --all-targets --tests -- -D warnings diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index fec31ca7..ef09b43c 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -6,8 +6,13 @@ name: release-please # # The version (including the +qn build-metadata marker) lives in # [workspace.package] version in Cargo.toml. release-please's default versioning -# strategy preserves build metadata across bumps, so +qn carries through -# automatically (0.2.14+qn -> fix -> 0.2.15+qn -> feat -> 0.3.0+qn). +# strategy carries build metadata through every bump -- each VersionUpdater +# (Major/Minor/Patch) re-emits version.build and Version.toString() appends it -- +# so +qn is preserved automatically (0.2.14+qn -> fix -> 0.2.15+qn -> feat -> +# 0.3.0+qn). Verified against release-please 17.x. NOTE: release-please does not +# *generate/increment* build metadata (see googleapis/release-please#1816); it +# only preserves the constant +qn we set. The `build` job's "Assert version +# carries +qn" guard fails the release PR loudly if a future version regresses. on: push: diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 45f2163e..c88c4ca7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.2.13" + ".": "0.2.13+qn" } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f48bc355..a6a2f5cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,8 +82,14 @@ bump from [conventional commits](https://www.conventionalcommits.org/): | `feat:` | minor | `0.2.14+qn` → `0.3.0+qn` | | `feat!:` / `BREAKING CHANGE:` | major | `0.2.14+qn` → `1.0.0+qn` | -release-please's default versioning strategy preserves the `+qn` build metadata -across bumps automatically, so it never needs to be re-added. +release-please's default versioning strategy carries the `+qn` build metadata +through every bump (each `VersionUpdater` re-emits `version.build`), so it never +needs to be re-added — verified against release-please 17.x. (Note: release-please +will not *generate or increment* build metadata, only preserve the constant `+qn` +we set; see [#1816](https://github.com/googleapis/release-please/issues/1816).) +The `build` CI job asserts the version still carries `+qn`, so if a future +release-please ever regressed and stripped it, the release PR fails CI rather than +silently shipping a `+qn`-less version. **Because PRs are squash-merged, the PR title must be a conventional-commit message** — that title becomes the commit release-please reads. Non-conventional diff --git a/Cargo.lock b/Cargo.lock index e4d8dfe7..aca36faf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2746,7 +2746,7 @@ dependencies = [ [[package]] name = "jito-protos" -version = "0.2.13" +version = "0.2.13+qn" dependencies = [ "prost 0.13.5", "prost-types 0.13.5", @@ -2757,7 +2757,7 @@ dependencies = [ [[package]] name = "jito-shredstream-proxy" -version = "0.2.13" +version = "0.2.13+qn" dependencies = [ "ahash 0.8.11", "arc-swap", diff --git a/Cargo.toml b/Cargo.toml index d6b99a18..638e7ef4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["examples", "jito_protos", "proxy"] resolver = "2" [workspace.package] -version = "0.2.13" # x-release-please-version +version = "0.2.13+qn" # x-release-please-version description = "Fast path to receive shreds from Jito, forwarding to local consumers. See https://docs.jito.wtf/lowlatencytxnfeed/ for details." authors = ["Jito Team "] homepage = "https://jito.wtf/"