7878 # check-release.yml gates merges on a `## [x.y.z]` CHANGELOG section
7979 # matching the bumped version, so without a seeded entry every regen PR
8080 # fails that check. Insert a stub (the spec-change title under ### Changed)
81- # right after `## [Unreleased]`; the PR author refines the wording before
82- # merge. Idempotent: skips if a section for this version already exists.
81+ # just above the most recent released section; the PR author refines the
82+ # wording before merge. Idempotent: skips if a section for this version
83+ # already exists.
8384 - name : Seed changelog entry
8485 env :
8586 VERSION : ${{ steps.pkg.outputs.version }}
@@ -97,13 +98,16 @@ jobs:
9798 if re.search(rf"^## \[{re.escape(version)}\]", text, re.M):
9899 print(f"CHANGELOG already has a [{version}] section; leaving it untouched.")
99100 raise SystemExit(0)
100- marker = "## [Unreleased]\n"
101- idx = text.find(marker)
102- if idx == -1:
101+ unreleased = re.search(r"^## \[Unreleased\]", text, re.M)
102+ if not unreleased:
103103 raise SystemExit("CHANGELOG.md has no '## [Unreleased]' section to anchor the new entry")
104- insert_at = idx + len(marker)
105- entry = f"## [{version}] - {date}\n\n### Changed\n\n- {title}\n"
106- text = text[:insert_at] + "\n" + entry + "\n" + text[insert_at:].lstrip("\n")
104+ # Insert before the first released section after [Unreleased] (falling
105+ # back to end of file) so any pending entries under [Unreleased] stay
106+ # attributed to it rather than being absorbed by the new version.
107+ nxt = re.search(r"^## \[", text[unreleased.end():], re.M)
108+ insert_at = unreleased.end() + nxt.start() if nxt else len(text)
109+ entry = f"## [{version}] - {date}\n\n### Changed\n\n- {title}\n\n"
110+ text = text[:insert_at] + entry + text[insert_at:]
107111 path.write_text(text)
108112 print(f"Inserted CHANGELOG [{version}] section.")
109113 PY
0 commit comments