Skip to content

promotion PR body should aggregate Closes #N so issues auto-close on release #77

@point-source

Description

@point-source

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:

  1. For each pending commit with a linked (#NN) suffix, fetch the linked PR's description (octokit.rest.pulls.get).
  2. Extract Closes #N / Fixes #N / Resolves #N references via regex.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions