From 5a4432742cb62dd4946e27a914cee9780b1ccf32 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 04:11:54 +0200 Subject: [PATCH] fix: clear an export's old files before writing An export wrote into its platform directory without removing what an earlier run left there, so RetroPie scriptmodules and RetroDECK manifests the current truth no longer corrects stayed beside a report saying they were untouched. Files the run does not produce are now removed first. --- scripts/export_native.py | 19 +++++++++++++++++++ tests/test_exporters.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) 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