54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""Print the CHANGELOG.md section for a version, for use as release notes.
|
|
|
|
VERSION accepts a tag name ("v2.7.0") or a plain version ("2.7.0"). Older
|
|
sections are titled with two components and a name, such as
|
|
"## Version 2.5: Help Formatter", so "2.5.0" also matches "## Version 2.5".
|
|
Each section keeps its own link definitions, so the output needs no fixup.
|
|
|
|
With --title, print the section heading instead of the body; this is the name
|
|
of the GitHub release.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
def extract(text: str, version: str) -> tuple[str, str] | None:
|
|
"""Return the heading and the body of the section for a version."""
|
|
heads = list(re.finditer(r"^## (Version (\S+?):?(?:[ \t].*)?)$", text, re.M))
|
|
for candidate in (version, version.rsplit(".", 1)[0]):
|
|
for i, head in enumerate(heads):
|
|
if head.group(2) != candidate:
|
|
continue
|
|
end = heads[i + 1].start() if i + 1 < len(heads) else len(text)
|
|
return head.group(1), text[head.end() : end].strip()
|
|
return None
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
|
|
)
|
|
parser.add_argument("version", help='tag ("v2.7.0") or version ("2.7.0")')
|
|
parser.add_argument("changelog", nargs="?", type=Path, default=Path("CHANGELOG.md"))
|
|
parser.add_argument(
|
|
"--title", action="store_true", help="print the heading, not the body"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
version = args.version.lstrip("v")
|
|
section = extract(args.changelog.read_text(encoding="utf-8"), version)
|
|
if section is None:
|
|
raise SystemExit(f"No '## Version {version}' section in {args.changelog}")
|
|
heading, notes = section
|
|
print(heading if args.title else notes)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|