mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 01:53:56 -06:00
feat: check refs into a submodule
idtech4a++ cites xash3d-fwgs filesystem/filesystem.c, starship and ghostship Torch src/Companion.cpp: files of submodules of the declared repository. No blob of the declared tree answers, so the parts stayed external and a bump moved the submodule commit with nothing checked. A project word now also resolves to the one submodule named after it, and a file under a submodule is read in the repository .gitmodules names, at the commit the declared revision pins it to.
This commit is contained in:
1 parent
d5c4cd3575
commit
739f651a16
3 files changed
+140
-6
No files matched your search
+44
-6
@@ -1463,14 +1463,40 @@ def tag_pinned_views(
|
||||
return tagged
|
||||
|
||||
|
||||
def resolve_through_gitlinks(path: str, links: Iterable[Iterable[str]]) -> str | None:
|
||||
"""Path of `project file` inside the one submodule named after the project.
|
||||
|
||||
idtech4a++ cites `xash3d-fwgs filesystem/filesystem.c` and starship
|
||||
`Torch src/Companion.cpp`: the declared tree holds a gitlink there, not
|
||||
the files, so no blob path answers and the part stayed external.
|
||||
"""
|
||||
word, _, rest = path.partition(" ")
|
||||
rest = rest.strip().lstrip("/")
|
||||
if not rest:
|
||||
return None
|
||||
word = word.lower()
|
||||
for paths in links:
|
||||
matches = [
|
||||
link for link in paths or ()
|
||||
if word in link.rsplit("/", 1)[-1].lower()
|
||||
]
|
||||
if len(matches) == 1:
|
||||
return f"{matches[0]}/{rest}"
|
||||
if matches:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_project_words(
|
||||
refs: list, tree_order: Callable[[], Iterable[list[str] | None]]
|
||||
refs: list, tree_order: Callable[[], Iterable[list[str] | None]],
|
||||
link_order: Callable[[], Iterable[Iterable[str]]] = lambda: (),
|
||||
) -> list:
|
||||
"""Point `project file` parts at the declared tree that vendors them.
|
||||
|
||||
tree_order() yields the trees to search, pin before HEAD. It is only
|
||||
called for a part that needs it, since reading a HEAD tree costs a
|
||||
request. Parts no tree carries stay external.
|
||||
tree_order() yields the trees to search, pin before HEAD, and
|
||||
link_order() the submodule paths of the declared trees at their pins.
|
||||
Each is only called for a part that needs it, since reading a tree costs
|
||||
a request. Parts no tree carries stay external.
|
||||
"""
|
||||
resolved: dict[str, str | None] = {}
|
||||
out = []
|
||||
@@ -1479,7 +1505,9 @@ def _resolve_project_words(
|
||||
for part in parts:
|
||||
if is_external_citation(part.path):
|
||||
if part.path not in resolved:
|
||||
resolved[part.path] = resolve_project_word(part.path, tree_order())
|
||||
resolved[part.path] = resolve_project_word(
|
||||
part.path, tree_order()
|
||||
) or resolve_through_gitlinks(part.path, link_order())
|
||||
if resolved[part.path]:
|
||||
part = replace(part, path=resolved[part.path])
|
||||
rewritten.append(part)
|
||||
@@ -1860,7 +1888,14 @@ def build_report(
|
||||
if key in mute:
|
||||
return None
|
||||
try:
|
||||
return upstream.fetch_file(repo, sha, wanted, cache_dir, offline)
|
||||
found = upstream.fetch_file(repo, sha, wanted, cache_dir, offline)
|
||||
if found is None and "/" in wanted:
|
||||
# A file of a submodule is read at the commit its gitlink
|
||||
# pins in this revision, so a bump moves it with the port.
|
||||
inner = upstream.through_gitlink(repo, sha, wanted, cache_dir, offline)
|
||||
if inner is not None:
|
||||
found = upstream.fetch_file(*inner, cache_dir, offline)
|
||||
return found
|
||||
except (upstream.RateLimitError, upstream.NotCachedError):
|
||||
raise
|
||||
except upstream.UpstreamError as exc:
|
||||
@@ -1917,6 +1952,9 @@ def build_report(
|
||||
lambda: itertools.chain(
|
||||
map(_pin_tree_for, views), map(lambda v: _context_for(v)[1], views)
|
||||
),
|
||||
lambda: (
|
||||
upstream.list_gitlinks(v.repo, v.pin, cache_dir, offline) for v in views
|
||||
),
|
||||
)
|
||||
|
||||
lines_cache: dict[tuple[str, str, int | None], list[str] | None] = {}
|
||||
|
||||
@@ -1115,6 +1115,58 @@ def list_gitlinks(
|
||||
}
|
||||
|
||||
|
||||
def submodule_urls(text: str, base: Repo) -> dict[str, Repo]:
|
||||
"""Submodule path -> repository, read from a .gitmodules text.
|
||||
|
||||
A relative url (`../xash3d-fwgs.git`) names a sibling of the repository
|
||||
that declares it, on the same forge.
|
||||
"""
|
||||
repos: dict[str, Repo] = {}
|
||||
path = url = None
|
||||
for line in [*text.splitlines(), "[end]"]:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("["):
|
||||
if path and url:
|
||||
if url.startswith(("../", "./")):
|
||||
segments = [base.owner, base.name]
|
||||
for part in url.split("/"):
|
||||
if part == "..":
|
||||
segments.pop()
|
||||
elif part not in (".", ""):
|
||||
segments.append(part)
|
||||
url = f"https://{base.host}/{'/'.join(segments)}"
|
||||
repo = parse_repo(url)
|
||||
if repo is not None:
|
||||
repos[path] = repo
|
||||
path = url = None
|
||||
continue
|
||||
key, _, value = stripped.partition("=")
|
||||
if key.strip() == "path":
|
||||
path = value.strip()
|
||||
elif key.strip() == "url":
|
||||
url = value.strip()
|
||||
return repos
|
||||
|
||||
|
||||
def through_gitlink(
|
||||
repo: Repo, sha: str, path: str, cache_dir: str, offline: bool = False
|
||||
) -> tuple[Repo, str, str] | None:
|
||||
"""The submodule repository, commit and inner path a path falls under.
|
||||
|
||||
starship cites `Torch src/Companion.cpp`, a file of the Torch submodule:
|
||||
the declared tree holds only the gitlink, which pins the revision.
|
||||
"""
|
||||
links = list_gitlinks(repo, sha, cache_dir, offline)
|
||||
link = next((p for p in links if path.startswith(p + "/")), None)
|
||||
if link is None:
|
||||
return None
|
||||
modules = fetch_file(repo, sha, ".gitmodules", cache_dir, offline)
|
||||
target = submodule_urls("\n".join(modules or ()), repo).get(link)
|
||||
if target is None:
|
||||
return None
|
||||
return target, links[link], path[len(link) + 1:]
|
||||
|
||||
|
||||
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":
|
||||
|
||||
@@ -3950,6 +3950,50 @@ class TestCollectCitations(unittest.TestCase):
|
||||
self.assertIsNone(resolve("munt ROMInfo.cpp", [tree]))
|
||||
self.assertIsNone(resolve("mupen64plus Config.cpp", [tree]))
|
||||
|
||||
def test_project_word_resolves_inside_a_submodule(self):
|
||||
"""idtech4a++ and starship cite files of a submodule. The declared
|
||||
tree holds only the gitlink, so the parts stayed external and moved
|
||||
unchecked with every bump."""
|
||||
links = ["Q3E/src/main/jni/xash3d/xash3d-fwgs", "tools/Torch", "libs/zlib"]
|
||||
resolve = profile_sync.resolve_through_gitlinks
|
||||
self.assertEqual(
|
||||
resolve("xash3d-fwgs filesystem/filesystem.c", [links]),
|
||||
"Q3E/src/main/jni/xash3d/xash3d-fwgs/filesystem/filesystem.c",
|
||||
)
|
||||
self.assertEqual(
|
||||
resolve("Torch src/Companion.cpp", [links]), "tools/Torch/src/Companion.cpp"
|
||||
)
|
||||
self.assertIsNone(resolve("munt ROMInfo.cpp", [links]))
|
||||
self.assertIsNone(resolve("lib x.c", [["a/lib1", "b/lib2"]]))
|
||||
|
||||
def test_a_submodule_file_is_read_at_its_pinned_commit(self):
|
||||
port = upstream.make_repo("github.com", "glKarin", "com.n0n3m4.diii4a")
|
||||
files = {
|
||||
(port.slug, "p1", ".gitmodules"): [
|
||||
'[submodule "Q3E/src/main/jni/xash3d/xash3d-fwgs"]',
|
||||
"\tpath = Q3E/src/main/jni/xash3d/xash3d-fwgs",
|
||||
"\turl = ../xash3d-fwgs.git",
|
||||
],
|
||||
("glKarin/xash3d-fwgs", "s1", "filesystem/filesystem.c"): ["getenv(x)"],
|
||||
}
|
||||
|
||||
def fetch(repo, sha, path, *_):
|
||||
return files.get((repo.slug, sha, path))
|
||||
|
||||
with mock.patch.object(upstream, "fetch_file", side_effect=fetch), \
|
||||
mock.patch.object(
|
||||
upstream, "list_gitlinks",
|
||||
return_value={"Q3E/src/main/jni/xash3d/xash3d-fwgs": "s1"},
|
||||
):
|
||||
target = upstream.through_gitlink(
|
||||
port, "p1",
|
||||
"Q3E/src/main/jni/xash3d/xash3d-fwgs/filesystem/filesystem.c", "c",
|
||||
)
|
||||
self.assertEqual(
|
||||
(target[0].slug, target[1], target[2]),
|
||||
("glKarin/xash3d-fwgs", "s1", "filesystem/filesystem.c"),
|
||||
)
|
||||
|
||||
def test_run_is_rendered_with_its_written_path(self):
|
||||
parts = [RefPart("mupen64plus-video-rice/upstream/src/Config.cpp", 38, 38, "38")]
|
||||
self.assertEqual(
|
||||
|
||||
Reference in new issue
Block a user