Skip to content

fix(ci): let the crates.io publish bootstrap a crate that does not exist yet - #5

Merged
spacedevin merged 1 commit into
mainfrom
fix/crates-publish-token-fallback
Aug 3, 2026
Merged

fix(ci): let the crates.io publish bootstrap a crate that does not exist yet#5
spacedevin merged 1 commit into
mainfrom
fix/crates-publish-token-fallback

Conversation

@spacedevin

Copy link
Copy Markdown
Owner

What happened

v1.2.1 promoted cleanly and @spacedevin/deck@1.2.1 reached npm, but the crate publish failed:

Authenticate to crates.io (Trusted Publishing)
Error: Failed to retrieve token from Cargo registry. Status: 400.
Error: No Trusted Publishing config found for repository `spacedevin/deck`.

Trusted Publishing config is attached to a crate you already own, so it can't publish a crate that has never been published — there's nothing to attach the config to. The workflow only had that path, which means the one publish it couldn't do was the first one: the one that claims the name.

Everything up to auth worked, so this is the only thing between here and deckfile existing:

✓ Emit the crate        ✓ Test the emitted crate        ✗ Authenticate

Fix

A CARGO_REGISTRY_TOKEN fallback, used only to bootstrap:

  1. Add CARGO_REGISTRY_TOKEN as a repo secret
  2. Release once → claims deckfile
  3. Configure Trusted Publishing on crates.io (deckfile → Settings → Trusted Publishing → owner spacedevin, repo deck, workflow crates-release.yml)
  4. Delete the secret — the workflow falls back to OIDC on its own

So the long-lived token exists only for as long as the bootstrap takes, and the steady state matches npm-release.yml: OIDC, no stored token.

One subtlety worth calling out

The token check can't be written the obvious way. The secrets context is not available in a step-level if — only in env. Gating directly on secrets.CARGO_REGISTRY_TOKEN == '' would have silently evaluated as empty and always taken the OIDC path, i.e. it would have looked correct and never worked. So presence is detected in a step that reads the secret through env and writes a step output, which the if then gates on.

Also fails with a message naming both options, instead of whatever cargo reports about a missing credential.

Note

Trusted Publishing is now the steady-state path, not the only one — worth knowing if you'd rather just keep the token, though deleting it after bootstrap is the stronger posture.

…ist yet

The v1.2.1 release failed to publish deckfile:

  Status: 400. No Trusted Publishing config found for repository `spacedevin/deck`.

Trusted Publishing config is attached to a crate you already own, so it cannot
publish a crate that has never been published — there is nothing to attach the
config to. The workflow only had that path, so the very first publish, the one
that claims the name, was the one case it could not do.

Adds a CARGO_REGISTRY_TOKEN fallback used only to bootstrap: set the secret,
release once to claim `deckfile`, configure Trusted Publishing, then delete the
secret and this reverts to OIDC on its own. The long-lived token exists only for
as long as the bootstrap takes, which keeps the steady state the same as
npm-release.yml's.

The token presence is detected in a step that reads it through `env` and writes
an output, because the `secrets` context is not available in a step-level `if` —
gating directly on `secrets.CARGO_REGISTRY_TOKEN` there would have silently
evaluated empty and always taken the OIDC path.

Also fails with an actionable message naming both options rather than whatever
cargo says about a missing credential.
@spacedevin
spacedevin merged commit ca0e7b3 into main Aug 3, 2026
5 checks passed
@spacedevin
spacedevin deleted the fix/crates-publish-token-fallback branch August 3, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant