mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 04:39:06 -05:00
fix: compare a fork clone with the project main
A fork's clone has origin alone. Pushed to the fork's main, the commit left origin/main...HEAD empty and --changed reported nothing with rc 0. When no remote names the project, its main is fetched and compared, and a fetch that fails is an error.
This commit is contained in:
1 parent
ec1001de95
commit
746dee530f
2 files changed
+99
-6
No files matched your search
+54
-6
@@ -20,6 +20,7 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -413,20 +414,67 @@ def _git_lines(*args: str) -> list[str] | None:
|
||||
return [line for line in result.stdout.splitlines() if line]
|
||||
|
||||
|
||||
PROJECT_URLS = (
|
||||
"https://github.com/Abdess/retrobios",
|
||||
"https://github.com/Abdess/retroarch_system",
|
||||
)
|
||||
|
||||
|
||||
def _repo_key(url: str) -> str:
|
||||
"""owner/name of a clone URL, whatever its scheme."""
|
||||
path = url.strip().rstrip("/").removesuffix(".git")
|
||||
return "/".join(re.split(r"[/:]", path)[-2:]).casefold()
|
||||
|
||||
|
||||
def _project_main_line() -> str | None:
|
||||
"""The project's main line, fetched when no remote of this clone names it.
|
||||
|
||||
A fork's clone has origin alone. Once the commit is pushed to the fork's
|
||||
main, origin/main is HEAD and the diff against it was empty, so --changed
|
||||
found nothing and exited 0. None leaves the upstream/ and origin/ lines
|
||||
in charge: a remote of that name names the project, or the clone has no
|
||||
remote at all and only its uncommitted files can be read.
|
||||
"""
|
||||
remotes = _git_lines("remote") or []
|
||||
if not remotes:
|
||||
return None
|
||||
project = {_repo_key(url) for url in PROJECT_URLS}
|
||||
for remote in remotes:
|
||||
urls = _git_lines("remote", "get-url", "--all", remote) or []
|
||||
if any(_repo_key(url) in project for url in urls):
|
||||
return None if remote in ("upstream", "origin") else f"{remote}/main"
|
||||
fetched = subprocess.run(
|
||||
["git", "fetch", "--quiet", "--no-tags", PROJECT_URLS[0], "main"],
|
||||
capture_output=True, text=True, check=False,
|
||||
)
|
||||
if fetched.returncode != 0:
|
||||
raise RuntimeError(
|
||||
f"no remote of this clone is {PROJECT_URLS[0]}, and fetching its "
|
||||
f"main failed: {fetched.stderr.strip()}"
|
||||
)
|
||||
return "FETCH_HEAD"
|
||||
|
||||
|
||||
def get_changed_files() -> list[str]:
|
||||
"""BIOS files this checkout changes against the project's main line.
|
||||
|
||||
Committed changes are read against upstream/<base> first, then
|
||||
origin/<base>: on a fork whose main carries the commit, origin/main is
|
||||
HEAD and the diff was empty. Uncommitted and untracked files count too,
|
||||
Committed changes are read against the project's main line: the remote
|
||||
that names the project (upstream/<base> first, then origin/<base>), or
|
||||
the project's main fetched when no remote does. Uncommitted and
|
||||
untracked files count too,
|
||||
since CONTRIBUTING has the file copied into bios/ before any commit.
|
||||
A git that fails outright is an error, never "nothing changed".
|
||||
"""
|
||||
changed: list[str] = []
|
||||
compared = False
|
||||
for remote in ("upstream", "origin"):
|
||||
for base in ("main", "master", "v2"):
|
||||
committed = _git_lines("diff", "--name-only", f"{remote}/{base}...HEAD")
|
||||
project_base = _project_main_line()
|
||||
remotes = [[project_base]] if project_base else [
|
||||
[f"{remote}/{base}" for base in ("main", "master", "v2")]
|
||||
for remote in ("upstream", "origin")
|
||||
]
|
||||
for candidates in remotes:
|
||||
for line in candidates:
|
||||
committed = _git_lines("diff", "--name-only", f"{line}...HEAD")
|
||||
if committed is None:
|
||||
continue
|
||||
compared = True
|
||||
|
||||
@@ -87,6 +87,51 @@ class ChangesNotYetCommittedAreRead(unittest.TestCase):
|
||||
self.assertIn("bios/new.bin", proc.stdout)
|
||||
|
||||
|
||||
class AForkPushedToItsOwnMain(unittest.TestCase):
|
||||
"""A fork's clone has origin alone. Once the commit was pushed to the
|
||||
fork's main, origin/main was HEAD, the diff was empty, and --changed
|
||||
said "No changed BIOS files detected" with rc 0."""
|
||||
|
||||
GIT = ["git", "-c", "user.email=a@b", "-c", "user.name=a",
|
||||
"-c", "init.defaultBranch=main"]
|
||||
|
||||
def _git(self, *args: str, cwd: str | None = None) -> None:
|
||||
subprocess.run([*self.GIT, *args], cwd=cwd, check=True, capture_output=True)
|
||||
|
||||
def _changed(self, clone: str, project: str) -> subprocess.CompletedProcess:
|
||||
return subprocess.run(
|
||||
[sys.executable, "-c", (
|
||||
f"import sys; sys.path.insert(0, {str(REPO_ROOT / 'scripts')!r}); "
|
||||
"import validate_pr; "
|
||||
f"validate_pr.PROJECT_URLS = ({project!r},); "
|
||||
"print(validate_pr.get_changed_files())"
|
||||
)],
|
||||
cwd=clone, capture_output=True, text=True, timeout=60, check=False,
|
||||
)
|
||||
|
||||
def test_the_project_main_is_fetched_and_compared(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
project, fork, clone = (str(Path(tmp, n)) for n in ("project", "fork", "clone"))
|
||||
self._git("init", "-q", project)
|
||||
Path(project, "README").write_text("x")
|
||||
self._git("add", "README", cwd=project)
|
||||
self._git("commit", "-qm", "init", cwd=project)
|
||||
self._git("clone", "-q", "--bare", project, fork)
|
||||
self._git("clone", "-q", fork, clone)
|
||||
Path(clone, "bios").mkdir()
|
||||
Path(clone, "bios", "new.bin").write_bytes(b"x")
|
||||
self._git("add", "bios/new.bin", cwd=clone)
|
||||
self._git("commit", "-qm", "add", cwd=clone)
|
||||
self._git("push", "-q", "origin", "main", cwd=clone)
|
||||
proc = self._changed(clone, project)
|
||||
self.assertEqual(proc.returncode, 0, proc.stderr)
|
||||
self.assertIn("bios/new.bin", proc.stdout)
|
||||
# A project that cannot be reached is an error, never "nothing".
|
||||
missing = self._changed(clone, str(Path(tmp, "absent")))
|
||||
self.assertNotEqual(missing.returncode, 0)
|
||||
self.assertIn("fetching its main failed", missing.stderr)
|
||||
|
||||
|
||||
class RefreshFailuresAreFailures(unittest.TestCase):
|
||||
def test_a_failed_refresh_is_returned_and_exits_nonzero(self):
|
||||
report = {"entries": [{"status": "UPDATED", "key": "dolphin-sys"},
|
||||
|
||||
Reference in new issue
Block a user