@splitfireai / agent / commits / cf210b4

ci: publish to crates.io via trusted publishing

Swap the CARGO_REGISTRY_TOKEN secret for crates.io trusted publishing: the job requests an OIDC token (permissions: id-token: write), rust-lang/crates-io-auth-action exchanges it for a short-lived crates.io token, and cargo publish --locked consumes it. The action revokes the token in its post step, so there is no long-lived credential in repo secrets. The action is pinned to v1.0.5 rather than the v1 the crates.io docs show, because that repo publishes no moving v1 tag and @v1 fails to resolve. Document the one-time crates.io-side setup in the release skill: a trusted publisher must name SplitFireAI / splitfire-agent / release-crates.yml, and the workflow filename is part of the trust claim. Co-Authored-By: SplitFire v1.1.1-acp <noreply@splitfire.ai>

Seto committed Oct 13, 2026 at 13:52 UTC cf210b43759c4e282844d75457d019894cd255bb
2 files changed +36 -2
.agents/skills/release/SKILL.md
+23 -1
@@ -14,7 +14,9 @@ How to cut a `splitfire-agent` release. Releases are driven by git tags. Pushing
14 (darwin-arm64/x64, linux-x64/arm64, windows-x64) and publishes a GitHub release
15 with auto-generated notes and the archives attached.
16 - `.github/workflows/release-crates.yml` — runs `cargo publish --locked` to push
17 - the crate to crates.io (needs the `CARGO_REGISTRY_TOKEN` repo secret).
17 + the crate to crates.io, authenticated by **trusted publishing**: a GitHub OIDC
18 + token is exchanged for a short-lived crates.io token. There is no
19 + `CARGO_REGISTRY_TOKEN` secret to store or rotate.
20
21 Branch model: work lands on `development` (via PRs); `main` is the release branch;
22 every tag points at a commit that is on `main`. Previous tags were plain annotated
@@ -22,6 +24,23 @@ every tag points at a commit that is on `main`. Previous tags were plain annotat
24
25 ---
26
27 +## 0. One-time setup: the trusted publisher
28 +
29 +Trusted publishing needs a publisher registered on crates.io **before** the first
30 +tagged run. The crate must already be on crates.io (a first-ever publish still
31 +needs a manual `cargo login` + `cargo publish`), and you must be a crate owner.
32 +Then: crate page → **Settings → Trusted Publishing → Add → GitHub**:
33 +
34 +- Repository owner: `SplitFireAI`
35 +- Repository name: `splitfire-agent`
36 +- Workflow filename: `release-crates.yml` (the bare filename, not a path)
37 +- Environment: leave blank unless the job also declares `environment:`
38 +
39 +The workflow filename is part of the trust claim, so renaming the workflow breaks
40 +publishing until the crates.io config is updated to match — keep the two in sync.
41 +
42 +---
43 +
44 ## 1. Confirm the version is bumped
45
46 The version bump often already landed with the feature PR — check before assuming
@@ -125,6 +144,9 @@ curl -s https://crates.io/api/v1/crates/splitfire-agent | python3 -Ic \
144 already on crates.io, so no dependency needs publishing first. To publish manually
145 instead of via CI: `cargo login` once (token from crates.io/settings/tokens), then
146 `cargo publish --locked` from the tagged commit.
147 +- `crates-io-auth-action` is pinned to `v1.0.5` deliberately: the action repo has
148 + no moving `v1` tag (only `v1.0.x`), so the `@v1` shown in the crates.io docs
149 + example fails to resolve. Bump the pin when a newer release lands.
150 - If a platform build fails after the tag is pushed, fix the cause and re-run the
151 failed jobs (`gh run rerun <run-id> --failed`); the release-create step tolerates
152 an already-existing release and `--clobber`s assets, so re-runs are safe.
.github/workflows/release-crates.yml
+13 -1
@@ -7,12 +7,24 @@ on:
7 jobs:
8 publish:
9 runs-on: ubuntu-latest
10 + permissions:
11 + id-token: write # required for the OIDC token exchange with crates.io
12 steps:
13 - uses: actions/checkout@v5
14 - uses: dtolnay/rust-toolchain@stable
15 + # Trusted publishing: no long-lived token to store or rotate. This action
16 + # exchanges a GitHub OIDC token for a short-lived crates.io token (valid 30
17 + # minutes) and revokes it in its post step.
18 + #
19 + # It only works if a trusted publisher is configured for this crate on
20 + # crates.io naming SplitFireAI / splitfire-agent / this exact filename
21 + # (release-crates.yml). The workflow filename is part of the trust check,
22 + # so renaming this file breaks publishing until crates.io is updated too.
23 + - uses: rust-lang/crates-io-auth-action@v1.0.5
24 + id: auth
25 # Publishes the version in Cargo.toml to crates.io. crates.io is immutable:
26 # a version can only be published once (then yanked, never replaced), so the
27 # tag's Cargo.toml version must not already be on crates.io.
28 - run: cargo publish --locked
29 env:
18 - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
30 + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}