fix: read the mirror of a gone source

select_views stopped on the first repository that answered as gone, so
yuzu's source_mirror was never read and the profile stayed unchecked. A
gone repository is now passed over, and a profile declared gone that
answers only through its mirror is not reported as a contradiction.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 19:13:48 +02:00
1 parent 6882848836
commit 5a3f621e94
2 files changed
+46 -11

No files matched your search

+20 -11
View File
@@ -1107,6 +1107,7 @@ def select_views(
"""
views: list[RepoView] = []
seen: set[tuple[str, str]] = set()
gone = False
# A port living on a branch of a fork is absent from the default tip, so
# the branch it was read from is followed when the profile names one.
declared = str(profile.get("source_branch") or "") or None
@@ -1121,21 +1122,29 @@ def select_views(
pin_field = "source" if field == "source_mirror" else field
branch = declared if pin_field == "source" else None
head = None
for candidate in branch_candidates(url):
head = upstream.resolve_head(repo, cache_dir, offline, candidate)
if head:
branch = candidate
break
pin, origin = resolve_pin(
profile, repo, cache_dir, offline, pin_field, mode, branch
)
if not pin:
try:
for candidate in branch_candidates(url):
head = upstream.resolve_head(repo, cache_dir, offline, candidate)
if head:
branch = candidate
break
pin, origin = resolve_pin(
profile, repo, cache_dir, offline, pin_field, mode, branch
)
if not pin:
continue
head = head or upstream.resolve_head(repo, cache_dir, offline, branch)
except upstream.GoneError:
# yuzu's source answers 451: the mirror declared after it is what
# keeps the profile checkable, so a gone repository is passed over.
gone = True
continue
head = head or upstream.resolve_head(repo, cache_dir, offline, branch)
if not head:
continue
seen.add((repo.host, repo.slug))
views.append(RepoView(repo, pin, head, origin, field))
if gone and not views:
raise upstream.GoneError(f"{profile.get('emulator', '?')}: every declared repository is gone")
views = _follow_fork_base(_follow_submodule(views, cache_dir, offline), cache_dir, offline)
return _hold_upstream(profile, views)
@@ -1583,7 +1592,7 @@ def build_report(
report.skipped = f"upstream gone (declared): {stated_gone}"
return report
raise
if stated_gone and views:
if stated_gone and any(view.field != "source_mirror" for view in views):
# Declared dead, yet it answered. A profile that keeps saying so stops
# being read, so the contradiction is the finding.
report.skipped = (
+26
View File
@@ -4912,6 +4912,32 @@ class UpstreamVendoredAsASubmodule(unittest.TestCase):
self.assertEqual(views[1].head, "nesbox-tip")
class AMirrorOutlivesAGoneSource(unittest.TestCase):
"""yuzu's source answers 451. select_views stopped on it and never read
the source_mirror declared after it, so the profile stayed unchecked."""
def test_the_mirror_is_read_when_the_source_is_gone(self):
from unittest import mock # noqa: PLC0415
gone_repo = profile_sync.upstream.make_repo("github.com", "yuzu-emu-mirror", "yuzu")
mirror = profile_sync.upstream.make_repo("github.com", "fork", "yuzu")
def head(repo, cache_dir, offline=False, branch=None):
if repo == gone_repo:
raise profile_sync.upstream.GoneError("HTTP 451")
return "mirror-head"
profile = {
"source": "https://github.com/yuzu-emu-mirror/yuzu",
"source_mirror": "https://github.com/fork/yuzu",
"source_commit": "pin",
}
with mock.patch.object(profile_sync.upstream, "resolve_head", side_effect=head), \
mock.patch.object(profile_sync, "resolve_pin", return_value=("pin", "source_commit")):
views = profile_sync.select_views(profile, "cache", True)
self.assertEqual([(v.repo, v.field) for v in views], [(mirror, "source_mirror")])
class AHeldUpstreamStaysOnItsPin(unittest.TestCase):
"""aps3e vendors RPCS3 1f080485 and yaps2 PCSX2 342db515: a bump moved
upstream_commit to the upstream HEAD neither port carries."""