Repository navigation
Conversation
gh-changelog regenerates the whole CHANGELOG.md on every run, which rewrites and backfills sections of versions that were already released. Add a "Preserve changelog history" step after "Generate changelog" that keeps the generated preamble and newest section and restores the previously released sections from the committed CHANGELOG.md. The step fails instead of losing history when the version is invalid, the generated changelog has no version sections or its newest section is not the requested version, or the committed changelog has headings it cannot restore. A committed Unreleased section is dropped with a warning. "Generate changelog" and "Update metadata.json" now read the version from inputs.version through the environment instead of interpolating github.event.inputs.version into the script. tests/module_release_prep/test_changelog_guard.sh runs the step against fixtures, under bash -e with and without pipefail, and lint.yml runs it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The workflow can advance metadata without a matching changelog section when the committed changelog is missing or title-only, and its version validation rejects valid SemVer inputs containing both prerelease and build metadata.
2 open issues
What changed in this PR
This PR adds changelog-history preservation to the reusable module release-prep workflow, aiming to prevent regeneration from rewriting previously released sections.
Changes:
- Restores committed release history while keeping the newly generated release section, with validation and re-run handling.
- Reads the requested version through environment variables and normalizes a leading
v. - Adds regression checks to CI and documents the new behavior.
| File | Description |
|---|---|
tests/module_release_prep/test_changelog_guard.sh |
Adds preservation, validation, and version-wiring regression checks. |
README.md |
Documents changelog preservation and failure behavior. |
CLAUDE.md |
Documents CI test coverage and the local test command. |
.github/workflows/module_release_prep.yml |
Adds history restoration and updates version handling. |
.github/workflows/lint.yml |
Runs the regression checks with read-only repository permissions. |
Review effort: Balanced
Comment on lines
+86
to
+89
| if [ ! -f CHANGELOG.md ] || ! grep -q '^## \[' CHANGELOG.md; then | ||
| echo "::error::Generated CHANGELOG.md is missing or has no version sections, refusing to drop the committed history" | ||
| exit 1 | ||
| fi |
| run: | | ||
| # Reject anything but a plain version, which also keeps it safe to use in | ||
| # the sed expression of the "Update metadata.json" step | ||
| if [[ "$VERSION" == *$'\n'* ]] || ! [[ "$VERSION" =~ ^v?[0-9]+(\.[0-9]+)*([-+][0-9A-Za-z.-]+)?$ ]]; then |
david22swan
reviewed
Nov 11, 2026
| file_or_dir: ".github/" | ||
| config_file: ".yamllint" | ||
|
|
||
| - name: "Test module_release_prep changelog preservation" |
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.


Summary
Jira: PE-47992
gh changelog newregenerates the wholeCHANGELOG.mdfrom every closed PR and tag, so eachmodule_release_preprun rewrites and backfills sections of versions that were already released. On puppetlabs-peadm PR #749 that removed an existing entry, insertedRevertlines for PRs merged after those releases, and reclassified headers in[v3.38.2]/[v3.38.3]. Each release needed a manual diff-and-revert.gh-changeloghas no flag that fixes this.--latestbuilds a one-entry changelog and--from-versionstill regenerates history, so this takes the ticket's second suggestion: a guard after the generator.Changes
module_release_prep.yml, right after "Generate changelog". It keeps the generated preamble and newest section and restores every older section from the committedCHANGELOG.md(HEAD). A re-run for a version already on top replaces that section instead of duplicating it.1.2.3, optionalv, one-preor+buildsuffix);## [version]sections;##sections, unless it is only a bare title such as# Changelog.Unreleasedsection is dropped with a::warning::.inputs.versionthroughenv:. They previously interpolated${{ github.event.inputs.version }}into the script. In aworkflow_callworkflow that is the caller's event payload, and it is only set when the caller is aworkflow_dispatchwith an input namedversion. This also removes the script injection. A leadingvis stripped for the generator andmetadata.json, which previously receivedvv1.2.3andv1.2.3.tests/module_release_prep/test_changelog_guard.shextracts the step from the workflow and runs it in throwaway git repos underbash -ewith and withoutpipefail: 127 checks covering the ticket's three symptoms, re-runs, CRLF, tab and other header styles, and every failure path. It also asserts the version wiring and step order.lint.ymlnow runs it, withpermissions: contents: read.README.mdandCLAUDE.mdupdated to match.Behaviour changes to be aware of
gh-changelogever omits the section.versioninput was not aworkflow_dispatchinput calledversionnow get the value they actually pass.Known limitations
Section splitting is line based on
##: lines inside code fences count as headers, and text before the first section or under a committedUnreleasedheading is dropped. Changelogs in other styles (setext, plain text, indented headings) keep the generated file with a warning. Legacy headers such as## Version 1.0.0are not recognised as the prepared version on a re-run, which leaves a stale duplicate and loses nothing.Not in this PR
gem_release_prep.ymlhas the samegh changelog newstep and the same history rewrite, and still interpolatesgithub.event.inputs.version. It is left for a follow-up.Testing
tests/module_release_prep/test_changelog_guard.sh: 127 checks pass on macOS (BSD awk) andubuntu:24.04(mawk 1.3.4, GNU grep).yamllint -c .yamllint .github/is clean onubuntu:24.04.Related Issues
Checklist
tests/module_release_prep/test_changelog_guard.sh, run bylint.yml.🤖 Generated with Claude Code