Release #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| # Publish the workspace's published crates to crates.io in dependency | |
| # order when a `v*` tag is pushed. Uses crates.io Trusted Publishing | |
| # (OIDC) — no long-lived `CARGO_REGISTRY_TOKEN` secret required. | |
| # | |
| # Setup checklist (one-time): | |
| # 1. Register each crate on crates.io as a Trusted Publisher | |
| # bound to this repo + this workflow file + the `crates-io` | |
| # environment below. | |
| # 2. Create a GitHub Environment named `crates-io` (Settings → | |
| # Environments) with required reviewers. The environment | |
| # gate is what makes a release require a human "approve" | |
| # click — without it, anyone with push access to `main` can | |
| # trigger a publish by pushing a tag. | |
| # | |
| # Release flow: | |
| # 1. Bump the workspace version in the root `Cargo.toml`. | |
| # 2. Update `CHANGELOG.md`. | |
| # 3. `git tag v0.1.0 && git push origin v0.1.0`. | |
| # 4. Approve the workflow run in the `crates-io` environment. | |
| on: | |
| push: | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" | |
| - "v[0-9]+.[0-9]+.[0-9]+-*" | |
| # Manual dispatch is useful for re-runs or for releasing without a | |
| # fresh tag (e.g. retrying after a yank). | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "If true, run `cargo publish --dry-run` instead of publishing." | |
| type: boolean | |
| default: false | |
| # Trusted publishing exchanges the workflow's OIDC token for a | |
| # short-lived crates.io API token. `id-token: write` is required to | |
| # mint the OIDC token; `contents: read` lets us check out the repo. | |
| permissions: | |
| id-token: write | |
| contents: read | |
| jobs: | |
| publish: | |
| name: Publish to crates.io | |
| runs-on: ubuntu-latest | |
| environment: crates-io | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install Rust toolchain (MSRV 1.90) | |
| uses: dtolnay/rust-toolchain@1.90.0 | |
| with: | |
| components: rustfmt, clippy | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Sanity gate — fmt, clippy, test, conformance run (best-effort) | |
| # The full CI already validates these on every push to main; | |
| # this step re-runs the cheap subset on the tagged commit so | |
| # we don't publish bytes that differ from what CI validated. | |
| run: | | |
| cargo fmt --all -- --check | |
| cargo clippy --workspace --all-targets --all-features --locked -- -D warnings | |
| cargo test --workspace --all-features --locked | |
| - name: Authenticate to crates.io via OIDC | |
| id: auth | |
| uses: rust-lang/crates-io-auth-action@v1 | |
| # Crates publish in dependency order; each `cargo publish` does | |
| # its own verification build against the freshly-uploaded prior | |
| # crate. We sleep a short interval between publications to let | |
| # the crates.io index propagate so the next crate's verify | |
| # build can resolve the dep. | |
| # Publish every crate in dependency order. Idempotent: a version already | |
| # on crates.io is skipped, so a re-run after a partial failure (e.g. a | |
| # missing per-crate Trusted Publisher) publishes only what's left instead | |
| # of erroring on the already-uploaded ones. Each real publish runs its own | |
| # verify build against the freshly-uploaded prior crate, so we sleep to let | |
| # the crates.io index propagate before the next one resolves it. | |
| - name: Publish crates (dependency order, idempotent) | |
| run: | | |
| set -euo pipefail | |
| DRY_RUN="${{ github.event.inputs.dry_run }}" | |
| UA="vgi-rpc-release (hello@query.farm)" | |
| for crate in vgi-rpc-macros vgi-rpc vgi-rpc-client vgi-rpc-s3 vgi-rpc-gcs; do | |
| ver=$(cargo metadata --no-deps --format-version 1 \ | |
| | jq -r --arg c "$crate" '.packages[] | select(.name==$c) | .version') | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "::group::dry-run $crate $ver" | |
| cargo publish --dry-run -p "$crate" --locked | |
| echo "::endgroup::" | |
| continue | |
| fi | |
| code=$(curl -s -o /dev/null -w '%{http_code}' -H "User-Agent: $UA" \ | |
| "https://crates.io/api/v1/crates/$crate/$ver") | |
| if [ "$code" = "200" ]; then | |
| echo "$crate $ver already on crates.io — skipping" | |
| continue | |
| fi | |
| echo "Publishing $crate $ver" | |
| cargo publish -p "$crate" --locked | |
| echo "Waiting 30s for crates.io index propagation..." | |
| sleep 30 | |
| done | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} | |
| - name: Summarise | |
| if: success() | |
| run: | | |
| echo "Published from tag ${GITHUB_REF_NAME} at commit ${GITHUB_SHA}" | |
| echo "Crates released:" | |
| echo " - https://crates.io/crates/vgi-rpc-macros" | |
| echo " - https://crates.io/crates/vgi-rpc" | |
| echo " - https://crates.io/crates/vgi-rpc-client" | |
| echo " - https://crates.io/crates/vgi-rpc-s3" | |
| echo " - https://crates.io/crates/vgi-rpc-gcs" |