From cf525d83d4351570dc983bc3c0c6e0a3c3c9858e Mon Sep 17 00:00:00 2001 From: Elaine Lyons Date: Wed, 28 Oct 2026 23:18:10 -0400 Subject: [PATCH] Collapse the detailed changelog lists in release notes The release page keeps the install steps and the version's intro paragraph visible, and puts the Added/Changed/Fixed lists in a
block. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 5 +-- scripts/changelog_section.py | 26 ++++++++++++--- tests/test_changelog_section.py | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 tests/test_changelog_section.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 91ddaf9..8034c8a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -160,8 +160,9 @@ jobs: echo echo "## Changes" echo - # The CHANGELOG section for this version, when there is one. - python3 scripts/changelog_section.py "${GITHUB_REF_NAME#v}" || echo "See CHANGELOG.md." + # The CHANGELOG section for this version, when there is one: its + # intro, with the detailed lists collapsed. + python3 scripts/changelog_section.py --collapse "${GITHUB_REF_NAME#v}" || echo "See CHANGELOG.md." } > notes.md cat notes.md - name: Create the GitHub release diff --git a/scripts/changelog_section.py b/scripts/changelog_section.py index 3771654..e4e0d72 100755 --- a/scripts/changelog_section.py +++ b/scripts/changelog_section.py @@ -1,11 +1,15 @@ #!/usr/bin/env python3 """Print the CHANGELOG.md section for one version (release-notes helper). - scripts/changelog_section.py 0.1.0-rc1 + scripts/changelog_section.py [--collapse] 0.1.0-rc1 Matches the `## []` heading and prints everything up to the next `## ` heading. Exits 1 when the version has no section, so the release workflow can fall back to a generic line. + +With --collapse, the section's intro (everything before its first `### ` +heading) stays visible and the rest goes in a collapsed `
` block, so +a release page leads with the summary. """ from __future__ import annotations @@ -23,16 +27,28 @@ def section(text: str, version: str) -> str | None: return m.group(1).strip() if m else None +def collapse(body: str) -> str: + m = re.search(r"^### ", body, re.M) + if m is None: + return body + intro, details = body[:m.start()].strip(), body[m.start():].strip() + # GitHub only renders Markdown inside
with blank lines around it. + folded = f"
\nFull changelog\n\n{details}\n\n
" + return f"{intro}\n\n{folded}" if intro else folded + + def main(argv: list[str]) -> int: - if len(argv) != 1: + folded = "--collapse" in argv + args = [a for a in argv if a != "--collapse"] + if len(args) != 1: print(__doc__, file=sys.stderr) return 2 path = Path(__file__).resolve().parent.parent / "CHANGELOG.md" - body = section(path.read_text(encoding="utf-8"), argv[0]) + body = section(path.read_text(encoding="utf-8"), args[0]) if body is None: - print(f"no CHANGELOG section for {argv[0]}", file=sys.stderr) + print(f"no CHANGELOG section for {args[0]}", file=sys.stderr) return 1 - print(body) + print(collapse(body) if folded else body) return 0 diff --git a/tests/test_changelog_section.py b/tests/test_changelog_section.py new file mode 100644 index 0000000..4a8f0d9 --- /dev/null +++ b/tests/test_changelog_section.py @@ -0,0 +1,57 @@ +"""Release notes from CHANGELOG.md: scripts/changelog_section.py.""" + +import importlib.util +import unittest +from pathlib import Path + +_PATH = Path(__file__).resolve().parent.parent / "scripts" / "changelog_section.py" +_spec = importlib.util.spec_from_file_location("changelog_section", _PATH) +changelog_section = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(changelog_section) + +CHANGELOG = """# Changelog + +## [Unreleased] + +## [2.0.0] - 2026-11-01 + +Copying replaces merging. + +- Works on the Steam Frame + +### Added + +- A thing + +### Fixed + +- Another thing + +## [1.0.0] - 2026-09-21 + +### Added + +- First release +""" + + +class CollapseTests(unittest.TestCase): + def test_keeps_the_intro_and_folds_the_lists(self): + body = changelog_section.section(CHANGELOG, "2.0.0") + self.assertEqual(changelog_section.collapse(body), ( + "Copying replaces merging.\n\n- Works on the Steam Frame\n\n" + "
\nFull changelog\n\n" + "### Added\n\n- A thing\n\n### Fixed\n\n- Another thing\n\n
")) + + def test_a_section_without_an_intro_is_all_folded(self): + body = changelog_section.section(CHANGELOG, "1.0.0") + self.assertEqual(changelog_section.collapse(body), ( + "
\nFull changelog\n\n" + "### Added\n\n- First release\n\n
")) + + def test_a_section_without_subheadings_is_unchanged(self): + self.assertEqual(changelog_section.collapse("Just a note."), "Just a note.") + + +if __name__ == "__main__": + unittest.main()