Files
OmniNX-Installer-Payload/tools/audit_update_list.py
T
niklascfw c033b988ca Update mode: stop deleting user-installed apps and sysmodules
The update list deleted ~80 apps the pack never shipped (Goldleaf,
linkalho, Moonlight, NX-Shell, ...), Downloader sysmodules (SysDVR,
MissionControl, ldn_mitm, Fizeau), game title ID folders (mods/cheats),
and user folders like "Patched Apps" or "games". Anything a user
installed via OmniNX Downloader was wiped on every update.

Update mode only runs on an existing OmniNX install, so the list now
keeps only what the pack ships, components OmniNX dropped, and
boot-relevant housekeeping. Foreign-pack leftovers stay in the clean
list. Also drops 15 entries nested in deleted folders, a case-duplicate
(themezernx) and a trailing-space path.

tools/audit_update_list.py checks the list against the OmniNX repo.
2026-11-12 22:53:28 +01:00

78 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""Audit source/deletion_lists_update.h against the OmniNX pack repo.
Lists every update-mode deletion entry and whether the pack ships it now,
shipped it in the past, or never did. Entries under switch/ or config/ that
the pack never shipped are almost always user installs and should not be in
the update list (see the header comment in deletion_lists_update.h).
usage: python3 tools/audit_update_list.py ../OmniNX
"""
import re
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
UPDATE_H = ROOT / "source" / "deletion_lists_update.h"
PACK_PREFIX = re.compile(r"^(?:variants/[^/]+/(?:extras/hekate-ams-pro/)?|staging/)")
# Not shipped as files, but reset on purpose during updates.
INTENTIONAL = {
"config/nx-hbmenu", # hbmenu settings (hbmenu.nro is shipped)
"config/sys-patch", # sys-patch settings (sys-patch is shipped)
"switch/tinfoil/db", # stale title DB cache
}
def pack_paths(repo: Path, all_history: bool) -> list[str]:
if all_history:
cmd = ["git", "-C", str(repo), "log", "--all", "--name-only", "--format=", "--", "variants", "staging"]
else:
cmd = ["git", "-C", str(repo), "ls-files", "variants", "staging"]
out = subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
paths = set()
for line in out.splitlines():
line = line.strip()
if line and PACK_PREFIX.match(line):
paths.add(PACK_PREFIX.sub("", line).lower())
return sorted(paths)
def covered(paths: list[str], entry: str) -> bool:
return any(p == entry or p.startswith(entry + "/") for p in paths)
def main() -> int:
if len(sys.argv) != 2:
print(__doc__.strip())
return 2
repo = Path(sys.argv[1])
current = pack_paths(repo, all_history=False)
history = pack_paths(repo, all_history=True)
text = UPDATE_H.read_text(encoding="utf-8")
suspicious = 0
for m in re.finditer(r"static const char\*\s+(\w+)\[\]\s*=\s*\{(.*?)\};", text, re.S):
for entry in re.findall(r'"sd:/([^"]+)"', m.group(2)):
e = entry.lower()
if covered(current, e):
state = "shipped"
elif covered(history, e):
state = "dropped"
else:
state = "never"
flag = ""
if state == "never" and e not in INTENTIONAL and (e.startswith("switch/") or e.startswith("config/")):
flag = " <-- user install? remove from update list"
suspicious += 1
print(f"{state:8} {m.group(1):36} {entry}{flag}")
print(f"\n{suspicious} suspicious entr{'y' if suspicious == 1 else 'ies'}")
return 1 if suspicious else 0
if __name__ == "__main__":
sys.exit(main())