Skip to content

ci(release): 👷 carry the upgrade notes from merged PRs into the release - #772

Merged
rhammen merged 1 commit into
mainfrom
ci/release-notes-from-prs
Aug 27, 2026
Merged

ci(release): 👷 carry the upgrade notes from merged PRs into the release#772
rhammen merged 1 commit into
mainfrom
ci/release-notes-from-prs

Conversation

@rhammen

@rhammen rhammen commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

🔍 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:

PR Heading it used
#728 ## ⚠️ Behaviour change worth noting in the release notes
#735 ## ⚠️ Upgrade note
#748 ## ⚠️ Upgrade note for the release
#750 ## 🔗 Migration
#759 ## ⚠️ Breaking change for dump consumers
#762 ## ⚠️ Note for release notes

None of them reached a release automatically. 2026.08.21 and 2026.08.23 are the bare title list; 2026.08.15, .17 and .18 carry 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 since PREVIOUS_STABLE (the tag the workflow already computes), lifts the upgrade-note section out of each body, and writes release_notes.md.
  • release.yml — a new step before the release, and body_path: release_notes.md on the release itself. The action pre-pends the body to the generated notes, so the result is the layout 2026.08.18 already had: upgrade notes first, then ## What's Changed.
  • .github/pull_request_template.md — new; the repo had none. It offers the canonical ## ⚠️ Upgrade notes heading, 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:

Three more details came out of the real bodies:

  • feat(sensor,const,lux_overrides): ✨ expose the cooling heat quantity counter #762's section ran to the end of the PR, so Refs #752 and the Claude Code trailer were being swallowed → trailers are stripped.
  • The 2026.08.18 note used ### subsections, so a section ends only at a heading of the same or higher level.
  • An untouched template section must yield nothing → HTML comments are stripped and an empty section is dropped, so leaving the template heading in place produces no note rather than an empty one.

Full suite 1169 passed · integration coverage still 100% · ruff check + format clean · codespell clean.

🔬 How to check it before trusting it

The gh api calls 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.

⚠️ Known limit

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

- 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
@rhammen
rhammen merged commit 630a84f into main Aug 27, 2026
8 checks passed
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.

1 participant