Cut a release of a Rust crate and publish to crates.io, with externally-gated merge and publish steps.
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
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'])"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 30gh pr view <pr> --json state,mergeable,mergeStateStatusgh pr merge <pr> --mergestate=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-runinclude list in Cargo.toml.cargo publishcargo 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> --latestgh 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.imbue-ai/offload
6998230
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.