diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index f1e3bc22..8ce4d9d1 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -32,7 +32,9 @@ DEFAULT_CACHE = ".cache/upstream" ANON_QUOTA = 60 TRIAGE_PATH_SAMPLE = 5 -STATUS_ORDER = ("ANCHORED", "SHIFTED", "RENAMED", "AMBIGUOUS", "CHANGED", "GONE") +STATUS_ORDER = ( + "ANCHORED", "EXTERNAL", "SHIFTED", "RENAMED", "AMBIGUOUS", "CHANGED", "GONE", +) REVIEW_STATUSES = ("CHANGED", "GONE", "AMBIGUOUS") REBASE_STATUSES = ("SHIFTED", "RENAMED") @@ -171,6 +173,20 @@ def collect_tokens(entry: dict) -> list[str]: return tokens +def is_external_citation(path: str) -> bool: + """True when a ref names a project rather than a path in a known repo. + + `munt ROMInfo.cpp` and `Nuked-SC55-CLAP rom_io.cpp` cite where a ROM is + identified inside a dependency the profile does not declare, so no + revision of the declared repositories can confirm or deny them. Reporting + them as broken would be wrong: they are simply out of reach. + """ + if " " not in path: + return False + head = path.split(" ", 1)[0] + return "/" not in head and "." not in head + + def _anchor_tokens(entry: dict) -> list[str]: """Values worth searching for when a cited line misses its subject. @@ -350,6 +366,11 @@ def anchor_part( `describe(path)` returns (repo slug, raw URL at HEAD) for the repository that owns the path, or (None, None) when the caller does not track it. """ + if is_external_citation(part.path): + return PartResult( + part, "EXTERNAL", None, None, None, [], + "names a project the profile does not declare", + ) slug, url, actual = ( describe(part.path) if describe else (None, None, part.path) ) diff --git a/tests/test_profile_sync.py b/tests/test_profile_sync.py index 6ebfe240..aece31fb 100644 --- a/tests/test_profile_sync.py +++ b/tests/test_profile_sync.py @@ -285,6 +285,38 @@ def _ambiguous(path, old, candidates): ) +class TestExternalCitation(unittest.TestCase): + def test_project_name_then_file(self): + self.assertTrue(profile_sync.is_external_citation("munt ROMInfo.cpp")) + self.assertTrue( + profile_sync.is_external_citation("Nuked-SC55-CLAP rom_io.cpp") + ) + self.assertTrue( + profile_sync.is_external_citation("EmuDeck emuDeckares.sh") + ) + + def test_plain_paths_are_not_citations(self): + for path in ("src/midi/mt32.cpp", "libretro.c", "a/b/c.h"): + self.assertFalse(profile_sync.is_external_citation(path), path) + + def test_a_path_with_a_directory_prefix_is_not_a_citation(self): + self.assertFalse( + profile_sync.is_external_citation("src/dir file.cpp") + ) + + def test_a_filename_prefix_is_not_a_citation(self): + self.assertFalse(profile_sync.is_external_citation("main.cpp note")) + + def test_external_part_is_reported_not_reviewed(self): + result = anchor_part( + RefPart("munt ROMInfo.cpp", 59, 59), + make_fetch({}), + renamer(CompareResult([], False)), + ) + self.assertEqual(result.status, "EXTERNAL") + self.assertNotIn("EXTERNAL", profile_sync.REVIEW_STATUSES) + + class TestAnchorTokens(unittest.TestCase): def test_archive_stem_is_searched_too(self): tokens = profile_sync._anchor_tokens({"name": "stvbios.zip"})