一键导入
release-to-crates
Cut a release of a Rust crate and publish to crates.io, with externally-gated merge and publish steps.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Cut a release of a Rust crate and publish to crates.io, with externally-gated merge and publish steps.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | release-to-crates |
| description | Cut a release of a Rust crate and publish to crates.io, with externally-gated merge and publish steps. |
Make the version-bump edits directly. A release bump is a mechanical release-engineering operation, not feature work, so it does not need to be routed through whatever feature-delegation process the repo otherwise follows.
cargo publish is irreversible: a crates.io version can never be
re-uploaded or reused. Always run cargo publish --dry-run first, and only publish
from a revision whose Cargo.toml version is confirmed not yet on crates.io.These three facts drive every branching step below:
.jj/ exists → this is a colocated jujutsu/git repo; use jj for all
commits/bookmarks/push, since raw git commands can corrupt jj state. Otherwise use
plain git.gh. GitLab → glab. If the required forge CLI is not on
PATH, stop and ask the human — do not improvise a web/manual flow.name from [package] in Cargo.toml.jj st shows no changes; @ is empty (or run jj new).git status clean.jj git fetch; confirm main == main@origin.git switch main && git pull --ff-only.version under [package] in Cargo.toml.User-Agent):
curl -s -H "User-Agent: <crate>-release (<you@example.com>)" \
https://crates.io/api/v1/crates/<crate> \
| python3 -c "import sys,json; print(json.load(sys.stdin)['crate']['max_version'])"
(A brand-new crate returns no crate object — treat as "never published".)Cargo.toml version != crates.io max_version, stop and report — the repo
is in an unexpected state (a prior release was half-finished or the tree is stale).<user>/bump-version-v<new-version> (e.g. danver/bump-version-v0.9.9).jj desc -m "Bump version to <new>" (add a body explaining the bump);
then edit, then jj bookmark create <user>/bump-version-v<new> -r @.git switch -c <user>/bump-version-v<new>.Cargo.toml: set version = "<new>".cargo build. This updates the crate's own entry in
Cargo.lock (easy to forget; the published package must carry a consistent lockfile).Cargo.toml and Cargo.lock, both just the
version line. jj diff --git / git diff.Bump version to <new>, no trailing period, no
Conventional-Commits prefix; optional body noting it's a non-breaking patch release.jj git push -b <bookmark> --allow-new (new bookmarks need --allow-new).git push -u origin <branch>.gh pr create --base main --head <branch> --title "Bump version to <new>" \
--body "Patch release of \`<crate>\`. Non-breaking; bumps Cargo.toml and Cargo.lock
from <old> to <new> in preparation for publishing to crates.io.
Last published version: <old>."
gh pr checks <pr> --watch --interval 30
gh pr view <pr> --json state,mergeable,mergeStateStatus
gh pr merge <pr> --merge
state=MERGED.jj git fetch; then jj new main -m "Publish <crate> <new> to crates.io".git switch main && git pull --ff-only.grep '^version' Cargo.toml shows <new> (the merge brought the bump in).cargo publish --dry-run
Warnings about test files "not included in the published package" are benign when
those tests are excluded by the include list in Cargo.toml.cargo publish
(Requires a crates.io token: cargo login, or CARGO_REGISTRY_TOKEN in the env.)max_version == <new>.Publishing to crates.io does not create a git tag or a forge (GitHub/GitLab) release — do this explicitly so the version is reflected in the repo's history.
v<new> (e.g. v0.9.6). Target the merged trunk commit
that carries the new version (main HEAD from Step 6), referenced by its full SHA.git tag -l | sort -V) and find the most
recent one that actually has a forge release. If earlier published versions were never
tagged, scope the changelog from the last released tag and say so in the body, so
the skipped versions are not lost.<Crate> <new>) and notes (group commits since that tag by theme;
end with a compare/<lasttag>...v<new> changelog link). Write the body to a file and
pass it with --notes-file to avoid shell-quoting issues.gh release create v<new> --target <main-sha> --title "<Crate> <new>" \
--notes-file <notes-file> --latest
gh release view v<new> shows draft=false, the expected targetCommitish,
and the tag (git ls-remote --tags origin v<new>) points at <main-sha>.Note for jj repos: gh release create creates the tag on the GitHub side, so it does
not touch local jj/git refs and is safe to run from a colocated repo.
@:
jj abandon @ (publishing changes no tracked files). Confirm jj st is clean.git status is clean.Each step maps to a swappable provider so the skill can support other stacks:
| Concern | GitHub + jj (this run) | git-only / GitLab variant |
|---|---|---|
| Commit/branch | jj desc + jj bookmark create | git switch -c |
| Push | jj git push -b … --allow-new | git push -u origin … |
| Open PR/MR | gh pr create | glab mr create |
| CI gate | gh pr checks --watch | glab ci status / pipeline poll |
| Merge | gh pr merge --merge | glab mr merge |
| Version truth | Cargo.toml ↔ crates.io API | same |
| Publish | cargo publish (+ --dry-run) | same |
| Tag release | gh release create v<new> | glab release create v<new> |
User-Agent; always send one.jj bookmarks do not auto-advance; create/move the bookmark to @ before pushing.--allow-new on push.cargo build leaves Cargo.lock's own package entry stale.--dry-run pre-flight is mandatory, not optional.cargo publish does not tag the repo; the forge release (Step 8) is a separate step.git tag -l before
scoping release notes so a previously-published-but-untagged version is not skipped.