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