What happens
Issues fixed via PRs that merge into develop stay open after the develop→main promotion lands. Reproduced in the v1.0.1 cycle: PRs #72, #73, #75, #76 each had Closes #N in their description and fixed #60, #70, #71 — but #60, #70, #71 had to be closed manually after v1.0.1 published.
Why
GitHub auto-closes an issue only when a PR/commit landing on the default branch contains a Closes #N (or Fixes / Resolves) reference in its body. In Flywheel's flow:
- Sub-PRs merge into
develop (non-default). GitHub does not auto-close from non-default-branch merges.
- The squash-merge commit body on
develop uses GitHub's default squash_merge_commit_message: COMMIT_MESSAGES, which concatenates the original commit messages — not the PR description, so the Closes references in the PR description never propagate into a commit.
- The develop→main promotion PR's body is generated by
formatPromotionBody in src/promotion.ts:281-308 and never contains Closes references — just the per-type pending-commit list. So when the promotion PR merges into main (default), nothing references the issues.
The release notes published by @semantic-release/github do aggregate closes [#A] [#B] … (visible on the v1.0.1 release page), which is suggestive but inert — release notes don't trigger GitHub's auto-close.
Recommended fix
Aggregate Closes references in the promotion PR body so they propagate to main.
In runPromotion (src/promotion.ts), when building the develop→main PR body:
- For each pending commit with a linked
(#NN) suffix, fetch the linked PR's description (octokit.rest.pulls.get).
- Extract
Closes #N / Fixes #N / Resolves #N references via regex.
- Append a single
Closes #A, #B, #C line to the promotion PR body.
When PR #74-style PRs merge with MERGE method, native auto-merge includes the PR body in the merge commit. GitHub sees the Closes refs on the default branch and auto-closes the issues. No new infrastructure, no changes for adopter contributors.
Touch points
src/promotion.ts:281-308 — extend formatPromotionBody to accept aggregated closes refs and emit a Closes … line.
src/promotion.ts:57-67 — fetch each pending commit's linked PR description (one pulls.get per pending PR; cache if perf becomes a concern, though pending lists are small).
src/github.ts — add a thin getPullDescription(number) helper alongside the existing pulls methods.
tests/promotion.test.ts — fixture: pending commits with linked PRs whose descriptions contain Closes #N produce a Closes #N line in the promotion PR body. Cover regex variants (Closes, Fixes, Resolves, comma-separated, multi-line).
Cheaper alternatives considered
- Close programmatically. Add a step in
flywheel-push.yml after a release on main that parses the v1.0.x release notes' closes [#A] [#B] … line (semantic-release already aggregates) and gh issue closes each. Lower-touch but bypasses GitHub's native flow and decouples the close from the PR merge.
- Document a contributor convention. Tell adopters to put
Closes #N in commit titles/bodies (not just PR descriptions). Cheapest, but relies on humans (and AI authors) to remember it on every PR.
The recommended fix is preferred because adopters who already follow conventions (Closes in PR description) get auto-close for free, and there's only one place in the action to maintain.
Repro
Any develop→main promotion since v1.0.0. The v1.0.1 cycle is the canonical example — see issues #60, #70, #71, all closed manually after PR #74 merged.
What happens
Issues fixed via PRs that merge into
developstay open after the develop→main promotion lands. Reproduced in the v1.0.1 cycle: PRs #72, #73, #75, #76 each hadCloses #Nin their description and fixed #60, #70, #71 — but #60, #70, #71 had to be closed manually after v1.0.1 published.Why
GitHub auto-closes an issue only when a PR/commit landing on the default branch contains a
Closes #N(orFixes/Resolves) reference in its body. In Flywheel's flow:develop(non-default). GitHub does not auto-close from non-default-branch merges.developuses GitHub's defaultsquash_merge_commit_message: COMMIT_MESSAGES, which concatenates the original commit messages — not the PR description, so theClosesreferences in the PR description never propagate into a commit.formatPromotionBodyinsrc/promotion.ts:281-308and never containsClosesreferences — just the per-type pending-commit list. So when the promotion PR merges intomain(default), nothing references the issues.The release notes published by
@semantic-release/githubdo aggregatecloses [#A] [#B] …(visible on the v1.0.1 release page), which is suggestive but inert — release notes don't trigger GitHub's auto-close.Recommended fix
Aggregate
Closesreferences in the promotion PR body so they propagate tomain.In
runPromotion(src/promotion.ts), when building the develop→main PR body:(#NN)suffix, fetch the linked PR's description (octokit.rest.pulls.get).Closes #N/Fixes #N/Resolves #Nreferences via regex.Closes #A, #B, #Cline to the promotion PR body.When PR #74-style PRs merge with MERGE method, native auto-merge includes the PR body in the merge commit. GitHub sees the
Closesrefs on the default branch and auto-closes the issues. No new infrastructure, no changes for adopter contributors.Touch points
src/promotion.ts:281-308— extendformatPromotionBodyto accept aggregated closes refs and emit aCloses …line.src/promotion.ts:57-67— fetch each pending commit's linked PR description (onepulls.getper pending PR; cache if perf becomes a concern, though pending lists are small).src/github.ts— add a thingetPullDescription(number)helper alongside the existing pulls methods.tests/promotion.test.ts— fixture: pending commits with linked PRs whose descriptions containCloses #Nproduce aCloses #Nline in the promotion PR body. Cover regex variants (Closes,Fixes,Resolves, comma-separated, multi-line).Cheaper alternatives considered
flywheel-push.ymlafter a release onmainthat parses the v1.0.x release notes'closes [#A] [#B] …line (semantic-release already aggregates) andgh issue closes each. Lower-touch but bypasses GitHub's native flow and decouples the close from the PR merge.Closes #Nin commit titles/bodies (not just PR descriptions). Cheapest, but relies on humans (and AI authors) to remember it on every PR.The recommended fix is preferred because adopters who already follow conventions (Closes in PR description) get auto-close for free, and there's only one place in the action to maintain.
Repro
Any develop→main promotion since v1.0.0. The v1.0.1 cycle is the canonical example — see issues #60, #70, #71, all closed manually after PR #74 merged.