Skip to content

Commit 218bbc2

Browse files
Zenofexclaude
andauthored
Add a workflow to yank a crates.io version (#10)
`bootintel-cli` 0.4.1 needs yanking. The crate was renamed to `bootintel`, and until the old name is yanked, two names install a working `bootintel` binary, which is the ambiguity the rename existed to remove. It cannot be done from a laptop. The registry token lives only as a repository secret, which is where a publish credential belongs, and GitHub secrets cannot be read back, only used inside Actions. So the yank runs where the publish ran, with the same secret. `workflow_dispatch` only, with no defaults, so it cannot fire by accident. Takes a reason for the run log, and refuses a version that is not on the registry so a typo fails before reaching crates.io. Unyank is the same workflow with the action flipped, because yanking is reversible where publishing is not. Documented in `docs/releasing.md`. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 05ee328 commit 218bbc2

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

‎.github/workflows/crates-yank.yml‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Yank or un-yank a published crate version.
2+
#
3+
# Exists because the crates.io token lives here as a repository secret and
4+
# nowhere else, which is correct: a publish credential should not sit on a
5+
# laptop. That also means a yank cannot be run locally by anyone who does not
6+
# separately hold a token, so it runs here, with the same secret the release
7+
# workflow uses.
8+
#
9+
# workflow_dispatch only, with no defaults, so it cannot fire by accident.
10+
# Yanking is reversible (`action: unyank`), unlike publishing: a version can
11+
# be yanked and restored, but never reused.
12+
name: crates-yank
13+
14+
on:
15+
workflow_dispatch:
16+
inputs:
17+
crate:
18+
description: 'Crate name (e.g. bootintel-cli)'
19+
required: true
20+
type: string
21+
version:
22+
description: 'Exact version to act on (e.g. 0.4.1)'
23+
required: true
24+
type: string
25+
action:
26+
description: 'yank removes it from new resolution; unyank restores it'
27+
required: true
28+
type: choice
29+
options: [yank, unyank]
30+
reason:
31+
description: 'Why, for the run log. Not sent to crates.io.'
32+
required: true
33+
type: string
34+
35+
permissions:
36+
contents: read
37+
38+
jobs:
39+
yank:
40+
name: ${{ inputs.action }} ${{ inputs.crate }}@${{ inputs.version }}
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- name: Install Rust toolchain
46+
uses: dtolnay/rust-toolchain@stable
47+
48+
- name: Require the registry token
49+
env:
50+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
51+
run: |
52+
if [ -z "${CARGO_REGISTRY_TOKEN}" ]; then
53+
echo "::error::CARGO_REGISTRY_TOKEN is not set. See docs/releasing.md."
54+
exit 1
55+
fi
56+
57+
- name: Record what is being done and why
58+
run: |
59+
echo "action: ${{ inputs.action }}"
60+
echo "crate: ${{ inputs.crate }}"
61+
echo "version: ${{ inputs.version }}"
62+
echo "reason: ${{ inputs.reason }}"
63+
64+
# Refuse to touch a version that does not exist, so a typo fails here
65+
# rather than producing a confusing registry error.
66+
- name: Confirm the version exists on the registry
67+
run: |
68+
if ! cargo info "${{ inputs.crate }}@${{ inputs.version }}" >/dev/null 2>&1; then
69+
echo "::error::${{ inputs.crate }} ${{ inputs.version }} is not on crates.io."
70+
exit 1
71+
fi
72+
73+
- name: ${{ inputs.action }}
74+
env:
75+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
76+
run: |
77+
if [ "${{ inputs.action }}" = "yank" ]; then
78+
cargo yank --version "${{ inputs.version }}" "${{ inputs.crate }}"
79+
else
80+
cargo yank --undo --version "${{ inputs.version }}" "${{ inputs.crate }}"
81+
fi

‎docs/releasing.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ having no brew line at all.
6363
6. Publish the draft and mark it latest. That creates the tag on the released
6464
commit.
6565

66+
## Yanking a published version
67+
68+
A crates.io version can be yanked, which stops new dependency resolution and
69+
new installs reaching it, and can be restored with unyank. It can never be
70+
reused: the number is spent whatever happens.
71+
72+
The registry token lives only as a repository secret, which is correct for a
73+
publish credential, so a yank cannot be run from a laptop that does not hold
74+
its own token. Dispatch the `crates-yank` workflow instead. It takes the crate,
75+
the exact version, yank or unyank, and a reason for the run log. It has no
76+
defaults, so it cannot fire by accident, and it refuses a version that is not
77+
on the registry so a typo fails before it reaches crates.io.
78+
6679
## Publishing to crates.io
6780

6881
Order is not optional. `bootintel` declares `bootintel-detectors` with

0 commit comments

Comments
 (0)