Files
qbz/scripts/audit-frontend-invokes.py
T
vicrodh 11b3486c63 fix(v2): close frontend/backend command audit gaps
- add/register missing v2 commands surfaced by audit
- migrate frontend invokes to v2 equivalents
- eliminate legacy invoke callsites in audit report
- harden invoke audit parser for generic/multiline invoke usage

cx
2026-02-18 12:59:28 -06:00

122 lines
3.9 KiB
Python
Executable File

#!/usr/bin/env python3
from __future__ import annotations
import json
import re
import sys
from collections import Counter
from pathlib import Path
def load_text(path: Path) -> str:
return path.read_text(encoding="utf-8", errors="ignore")
def main() -> int:
root = Path(__file__).resolve().parent.parent
front_globs = [
"src/lib/**/*.ts",
"src/lib/**/*.svelte",
"src/routes/**/*.ts",
"src/routes/**/*.svelte",
]
# Match both invoke('cmd') and invoke<Type>('cmd')
invoke_pat = re.compile(r"invoke(?:<[^>]*>)?\(\s*['\"]([^'\"]+)['\"]")
registrations_pat = re.compile(r"commands_v2::(v2_[A-Za-z0-9_]+|runtime_[A-Za-z0-9_]+)")
front_paths = []
for g in front_globs:
front_paths.extend(root.glob(g))
invokes: list[tuple[str, str]] = []
for path in front_paths:
text = load_text(path)
rel = path.relative_to(root)
for m in invoke_pat.finditer(text):
line_no = text.count("\n", 0, m.start()) + 1
invokes.append((m.group(1), f"{rel}:{line_no}"))
lib_rs = load_text(root / "src-tauri/src/lib.rs")
registered_v2 = set(registrations_pat.findall(lib_rs))
legacy_patterns = [
r"commands::([A-Za-z0-9_]+)",
r"library::commands::([A-Za-z0-9_]+)",
r"cast::commands::([A-Za-z0-9_]+)",
r"cast::dlna::commands::([A-Za-z0-9_]+)",
r"offline_cache::commands::([A-Za-z0-9_]+)",
r"offline::commands::([A-Za-z0-9_]+)",
r"network::commands::([A-Za-z0-9_]+)",
r"lyrics::commands::([A-Za-z0-9_]+)",
r"reco_store::commands::([A-Za-z0-9_]+)",
]
registered_legacy = set()
for pat in legacy_patterns:
registered_legacy |= set(re.findall(pat, lib_rs))
v2_ok: list[tuple[str, str]] = []
missing_v2: list[tuple[str, str]] = []
legacy_calls: list[tuple[str, str]] = []
unknown: list[tuple[str, str]] = []
for cmd, loc in invokes:
if cmd.startswith("v2_") or cmd.startswith("runtime_"):
if cmd in registered_v2:
v2_ok.append((cmd, loc))
else:
missing_v2.append((cmd, loc))
else:
if cmd in registered_legacy:
legacy_calls.append((cmd, loc))
else:
unknown.append((cmd, loc))
summary = {
"total_callsites": len(invokes),
"v2_ok_callsites": len(v2_ok),
"missing_v2_callsites": len(missing_v2),
"legacy_callsites": len(legacy_calls),
"unknown_callsites": len(unknown),
"unique_total": len({c for c, _ in invokes}),
"unique_v2_ok": len({c for c, _ in v2_ok}),
"unique_missing_v2": len({c for c, _ in missing_v2}),
"unique_legacy": len({c for c, _ in legacy_calls}),
"unique_unknown": len({c for c, _ in unknown}),
}
report_dir = root / "tmp"
report_dir.mkdir(parents=True, exist_ok=True)
(report_dir / "frontend_invoke_summary.json").write_text(
json.dumps(summary, indent=2), encoding="utf-8"
)
def write_tsv(name: str, rows: list[tuple[str, str]]) -> None:
with (report_dir / name).open("w", encoding="utf-8") as fh:
for cmd, loc in rows:
fh.write(f"{cmd}\t{loc}\n")
write_tsv("frontend_v2_ok_callsites.tsv", v2_ok)
write_tsv("frontend_missing_v2_callsites.tsv", missing_v2)
write_tsv("frontend_legacy_callsites.tsv", legacy_calls)
write_tsv("frontend_unknown_callsites.tsv", unknown)
print(json.dumps(summary, indent=2))
print("\nTop missing_v2 commands:")
for cmd, count in Counter(c for c, _ in missing_v2).most_common(20):
print(f"{count}\t{cmd}")
print("\nTop legacy commands:")
for cmd, count in Counter(c for c, _ in legacy_calls).most_common(20):
print(f"{count}\t{cmd}")
# Hard fail if frontend still invokes any legacy command.
if legacy_calls:
return 1
return 0
if __name__ == "__main__":
sys.exit(main())