diff --git a/scripts/export_native.py b/scripts/export_native.py index 324f4faf..5920694a 100644 --- a/scripts/export_native.py +++ b/scripts/export_native.py @@ -122,6 +122,24 @@ def fetch( return payload +def _remove_stale(platform_dir: Path, produced: set[str]) -> None: + """Remove what an earlier run wrote and this one does not. + + RetroPie writes one scriptmodule per package it corrects and RetroDECK one + manifest per component: a package corrected by an older truth kept its + file beside a report saying it was no longer touched, and the directory + contradicted the run. + """ + if not platform_dir.is_dir(): + return + for path in sorted(platform_dir.rglob("*"), reverse=True): + relative = path.relative_to(platform_dir).as_posix() + if path.is_file() and relative not in produced: + path.unlink() + elif path.is_dir() and not any(path.iterdir()): + path.rmdir() + + def pinned_base(wanted: dict[str, str], scraped: dict | None) -> str: """The revision our transcription came from, when the YAML records it. @@ -362,6 +380,7 @@ def export_platform( issues = exporter.validate(systems, produced) platform_dir = output_dir / platform + _remove_stale(platform_dir, set(produced)) for relative, content in produced.items(): path = platform_dir / relative path.parent.mkdir(parents=True, exist_ok=True) diff --git a/tests/test_exporters.py b/tests/test_exporters.py index 1039758d..f8064b4c 100644 --- a/tests/test_exporters.py +++ b/tests/test_exporters.py @@ -784,6 +784,34 @@ class RommExport(unittest.TestCase): self.assertTrue(all(k.startswith("psx:") for k in data)) +class AnExportLeavesNoStaleFile(unittest.TestCase): + """RetroPie writes one scriptmodule per corrected package. A second run on + a fresher truth printed "2 packages corrected" over a directory still + holding the six files of the first run.""" + + def test_files_this_run_does_not_write_are_removed(self): + import export_native # noqa: PLC0415 + + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "retropie" + (root / "scriptmodules" / "libretrocores").mkdir(parents=True) + kept = root / "scriptmodules" / "libretrocores" / "lr-mgba.sh" + stale = root / "scriptmodules" / "libretrocores" / "lr-pcsx-rearmed.sh" + gone_dir = root / "scriptmodules" / "emulators" + gone_dir.mkdir() + for path in (kept, stale, gone_dir / "pcsx-rearmed.sh"): + path.write_text("old", encoding="utf-8") + export_native._remove_stale(root, {"scriptmodules/libretrocores/lr-mgba.sh"}) + self.assertTrue(kept.exists()) + self.assertFalse(stale.exists()) + self.assertFalse(gone_dir.exists()) + + def test_the_export_clears_before_it_writes(self): + source = (REPO_ROOT / "scripts" / "export_native.py").read_text() + body = source[source.index("def export_platform("):] + self.assertLess(body.index("_remove_stale("), body.index(".write_text(")) + + class RommKeepsItsFixture(unittest.TestCase): """The export rebuilt known_bios_files.json from our model in our order: 3 hash corrections came out as a 3116-line diff, and a key RomM had that