From 592f8fef2dcbce6f76af1569e13e483d9a0a9772 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 07:40:32 +0200 Subject: [PATCH] fix: exit 2 when the BIOS folder cannot be made --- install.ps1 | 2 +- install.py | 6 +++++- install.sh | 2 +- tests/test_install.py | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/install.ps1 b/install.ps1 index 98f34278..57e5a66f 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 = "974ecc9fd5fc23433ebd50488629abc20aa861a21389cf31044643ddd6e62418" +$defaultInstallSha256 = "8c5f113a893373b7bc0be00766f52b5c1629b9d6a587291428c06fed5c5ea61f" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 5b424eba..0da6a110 100644 --- a/install.py +++ b/install.py @@ -3273,11 +3273,15 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None: try: bios_path.mkdir(parents=True, exist_ok=True) except OSError as exc: + sys.stdout.flush() print(f"\nCannot create {bios_path}.", file=sys.stderr) print(f" {exc.strerror}.", file=sys.stderr) print(_write_advice(bios_path, bios_path in assumed_dests), file=sys.stderr) - sys.exit(1) + # The documented code for a folder that cannot be written, + # as when it exists read-only; 1 once an earlier platform + # of the run installed something. + sys.exit(1 if total_downloaded else 2) _check_free_space(bios_path, dl_size) progress = Progress(len(to_download), dl_size, term) failed, interrupted = download_files( diff --git a/install.sh b/install.sh index fe8db511..319b4ef0 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="974ecc9fd5fc23433ebd50488629abc20aa861a21389cf31044643ddd6e62418" +DEFAULT_INSTALL_SHA256="8c5f113a893373b7bc0be00766f52b5c1629b9d6a587291428c06fed5c5ea61f" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install.py b/tests/test_install.py index e87ef7d8..88747002 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -13,6 +13,7 @@ import shlex import re import shutil import subprocess +import sys import tempfile import threading import unittest @@ -2064,5 +2065,44 @@ class ThePowerShellWrapperPassesTheStatusOn(unittest.TestCase): self.assertEqual(proc.returncode, code, proc.stderr) self.assertNotIn("installer failed", proc.stdout + proc.stderr) + +@unittest.skipIf(os.name == "nt" or os.geteuid() == 0, "needs a folder this user cannot write") +class AnUnwritableFolderIsCodeTwo(unittest.TestCase): + """A BIOS folder that could not be created ended with 1, while the same + folder existing read-only ended with the documented 2.""" + + def test_both_shapes_exit_two(self): + 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"))) + locked = home / "locked" + locked.mkdir() + locked.chmod(0o555) + self.addCleanup(shutil.rmtree, home, True) + self.addCleanup(locked.chmod, 0o755) + env = { + **os.environ, + "HOME": str(home), + "RETROBIOS_OS": "linux", + "RETROBIOS_BASE_URL": f"http://127.0.0.1:{httpd.server_address[1]}", + } + for dest in (locked / "sub", locked): + with self.subTest(dest=dest.name): + proc = subprocess.run( + [sys.executable, "install.py", "--platform", "retroarch", + "--dest", str(dest), "--system", "atari-lynx", "--no-input"], + cwd=str(REPO_ROOT), env=env, capture_output=True, text=True, + timeout=300, check=False, + ) + self.assertEqual(proc.returncode, 2, proc.stdout + proc.stderr) + + if __name__ == "__main__": unittest.main()