Skip to content

feat(create-release-pr): aggregate RC changelogs when promoting to stable#239

Closed
piontec wants to merge 2 commits into
mainfrom
aggregate-rc-changelogs-on-promotion
Closed

feat(create-release-pr): aggregate RC changelogs when promoting to stable#239
piontec wants to merge 2 commits into
mainfrom
aggregate-rc-changelogs-on-promotion

Conversation

@piontec

@piontec piontec commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What

When promoting a release candidate to a stable release, the stable version's CHANGELOG.md section now aggregates the changelogs of all its release candidates (oldest → newest) plus any Unreleased delta, merged by Keep a Changelog category, with a note under ### Changed.

Why

A GitHub release page shows only that version's own changelog section. On an RC → stable promotion, architect prepare-release moves just the Unreleased delta into ## [X.Y.Z] — which is usually empty, since all the real changes were already recorded under the ## [X.Y.Z-rc.N] sections. That left the promoted stable release (the one users see as "Latest release", since RCs are marked as pre-releases) showing an empty or near-empty changelog, and it also tripped validate-changelog.yaml's "Empty Changelog" check.

How

A new step, "Aggregate release-candidate changelogs on promotion", runs in the create_release_pr job immediately after architect prepare-release and before the existing commit/push:

  • Promotion detection (token-agnostic): runs only when the target is stable (is_rc == 'false') and CHANGELOG.md already contains ## [X.Y.Z-rc.N] entries for the same core version. Works for #rc-release, an explicit #vX.Y.Z, etc.
  • Merge by category: combines the Unreleased delta (on top) and every RC section (oldest → newest, ordered with sort -V) into a single set of Keep-a-Changelog H3 sections.
  • Note: prepends a bullet under ### Changed"This release aggregates all changes from release candidate(s) X.Y.Z-rc.1 through X.Y.Z-rc.N."
  • Preserves everything else: the RC sections, link references and the ## [Unreleased] heading are left untouched.

The existing commit/push carries the result into the release PR, and create-release.yaml reads that same section verbatim for the GitHub release body — no change needed there. validate-changelog.yaml also needs no change: the merged section always has content, so it passes as-is.

No-op / safe cases: RC releases, stable releases with no matching RC entries (a genuinely empty release still correctly fails validation), and re-runs (idempotent — skips if the note is already present).

Testing

  • Merge logic exercised against fixtures covering: full aggregation, idempotency, no-RC no-op, RC no-op, sort -V ordering (rc.1/rc.2/rc.10), and empty-stable + single RC — all produce the expected output and pass a faithful replica of the validate-changelog.yaml content check.
  • The script extracted directly from this workflow YAML was run against a fixture and produced the expected result with RC sections and link references preserved.
  • yamllint (repo config) and YAML parse pass.

Note: not yet exercised in a live end-to-end release run (which needs the real architect/gitsemver binaries); recommend a promotion run in a sandbox repo before relying on it.

🤖 Generated with Claude Code

…able

On an RC->stable promotion, the stable version's CHANGELOG section now aggregates all changes from its release-candidate entries (oldest->newest) plus any Unreleased delta, merged by Keep a Changelog category, with a note under `### Changed`. A GitHub release page shows only that version's own section, so the promoted stable release now lists the full change set instead of the (often empty) delta left after the RCs were cut. The change is a no-op for RC releases and for stable releases with no matching RC entries, and is idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@piontec piontec requested a review from a team as a code owner July 9, 2026 11:20
…gelog content

Harden the RC-changelog aggregation step from PR review:

- Error out if the '## [<version>]' stable header is absent, instead of
  committing an unchanged changelog while logging success (the splice
  would otherwise discard the merged body silently).
- Guard against a non-Keep-a-Changelog H3 in the 'Unreleased' delta,
  which validate-changelog.yaml never gates: the category merge would
  drop it silently, so now fail and name the offending heading.
- Make extract_section skip leading and trim trailing blank lines so it
  genuinely mirrors create-release.yaml's extractor, as its comment claims.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@uvegla

uvegla commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Maybe there is a better place for this logic in architect, cos we already manipulate the CHANGELOG.md in there, e.g.: https://github.com/giantswarm/architect/blob/63ca66472c0497dabf53ced795825a6bbf2e0d49/cmd/preparerelease/internal/modifier.go#L46. Wdyt?

@piontec

piontec commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by giantswarm/architect#1379, which moves this RC-changelog aggregation into the architect prepare-release binary (the tool that already owns CHANGELOG.md writing) instead of post-processing its output in shell. The Go implementation is byte-for-byte identical to this shell step on the same input.

Once architect ships the change, create-release-pr.yaml just bumps its architect version pin — no shell step needed. Closing this in favor of that approach.

@piontec piontec closed this Jul 9, 2026
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.

2 participants