From fa7977d2c3c7035d322dd2d4f51f9cfbc8aa4fc1 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:00:21 +0200 Subject: [PATCH] fix: ask again for a tip older than the pin A branch tip is cached for six hours. scummvm-standalone was pinned on 6f7b060d while the cache still held 77d80d7, its ancestor, so profile_sync showed a HEAD behind the pin and a bump would have moved it backwards. A tip that is a strict ancestor of the pin is now asked for again. --- scripts/profile_sync.py | 7 +++++++ scripts/upstream.py | 8 +++++--- tests/test_profile_sync.py | 22 +++++++++++++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 62fa1241..3ce4d74a 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -1141,6 +1141,13 @@ def select_views( continue if not head: continue + if head != pin and not offline: + # A tip cached before the pin was written is older than the pin: + # scummvm-standalone pinned 6f7b060d while the cache still held + # 77d80d7, and a bump would have moved the pin backwards. + relation = upstream.ancestry(repo, pin, head, cache_dir, offline) + if relation and relation[0] == 0 and relation[1] > 0: + head = upstream.resolve_head(repo, cache_dir, offline, branch, fresh=True) or head seen.add((repo.host, repo.slug)) views.append(RepoView(repo, pin, head, origin, field)) if gone and not views: diff --git a/scripts/upstream.py b/scripts/upstream.py index 1c225600..c2cb95a0 100644 --- a/scripts/upstream.py +++ b/scripts/upstream.py @@ -527,16 +527,18 @@ def current_slug(repo: Repo, cache_dir: str, offline: bool = False) -> str | Non def resolve_head( - repo: Repo, cache_dir: str, offline: bool = False, branch: str | None = None + repo: Repo, cache_dir: str, offline: bool = False, branch: str | None = None, + fresh: bool = False, ) -> str | None: """Sha of a branch tip, the default branch unless one is named. A libretro port often lives on a branch of a fork rather than on the default branch, so the code a profile documents can be absent from the - tip the forge serves by default. + tip the forge serves by default. ``fresh`` asks the forge again whatever + the cache holds. """ return _first_sha( - _api(_commits_url(repo, None, branch), cache_dir, offline, MOVING_TTL) + _api(_commits_url(repo, None, branch), cache_dir, offline, 0 if fresh else MOVING_TTL) ) diff --git a/tests/test_profile_sync.py b/tests/test_profile_sync.py index d5b7548c..0ee046b2 100644 --- a/tests/test_profile_sync.py +++ b/tests/test_profile_sync.py @@ -1182,7 +1182,10 @@ class TestBuildReport(unittest.TestCase): profile_sync.upstream.list_gitlinks, profile_sync.upstream.current_slug, profile_sync.upstream.fork_base, + profile_sync.upstream.ancestry, ) + # Pin and tip are unrelated shas in these fixtures: no ancestry. + profile_sync.upstream.ancestry = lambda *a, **k: None self.fork_bases: dict[str, str] = {} profile_sync.upstream.fork_base = ( lambda repo, sha, fork, fork_sha, cache_dir, offline=False: @@ -1226,7 +1229,7 @@ class TestBuildReport(unittest.TestCase): ) ) profile_sync.upstream.resolve_head = ( - lambda repo, cache_dir, offline=False, branch=None: "headsha" + lambda repo, cache_dir, offline=False, branch=None, fresh=False: "headsha" ) profile_sync.upstream.resolve_commit_at = ( lambda repo, date, cache_dir, offline=False, branch=None: "pinsha" @@ -1250,6 +1253,7 @@ class TestBuildReport(unittest.TestCase): profile_sync.upstream.list_gitlinks, profile_sync.upstream.current_slug, profile_sync.upstream.fork_base, + profile_sync.upstream.ancestry, ) = self._orig self.tmp.cleanup() @@ -5034,6 +5038,22 @@ class UpstreamForkedByThePort(unittest.TestCase): self.assertEqual(views[1].head, "up-tip") +class ACachedTipOlderThanThePin(TestBuildReport): + """scummvm-standalone was pinned on 6f7b060d while the cached tip was + 77d80d7, its ancestor: a bump would have moved the pin backwards.""" + + def test_the_tip_is_asked_again(self): + profile_sync.upstream.ancestry = lambda *a, **k: (0, 3) + profile_sync.upstream.resolve_head = ( + lambda repo, cache_dir, offline=False, branch=None, fresh=False: + "newtip" if fresh else "staletip" + ) + self.files[("pinsha", "a.c")] = ["x", "hit"] + self.files[("newtip", "a.c")] = ["x", "hit"] + report = build_report("test", self._profile(["a.c:2"]), self.dir) + self.assertEqual(report.head, "newtip") + + class OfflineCacheMissIsNotGone(TestBuildReport): """Offline, a file the cache never stored came back as None and the report read "GONE (absent at HEAD, no rename found)" for files that