diff --git a/scripts/restore_large_files.py b/scripts/restore_large_files.py index 973872fa3..a902d38a7 100644 --- a/scripts/restore_large_files.py +++ b/scripts/restore_large_files.py @@ -29,14 +29,15 @@ from common import copy_file_atomic, load_database def gitignored_paths(gitignore: str) -> set[str]: - """Paths the repository keeps out of git, as written in .gitignore.""" - try: - with open(gitignore) as f: - return { - line.strip() for line in f if line.strip().startswith("bios/") - } - except FileNotFoundError: - return set() + """Paths the repository keeps out of git, as written in .gitignore. + + A missing file raises: read as empty, it left nothing to restore and + nothing unsatisfied, and the run exited 0 with every asset absent. + """ + with open(gitignore) as f: + return { + line.strip() for line in f if line.strip().startswith("bios/") + } def index_cache(cache_dir: str) -> dict[str, str]: @@ -144,6 +145,8 @@ def main() -> None: help="the .gitignore listing release-asset files (default: %(default)s)", ) args = parser.parse_args() + if not os.path.isfile(args.gitignore): + parser.error(f"no file at {args.gitignore}: it lists the release assets") _restored, unsatisfied = restore(args.cache, args.db, args.gitignore) if unsatisfied: raise SystemExit(1) diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index a93c0a8c8..284c3e730 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -2061,6 +2061,25 @@ class CheckoutCompletenessRegressions(unittest.TestCase): finally: os.chdir(cwd) + def test_restore_without_its_registry_fails(self): + """A mistyped --gitignore read as an empty registry: nothing to + restore, nothing unsatisfied, exit 0 with every asset absent.""" + import subprocess + + from scripts.restore_large_files import gitignored_paths + + with tempfile.TemporaryDirectory(dir=TMP_ROOT) as directory: + missing = str(Path(directory) / "absent") + with self.assertRaises(FileNotFoundError): + gitignored_paths(missing) + proc = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "restore_large_files.py"), + "--gitignore", missing, "--cache", directory], + capture_output=True, text=True, timeout=120, + ) + self.assertEqual(proc.returncode, 2, proc.stdout + proc.stderr) + self.assertIn("lists the release assets", proc.stderr) + def test_restore_refuses_a_copy_replaced_after_the_index(self): """The cache is hashed first and copied later: another run that replaced the cached name in between put its bytes at a path the