mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-28 06:37:24 -05:00
chore: merge main into the interface branch
This commit is contained in:
commit
48086b9966
5 files changed
+189
-15
No files matched your search
+82
-5
@@ -2216,15 +2216,41 @@ def page_version(profile: dict, cache_dir: str, offline: bool) -> str | None:
|
||||
return match.group(1) if match else None
|
||||
|
||||
|
||||
def version_warning(report: VersionReport | None, pin: str | None) -> str | None:
|
||||
"""Flag a declared version that names a tag away from the resolved pin."""
|
||||
def version_warning(
|
||||
report: VersionReport | None,
|
||||
pin: str | None,
|
||||
relation: tuple[int, int] | None = None,
|
||||
passed_release: str | None = None,
|
||||
) -> str | None:
|
||||
"""Flag a declared version that names a tag away from the resolved pin.
|
||||
|
||||
``relation`` is the pin's (ahead, behind) count against the tag commit.
|
||||
A pin after its tag is a build between two releases. A pin before it
|
||||
describes code the declared release had already replaced.
|
||||
``passed_release`` is a later release the pin already contains, which
|
||||
core_version should name instead.
|
||||
"""
|
||||
if report is None or not report.tag_commit or not pin:
|
||||
return None
|
||||
if report.tag_commit == pin:
|
||||
if report.tag_commit == pin and not passed_release:
|
||||
return None
|
||||
if passed_release:
|
||||
return (
|
||||
f"core_version {report.declared} is older than the code: the pin "
|
||||
f"{pin[:7]} already contains release {passed_release}"
|
||||
)
|
||||
where = ""
|
||||
if relation is not None:
|
||||
ahead, behind = relation
|
||||
if ahead and not behind:
|
||||
where = f", {ahead} commits after the tag"
|
||||
elif behind and not ahead:
|
||||
where = f", {behind} commits before the tag: core_version is later than the code"
|
||||
elif ahead and behind:
|
||||
where = f", off the tag's history ({ahead} ahead, {behind} behind)"
|
||||
return (
|
||||
f"core_version {report.declared} names tag commit {report.tag_commit[:7]}, "
|
||||
f"the pin resolved to {pin[:7]}"
|
||||
f"the pin resolved to {pin[:7]}{where}"
|
||||
)
|
||||
|
||||
|
||||
@@ -2901,6 +2927,16 @@ def bump_commit(
|
||||
if pending_recale(report, accept_changed, text):
|
||||
return False
|
||||
document = yaml.safe_load(text)
|
||||
if document.get("built_by"):
|
||||
# The pin is the commit a platform builds (recalbox gsplus.mk, the
|
||||
# mame0258 its package fetches), not a point on a moving branch:
|
||||
# HEAD is a build nobody ships under this name.
|
||||
print(
|
||||
f"{path.stem}: pinned to the build {', '.join(document['built_by'])} "
|
||||
"ships, advanced by hand",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False
|
||||
if any(isinstance(document.get(f), dict) for f in ("source_commit", "upstream_commit")):
|
||||
# A pin per build mode names one revision per repository, and the
|
||||
# report heads only the primary one: rewriting str(dict) was a
|
||||
@@ -3937,7 +3973,48 @@ def _attach_version(
|
||||
except upstream.UpstreamError as exc:
|
||||
print(f"{report.name}: version check skipped: {exc}", file=sys.stderr)
|
||||
return
|
||||
report.version_warning = version_warning(report.version, report.pin)
|
||||
relation = None
|
||||
passed = None
|
||||
version = report.version
|
||||
try:
|
||||
if version and version.tag_commit and report.pin and version.tag_commit != report.pin:
|
||||
relation = upstream.ancestry(
|
||||
repo, version.tag_commit, report.pin, args.cache_dir, args.offline
|
||||
)
|
||||
passed = _passed_release(args, repo, version, report.pin)
|
||||
except upstream.RateLimitError:
|
||||
raise
|
||||
except upstream.UpstreamError as exc:
|
||||
print(f"{report.name}: tag ancestry unread: {exc}", file=sys.stderr)
|
||||
report.version_warning = version_warning(version, report.pin, relation, passed)
|
||||
|
||||
|
||||
def _passed_release(
|
||||
args: argparse.Namespace,
|
||||
repo: upstream.Repo,
|
||||
version: VersionReport | None,
|
||||
pin: str | None,
|
||||
) -> str | None:
|
||||
"""The latest release, when the pin already contains it and core_version
|
||||
names another one.
|
||||
|
||||
The latest tag is no witness: nightly, continuous and Pre-release tags
|
||||
sort first on many forges. The latest release is a version the project
|
||||
published as such.
|
||||
"""
|
||||
if not version or not pin or not version.latest_release:
|
||||
return None
|
||||
if version.latest_release == version.declared:
|
||||
return None
|
||||
commit = upstream.tag_commit(repo, version.latest_release, args.cache_dir, args.offline)
|
||||
if not commit:
|
||||
return None
|
||||
if commit == pin:
|
||||
return version.latest_release
|
||||
relation = upstream.ancestry(repo, commit, pin, args.cache_dir, args.offline)
|
||||
if relation and relation[1] == 0:
|
||||
return version.latest_release
|
||||
return None
|
||||
|
||||
|
||||
def _print_extras(
|
||||
|
||||
@@ -742,6 +742,41 @@ def compare(
|
||||
return CompareResult(files, truncated, payload.get("status") == "diverged")
|
||||
|
||||
|
||||
def _commits_ahead(payload: object, family: str) -> int | None:
|
||||
"""Commits the compare head carries that its base does not."""
|
||||
if not isinstance(payload, dict):
|
||||
return None
|
||||
if family == "github":
|
||||
value = payload.get("ahead_by")
|
||||
elif family == "gitlab":
|
||||
value = len(payload.get("commits") or [])
|
||||
else:
|
||||
value = payload.get("total_commits")
|
||||
return value if isinstance(value, int) else None
|
||||
|
||||
|
||||
def ancestry(
|
||||
repo: Repo, base: str, head: str, cache_dir: str, offline: bool = False
|
||||
) -> tuple[int, int] | None:
|
||||
"""(ahead, behind): commits head has that base lacks, and the reverse.
|
||||
|
||||
Two shas never change their history, so both answers are cached for
|
||||
good. GitHub gives both counts in one answer, GitLab and Forgejo list
|
||||
one direction only and are asked twice.
|
||||
"""
|
||||
forward = _api(_compare_url(repo, base, head), cache_dir, offline)
|
||||
ahead = _commits_ahead(forward, repo.family)
|
||||
if ahead is None:
|
||||
return None
|
||||
if repo.family == "github":
|
||||
behind = forward.get("behind_by")
|
||||
else:
|
||||
behind = _commits_ahead(
|
||||
_api(_compare_url(repo, head, base), cache_dir, offline), repo.family
|
||||
)
|
||||
return (ahead, behind) if isinstance(behind, int) else None
|
||||
|
||||
|
||||
def find_renamed(result: CompareResult, path: str) -> str | None:
|
||||
"""New path of a file the comparison reports as renamed."""
|
||||
for change in result.files:
|
||||
|
||||
@@ -2999,6 +2999,24 @@ class TestBumpCommit(unittest.TestCase):
|
||||
self.assertEqual(profile_sync.pending_recale(report), 0)
|
||||
self.assertTrue(bump_commit(self.path, report))
|
||||
|
||||
def test_refused_on_a_build_a_platform_compiles(self):
|
||||
# mame0258 documents the MAME Recalbox builds: libretro/mame HEAD
|
||||
# is a later release, and every ref still anchors there.
|
||||
self.path.write_text(SAMPLE + "built_by: [recalbox]\n", encoding="utf-8")
|
||||
part = PartResult(
|
||||
RefPart("a.c", 10, 12, "a.c:10-12"), "ANCHORED", None, 10, 12, []
|
||||
)
|
||||
report = ProfileReport(
|
||||
name="p", repo="o/n", pin="pin", head="newhead",
|
||||
entries=[
|
||||
EntryReport("a.bin", "a.c:10-12, (just a note)", "ANCHORED", [part])
|
||||
],
|
||||
counts={"ANCHORED": 1},
|
||||
)
|
||||
with contextlib.redirect_stderr(io.StringIO()):
|
||||
self.assertFalse(bump_commit(self.path, report))
|
||||
self.assertNotIn("newhead", self.path.read_text())
|
||||
|
||||
def test_refused_when_no_ref_reads_the_pinned_repository(self):
|
||||
"""gam4980: every bare libretro.c ref anchored in upstream, and the
|
||||
bump wrote the fork's HEAD over refs that never described it."""
|
||||
@@ -3171,6 +3189,55 @@ class TestCheckVersion(unittest.TestCase):
|
||||
result = check_version({"core_version": "v3.0.0"}, self.repo, self.dir, False)
|
||||
self.assertIsNone(version_warning(result, "tagsha"))
|
||||
|
||||
def test_warning_says_which_side_of_the_tag_the_pin_is(self):
|
||||
# 52 warnings read alike: a build after its release and a pin older
|
||||
# than the release it claims could not be told apart.
|
||||
result = check_version({"core_version": "v3.0.0"}, self.repo, self.dir, False)
|
||||
self.assertIn("4 commits after the tag", version_warning(result, "p", (4, 0)))
|
||||
self.assertIn("core_version is later than the code",
|
||||
version_warning(result, "p", (0, 9)))
|
||||
self.assertIn("off the tag's history", version_warning(result, "p", (2, 3)))
|
||||
|
||||
def test_warning_names_a_release_the_pin_already_contains(self):
|
||||
result = check_version({"core_version": "v3.0.0"}, self.repo, self.dir, False)
|
||||
warning = version_warning(result, "tagsha", None, "v3.1.0")
|
||||
self.assertIn("already contains release v3.1.0", warning)
|
||||
|
||||
|
||||
class TestAncestry(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.saved = profile_sync.upstream._http_json
|
||||
|
||||
def tearDown(self):
|
||||
profile_sync.upstream._http_json = self.saved
|
||||
self.tmp.cleanup()
|
||||
|
||||
def test_github_answers_both_directions_at_once(self):
|
||||
calls = []
|
||||
|
||||
def answer(url):
|
||||
calls.append(url)
|
||||
return {"ahead_by": 3, "behind_by": 0}
|
||||
|
||||
profile_sync.upstream._http_json = answer
|
||||
repo = profile_sync.upstream.parse_repo("https://github.com/o/n")
|
||||
self.assertEqual(
|
||||
profile_sync.upstream.ancestry(repo, "tag", "pin", self.tmp.name), (3, 0)
|
||||
)
|
||||
self.assertEqual(len(calls), 1)
|
||||
|
||||
def test_gitlab_is_asked_both_ways(self):
|
||||
def answer(url):
|
||||
forward = "from=tag&to=pin" in url
|
||||
return {"commits": [{}] * (0 if forward else 5)}
|
||||
|
||||
profile_sync.upstream._http_json = answer
|
||||
repo = profile_sync.upstream.parse_repo("https://gitlab.com/o/n")
|
||||
self.assertEqual(
|
||||
profile_sync.upstream.ancestry(repo, "tag", "pin", self.tmp.name), (0, 5)
|
||||
)
|
||||
|
||||
|
||||
HASH_PROFILE = {
|
||||
"files": [
|
||||
|
||||
@@ -76,8 +76,7 @@ It shows your setup and its BIOS folder:
|
||||
```
|
||||
|
||||
- **Right folder?** Go on.
|
||||
- **Wrong folder?** Press Ctrl+C. Run the command again with your folder
|
||||
after `--dest`:
|
||||
- **Wrong folder?** Press Ctrl+C. Run it again with your folder:
|
||||
|
||||
<!-- os-tabs windows: --dest D:\path\to\bios -->
|
||||
```sh
|
||||
@@ -120,14 +119,12 @@ Add `--check` to the same command. It changes nothing.
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --check
|
||||
```
|
||||
|
||||
All good when every file is present and none is wrong:
|
||||
All good when every file is present:
|
||||
|
||||
```
|
||||
109/109 present (109 verified, 0 wrong hash)
|
||||
```
|
||||
|
||||
An empty folder also shows `0 wrong hash`, with `0/109 present`.
|
||||
|
||||
## Undo
|
||||
|
||||
It keeps every file it replaces in a `retrobios-backup` folder, next to your
|
||||
@@ -138,8 +135,7 @@ BIOS folder. The end of the run prints the line that puts them back.
|
||||
### No supported platform detected.
|
||||
|
||||
At a terminal, a menu follows. Pick your setup by its number, then type its
|
||||
BIOS folder, or press Enter for the usual one. A folder pasted with quotes
|
||||
around it is fine.
|
||||
BIOS folder, or press Enter for the usual one.
|
||||
|
||||
With `--no-input`, or without a terminal, it stops and prints the command to
|
||||
use.
|
||||
|
||||
@@ -66,9 +66,8 @@ version.
|
||||
|
||||
## If the command fails { #installation-script-fails }
|
||||
|
||||
- **Network error:** try again once github.com opens.
|
||||
- **`The clock reads ...`:** the date is wrong. Let it sync over Wi-Fi, or set
|
||||
it, then run again.
|
||||
- **Network error:** try again later.
|
||||
- **`The clock reads ...`:** set the right date.
|
||||
- **Permission denied:** do what the message says. It names what to run.
|
||||
- **No setup found:** name it and its folder:
|
||||
|
||||
|
||||
Reference in new issue
Block a user