72 lines
2.2 KiB
Python
72 lines
2.2 KiB
Python
# app/services/changelog_loader.py
|
|
from __future__ import annotations
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any, List, Optional, Dict
|
|
import yaml
|
|
|
|
from flask import Blueprint, current_app, render_template
|
|
|
|
@dataclass
|
|
class ChangeItem:
|
|
title: str
|
|
details: List[str]
|
|
|
|
@dataclass
|
|
class VersionLog:
|
|
version: str
|
|
features: List[ChangeItem]
|
|
refactors: List[ChangeItem]
|
|
fixes: List[ChangeItem]
|
|
notes: List[str]
|
|
|
|
@dataclass
|
|
class Changelog:
|
|
unreleased: Dict[str, List[ChangeItem]]
|
|
versions: List[VersionLog]
|
|
|
|
def _coerce_items(items: Optional[List[Dict[str, Any]]]) -> List[ChangeItem]:
|
|
out: List[ChangeItem] = []
|
|
for it in items or []:
|
|
title = str(it.get("title", "")).strip()
|
|
details = [str(d) for d in (it.get("details") or [])]
|
|
out.append(ChangeItem(title=title, details=details))
|
|
return out
|
|
|
|
def load_changelog(path: Path) -> Changelog:
|
|
"""
|
|
Load changelog.yaml and coerce into dataclasses.
|
|
"""
|
|
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
|
|
unreleased = {
|
|
"features": _coerce_items(data.get("unreleased", {}).get("features")),
|
|
"refactors": _coerce_items(data.get("unreleased", {}).get("refactors")),
|
|
"fixes": _coerce_items(data.get("unreleased", {}).get("fixes")),
|
|
}
|
|
|
|
versions: List[VersionLog] = []
|
|
for v in data.get("versions", []):
|
|
versions.append(
|
|
VersionLog(
|
|
version=str(v.get("version")),
|
|
features=_coerce_items(v.get("features")),
|
|
refactors=_coerce_items(v.get("refactors")),
|
|
fixes=_coerce_items(v.get("fixes")),
|
|
notes=[str(n) for n in (v.get("notes") or [])],
|
|
)
|
|
)
|
|
|
|
return Changelog(unreleased=unreleased, versions=versions)
|
|
|
|
|
|
bp = Blueprint("changelog", __name__)
|
|
|
|
@bp.route("/changelog")
|
|
def view_changelog():
|
|
# Configurable path with sensible default at project root
|
|
cfg_path = current_app.config.get("CHANGELOG_FILE")
|
|
path = Path(cfg_path) if cfg_path else (Path(current_app.root_path).parent / "changelog.yaml")
|
|
changelog = load_changelog(path)
|
|
return render_template("changelog.html", changelog=changelog)
|