fix: find the branch in a URL into a folder

A /tree/<branch>/<dir> URL was read as one branch name, so the
repository never resolved. Each prefix is tried, shortest first.
A repository that answers nothing now says so, or reports the
declared upstream_gone.
This commit is contained in:
Abdessamad Derraz committed 2026-10-18 13:34:37 +02:00
1 parent 16c0b27213
commit db3eba1cfc
3 files changed
+92 -6

No files matched your search

+1
View File
@@ -5,6 +5,7 @@ source: "https://github.com/kitao/pyxel"
upstream: "https://github.com/kitao/pyxel"
profiled_date: "2026-10-04"
source_commit: "50f9bd77780c993aca62b5b221766bde5791081d"
upstream_gone: "github.com/kitao/pyxel answers HTTP 404 to the API and the web, with no redirect. Observed 2026-10-18; no mirror found."
core_version: "v2.9.9"
display_name: "Pyxel"
cores: [pyxel]
+36 -6
View File
@@ -1293,15 +1293,19 @@ def select_views(
# no profile carries, and fell back to profiled_date, which a bump
# never moves (eden judged from f6e76860 while pinned on d16735f5).
pin_field = "source" if field == "source_mirror" else field
branch = branch_in_url(url) or (
declared if pin_field == "source" else None
)
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:
continue
head = upstream.resolve_head(repo, cache_dir, offline, branch)
head = head or upstream.resolve_head(repo, cache_dir, offline, branch)
if not head:
continue
seen.add((repo.host, repo.slug))
@@ -1372,12 +1376,29 @@ def branch_in_url(url: str) -> str | None:
"""Branch named by a forge URL pointing inside a repository.
A profile can cite a fork as `.../Ymir/tree/libretro`, which names both
the repository and the branch the port lives on.
the repository and the branch the port lives on. The tail may go on into
a directory (`.../libretro-samples/tree/master/tests/cruzes`): this is
the whole tail, and branch_candidates() its prefixes.
"""
_, sep, tail = url.partition("/tree/")
return tail.strip("/") or None if sep else None
def branch_candidates(url: str) -> list[str]:
"""Each prefix of a /tree/ tail, shortest first, that may be the branch.
A branch name can hold a slash, so the tail is not split blindly; the
first prefix the forge resolves is the branch, the rest a directory.
Taken whole, `master/tests/cruzes` resolved to nothing and the profile
was skipped as having no resolvable pin.
"""
tail = branch_in_url(url)
if not tail:
return []
parts = tail.split("/")
return ["/".join(parts[:n]) for n in range(1, len(parts) + 1)]
SELF_CHECK_CONTEXT = 2
@@ -1632,9 +1653,18 @@ def build_report(
f"upstream_gone is declared but the forge answers: {stated_gone}"
)
return report
if not views and stated_gone:
report.skipped = f"upstream gone (declared): {stated_gone}"
return report
if not views:
# A declared pin whose repository or branch does not answer is not a
# profile without a pin: pyxel's kitao/pyxel answers 404, and the
# message sent the reader looking for a missing source_commit.
report.skipped = (
"no source_commit and no resolvable profiled_date "
"no declared repository answers (moved, removed, or a branch the "
"forge does not know)"
if profile.get("source_commit") or profile.get("upstream_commit")
else "no source_commit and no resolvable profiled_date "
"on any declared repository"
)
return report
+55
View File
@@ -4070,5 +4070,60 @@ class TestSelectionNamesWhatMatchedNothing(unittest.TestCase):
self.assertEqual(sorted(selected), ["handy", "mednafen_lynx"])
class TestTreeUrlIntoADirectory(unittest.TestCase):
"""`.../libretro-samples/tree/master/tests/cruzes` took the whole tail as
the branch; nothing resolved and cruzes was skipped as having no pin."""
def test_candidates_are_prefixes_shortest_first(self):
self.assertEqual(
profile_sync.branch_candidates(
"https://github.com/libretro/libretro-samples/tree/master/tests/cruzes"
),
["master", "master/tests", "master/tests/cruzes"],
)
self.assertEqual(profile_sync.branch_candidates("https://github.com/o/r"), [])
def test_the_first_resolving_prefix_is_the_branch(self):
from unittest import mock
asked = []
def head(repo, cache, offline=False, branch=None):
asked.append(branch)
return "tipsha" if branch == "master" else None
profile = {"source": "https://github.com/libretro/libretro-samples/tree/master/tests/cruzes",
"source_commit": "pinsha"}
with mock.patch.object(profile_sync.upstream, "resolve_head", side_effect=head):
views = profile_sync.select_views(profile, ".cache", True)
self.assertEqual([(v.pin, v.head) for v in views], [("pinsha", "tipsha")])
self.assertEqual(asked[0], "master")
class TestAnUnansweringRepository(unittest.TestCase):
"""pyxel declares source_commit, yet the skip read "no source_commit":
its repository answers 404. A declared upstream_gone was honoured only
when the forge raised GoneError, never on a plain non-answer."""
def _report(self, profile):
from unittest import mock
with mock.patch.object(profile_sync, "select_views", return_value=[]):
return profile_sync.build_report("p", profile, ".cache", True)
def test_a_declared_pin_names_the_repository(self):
report = self._report({"source": "https://github.com/o/r", "source_commit": "abc",
"files": [{"name": "a", "source_ref": "a.c:1"}]})
self.assertIn("no declared repository answers", report.skipped)
def test_a_declared_gone_upstream_is_honoured(self):
report = self._report({"source": "https://github.com/o/r", "source_commit": "abc",
"upstream_gone": "404 since 2026-10-18",
"files": [{"name": "a", "source_ref": "a.c:1"}]})
self.assertEqual(report.skipped, "upstream gone (declared): 404 since 2026-10-18")
if __name__ == "__main__":
unittest.main()