Skip to content

promotion PRs squash-merged into production, collapsing changelog to one entry #78

@point-source

Description

@point-source

Symptom

The v1.0.1 release on main contains a single changelog entry:

Bug Fixes

It should contain one entry per fix/change merged to develop since the last production release (v1.0.0). The corresponding v1.0.1-dev.1 prerelease on develop got it right:

The v1.0.0 release got this right too — the merge commit for promotion PR #67 was 63f5b26 Merge pull request #67 from point-source/develop (a true merge), and semantic-release walked into the second parent and listed each squash from develop.

Root cause

Promotion PR #74 was squash-merged instead of merge-committed. Its mergeCommit is bedf3cd fix: promote develop → main (#74) — a single squash with all develop commits collapsed into the body. semantic-release on main only sees that one commit since v1.0.0, parses its title (fix: promote ...), and emits one Bug Fixes entry.

This happens because two flows both call enableAutoMerge on the same promotion PR with conflicting methods, and the later one wins:

  1. src/promotion.ts:108 (push-flow path) — opens the promotion PR and sets method: MergeMethod = \"MERGE\". Comment on that line is explicit:

    // Promotion PRs always use a true merge commit. Squash on this edge severs
    // ancestry between source and target — see docs/squash-merge-issues.md.
    const method: MergeMethod = \"MERGE\";
  2. src/pr-flow.ts:140 (pull_request event path) — runs on every pull_request event for any managed PR. It has no notion of "promotion PR" and hardcodes:

    // Feature PRs into stream branches always squash so each PR contributes
    // exactly one CHANGELOG entry...
    const method: MergeMethod = \"SQUASH\";
    const result = await gh.enableAutoMerge(pr.nodeId, method);

    For PR fix: promote develop → main #74 (fix: promote develop → main, base main), fix is in main.auto_merge, so it hits the eligible branch and re-enables auto-merge with SQUASH. The final auto-merge method registered on the PR is whatever the last call set, so SQUASH wins on any pull_request event after the initial open.

Reproduction

Suggested fix

In src/pr-flow.ts, detect promotion PRs and skip the merge-method override (or switch the method to MERGE for that case). A promotion PR is identifiable by:

  • head is one of the configured stream branches AND base is the next branch in that stream's chain, AND
  • title matches the ^[a-z]+(\\([^)]+\\))?!?: promote <source> → <target>$ shape already encoded at src/promotion.ts:234.

Either centralize the "is this a promotion PR" check or have pr-flow short-circuit when the head/base pair matches a stream's promotion edge. promotion.ts already uses MergeMethod = \"MERGE\" correctly; the gap is purely that pr-flow stomps on it.

Test coverage to add

A test in tests/pr-flow.test.ts that asserts: when the PR's head/base match a stream's promotion edge, enableAutoMerge is either not called by pr-flow, or is called with \"MERGE\". The current suite has no such case, which is why this regressed silently.

Impact / mitigation

  • Affects every production release after a promotion. The dev changelog stays correct (the squash on main happens after the dev release is cut), so adopters reading v*.dev.N notes are fine, but the user-facing v* release on main loses fidelity.
  • Workaround until fixed: manually merge the promotion PR via the GitHub UI with "Create a merge commit" instead of letting auto-merge run, OR remove fix / feat / etc. from main.auto_merge in .flywheel.yml so pr-flow takes the needs-review path on promotion PRs. Both are unsatisfying.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions