mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-18 09:23:26 -05:00
fix: follow an upstream the port vendors as module
libretro/TIC-80 builds nesbox/TIC-80 through its core submodule and tic80's upstream_commit is that submodule commit. Compared to nesbox HEAD, profile_sync reported changes the shipped core never had, and a bump would have moved the pin past it. When an upstream pin is a submodule pointer of the source pin, its head is the pointer at the source HEAD.
This commit is contained in:
1 parent
99d9ac12c2
commit
b91449590d
3 files changed
+114
-2
No files matched your search
+39
-2
@@ -21,7 +21,7 @@ import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Callable, Iterable, Iterator, Sequence
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from dataclasses import asdict, dataclass, field, replace
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -1293,7 +1293,44 @@ def select_views(
|
||||
continue
|
||||
seen.add((repo.host, repo.slug))
|
||||
views.append(RepoView(repo, pin, head, origin, field))
|
||||
return views
|
||||
return _follow_submodule(views, cache_dir, offline)
|
||||
|
||||
|
||||
def _follow_submodule(
|
||||
views: list[RepoView], cache_dir: str, offline: bool
|
||||
) -> list[RepoView]:
|
||||
"""An upstream the port vendors as a submodule moves with the port.
|
||||
|
||||
libretro/TIC-80 builds nesbox/TIC-80 through its `core` submodule, and
|
||||
upstream_commit was that submodule's commit. Compared to nesbox's own
|
||||
HEAD, the view reported changes the shipped core never had, and a bump
|
||||
would have moved upstream_commit past what it builds. The commit the
|
||||
port's HEAD points the submodule at is the upstream HEAD that matters.
|
||||
"""
|
||||
source = next((v for v in views if v.field == "source"), None)
|
||||
behind = [v for v in views if v.field == "upstream" and v.pin != v.head]
|
||||
if source is None or not behind:
|
||||
return views
|
||||
pinned = {
|
||||
sha: path
|
||||
for path, sha in upstream.list_gitlinks(
|
||||
source.repo, source.pin, cache_dir, offline
|
||||
).items()
|
||||
}
|
||||
if not pinned:
|
||||
return views
|
||||
followed: list[RepoView] = []
|
||||
for view in views:
|
||||
path = pinned.get(view.pin) if view.field == "upstream" else None
|
||||
head = (
|
||||
upstream.list_gitlinks(source.repo, source.head, cache_dir, offline).get(path)
|
||||
if path else None
|
||||
)
|
||||
followed.append(
|
||||
replace(view, head=head, origin=f"{view.origin} via {path}")
|
||||
if head else view
|
||||
)
|
||||
return followed
|
||||
|
||||
|
||||
def declared_repositories(profile: dict) -> list[tuple[str, str, str]]:
|
||||
|
||||
@@ -673,6 +673,45 @@ def list_tree(
|
||||
return paths, bool(payload.get("truncated"))
|
||||
|
||||
|
||||
def list_gitlinks(
|
||||
repo: Repo, sha: str, cache_dir: str, offline: bool = False
|
||||
) -> dict[str, str]:
|
||||
"""Submodule path -> the commit it points at, at one revision.
|
||||
|
||||
Read from the same tree listing as list_tree, so the call costs nothing
|
||||
once the tree is cached.
|
||||
"""
|
||||
if repo.family == "gitlab":
|
||||
links: dict[str, str] = {}
|
||||
for page in range(1, MAX_TREE_PAGES + 1):
|
||||
url = (
|
||||
f"{repo.api_base}/projects/{_project(repo)}/repository/tree"
|
||||
f"?ref={sha}&recursive=true&per_page={TREE_PAGE_SIZE}&page={page}"
|
||||
)
|
||||
payload = _api(url, cache_dir, offline)
|
||||
if not isinstance(payload, list):
|
||||
break
|
||||
links.update(
|
||||
(entry["path"], entry["id"])
|
||||
for entry in payload
|
||||
if isinstance(entry, dict) and entry.get("type") == "commit"
|
||||
and entry.get("path") and entry.get("id")
|
||||
)
|
||||
if len(payload) < TREE_PAGE_SIZE:
|
||||
break
|
||||
return links
|
||||
url = _tree_url(repo, sha)
|
||||
payload = _api(url, cache_dir, offline) if url else None
|
||||
if not isinstance(payload, dict):
|
||||
return {}
|
||||
return {
|
||||
entry["path"]: entry["sha"]
|
||||
for entry in payload.get("tree") or []
|
||||
if isinstance(entry, dict) and entry.get("type") == "commit"
|
||||
and entry.get("path") and entry.get("sha")
|
||||
}
|
||||
|
||||
|
||||
def commit_date(repo: Repo, sha: str, cache_dir: str, offline: bool = False) -> str | None:
|
||||
"""When a revision was committed, as the forge reports it (ISO 8601)."""
|
||||
if repo.family == "gitlab":
|
||||
|
||||
@@ -1155,8 +1155,13 @@ class TestBuildReport(unittest.TestCase):
|
||||
profile_sync.upstream.tag_commit,
|
||||
profile_sync.upstream._http_json,
|
||||
profile_sync.upstream._http_text,
|
||||
profile_sync.upstream.list_gitlinks,
|
||||
)
|
||||
self.tags: list[str] = []
|
||||
self.gitlinks: dict[str, dict[str, str]] = {}
|
||||
profile_sync.upstream.list_gitlinks = (
|
||||
lambda repo, sha, cache_dir, offline=False: self.gitlinks.get(sha, {})
|
||||
)
|
||||
self.tag_commits: dict[str, str] = {}
|
||||
# Any code path reaching the HTTP layer is a leak, not a slow test.
|
||||
def _no_network(url):
|
||||
@@ -1205,6 +1210,7 @@ class TestBuildReport(unittest.TestCase):
|
||||
profile_sync.upstream.tag_commit,
|
||||
profile_sync.upstream._http_json,
|
||||
profile_sync.upstream._http_text,
|
||||
profile_sync.upstream.list_gitlinks,
|
||||
) = self._orig
|
||||
self.tmp.cleanup()
|
||||
|
||||
@@ -3896,5 +3902,35 @@ class TestEmulatorsDirStatus(unittest.TestCase):
|
||||
profile_sync.emulators_dir_is_dirty(root)
|
||||
|
||||
|
||||
class UpstreamVendoredAsASubmodule(unittest.TestCase):
|
||||
"""libretro/TIC-80 builds nesbox/TIC-80 through its `core` submodule, and
|
||||
tic80's upstream_commit is that submodule's commit. Compared to nesbox's
|
||||
own HEAD it reported changes the shipped core never had, and a bump would
|
||||
have moved upstream_commit past what libretro builds."""
|
||||
|
||||
def _views(self, links: dict) -> list:
|
||||
from unittest import mock # noqa: PLC0415
|
||||
|
||||
repo_s, repo_u = object(), object()
|
||||
views = [
|
||||
profile_sync.RepoView(repo_s, "src-pin", "src-head", "source_commit", "source"),
|
||||
profile_sync.RepoView(repo_u, "sub-old", "nesbox-tip", "upstream_commit", "upstream"),
|
||||
]
|
||||
with mock.patch.object(
|
||||
profile_sync.upstream, "list_gitlinks",
|
||||
side_effect=lambda repo, sha, *a, **k: links.get(sha, {}),
|
||||
):
|
||||
return profile_sync._follow_submodule(views, "cache", True)
|
||||
|
||||
def test_the_upstream_head_is_what_the_port_head_builds(self):
|
||||
views = self._views({"src-pin": {"core": "sub-old"}, "src-head": {"core": "sub-new"}})
|
||||
self.assertEqual(views[1].head, "sub-new")
|
||||
self.assertEqual(views[0].head, "src-head")
|
||||
|
||||
def test_an_upstream_the_port_does_not_vendor_keeps_its_tip(self):
|
||||
views = self._views({"src-pin": {"deps/zlib": "other"}, "src-head": {}})
|
||||
self.assertEqual(views[1].head, "nesbox-tip")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user