ci(release): 👷 carry the upgrade notes from merged PRs into the release - #772
Merged
Conversation
- the release workflow asks GitHub to generate its notes, which yields the PR titles and nothing else, so anything an author wrote for users was lost: #728, #735, #748, #750, #759 and #762 each carried an upgrade note and none of them reached a release. The 2026.08.15, .17 and .18 releases carry the same block pasted in by hand three times instead - collect_release_notes.py walks the PRs merged since the previous full release, lifts the note out of each body and writes it to a file the release step passes as body_path, which the action pre-pends to the generated notes - matching is loose on purpose: those six PRs each invented their own heading, so any heading mentioning an upgrade note, release note, breaking change, migration or behaviour change counts. Verified against all 60 PRs merged since June: it finds exactly those six and nothing else - a heading that negates the keyword is skipped, since #749 wrote "No behaviour change" and meant the opposite; issue trailers, the generated-with line and HTML comments are stripped so a section running to the end of the body does not drag them in - add a PR template offering the canonical heading, with the instructions in a comment so an untouched section produces no note - notes repeat across consecutive pre-releases, matching both the existing previous_tag behaviour and what was being done by hand
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔍 What this fixes
The release workflow asks GitHub to generate its notes, which yields the PR titles and nothing else. So anything a PR author wrote for users never reached a release.
That has been happening all along. Six PRs merged since June each carried an upgrade note, under six different headings:
## ⚠️ Behaviour change worth noting in the release notes## ⚠️ Upgrade note## ⚠️ Upgrade note for the release## 🔗 Migration## ⚠️ Breaking change for dump consumers## ⚠️ Note for release notesNone of them reached a release automatically.
2026.08.21and2026.08.23are the bare title list;2026.08.15,.17and.18carry the #748 and #735 notes pasted in by hand, the same block three times, because each pre-release spans back to the last full release.✨ Changes
.github/scripts/collect_release_notes.py— walks the PRs merged sincePREVIOUS_STABLE(the tag the workflow already computes), lifts the upgrade-note section out of each body, and writesrelease_notes.md.release.yml— a new step before the release, andbody_path: release_notes.mdon the release itself. The action pre-pends the body to the generated notes, so the result is the layout2026.08.18already had: upgrade notes first, then## What's Changed..github/pull_request_template.md— new; the repo had none. It offers the canonical## ⚠️ Upgrade notesheading, with the instructions in an HTML comment and a note that the text is copied verbatim into the release, so it should be written for the person installing the update.Matching is loose on purpose — any second- or third-level heading mentioning an upgrade note, release note, breaking change, migration or behaviour change counts. Requiring one exact heading would mean all six PRs above missed.
🧪 Tests
16 tests, and the extractor was validated against real data rather than only fixtures: run over all 60 PRs merged since June it finds exactly the six above and nothing else.
That check earned its keep — it caught a false positive that would otherwise have shipped:
## ✅ No behaviour change— matches the keyword while meaning the opposite. Negated headings are now skipped.Three more details came out of the real bodies:
Refs #752and the Claude Code trailer were being swallowed → trailers are stripped.2026.08.18note used###subsections, so a section ends only at a heading of the same or higher level.Full suite 1169 passed · integration coverage still 100% ·
ruff check+formatclean ·codespellclean.🔬 How to check it before trusting it
The
gh apicalls only run inside the workflow, but the script is runnable locally — this reproduces exactly what the next release would prepend:GITHUB_REPOSITORY=BenPru/luxtronik PREVIOUS_STABLE=2026.08.18 GITHUB_SHA=main \ python3 .github/scripts/collect_release_notes.py && cat release_notes.md#771 is the first PR carrying the canonical heading, so it is the first one that will appear in a release automatically.
The compare API returns at most 250 commits. A release spanning more than that would silently lose the notes of its oldest PRs, so the script emits a
::warning::in the job log rather than failing quietly. Far beyond this repo's release cadence.🤖 Generated with Claude Code