From b59f76792a9359ca6405c2e3236666f7676484de Mon Sep 17 00:00:00 2001 From: Chetan Conikee Date: Mon, 13 Jul 2026 16:32:03 -0700 Subject: [PATCH] ci: publish crates.io from the same release tag (one go: binaries + brew + crates) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a dist custom publish job so a version tag ships everything in one pipeline: GitHub binaries, the Homebrew formula, AND every public crate to crates.io. - dist-workspace.toml: publish-jobs = ["homebrew", "./publish-crates"]. - .github/workflows/publish-crates.yml: a workflow_call job that runs `cargo publish --workspace --locked` (cargo resolves the dependency order and skips the publish=false crates: bench, xtask, edge-worker, tmux). Gated on non-prerelease; needs the CARGO_REGISTRY_TOKEN secret. - release.yml: regenerated by `dist generate` — announce now waits on the crates job (only new wiring; no target/version churn). - justfile: a `just publish` recipe as the manual equivalent. Verified: `dist generate --check` is clean; `cargo publish -p waggle-tree/-p waggle-core --dry-run` package and verify (new crate waggle-tree included). Prerequisite (one-time): add a CARGO_REGISTRY_TOKEN repo secret (a crates.io API token owned by a crate owner — the crates are owned by conikeec, currently at 0.4.0 on crates.io). Until then this job fails while the rest of the release still ships. The next tag after the token is set publishes crates.io too. --- .github/workflows/publish-crates.yml | 38 ++++++++++++++++++++++++++++ .github/workflows/release.yml | 17 ++++++++++++- dist-workspace.toml | 6 ++++- justfile | 8 ++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/publish-crates.yml diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml new file mode 100644 index 0000000..a016c89 --- /dev/null +++ b/.github/workflows/publish-crates.yml @@ -0,0 +1,38 @@ +# A dist custom publish job: publishes every public crate to crates.io as part of +# the same release the binaries and Homebrew formula ship from. dist invokes this +# in the publish phase (only on a real, non-prerelease tag) and passes the release +# `plan`. cargo publish --workspace resolves the dependency order itself and skips +# `publish = false` crates (bench, xtask, edge-worker, tmux). +# +# Requires a CARGO_REGISTRY_TOKEN repo secret (a crates.io API token owned by a +# crate owner). Without it, this job fails while the rest of the release still +# ships — crates.io simply stays a release behind until the token is present. +name: Publish to crates.io + +on: + workflow_call: + inputs: + plan: + required: true + type: string + secrets: + CARGO_REGISTRY_TOKEN: + required: true + +jobs: + crates-io: + runs-on: ubuntu-latest + # Never push a prerelease tag (v1.2.3-rc.1) to crates.io. + if: ${{ !fromJson(inputs.plan).announcement_is_prerelease }} + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + steps: + - uses: actions/checkout@v5 + with: + submodules: recursive + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Publish public crates (dependency-ordered) + # --workspace publishes each publishable crate in topological order, + # waiting for the index between crates; --locked pins the resolved graph. + run: cargo publish --workspace --locked diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 10aab46..272bbaf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -324,15 +324,30 @@ jobs: done git push + custom-publish-crates: + needs: + - plan + - host + if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} + uses: ./.github/workflows/publish-crates.yml + with: + plan: ${{ needs.plan.outputs.val }} + secrets: inherit + # publish jobs get escalated permissions + permissions: + "id-token": "write" + "packages": "write" + announce: needs: - plan - host - publish-homebrew-formula + - custom-publish-crates # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! - if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} + if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') && (needs.custom-publish-crates.result == 'skipped' || needs.custom-publish-crates.result == 'success') }} runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/dist-workspace.toml b/dist-workspace.toml index 2b0bdee..cece5ba 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -14,7 +14,11 @@ installers = ["shell", "homebrew"] # absent, the homebrew publish job fails while the shell installer and # binaries still ship. tap = "modiqo/homebrew-tap" -publish-jobs = ["homebrew"] +# Publish everything from one tag: the Homebrew formula, and every public crate +# to crates.io (the ./publish-crates custom job below). The crates job needs a +# CARGO_REGISTRY_TOKEN repo secret; without it that job fails while the binaries, +# shell installer, and Homebrew formula still ship. +publish-jobs = ["homebrew", "./publish-crates"] # Target platforms to build apps for (Rust target-triple syntax) targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Path that installers should place binaries in diff --git a/justfile b/justfile index bd58cd8..e9d0db8 100644 --- a/justfile +++ b/justfile @@ -8,6 +8,14 @@ dev-install: cargo install --path crates/waggle-cli --locked --force -waggle daemon restart +# Publish every public crate to crates.io in dependency order. This is the manual +# equivalent of what CI does automatically: pushing a version tag (`v0.5.0`) runs +# the release pipeline, which ships the binaries, the Homebrew formula, AND these +# crates in one go. Needs a crates.io token (`cargo login`) locally; CI uses the +# CARGO_REGISTRY_TOKEN secret. +publish: + cargo publish --workspace --locked + # Run the full-lifecycle demo (docs/guide/06) against a throwaway store demo: bash scripts/demo.sh