Show VERSIONS.md reason extracts in dwarf status

Parses the gen/schema log lines apply_bump() writes to VERSIONS.md and
prints a truncated reason under each gen/schemaN row, so `dwarf status`
answers "why does this version exist" without opening the changelog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 11:13:35 +02:00
parent 57e044e347
commit 857d8b315f
+52
View File
@@ -26,6 +26,17 @@ from pathlib import Path
GEN_RE = re.compile(r"^gen(\d+)$")
SCHEMA_RE = re.compile(r"^schema(\d+)$")
# Match the log lines written by apply_bump()/plan_bump_gen()/plan_bump_schema():
# - `gen2` (kind=steps) — 2026-01-01 — reason text (by)
# - `gen2`/`schema2` (kind=steps) — 2026-01-01 — reason text (by)
VERSIONS_SCHEMA_LINE_RE = re.compile(
r"^- `(?P<gen>gen\d+)`/`(?P<schema>schema\d+)` \(kind=(?P<kind>[\w-]+)\)"
r"\d{4}-\d{2}-\d{2} — (?P<reason>.+)$"
)
VERSIONS_GEN_LINE_RE = re.compile(
r"^- `(?P<gen>gen\d+)` \(kind=(?P<kind>[\w-]+)\) — \d{4}-\d{2}-\d{2} — (?P<reason>.+)$"
)
# One color per tree level in `dwarf status` output, so the eye can jump
# straight to e.g. "all the schema rows" or "all the totals".
_LEVEL_COLORS = {
@@ -34,6 +45,7 @@ _LEVEL_COLORS = {
"bucket": "\033[34m", # blue — raw/processed subtotals
"schema": "\033[32m", # green — schemaN rows
"root": "\033[1;35m", # bold magenta — derived/, pools/, grand total
"reason": "\033[2m", # dim — VERSIONS.md reason extract under gen/schema rows
}
_RESET = "\033[0m"
@@ -232,6 +244,35 @@ def _referenced_parquet_count(
return total, referenced
def _parse_versions(
versions_path: Path,
) -> tuple[dict[tuple[str, str], str], dict[tuple[str, str, str], str]]:
"""Read VERSIONS.md and return (gen_reasons, schema_reasons) keyed by
(kind, gen_tag) and (kind, gen_tag, schema_tag) respectively. Later entries
for the same key win, since VERSIONS.md is append-only and chronological."""
gen_reasons: dict[tuple[str, str], str] = {}
schema_reasons: dict[tuple[str, str, str], str] = {}
if not versions_path.is_file():
return gen_reasons, schema_reasons
for line in versions_path.read_text().splitlines():
line = line.strip()
m = VERSIONS_SCHEMA_LINE_RE.match(line)
if m:
schema_reasons[(m["kind"], m["gen"], m["schema"])] = m["reason"]
continue
m = VERSIONS_GEN_LINE_RE.match(line)
if m:
gen_reasons[(m["kind"], m["gen"])] = m["reason"]
return gen_reasons, schema_reasons
def _truncate(text: str, width: int = 72) -> str:
text = text.strip()
if len(text) <= width:
return text
return text[: width - 1].rstrip() + ""
def _human_size(n: int) -> str:
size = float(n)
for unit in ("B", "KB", "MB", "GB", "TB"):
@@ -268,12 +309,17 @@ def _row(
return _colorize(row, level) if level else row
def _reason_line(reason: str, indent: int) -> str:
return _colorize(" " * indent + "" + _truncate(reason), "reason")
def print_status(root: Path) -> None:
raw_root = root / "raw"
if not raw_root.is_dir():
print(f"no raw/ tree found under {root}")
return
manifest_referenced = _manifest_referenced_files(root / "pools")
gen_reasons, schema_reasons = _parse_versions(root / "VERSIONS.md")
grand_total = 0
grand_files = 0
for kind_dir in sorted(p for p in raw_root.iterdir() if p.is_dir()):
@@ -316,6 +362,9 @@ def print_status(root: Path) -> None:
kind_files += gen_files
print(_row(gen_tag, gen_total, indent=1, level="gen", count=gen_files))
gen_reason = gen_reasons.get((kind, gen_tag))
if gen_reason:
print(_reason_line(gen_reason, indent=2))
print(
_row(
"raw", raw_size, indent=2, level="bucket",
@@ -337,6 +386,9 @@ def print_status(root: Path) -> None:
count=s_total, referenced=s_referenced,
)
)
schema_reason = schema_reasons.get((kind, gen_tag, f"schema{s}"))
if schema_reason:
print(_reason_line(schema_reason, indent=4))
else:
print(_colorize(" (none)", "schema"))
print(_row(f"{kind} total", kind_total, indent=1, level="gen", count=kind_files))