From fbc530123e8d95f47f1877a790dd64d472956d40 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:28:28 +0100 Subject: [PATCH] fix: say offline that a platform has no targets --- install.ps1 | 2 +- install.py | 18 +++++++++++++++++- install.sh | 2 +- install/launchers/index.json | 16 ++++++++-------- tests/test_install.py | 22 ++++++++++++++++++++++ wiki/installer.md | 2 +- 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/install.ps1 b/install.ps1 index 3662a3748..8f21607a8 100644 --- a/install.ps1 +++ b/install.ps1 @@ -16,7 +16,7 @@ # and Invoke-WebRequest throws on a cut transfer before the hash is read: # the site's build rewrites this line there (scripts/sitebootstrap.py). $mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that." - $defaultInstallSha256 = "fdc80bcbe544c34e66af5cb953edd9b2b18c4722d32077f570f8503cc6815ca1" + $defaultInstallSha256 = "389da8c666aecb14f32e064a53f05a9cc26481317ad5a877688c3e485b6c6e44" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 64dde5a76..d436fb1e2 100644 --- a/install.py +++ b/install.py @@ -104,6 +104,14 @@ def _checked_base_url(value: str) -> str: BASE_URL = _checked_base_url(os.environ.get("RETROBIOS_BASE_URL", DEFAULT_BASE_URL)) MANIFEST_URL = f"{BASE_URL}/install/{{platform}}.json" TARGETS_URL = f"{BASE_URL}/install/targets/{{platform}}.json" +# The platforms that publish a core list per board, install/targets/ in the +# repository: a test holds the two equal. Offline, a platform outside it +# has no hardware targets, where the run sent the user after a list that +# exists nowhere. +TARGET_PLATFORMS = frozenset({ + "batocera", "emudeck", "knulli", "lakka", "recalbox", "retroarch", "retrobat", + "retrodeck", "retropie", "rocknix", "romm", +}) RAW_FILE_URL = f"{BASE_URL}/{{path}}" RELEASE_URL = ( "https://github.com/Abdess/retrobios/releases/download/large-files/{asset}" @@ -4209,6 +4217,8 @@ def fetch_targets(plat: str) -> "dict | None": held = local_targets(plat) if held is not None: return held + if LOCAL.offline and plat not in TARGET_PLATFORMS: + return {} if LOCAL.offline: source = (f"{LOCAL.pack.name} carries none" if LOCAL.pack is not None else f"{offline_named()} makes no request") @@ -13327,8 +13337,14 @@ def main() -> None: # The runs the full-screen interface starts read it from here, and # the words name the option given. os.environ[OFFLINE_VARIABLE] = OFFLINE_FLAG_VALUE + named = next((plat for plat in AVAILABLE_PLATFORMS if args.platform and _name_key( + args.platform) in (_name_key(plat), _name_key(display_name(plat)))), "") + if offline_requested() and args.target and named and named not in TARGET_PLATFORMS: + refuse_name(named, f"Error: {display_name(named)} has no hardware targets: run " + "again without --target.") if (offline_requested() and (args.target or args.list_targets) - and not (args.manifest or args.from_pack)): + and not (args.manifest or args.from_pack) + and not (named and named not in TARGET_PLATFORMS)): # No list can be at hand: a pack carries one, and so can the folder # of a --manifest (targets/.json). parser.error("--target and --list-targets read a core list from the network, " diff --git a/install.sh b/install.sh index b814f7f24..9fc1dd152 100755 --- a/install.sh +++ b/install.sh @@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins # curl or wget stop on a cut transfer before the hash is read: the site's # build rewrites this line there (scripts/sitebootstrap.py). MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that." -DEFAULT_INSTALL_SHA256="fdc80bcbe544c34e66af5cb953edd9b2b18c4722d32077f570f8503cc6815ca1" +DEFAULT_INSTALL_SHA256="389da8c666aecb14f32e064a53f05a9cc26481317ad5a877688c3e485b6c6e44" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/install/launchers/index.json b/install/launchers/index.json index fd23b4139..aa7c900f6 100644 --- a/install/launchers/index.json +++ b/install/launchers/index.json @@ -27,8 +27,8 @@ { "dest": "roms/ports/retrobios/install.py", "repo_path": "install.py", - "size": 1092074, - "sha256": "fdc80bcbe544c34e66af5cb953edd9b2b18c4722d32077f570f8503cc6815ca1", + "size": 1093006, + "sha256": "389da8c666aecb14f32e064a53f05a9cc26481317ad5a877688c3e485b6c6e44", "executable": false }, { @@ -71,8 +71,8 @@ "muos": { "archive": { "dest": "ARCHIVE/RetroBIOS.muxapp", - "size": 1145701, - "sha256": "844b1ee7a71dabae0fa7fb9aedba24ab8255dfab867c2ef4e8189ea286abc6e3" + "size": 1146633, + "sha256": "c221a6529cb99a510a28eb739a69237cd1b77fb0d9b9a97b3a7e14acc275b54a" }, "members": [ { @@ -92,8 +92,8 @@ { "name": "RetroBIOS/install.py", "repo_path": "install.py", - "size": 1092074, - "sha256": "fdc80bcbe544c34e66af5cb953edd9b2b18c4722d32077f570f8503cc6815ca1", + "size": 1093006, + "sha256": "389da8c666aecb14f32e064a53f05a9cc26481317ad5a877688c3e485b6c6e44", "executable": false }, { @@ -166,8 +166,8 @@ { "dest": ".config/modules/retrobios/install.py", "repo_path": "install.py", - "size": 1092074, - "sha256": "fdc80bcbe544c34e66af5cb953edd9b2b18c4722d32077f570f8503cc6815ca1", + "size": 1093006, + "sha256": "389da8c666aecb14f32e064a53f05a9cc26481317ad5a877688c3e485b6c6e44", "executable": false }, { diff --git a/tests/test_install.py b/tests/test_install.py index b9b23f1af..3dd58774a 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -3362,6 +3362,28 @@ class ACardPreparedOnAComputerGetsItsBoard(unittest.TestCase): self.assertEqual(state.board, "Board bcm2711 found on the card (x)") +class APlatformWithoutTargetsSaysSoOffline(unittest.TestCase): + """Offline, --target on AmberELEC asked for install/targets/amberelec.json, + which exists nowhere: online the same run says it has no targets.""" + + def test_the_list_matches_the_published_targets(self): + published = {path.stem for path in (REPO_ROOT / "install" / "targets").glob("*.json")} + self.assertEqual(install.TARGET_PLATFORMS, published) + + def test_offline_says_there_are_no_targets(self): + dest = Path(_tempdir(self)) + for extra in ([], ["--manifest", str(REPO_ROOT / "install" / "amberelec.json")]): + with self.subTest(manifest=bool(extra)): + proc = subprocess.run( + [sys.executable, str(REPO_ROOT / "install.py"), "--platform", "amberelec", + "--dest", str(dest), "--target", "rk3326", "--offline", "--no-input", + "--dry-run", *extra], + capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) + self.assertEqual(proc.returncode, 2, proc.stderr) + self.assertIn("AmberELEC has no hardware targets", proc.stderr) + self.assertNotIn("targets/amberelec.json", proc.stderr) + + class TheBoardTheSystemNamesIsTheTarget(unittest.TestCase): """Batocera, Recalbox and ROCKNIX each write the board they were built for, but the installer read none of them, so a Batocera RPi4 installed diff --git a/wiki/installer.md b/wiki/installer.md index 1433949b7..08fbb5adc 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -1732,7 +1732,7 @@ fields, the installer refuses to narrow rather than install everything. |------|---------| | `0` | everything asked for is installed or already in place | | `1` | a failure: the one-liner could not download or verify `install.py`, or found no Python 3.8 with its ssl, unicodedata and zlib modules (nothing was run), an unusable base URL, `--installed-cores` with no core found, a manifest that fails validation, a fetch that never succeeded, no platform to install for in a non-interactive run, some files failed or were left out by `--offline` while others installed, `--check` found files to fetch, a `--from-pack` pack that cannot be read, lacks a part or is for another platform, a `--from` folder that does not exist or cannot be read, a `--dest` that is not the card of the system named (another system's card, a folder inside one, a drive with none of its files where its card always has them, or a folder that does not exist), `--restore` could not put a file back, or `--add-app` could not put every file of the app on the card | -| `2` | nothing was installed: every file the run tried failed (a BIOS folder that cannot be written, for one), or `--offline` left every file out, the drive has too little free space for the download (checked before any folder is created), or the question about a folder that does not exist, named with `--dest` or typed at the folder question, was answered no. Also an option the installer refused before any download: an unknown option, `--jobs` outside 1 to 32, `--target` with `--every-board`, `--target` with `--from-pack` or `--offline` (or `RETROBIOS_INSTALL_OFFLINE` set) when no core list per board is at hand (the pack carries none and none is beside `--manifest` as `targets/.json`), `--list-targets` with `--offline` and neither, `--add-app` with `--check`, `--dry-run`, a listing, `--from-pack` or `--offline`. The message then starts with `install.py: error:`. Also an unknown platform, target, system, core or emulator, said in an `Error:` or `Unknown platform` line and an `error` event whose `stage` is `option`. Also `--add-app` for a folder on no card that takes the app, said in an `Error:` line and an `app` event before anything is written. With `--restore`, a folder that is not a dated folder inside `retrobios-backup`. Also a quit chosen before the install, as `gh` ends a cancelled prompt: `q` at a question, `q` or **Quit** on the full screen, which then keeps `RetroBIOS: quit. Nothing was installed.`, and the full screen closed with no button pressed (`RETROBIOS_IDLE_EXIT`). No code but `0` closes a Windows Terminal tab started on the installer itself, so such a tab stays open on a quit | +| `2` | nothing was installed: every file the run tried failed (a BIOS folder that cannot be written, for one), or `--offline` left every file out, the drive has too little free space for the download (checked before any folder is created), or the question about a folder that does not exist, named with `--dest` or typed at the folder question, was answered no. Also an option the installer refused before any download: an unknown option, `--jobs` outside 1 to 32, `--target` with `--every-board`, `--target` with `--from-pack` or `--offline` (or `RETROBIOS_INSTALL_OFFLINE` set) when no core list per board is at hand (the pack carries none and none is beside `--manifest` as `targets/.json`), or for a platform that publishes none, which is said to have no hardware targets, `--list-targets` with `--offline` and neither, `--add-app` with `--check`, `--dry-run`, a listing, `--from-pack` or `--offline`. The message then starts with `install.py: error:`. Also an unknown platform, target, system, core or emulator, said in an `Error:` or `Unknown platform` line and an `error` event whose `stage` is `option`. Also `--add-app` for a folder on no card that takes the app, said in an `Error:` line and an `app` event before anything is written. With `--restore`, a folder that is not a dated folder inside `retrobios-backup`. Also a quit chosen before the install, as `gh` ends a cancelled prompt: `q` at a question, `q` or **Quit** on the full screen, which then keeps `RetroBIOS: quit. Nothing was installed.`, and the full screen closed with no button pressed (`RETROBIOS_IDLE_EXIT`). No code but `0` closes a Windows Terminal tab started on the installer itself, so such a tab stays open on a quit | | `130` | stopped with Ctrl+C, or with Ctrl+C or Ctrl+D at a question, the terminal keeping `RetroBIOS: stopped. Nothing was installed.` before an install. Run the same command again to carry on | | `141` | the output was piped to a reader that closed early (`--list-platforms \| head`) |