Files
claude-skills-reference/product-team/roadmap-communicator/scripts/changelog_generator.py

99 lines
2.8 KiB
Python
Executable File

#!/usr/bin/env python3
"""Generate changelog sections from git log using conventional commit prefixes."""
import argparse
import subprocess
from collections import defaultdict
SECTIONS = {
"feat": "Features",
"fix": "Fixes",
"docs": "Documentation",
"refactor": "Refactors",
"test": "Tests",
"chore": "Chores",
"perf": "Performance",
"ci": "CI",
"build": "Build",
"style": "Style",
"revert": "Reverts",
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Generate changelog from git commits.")
parser.add_argument("--from", dest="from_ref", default="HEAD~50")
parser.add_argument("--to", dest="to_ref", default="HEAD")
parser.add_argument("--format", choices=["markdown", "text"], default="markdown")
return parser.parse_args()
def get_git_log(from_ref: str, to_ref: str) -> list[str]:
commit_range = f"{from_ref}..{to_ref}"
cmd = ["git", "log", "--pretty=format:%s", commit_range]
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
lines = [line.strip() for line in result.stdout.splitlines() if line.strip()]
return lines
def group_commits(subjects: list[str]) -> dict[str, list[str]]:
grouped = defaultdict(list)
grouped["other"] = []
for subject in subjects:
commit_type = "other"
for prefix in SECTIONS:
if subject.startswith(f"{prefix}:"):
commit_type = prefix
break
grouped[commit_type].append(subject)
return grouped
def render_markdown(grouped: dict[str, list[str]]) -> str:
out = ["# Changelog", ""]
ordered_types = list(SECTIONS.keys()) + ["other"]
for commit_type in ordered_types:
commits = grouped.get(commit_type, [])
if not commits:
continue
header = SECTIONS.get(commit_type, "Other")
out.append(f"## {header}")
for item in commits:
out.append(f"- {item}")
out.append("")
return "\n".join(out).rstrip() + "\n"
def render_text(grouped: dict[str, list[str]]) -> str:
out = []
ordered_types = list(SECTIONS.keys()) + ["other"]
for commit_type in ordered_types:
commits = grouped.get(commit_type, [])
if not commits:
continue
header = SECTIONS.get(commit_type, "Other")
out.append(header.upper())
for item in commits:
out.append(f"* {item}")
out.append("")
return "\n".join(out).rstrip() + "\n"
def main() -> int:
args = parse_args()
subjects = get_git_log(args.from_ref, args.to_ref)
grouped = group_commits(subjects)
if args.format == "markdown":
print(render_markdown(grouped), end="")
else:
print(render_text(grouped), end="")
return 0
if __name__ == "__main__":
raise SystemExit(main())