Skip to content

Commit 59ff240

Browse files
bpamiriclaude
andauthored
ci(release): harden CHANGELOG separator check and correct release playbook (#2818)
Prep for the 4.0.2 cut. - CHANGELOG.md: the separator below [Unreleased]/[4.0.2] was `----` (4 dashes). release.yml extracts release notes with the awk range /^# \[VERSION\]/,/^---$/, which terminates only on exactly three dashes, so a 4-dash separator silently pulls all of 4.0.1's notes into the 4.0.2 release. Flipped to `---`. (Same regression as #2606, #2768.) - release.yml: added a main-gated guard in the release-checklist step that mirrors the extraction awk and fails the release if the range bleeds into a later version heading, so this stops recurring. - RELEASE_PLAYBOOK.md: corrected stale guidance — version source is wheels.json (not box.json) since the 4.0 rebrand, and the release-day flow is PR-into-main + softprops auto-tag (main's PRs-only ruleset rejects `git push origin main --tags`). Documented the new separator guard and its failure mode. Signed-off-by: Peter Amiri <peter@alurium.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e07358f commit 59ff240

3 files changed

Lines changed: 53 additions & 15 deletions

File tree

.github/RELEASE_PLAYBOOK.md

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,45 @@ on them is green. ~5 minutes/day max.
2929

3030
1. **Cherry-pick or merge stabilization commits to `release/X.Y.Z`** if using a
3131
release-branch workflow. Skip this if cutting directly from develop.
32-
2. **Update `CHANGELOG.md`** with the release date (replace `=> TBD` for the
33-
target version). `release.yml` will refuse to publish if it sees TBD on the
34-
target version.
35-
3. **Verify `box.json` version** matches what you want to release. After the
36-
last GA, the `bump-develop-version.yml` workflow set it to next-patch; if
37-
you're shipping a bigger bump, update it manually.
32+
2. **Update `CHANGELOG.md`**: rename `# [Unreleased]` to
33+
`# [X.Y.Z](...) => YYYY-MM-DD` with the real release date (replace any
34+
`=> TBD`). Then confirm the separator line directly below the renamed
35+
section is exactly `---` (three dashes), NOT `----`. `release.yml` refuses
36+
to publish if it sees TBD on the target version, and also fails if a `----`
37+
separator would make the release-notes awk bleed into the previous version's
38+
notes (recurring footgun — see #2606, #2768; now guarded in `release.yml`).
39+
3. **Verify `wheels.json` version** matches what you want to release.
40+
`wheels.json` is the canonical version source since the 4.0 rebrand (the
41+
workflows read it; `box.json` is legacy and may be absent or empty). After
42+
the last GA, the `bump-develop-version.yml` workflow set it to next-patch;
43+
if you're shipping a bigger bump, update it manually.
3844

3945
### Release day
4046

41-
```bash
42-
# 1. On main, fast-forward from the release branch.
43-
git checkout main
44-
git merge --ff-only release/X.Y.Z # (or develop, if no release branch)
47+
`main` has a **"PRs only" ruleset**`git push origin main` is rejected with
48+
`GH013`. You also never tag manually: `release.yml` auto-creates the tag via
49+
`softprops/action-gh-release` once the merge lands on main.
4550

46-
# 2. Tag and push. release.yml fires automatically.
47-
git tag -a vX.Y.Z -m "Release X.Y.Z"
48-
git push origin main --tags
51+
```bash
52+
# 1. Open a PR into main from a release branch. (Cutting from develop directly
53+
# is fine — just branch off develop so the PR has a head ref to merge.)
54+
git checkout develop && git pull
55+
git checkout -b release/X.Y.Z-to-main
56+
git push -u origin release/X.Y.Z-to-main
57+
gh pr create --base main --head release/X.Y.Z-to-main \
58+
--title "Release X.Y.Z" --body "Cut X.Y.Z. See CHANGELOG."
59+
60+
# 2. Merge with "Create a merge commit" (NOT squash — preserves develop
61+
# history on main).
62+
gh pr merge --merge --delete-branch
4963
```
5064

65+
The push-to-main from that merge triggers `release.yml`, which builds the
66+
artifacts and calls `softprops/action-gh-release` with `tag_name: vX.Y.Z`,
67+
creating the tag at main HEAD automatically. Do **not** run `git tag` /
68+
`git push --tags` — the legacy `git push origin main --tags` flow is rejected
69+
by the ruleset anyway.
70+
5171
The tag push triggers:
5272
- `release.yml` builds artifacts, publishes to `wheels-dev/wheels/releases`
5373
- `release.yml`'s "Dispatch downstream package managers" step then fires
@@ -149,7 +169,8 @@ If any pin to `<X.Y.Z`, open issues on those repos to widen the constraint.
149169
| Symptom | Cause | Fix |
150170
|---|---|---|
151171
| `release.yml` fails: "CHANGELOG contains TBD" | Forgot to update changelog | Add release date to `# [X.Y.Z]` line, push, re-run workflow |
152-
| `release.yml` fails: "version contains -SNAPSHOT" | `box.json` has `-SNAPSHOT` suffix on main | Strip suffix, push (release.yml asserts clean main versions) |
172+
| `release.yml` fails: "release-notes range … bleeds into a later version section" | Separator below the target version is `----` (4 dashes), not `---` — the awk extraction overruns into the previous version | Change that separator line to exactly `---`, push, re-run |
173+
| `release.yml` fails: "version contains -SNAPSHOT" | `wheels.json` has a `-snapshot` suffix on main | Strip the suffix in `wheels.json`, push (release.yml asserts clean main versions) |
153174
| brew tap PR doesn't open after release | `DOWNSTREAM_DISPATCH_TOKEN` expired or unset | Rotate token in repo secrets; re-run `release.yml` workflow_dispatch |
154175
| develop-bump PR doesn't open after GA | `DOWNSTREAM_DISPATCH_TOKEN` expired/unset, OR the `bump-develop` dispatch step warned and exited 0 | Fire it manually: Actions → "Bump Develop Version" → "Run workflow" → enter the just-released version (e.g. `4.0.0`). The workflow has a `workflow_dispatch` fallback for exactly this case. |
155176
| `brew install wheels` post-release fails | Formula sha256 mismatch | Re-run the tap bump workflow with `workflow_dispatch` to recompute |

.github/workflows/release.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ jobs:
105105
exit 1
106106
fi
107107
108+
# Check that the CHANGELOG separator below this version is exactly
109+
# '---' (3 dashes). The release-notes extraction step below uses the
110+
# awk range /^# \[VERSION\]/,/^---$/, which terminates ONLY on a line
111+
# of exactly three dashes; a '----' (4-dash) separator silently
112+
# extends the range into the PREVIOUS version's notes. We mirror that
113+
# exact awk here and fail if the extracted range pulls in another
114+
# version heading. (Recurring footgun — see #2606, #2768.)
115+
VERSION_PATTERN=$(echo "${WHEELS_VERSION%+*}" | sed 's/\./\\./g')
116+
BLED=$(awk '/^# \['"${VERSION_PATTERN}"'\]/,/^---$/ {print}' CHANGELOG.md \
117+
| tail -n +2 | grep -cE '^# \[' || true)
118+
if [ "${BLED}" -ne 0 ]; then
119+
echo "ERROR: CHANGELOG release-notes range for [${WHEELS_VERSION%+*}] bleeds into a later version section."
120+
echo "The separator directly below the [${WHEELS_VERSION%+*}] block must be exactly '---' (3 dashes), not '----'."
121+
echo "This check mirrors the awk extraction used to build the GitHub Release notes."
122+
exit 1
123+
fi
124+
108125
# Check that source version in wheels.json is clean (no -snapshot suffix).
109126
# The workflow injects -snapshot.N for develop builds automatically.
110127
BASE_VERSION=$(jq -r '.version' wheels.json)

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo
5454
- The documented Linux bleeding-edge install commands in the guides 404'd after #2759 renamed the snapshot artifacts from `wheels_*` to `wheels-be_*` (deb) / `wheels-be-*` (rpm) to differentiate the channel by package name. All six affected pages — three unique (`start-here/installing`, `start-here/release-channels`, `command-line-tools/installation`) mirrored across the v4-0-0 and v4-0-1-snapshot doc versions — now point at the correct `wheels-be_*` assets, and the "switching channels" snippets reflect the real `Conflicts: wheels` package metadata instead of assuming same-package-name `--allow-downgrades` / `dnf downgrade` transitions (#2777)
5555
- Stale `schema_migrations` table-name references in the v4 migration and seeding guides (`v4-0-0/basics/seeding`, `v4-0-1-snapshot/basics/migrations`, `v4-0-1-snapshot/basics/seeding`) now read `wheels_migrator_versions`, matching the on-disk table name since the `c_o_r_e_*``wheels_*` rename — no `schema_migrations` table exists anywhere in `vendor/wheels/`, `cli/`, or `app/`. Carryover from #2799 (#2801)
5656

57-
----
57+
---
5858

5959
# [4.0.1](https://github.com/wheels-dev/wheels/releases/tag/v4.0.1) => 2026-05-20
6060

0 commit comments

Comments
 (0)