From 738577b5a4b7fe565a3b9e56aa241e62719ecab8 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:25:29 +0200 Subject: [PATCH] fix: run verify and generate_pack from any dir Started outside the repository root, both ended on a FileNotFoundError traceback: the database and the data registry name files from the root. They now move to the root after reading the paths the caller typed, which stay relative to where they were typed. --- scripts/generate_pack.py | 6 ++++++ scripts/verify.py | 25 +++++++++++++++++++++++++ tests/test_audit_regressions.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index 0b5ba8a7f..a7892e3b6 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -3160,6 +3160,12 @@ def _main(held: contextlib.ExitStack) -> None: help="Extract and verify pack integrity (path + hash)", ) args = parser.parse_args() + from verify import work_from_repo_root + + work_from_repo_root( + args, parser, + ["db", "platforms_dir", "bios_dir", "output_dir", "emulators_dir", "from_md5_file"], + ) packresolve.set_offline(bool(args.offline)) if args.platform: require_platform(args.platform, args.platforms_dir) diff --git a/scripts/verify.py b/scripts/verify.py index dfae360a1..c7078a22e 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -109,6 +109,30 @@ DEFAULT_BIOS_DIR = "bios" # that pass no directory. _REPO_PLATFORMS = os.path.join(os.path.dirname(__file__), "..", "platforms") DEFAULT_EMULATORS_DIR = "emulators" +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def work_from_repo_root( + args: argparse.Namespace, parser: argparse.ArgumentParser, path_flags: list[str] +) -> None: + """Run from the repository root, whichever directory the command started in. + + database.json records every file from the root and the data registry + names its caches the same way, so a run from scripts/ ended on a + FileNotFoundError traceback. A path the caller typed is read from where + they typed it, a default from the root. + """ + if os.path.realpath(os.getcwd()) == os.path.realpath(REPO_ROOT): + return + for flag in path_flags: + value = getattr(args, flag, None) + if value and value != parser.get_default(flag): + setattr(args, flag, os.path.abspath(value)) + os.chdir(REPO_ROOT) + if getattr(args, "platform", None): + # Read before the move, the registry was not there to map + # "RetroArch" to its slug. + args.platform = platform_arg(args.platform) # Status model: aligned with Batocera BiosStatus (batocera-systems:967-969) @@ -2367,6 +2391,7 @@ def _main(held: contextlib.ExitStack) -> None: "present with an unconfirmed hash is a warning, not a failure", ) args = parser.parse_args() + work_from_repo_root(args, parser, ["db", "platforms_dir", "emulators_dir"]) if args.platform: require_platform(args.platform, args.platforms_dir) diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 42e2d6b5b..52d402416 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -5995,5 +5995,36 @@ class PipelineOutputDirIsTheOneItWrites(unittest.TestCase): ) +class ToolsRunFromAnyDirectory(unittest.TestCase): + """From scripts/, verify.py and generate_pack.py ended on a + FileNotFoundError traceback about platforms/_registry.yml.""" + + def _run(self, cwd: Path, *argv: str) -> subprocess.CompletedProcess: + return subprocess.run( + [sys.executable, *argv], cwd=cwd, capture_output=True, text=True, + timeout=600, + ) + + def test_listing_works_from_scripts(self): + for script in ("verify.py", "generate_pack.py"): + with self.subTest(script=script): + proc = self._run( + ROOT / "scripts", str(ROOT / "scripts" / script), + "--platform", "RetroArch", "--list-systems", + ) + self.assertEqual(proc.returncode, 0, proc.stderr) + self.assertNotIn("Traceback", proc.stderr) + self.assertIn("sony-playstation", proc.stdout) + + def test_a_typed_path_is_read_from_where_it_was_typed(self): + with tempfile.TemporaryDirectory(dir=TMP_ROOT) as tmp: + proc = self._run( + Path(tmp), str(ROOT / "scripts" / "generate_pack.py"), + "--manifest-targets", "--output-dir", "lists", + ) + self.assertEqual(proc.returncode, 0, proc.stderr) + self.assertTrue(Path(tmp, "lists", "retroarch.json").is_file()) + + if __name__ == "__main__": unittest.main()