From 4603b6ec77536b155fc2a15612f0120cd2bf2cdd Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Tue, 20 Oct 2026 04:41:08 +0200 Subject: [PATCH] fix: the check names files it cannot supply --check counted the required files the list leaves out and named none, then said all files were up to date. A Recalbox user asking why Caesar III would not start learned nothing, though the list names c3.555. The count line no longer calls a file not found unsafe. --- install.ps1 | 2 +- install.py | 28 +++++++++++++++++++++++++-- install.sh | 2 +- tests/test_install.py | 44 +++++++++++++++++++++++++++++++++++++++++++ wiki/installer.md | 6 ++++-- 5 files changed, 76 insertions(+), 6 deletions(-) diff --git a/install.ps1 b/install.ps1 index 5609470d..233fa84f 100644 --- a/install.ps1 +++ b/install.ps1 @@ -8,7 +8,7 @@ param( $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -$defaultInstallSha256 = "bc7e519f5bc6dbbc062a0ac29558dbe6862299e6d511b97c048f52ce2db5fba3" +$defaultInstallSha256 = "26f2b2fc61c828de3eceec4cc6ba90d10801b3c97a88531adf9b8307a3866e2e" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 98f90ded..91692084 100644 --- a/install.py +++ b/install.py @@ -2816,6 +2816,22 @@ def _omission_lines(count: int, entries: list[dict]) -> list[str]: return lines +def _absent_required(omitted: list[dict], bios_path: Path) -> list[str]: + """Required files the manifest leaves out that the user has not placed.""" + names = set() + for entry in omitted: + dest = entry.get("dest") or entry.get("name", "") + if not entry.get("required", True) or not dest: + continue + try: + placed = _destination_path(bios_path, dest).is_file() + except ValueError: + placed = False # a path climbing out of the folder is never one we read + if not placed: + names.add(dest) + return sorted(names) + + # Asked before, in an interactive run: past this size a download is worth a # second thought, and --system, --core or --region can make it smaller. CONFIRM_FROM_BYTES = 1024 * 1024 * 1024 @@ -3960,8 +3976,8 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None: for reason, count in sorted(reasons.items()) ) print( - f" Safety notice: {len(omitted_files)} unavailable or unsafe " - f"entries omitted ({required_omitted} required; {reason_summary})." + f" Not installable: {_plural(len(omitted_files), 'file')} " + f"({required_omitted} required, {reason_summary})." ) total_omitted += len(omitted_files) omitted_seen.extend(omitted_files) @@ -4016,6 +4032,14 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None: check_pending += len(to_download) else: print("\n All files up to date.") + absent = _absent_required(omitted_files, bios_path) + if absent: + # The collection cannot supply these, so they leave the exit + # code alone, but a user asking why a game will not start + # needs their names. + print(f" Not in the collection and absent here, needed to start " + f"an emulator: {', '.join(absent[:10])}" + + (f" and {len(absent) - 10} more" if len(absent) > 10 else "")) for entry in data_dirs: _root, needed, present = data_directory_state(entry, bios_path) if needed: diff --git a/install.sh b/install.sh index 8b2da94a..0f038281 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="bc7e519f5bc6dbbc062a0ac29558dbe6862299e6d511b97c048f52ce2db5fba3" +DEFAULT_INSTALL_SHA256="26f2b2fc61c828de3eceec4cc6ba90d10801b3c97a88531adf9b8307a3866e2e" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install.py b/tests/test_install.py index 43a09992..1c685f64 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -2808,6 +2808,50 @@ class AResumedCopyIsNotSpeed(unittest.TestCase): +class ACheckNamesWhatTheCollectionLacks(unittest.TestCase): + """--check counted the required files the manifest leaves out and named + none, then said all files were up to date: on Recalbox, c3.555 stayed + unnamed for a user asking why Caesar III would not start.""" + + def test_recalbox_caesar3(self): + import shutil # noqa: PLC0415 + import subprocess # noqa: PLC0415 + + manifest = json.loads((REPO_ROOT / "install" / "recalbox.json").read_text()) + lacking = sorted( + e["dest"] for e in manifest.get("omitted_files", []) + if e.get("required") and e.get("system") == "caesar3" + ) + if not lacking: + self.skipTest("the Recalbox manifest omits no required caesar3 file") + handler = functools.partial( + http.server.SimpleHTTPRequestHandler, directory=str(REPO_ROOT) + ) + handler.log_message = lambda *args: None + httpd = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler) + threading.Thread(target=httpd.serve_forever, daemon=True).start() + self.addCleanup(httpd.server_close) + self.addCleanup(httpd.shutdown) + (REPO_ROOT / "tmp").mkdir(exist_ok=True) + home = Path(tempfile.mkdtemp(dir=str(REPO_ROOT / "tmp"))) + self.addCleanup(shutil.rmtree, home, True) + env = { + **os.environ, "HOME": str(home), "RETROBIOS_OS": "linux", + "RETROBIOS_BASE_URL": f"http://127.0.0.1:{httpd.server_address[1]}", + } + proc = subprocess.run( + [sys.executable, "-I", "install.py", "--platform", "recalbox", + "--no-input", "--dest", str(home / "bios"), "--check"], + cwd=REPO_ROOT, env=env, capture_output=True, text=True, timeout=300, + check=False, + ) + self.assertNotIn("Traceback", proc.stderr) + for dest in lacking: + self.assertIn(dest, proc.stdout) + self.assertNotIn("unsafe", proc.stdout) + + + class ThePagesQuoteTheBootstrapsOwnWords(unittest.TestCase): """The installer page and the FAQ quoted "install.py SHA-256 mismatch", a message the bootstraps no longer print.""" diff --git a/wiki/installer.md b/wiki/installer.md index d141a62d..b9b9b456 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -111,8 +111,10 @@ Checking existing files... ``` When the manifest leaves entries out (a file the collection cannot supply, or -one that failed a safety check), a `Safety notice:` line under the index count -says how many and why. +one that failed its hash check), a `Not installable:` line under the index count +says how many and why. `--check` also names the required ones missing from the +folder. They do not change its exit code, since no run of the installer can +fetch them. ## Choosing what to install