diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 19c522b1..4683835a 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -3422,27 +3422,36 @@ def _print_triage(args, selected: dict, reports: list[ProfileReport]) -> None: profile = selected[report.name] version = None commits = 0 + refused = "" repo = select_repo(profile) if report.head and repo is not None: - version = check_version(profile, repo, args.cache_dir, args.offline) paths = _cited_paths(report) - for path in paths[:TRIAGE_PATH_SAMPLE]: - commits += upstream.commits_touching( - repo, path, report.pin, args.cache_dir, args.offline, - head=report.head, - ) - if len(paths) > TRIAGE_PATH_SAMPLE: + try: + version = check_version(profile, repo, args.cache_dir, args.offline) + for path in paths[:TRIAGE_PATH_SAMPLE]: + commits += upstream.commits_touching( + repo, path, report.pin, args.cache_dir, args.offline, + head=report.head, + ) + except upstream.UpstreamError as exc: + # One forge refusing a request (Eden's answers 403 to commit + # lookups) leaves this profile unscored, not the whole ranking. + refused = "commit history unreachable" + print(f"{report.name}: {exc}", file=sys.stderr) + if len(paths) > TRIAGE_PATH_SAMPLE and not refused: print( f"{report.name}: commit count sampled on " f"{TRIAGE_PATH_SAMPLE} of {len(paths)} paths", file=sys.stderr, ) - ranked.append((drift_score(report, version, commits), report)) - for score, report in sorted(ranked, key=lambda item: -item[0]): + ranked.append((drift_score(report, version, commits), report, refused)) + for score, report, refused in sorted(ranked, key=lambda item: -item[0]): state = report.skipped or f"{report.needs_review()} to review" unchecked = (report.counts or {}).get("UNCHECKED", 0) if unchecked and not report.skipped: state += f", {unchecked} unchecked" + if refused: + state += f", {refused}" print(f"{score:5d} {report.name:30s} {state}") diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index d0a7f10e..3a12e17d 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -3040,3 +3040,38 @@ class ACitationInsideASourceRefIsTracked(unittest.TestCase): self.assertEqual(len(masked), len(ref)) self.assertEqual(masked.index("b.c:7"), ref.index("b.c:7")) self.assertNotIn("a.c", masked) + + +class TriageSurvivesAForgeRefusal(unittest.TestCase): + """git.eden-emu.dev answers 403 to commit lookups: `--all --triage` + died on the traceback after half an hour and printed no ranking.""" + + def test_the_ranking_is_printed_with_the_profile_marked(self): + from types import SimpleNamespace + + from scripts import profile_sync + + reports = [ + profile_sync.ProfileReport(name="eden", pin="a" * 40, head="b" * 40), + profile_sync.ProfileReport(name="vice", pin="c" * 40, head="d" * 40), + ] + selected = { + "eden": {"source": "https://git.eden-emu.dev/eden-emu/eden"}, + "vice": {"source": "https://github.com/libretro/vice-libretro"}, + } + + def check_version(profile, *_args): + if "eden" in profile["source"]: + raise profile_sync.upstream.UpstreamError("HTTP 403") + return None + + args = SimpleNamespace(cache_dir="", offline=True) + out = io.StringIO() + with mock.patch.object(profile_sync, "check_version", check_version), \ + mock.patch.object(profile_sync, "select_repo", lambda p: object()), \ + contextlib.redirect_stdout(out), \ + contextlib.redirect_stderr(io.StringIO()): + profile_sync._print_triage(args, selected, reports) + lines = out.getvalue().splitlines() + self.assertEqual(len(lines), 2) + self.assertTrue(any("eden" in l and "unreachable" in l for l in lines))