Context
The release flow has an intentional human checkpoint between merging the release-please PR and creating the git tag. After the release PR merges:
- `.release-please-manifest.json` is bumped on `main`
- `Cargo.toml` workspace versions are bumped
- `CHANGELOG.md` is updated
- The maintainer manually creates the tag — this is the gate that lets us stop an incorrect release before it permanently ships to crates.io and npm.
This is the right design (crates.io versions are immutable; even `cargo yank` doesn't free the version number), but the procedure is not currently documented. v0.3.0 was shipped via this manual step but the next maintainer who has to ship cold won't know the exact `gh` commands.
What to add
A subsection in `docs/GITHUB_OPERATIONS.md` under the existing "Cutting a release" section. Suggested content:
Manual tag step (after release-please PR merge)
After release-please's "chore: release main" PR merges, the maintainer manually creates the GitHub Release / git tag. This is a deliberate human checkpoint — once the tag exists, `release.yml` fires automatically and publishes to crates.io, where versions are permanent. Inspect the merged commit before tagging.
```bash
1. Fetch and confirm main is in the right shape.
git fetch upstream main
git log -1 upstream/main --format="%H %s"
Should show: chore: release main (#NN)
2. Sanity-check the manifest matches what you expect to release.
gh api repos/tableau/hyper-api-rust/contents/.release-please-manifest.json?ref= \
--jq '.content' | base64 -d
Should show: { ".": "X.Y.Z" }
3. Extract the release notes from the new CHANGELOG.md section.
awk between the two H2 anchors, drop the H2 line and the trailing
blank that separates it from the next section.
awk '/^## \[X\.Y\.Z\]/,/^## \[\]/' CHANGELOG.md \
| sed '$d' | tail -n +3 > /tmp/vX.Y.Z-notes.md
4. Create the tag + GitHub Release. --target accepts the merge SHA;
the positional arg is the tag name. release.yml will fire on the
`release: published` event.
gh release create vX.Y.Z \
-R tableau/hyper-api-rust \
--target \
--title "vX.Y.Z" \
--notes-file /tmp/vX.Y.Z-notes.md \
--latest # OR --prerelease for rc tags
5. Promote the release PR's label so future release-please runs
don't abort with "untagged, merged release PRs outstanding".
gh pr edit -R tableau/hyper-api-rust \
--remove-label "autorelease: pending" \
--add-label "autorelease: tagged"
```
After the tag is created
- Watch `release.yml` at https://github.com/tableau/hyper-api-rust/actions/workflows/release.yml — it polls required CI check-runs on the tagged SHA, then publishes to crates.io in dependency order.
- Watch `npm-build-publish.yml` in parallel.
- Verify v0.3.0 (or whichever version) is on https://crates.io/crates/hyperdb-api and the npm registry.
To stop a release after the PR merges but before tagging
If you find a problem in the merged release PR before creating the tag, you have time to investigate. Options:
- Land a fix on main that addresses the issue. release-please will open a new "chore: release main" PR rolling that fix into the next version. Promote the old PR's label to `autorelease: snooze` to mark the original version as "don't re-propose" (but only if the new release will use a different version number).
- Force a Release-As bump to skip the bad version: `git commit --allow-empty -m "chore: release X.Y.(Z+1)\n\nRelease-As: X.Y.(Z+1)"`. release-please will open a fresh PR for the next patch version.
- Revert the release PR's commit on main if the bump itself is wrong. Then update the manifest by hand if needed and let release-please reconcile.
Why this is the right design
- crates.io is immutable. Once `cargo publish` succeeds, the version exists forever. `cargo yank` marks it as "don't pick up by default" but the version number is permanently consumed.
- npm is similar — `npm unpublish` only works within 72 hours and nukes downstream caches; effectively the version is locked once published.
- The manual tag step is the only point where a maintainer can stop a bad release. Everything before is reversible (revert PRs, edit CHANGELOG, close release-please PRs); everything after is permanent.
Acceptance criteria
Priority
Low. v0.3.0 shipped successfully without the docs; the procedure was reconstructed from the AGENT.md / claude.md notes and upstream issue threads. But the next maintainer hitting this cold will have a smoother time with explicit docs.
Closes follow-up from #82.
Context
The release flow has an intentional human checkpoint between merging the release-please PR and creating the git tag. After the release PR merges:
This is the right design (crates.io versions are immutable; even `cargo yank` doesn't free the version number), but the procedure is not currently documented. v0.3.0 was shipped via this manual step but the next maintainer who has to ship cold won't know the exact `gh` commands.
What to add
A subsection in `docs/GITHUB_OPERATIONS.md` under the existing "Cutting a release" section. Suggested content:
Why this is the right design
Acceptance criteria
Priority
Low. v0.3.0 shipped successfully without the docs; the procedure was reconstructed from the AGENT.md / claude.md notes and upstream issue threads. But the next maintainer hitting this cold will have a smoother time with explicit docs.
Closes follow-up from #82.