#!/usr/bin/env python3 """Sync the Settings "What's New" modal with CHANGELOG.md. The modal (neode-ui/src/views/settings/AccountInfoSection.vue) hardcodes one HTML block per release. It has repeatedly drifted behind CHANGELOG.md (it sat at v1.7.84 while the fleet shipped through v1.7.92). This script is the fix: for every version in CHANGELOG.md that has no block in the modal, it generates a block (from the curated CHANGELOG bullets) and inserts it newest-first. python3 scripts/sync-whats-new.py # insert any missing blocks python3 scripts/sync-whats-new.py --check # exit 1 if anything is missing Dev-process bullets ("Validation passed…/pending…") are dropped — the modal is user-facing. Only CHANGELOG versions are managed; older hand-written blocks (pre-CHANGELOG history) are never touched or removed. """ import re import sys import html from pathlib import Path REPO = Path(__file__).resolve().parent.parent CHANGELOG = REPO / "CHANGELOG.md" MODAL = REPO / "neode-ui/src/views/settings/AccountInfoSection.vue" MONTHS = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] HEADER_RE = re.compile(r"^## (v\d+\.\d+\.\d+\S*) \((\d{4})-(\d{2})-(\d{2})\)") def parse_changelog(): """Return [(version, 'Month D, YYYY', [bullet, ...]), ...] newest-first.""" entries = [] cur = None for line in CHANGELOG.read_text().splitlines(): m = HEADER_RE.match(line) if m: ver, y, mo, d = m.groups() cur = {"ver": ver, "date": f"{MONTHS[int(mo)]} {int(d)}, {y}", "bullets": []} entries.append(cur) continue if cur is not None and line.startswith("- "): text = line[2:].strip() if text.lower().startswith("validation "): continue # dev-process note, not user-facing cur["bullets"].append(text) return entries def existing_versions(): text = MODAL.read_text() return set(re.findall(r"", text)) def to_html(text): text = text.replace("`", "") # drop markdown code ticks (plain prose) return html.escape(text, quote=False) # & < > (Vue template-safe) def render_block(entry): paras = "\n".join( f"
{to_html(b)}
" for b in entry["bullets"] ) return ( f" \n" f"