Files
pkgmgr/src/pkgmgr/actions/review/render.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 1b31b10df6 feat(review): render a branch review as a local HTML page
pkgmgr review writes one self-contained HTML page for a repository: commits ahead of the base, the staged, unstaged and untracked diff per file, and the checks of the change set. Repository text reaches the packaged Jinja template only as autoescaped data, git runs in scan mode (now also without replace objects), and the page is written atomically through mkstemp and os.replace.

🎯 Fix: 92 %
💥 Break: 3 %
💸 Cost: new
⏱️ Time: new
🛡️ Security: 100 %
🏆 Score: 89
🚦 Gate: cost new, time new: cleared by the operator's commit word for C (2026-11-07)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:39:52 +00:00

87 lines
2.7 KiB
Python

from __future__ import annotations
import base64
import hashlib
import os
import tempfile
from jinja2 import Environment, FileSystemLoader, StrictUndefined
from .collect import collect_review
from .model import Review, ReviewError
TEMPLATE = "review.html.j2"
INVISIBLE = {
code: f"<U+{code:04X}>"
for code in (
*range(0x200B, 0x2010),
*range(0x202A, 0x202F),
*range(0x2066, 0x206A),
0x061C,
0xFEFF,
)
}
def _csp_hash(text: str) -> str:
digest = hashlib.sha256(str(text).encode("utf-8")).digest()
return "sha256-" + base64.b64encode(digest).decode("ascii")
def _visible(value: object) -> object:
# not isinstance: Markup.translate() would splice the marks in unescaped,
# and macro output is Markup whose text was already marked when built
return value.translate(INVISIBLE) if type(value) is str else value
def render_review(review: Review) -> str:
environment = Environment(
loader=FileSystemLoader(os.path.dirname(os.path.abspath(__file__))),
autoescape=True,
undefined=StrictUndefined,
trim_blocks=True,
lstrip_blocks=True,
keep_trailing_newline=True,
finalize=_visible,
)
environment.filters["csp_hash"] = _csp_hash
return environment.get_template(TEMPLATE).render(review=review)
def write_review(
cwd: str, out: str, max_file_lines: int, base: str | None, notes: list[str]
) -> str:
"""
Args:
cwd: Directory inside the git working copy to review.
out: Output HTML path, relative to ``cwd`` unless absolute; written as
a new mode 0600 file renamed onto it, so a symbolic link there is
replaced, never followed.
max_file_lines: Diff lines shown per file before the rest is summarised.
base: Branch whose ``REF..HEAD`` commits are listed, or None.
notes: Text files shown verbatim at the top of the page.
Returns:
The absolute path of the written page.
"""
html = render_review(
collect_review(cwd=cwd, max_file_lines=max_file_lines, base=base, notes=notes)
)
path = os.path.abspath(os.path.join(cwd, out))
directory = os.path.dirname(path)
try:
os.makedirs(directory, exist_ok=True)
fd, temp = tempfile.mkstemp(prefix=".review-", suffix=".tmp", dir=directory)
try:
with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as handle:
handle.write(html)
handle.flush()
os.fsync(handle.fileno())
os.replace(temp, path)
except OSError:
os.unlink(temp)
raise
except OSError as exc:
raise ReviewError(f"cannot write {path!r}: {exc.strerror}") from None
return path