Repository navigation
ci: publish rs-materials via crates.io trusted publishing (OIDC) - #245
Merged
Merged
Conversation
The v0.3.0 publish failed with `403 authentication failed`: the `CARGO_REGISTRY_TOKEN` secret was minted 2026-03-24 and is no longer valid. Rotating it would buy another finite window and another silent expiry; crates.io has supported trusted publishing since 2025-07, and this repo already publishes to PyPI that way. - `rust-lang/crates-io-auth-action@v1` exchanges the job's GitHub OIDC identity for a crates.io token valid ~30 min, revoked in the action's post step. No secret to rotate, nothing to expire. - New `crates-io` GitHub environment, mirroring `pypi` in release.yml. A trusted publisher can be scoped to an environment, and it gives one place to add an approval rule without editing the workflow. - `--locked` replaces `--allow-dirty`. `cargo publish` refreshes the crates.io index, which can rewrite the tracked `mat-rs/Cargo.lock` and trip cargo's own dirty-tree check — that was the v0.2.0 failure the `--allow-dirty` fix silenced. Silencing the check would also have silenced a genuinely modified tree. `--locked` removes the cause and keeps the check. Also applied to the `test` job so a stale lockfile fails before the publish gate rather than after it. - `workflow_dispatch` on both publish workflows. Re-running a failed tag run replays the workflow file as it was at that tag, so a fix to the workflow only takes effect on a fresh run; without dispatch the only retry path is deleting and re-pushing the tag. Requires a one-time trusted-publisher entry at https://crates.io/crates/rs-materials/settings naming MorePET/mat, `release-rs-materials.yml`, and environment `crates-io`. The stale `CARGO_REGISTRY_TOKEN` secret should be deleted once a publish succeeds. Claude-Session: https://claude.ai/code/session_01PuXPyTpjcG1B65fpaz1jCX
Review of #245 found four real gaps: - Trusted Publishing authenticates the workflow, not the ref. Combined with `workflow_dispatch` that let any branch publish whatever its manifest claimed. Both environments now carry a deployment branch policy: `pypi` allows `main` + `v*`, `crates-io` allows `main` + `rs-materials/v*`. `main` is allowed only because it is the workflow-file-fix retry path. - Job-level `permissions` replaces the workflow-level block rather than merging, so the publish job had `contents: none`. Checkout worked anyway via anonymous clone — but only because the repo is public. Restated `contents: read` explicitly. - RELEASE_PROCESS.md asserted no long-lived token is in repo secrets; the dead `CARGO_REGISTRY_TOKEN` is still there until a publish succeeds. Reworded to what is true — no publishing job reads one — and noted the pending deletion. - The retry recipe said `--ref main`, which only publishes the tagged version while main still points at the release commit. `--ref <tag>` is the general case; `--ref main` is the exception for a broken workflow file, and the doc now separates the two. Claude-Session: https://claude.ai/code/session_01PuXPyTpjcG1B65fpaz1jCX
Review blocker: `publish-mcp.yml` also deploys to the `pypi` environment, and the branch policy added in b57b78f allowed only `main` and `v*`. Deployment-branch patterns do not let `*` cross `/`, so the next `pymat-mcp/v*` tag would have built green and then failed with "Branch not allowed to deploy" — a workflow that was never opened in the diff, broken by a policy meant to protect a different one. - Added tag pattern `pymat-mcp/v*` to the `pypi` environment. - Added `workflow_dispatch` to publish-mcp.yml so the retry path this PR introduces applies to all three publishing workflows, not two. - RELEASE_PROCESS.md lists publish-mcp.yml in the auth table, corrects the allowed-refs table, and states the rule that produced the bug: every tag prefix deploying to an environment needs its own pattern. Audited: three workflows use an environment, three tag patterns exist, all three are now covered. Claude-Session: https://claude.ai/code/session_01PuXPyTpjcG1B65fpaz1jCX
`gh workflow run --ref <tag>` reads the trigger list from the target ref, so a tag whose workflow file has no `workflow_dispatch:` cannot be dispatched at all. The example named `rs-materials/v0.3.0`, which is exactly such a tag — the recipe would have failed for the one release it was written to unblock. Claude-Session: https://claude.ai/code/session_01PuXPyTpjcG1B65fpaz1jCX
gerchowl
added a commit
that referenced
this pull request
Aug 21, 2026
rs-materials 0.3.0 published via trusted publishing (run 32497790484), so the dead token secret was deleted. `gh secret list` is now empty. Verified the published artifact rather than trusting the green run: a fresh crate depending on rs-materials = "0.3.0" from crates.io loads 169 materials and 32 surfaces, resolves lyso.Ce n@420nm = 1.82 and the measured Ce self-absorption length 588 mm, and reads 7 declared absences — i.e. the symlinked data TOMLs survived `cargo package` into the tarball, which was the one thing a green publish would not prove. Refs: N/A — follow-up to #245. Claude-Session: https://claude.ai/code/session_01PuXPyTpjcG1B65fpaz1jCX
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
rs-materials/v0.3.0is tagged, tested green, and not published — run 32396698845 failed with403 Forbidden: authentication failed.CARGO_REGISTRY_TOKENwas set 2026-03-24 and is no longer valid. Rotating it buys another finite window and another silent expiry at the worst possible moment. crates.io has supported trusted publishing since 2025-07 and this repo already publishes to PyPI that way, so the Rust side was the odd one out, not the norm. This PR moves it to OIDC, pins which refs are allowed to publish, and documents both.Type of Change
chore/refactor/test/ci/build/style— No releaseRequired
uv run pytest)Refs:line at the bottom (or explained why none)Additional Notes
What changed
rust-lang/crates-io-auth-action@v1exchanges the job's GitHub identity for a crates.io token valid ~30 min; the action's post step revokes it. Nothing to rotate, nothing to expire, no credential at rest.crates-ioenvironment, mirroringpypiinrelease.yml.workflow_dispatchthat would let any branch publish whatever its manifest claimed.pypinow allowsmain,v*,pymat-mcp/v*;crates-ioallowsmain,rs-materials/v*.--lockedreplaces--allow-dirty.cargo publishrefreshes the crates.io index, which can rewrite the trackedmat-rs/Cargo.lockand trip cargo's own dirty-tree check — that is the v0.2.0 failure--allow-dirtysilenced. Silencing the check would also silence a genuinely modified tree being published.--lockedremoves the cause and keeps the check. Also added to thetestjob so a stale lockfile fails before the publish gate rather than after it.workflow_dispatchon all three publishing workflows. Re-running a failed tag run replays the workflow file as it was at that tag, so a fix has no effect on a re-run; without dispatch the only retry path is deleting and re-pushing the tag.RELEASE_PROCESS.mddocuments both registries' auth, the ref policy, and the retry path.vars.CRATES_IO_PUBLISH_ENABLEDstays as a kill switch — now independent of credentials rather than a stand-in for them.Review rounds
Three fresh-context review passes; the second returned a blocker.
permissionsreplaces the workflow-level block, so the publish job hadcontents: none; checkout worked only via anonymous clone on a public repocontents: readworkflow_dispatch+ no environment protection = any branch can publish--ref main, which only publishes the tagged version while main points at the release commit--ref <tag>as the general case,--ref mainas the exceptionpublish-mcp.ymlalso deploys topypi; the new policy allowed onlymain+v*, and*does not cross/, so the nextpymat-mcp/v*tag would build green then fail "Branch not allowed to deploy"pymat-mcp/v*; audited all 3 env users against all 3 tag patternsgh workflow run --ref <tag>reads the trigger from the target ref, so the documented example tag (which predatesworkflow_dispatch:) could not be dispatched at all--ref <tag>won't workManual step this PR cannot do
One-time, at https://crates.io/crates/rs-materials/settings → Trusted Publishing → Add:
MorePETmatrelease-rs-materials.ymlcrates-iocrates.io requires the crate to already exist (0.1.0 and 0.2.0 are published) and the configuring user to be an owner.
Verification after merge
gh workflow run release-rs-materials.yml --ref main--ref mainrather than the tag, because the v0.3.0 tag's workflow file has no dispatch trigger.mainis the v0.3.0 release commit, so the published content is identical to the tag. Once green:gh secret delete CARGO_REGISTRY_TOKEN.Checks run locally
yamllint --stricton all three workflows — cleanpymarkdown -c .pymarkdown scan RELEASE_PROCESS.md— back to the pre-existing baseline (5 MD013 hits, all on untouched lines)cargo publish --manifest-path mat-rs/Cargo.toml --locked --dry-run— exit 0, packages 25 files, leaves the tree cleanrust-lang/crates-io-auth-actionv1tag and itstokenoutput verified against the action's publishedaction.ymlgh api .../deployment-branch-policiesoutput for both environments matches the table inRELEASE_PROCESS.mdRefs: N/A — release infrastructure only; unblocks the stalled
rs-materials/v0.3.0publish (run 32396698845).https://claude.ai/code/session_01PuXPyTpjcG1B65fpaz1jCX