- New Features - Introduced distinct sections for Performance, Refactor, Tests, Style, and Chore in changelog generation. - Added dedicated emojis for each new category. - Updated section ordering to reflect the new categories. - Enhancements - Enhanced push logic to fetch upstream before push, avoiding non‑fast‑forward rejections. - Refactored `is_ahead_of_remote` with a fetch flag to reduce network calls and TOCTOU windows. - Improved error logging with full traceback for group processing failures. - Bug Fixes - Fixed emoji mappings for "Style" and "Other Changes". - Corrected handling of empty upstream ref to prevent errors. - Ensured proper exception handling when remote branch is missing.
73 lines
2.2 KiB
Python
73 lines
2.2 KiB
Python
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from devgen.modules.changelog_generator import ChangelogGenerator
|
|
from devgen.utils import configure_logger
|
|
|
|
|
|
class ReleaseNotesGenerator(ChangelogGenerator):
|
|
"""Generate short, clean release notes from git history."""
|
|
|
|
SECTION_EMOJIS = {
|
|
"BREAKING CHANGES": "⚠️",
|
|
"Features": "✨",
|
|
"Bug Fixes": "🐛",
|
|
"Performance": "⚡",
|
|
"Documentation": "📚",
|
|
"Refactor": "♻️",
|
|
"Tests": "✅",
|
|
"Chore": "🔧",
|
|
"Style": "💄",
|
|
"Other Changes": "🧹",
|
|
}
|
|
|
|
def __init__(self, logger=None):
|
|
super().__init__(logger or configure_logger("devgen.releasenotes"))
|
|
|
|
def generate_release_markdown(self, groups, version="Unreleased"):
|
|
"""Generate human-friendly release notes."""
|
|
date_str = datetime.now().strftime("%Y-%m-%d")
|
|
md = [f"## 🚀 Release {version} — {date_str}\n"]
|
|
|
|
order = [
|
|
"BREAKING CHANGES",
|
|
"Features",
|
|
"Bug Fixes",
|
|
"Performance",
|
|
"Documentation",
|
|
"Refactor",
|
|
"Tests",
|
|
"Style",
|
|
"Chore",
|
|
"Other Changes",
|
|
]
|
|
|
|
for section in order:
|
|
commits = groups.get(section)
|
|
if not commits:
|
|
continue
|
|
|
|
emoji = self.SECTION_EMOJIS.get(section, "")
|
|
md.append(f"### {emoji} {section}")
|
|
|
|
for c in commits:
|
|
scope = f"**{c['scope']}**: " if c["scope"] else ""
|
|
md.append(f"- {scope}{c['subject']}")
|
|
|
|
md.append("")
|
|
|
|
return "\n".join(md)
|
|
|
|
def run(self, output_file="RELEASE-NOTES.md", version="Unreleased", from_ref=""):
|
|
"""Main logic: get commits → parse → generate → write."""
|
|
raw_commits = self.get_commits(from_ref)
|
|
if not raw_commits or not raw_commits[0]:
|
|
print("❗ No commits found for release notes.")
|
|
return
|
|
|
|
parsed = self.parse_commits(raw_commits)
|
|
md = self.generate_release_markdown(parsed, version=version)
|
|
|
|
Path(output_file).write_text(md, encoding="utf-8")
|
|
print(f" Release notes written to {output_file}")
|