Skip to content

fix(ci): release-please squash-merge auto-tag + populate Release body#133

Merged
fentas merged 3 commits into
masterfrom
fix/release-please-auto-tag-squash
May 19, 2026
Merged

fix(ci): release-please squash-merge auto-tag + populate Release body#133
fentas merged 3 commits into
masterfrom
fix/release-please-auto-tag-squash

Conversation

@fentas
Copy link
Copy Markdown
Owner

@fentas fentas commented May 19, 2026

Summary

Two long-standing release-flow bugs surfaced cutting v0.4.0 / v0.5.0:

1. release-please-action skips tag-push on squash-merge

The action only fires tag-push when the release PR is merged via "Create a merge commit." This repo squashes everything (per CLAUDE.md), so the action's heuristic finds no merge commit matching its PR's tracked SHA and silently leaves the PR labelled autorelease: pending. Subsequent runs then see untagged release PR outstanding — aborting and refuse to open a new PR.

PR #11 (v0.4.0) and PR #131 (v0.5.0) both required a manual git tag && git push --tags to unblock.

Fix: follow-up step in release-please.yml reads the post-merge .release-please-manifest.json, computes the expected tag, and pushes it if missing. Identifies the source PR by label + title search, swaps the label to autorelease: tagged after tagging. Idempotent — unrelated pushes see the tag exists and exit.

2. GitHub Release body was empty

release.yml used generate_release_notes: false and never passed a body, so even the v0.4.0 Release (built via the existing flow) had a blank description. release-please-action normally populates the Release itself when it tags, but the new squash-merge auto-tag path bypasses that.

Fix: new extract changelog section step in release.yml awks the matching ## [X.Y.Z] section out of CHANGELOG.md and passes it as body_path to softprops/action-gh-release@v3.

After this lands

The v0.6.0 cycle should be hands-off: merge the release-please PR (squash is fine), the auto-tag step pushes v0.6.0, release.yml builds binaries AND creates the Release with populated body — no manual intervention.

Test plan

  • awk extraction works locally — extracted v0.4.0 (58 lines) and v0.5.0 (151 lines) sections from CHANGELOG.md.
  • auto-tag step's idempotency: when v0.5.0 tag already exists, the step's two if-guards short-circuit out.
  • PR identification: gh pr list --state merged --label "autorelease: pending" --search "in:title chore release" correctly returns the most recent release PR.
  • End-to-end: cuts v0.6.0 from the next release-please PR merge without manual steps. Verified next release cycle.
  • Backfill v0.4.0 + v0.5.0 release bodies (separate manual step — once this PR lands, future releases auto-populate; the existing two need a one-time gh release edit).

🤖 Generated with Claude Code

fentas and others added 3 commits May 19, 2026 21:43
Two long-standing release-flow bugs surfaced when v0.4.0 / v0.5.0
were cut from this repo's squash-merged release-please PRs:

1. **release-please-action skips the tag-push on squash-merge.**
   The action only fires tag-push when the release PR is merged via
   "Create a merge commit." Every release here is squash-merged (per
   CLAUDE.md), so the action's heuristic finds no merge commit
   matching its PR's tracked SHA and silently leaves the PR labelled
   `autorelease: pending`. Subsequent runs then see "untagged
   release PR outstanding — aborting" and refuse to open a new PR.

   Fix: a follow-up step in release-please.yml reads the post-merge
   `.release-please-manifest.json`, computes the expected tag, and
   pushes it if missing. Identifies the source PR by label + title
   search, swaps the label to `autorelease: tagged` after tagging
   so future runs ignore the now-closed loop. Idempotent — unrelated
   pushes see the tag exists and exit.

2. **GitHub Release body was empty.** `release.yml` used
   `generate_release_notes: false` and never passed a body, so even
   the v0.4.0 Release (built via the existing flow) had a blank
   description. release-please-action normally populates the Release
   itself, but the new squash-merge auto-tag path bypasses that.

   Fix: new `extract changelog section` step in release.yml awk's
   the matching `## [X.Y.Z]` section out of CHANGELOG.md and passes
   it as `body_path` to softprops/action-gh-release.

After this lands, the v0.6.0 cycle should be hands-off: merge the
release-please PR (squash is fine), the auto-tag step pushes
`v0.6.0`, release.yml builds binaries and creates the Release with
populated body — no manual intervention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round 1 findings:
- `release.yml` `extract changelog section`: switch from regex
  `^## \[ver\]` to literal substring `index($0, "## [ver]") == 1`.
  Stable X.Y.Z values worked because they were unique, but
  pre-release tags like `0.5.0-rc.1` would mis-match through
  the regex `.` wildcards. Index-based scan can't be fooled.
- `release-please.yml` `auto-tag` PR query: narrow from
  `--search "in:title chore release"` (any PR with those words
  in the title) to `--head "release-please--branches--master"`
  (the exact branch release-please-action publishes). A mis-
  labelled out-of-band PR with `autorelease: pending` can't be
  picked up by mistake any more.
- `release-please.yml`: defensive `git cat-file -e` before
  `git tag` so a rebased/force-pushed merge SHA surfaces a
  clear error instead of `git tag: unknown revision`.

Verified locally: the new awk extractor produces identical
line counts (151 for 0.5.0, 58 for 0.4.0) as the regex version.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Subagent round 2 nit: `jq -r '.["."]' .release-please-manifest.json`
on a corrupt/empty manifest emits `null`, downstream `tag="v${ver}"`
becomes `vnull`, neither tag-exists check matches, and the script
then tag-pushes a real PR SHA as `vnull` — confusing failure mode.

Regex-check `ver` matches `X.Y.Z[-+rest]` semver before proceeding;
bail loudly with a clear error message otherwise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fentas
Copy link
Copy Markdown
Owner Author

fentas commented May 19, 2026

/review-loop complete — 3 rounds, subagent-only

Round Verdict Action
1 fix-and-ship 45a55ac — awk extractor switched from regex to literal index($0, hdr) == 1 (regex . wildcards would mis-match RC tags); auto-tag PR query narrowed to --head release-please--branches--master (no out-of-band PR mistakes); defensive git cat-file -e $pr_sha^{commit} before tagging.
2 fix-and-ship ac616ea — semver regex check on manifest version. A corrupt/empty manifest emits null from jq; without the guard the script would push a real PR SHA tagged as vnull.
3 ship Final pass; YAML parses, awk pattern verified against real CHANGELOG, semver regex empirically validated for stable + rc + build-metadata + rejects null/garbage.

Ready for merge. The PR fixes BOTH the squash-merge auto-tag gap AND the empty Release body — after this lands, v0.6.0 should cut hands-off.

🤖 /review-loop with Claude Code

@fentas fentas merged commit 852a7de into master May 19, 2026
4 checks passed
@fentas fentas deleted the fix/release-please-auto-tag-squash branch May 19, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant