# -*- coding: utf-8 -*- """Backups IA-split move check (v0.124.0, one-shot) — the split MOVED the v0.123.0 sections, it did not rewrite them: every moved block from the pre-split backups.html (commit df7ad37) must appear, whitespace-normalized, on EXACTLY its mapped page. Allowed divergences are encoded explicitly (the tier-3 anchor retarget; the restore-to-verify form's relocation). Run from controller/: python scripts/backups_split_move_check.py """ import io, os, re, subprocess, sys BASELINE = "df7ad37" OLD_PATH = "controller/internal/web/templates/backups.html" TPL = os.path.join("internal", "web", "templates") # (name, 1-indexed start, end inclusive, target template file, normalizer applied to the NEW page) BLOCKS = [ ("storage-overview", 32, 77, "backups.html", None), ("whole-guest", 79, 124, "backups.html", None), ("stat-cards", 227, 271, "backups.html", None), ("offbox-pre", 127, 186, "backups_remote.html", None), ("offbox-post", 193, 221, "backups_remote.html", None), ("apps-divider", 224, 225, "backups_apps.html", None), ("schedule", 273, 302, "backups_apps.html", None), ("databases", 304, 376, "backups_apps.html", None), # the tier-3 action anchors were retargeted cross-page — normalize them back before comparing ("per-app-rows", 378, 541, "backups_apps.html", lambda s: s.replace('href="/backups/remote#offbox-section"', 'href="#offbox-section"')), ("restore-panel", 543, 586, "backups_restore.html", None), ("offbox-verify-form", 188, 191, "backups_restore.html", None), ("banner-js", 591, 627, "backups_shared.html", None), ("apps-js", 628, 674, "backups_apps.html", None), ("guest-js", 676, 712, "backups.html", None), ("restore-js", 714, 841, "backups_restore.html", None), ] PAGES = ["backups.html", "backups_remote.html", "backups_apps.html", "backups_restore.html", "backups_shared.html"] def norm(s): return re.sub(r"\s+", " ", s).strip() def main(): old = subprocess.run(["git", "show", BASELINE + ":" + OLD_PATH], capture_output=True, text=True, encoding="utf-8") if old.returncode != 0: print("cannot read baseline %s:%s — %s" % (BASELINE, OLD_PATH, old.stderr.strip())) sys.exit(2) old_lines = old.stdout.split("\n") pages = {} for fn in PAGES: pages[fn] = io.open(os.path.join(TPL, fn), encoding="utf-8").read() failed = 0 for name, a, b, target, fix in BLOCKS: block = norm("\n".join(old_lines[a - 1:b])) hits = [] for fn in PAGES: content = pages[fn] if fix: content = fix(content) if block in norm(content): hits.append(fn) if hits != [target]: failed += 1 print("MOVED-BLOCK MISMATCH %-18s (old L%d-%d): expected [%s], found %s" % (name, a, b, target, hits)) if failed: print("MOVE CHECK FAILED: %d block(s) rewritten, duplicated or lost" % failed) sys.exit(1) print("move check OK — all %d v0.123.0 blocks moved verbatim to their mapped page" % len(BLOCKS)) if __name__ == "__main__": main()