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()