mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 20:59:03 -05:00
feat: offer winget when windows lacks python
Without Python, install.ps1 at a console now offers to run winget install Python.Python.3.12, rereads PATH from the registry and carries on. The Store's python.exe placeholder fails the version check like a missing Python.
This commit is contained in:
1 parent
c29c456206
commit
26a0717601
3 files changed
+92
-14
No files matched your search
+39
-12
@@ -64,24 +64,51 @@
|
||||
$installer = $temporary
|
||||
}
|
||||
|
||||
$launcher = @()
|
||||
$python = Get-Command py -ErrorAction SilentlyContinue
|
||||
if ($python) {
|
||||
$launcher = @("-3")
|
||||
} else {
|
||||
$python = Get-Command python3 -ErrorAction SilentlyContinue
|
||||
if (-not $python) {
|
||||
$python = Get-Command python -ErrorAction SilentlyContinue
|
||||
# The py launcher first, then python3 and python. The Microsoft Store
|
||||
# places a python.exe that only points at the Store: it fails the
|
||||
# version check like a missing one.
|
||||
$findPython = {
|
||||
$found = Get-Command py -ErrorAction SilentlyContinue
|
||||
if ($found) { return ,@($found, @("-3")) }
|
||||
foreach ($name in @("python3", "python")) {
|
||||
$found = Get-Command $name -ErrorAction SilentlyContinue
|
||||
if ($found) { return ,@($found, @()) }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
$usable = {
|
||||
param($candidate)
|
||||
if (-not $candidate) { return $false }
|
||||
$arguments = $candidate[1]
|
||||
& $candidate[0].Source @arguments -c "import sys; raise SystemExit(sys.version_info < (3, 8))" | Out-Null
|
||||
return ($LASTEXITCODE -eq 0)
|
||||
}
|
||||
$needPython = "Python 3.8 or newer is required. Install it with " +
|
||||
"'winget install Python.Python.3.12' or from https://www.python.org/downloads/ " +
|
||||
"(tick 'Add python.exe to PATH'), then open a new PowerShell window and run this command again."
|
||||
if (-not $python) {
|
||||
throw $needPython
|
||||
$candidate = & $findPython
|
||||
if (-not (& $usable $candidate)) {
|
||||
# winget ships with Windows 10 and 11: at a console the bootstrap
|
||||
# offers to run the install the message names, then carries on.
|
||||
$winget = Get-Command winget -ErrorAction SilentlyContinue
|
||||
if ($winget -and -not [Console]::IsInputRedirected) {
|
||||
$answer = Read-Host "Python 3.8 or newer is required. Install Python 3.12 now with winget? [Y/n]"
|
||||
if ($answer -notmatch '^\s*[nNqQ]') {
|
||||
& $winget.Source install --id Python.Python.3.12 --exact --source winget `
|
||||
--accept-package-agreements --accept-source-agreements
|
||||
# The installer adds Python to the PATH of new sessions:
|
||||
# this one reads it again from the registry.
|
||||
if ($env:OS -eq "Windows_NT") {
|
||||
$env:Path = [Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
|
||||
[Environment]::GetEnvironmentVariable("Path", "User")
|
||||
}
|
||||
$candidate = & $findPython
|
||||
}
|
||||
}
|
||||
if (-not (& $usable $candidate)) { throw $needPython }
|
||||
}
|
||||
& $python.Source @launcher -c "import sys; raise SystemExit(sys.version_info < (3, 8))"
|
||||
if ($LASTEXITCODE -ne 0) { throw $needPython }
|
||||
$python = $candidate[0]
|
||||
$launcher = $candidate[1]
|
||||
# The installer rebuilds this command in its hints, and the user has
|
||||
# no install.py of their own to run.
|
||||
$previousBootstrap = $env:RETROBIOS_BOOTSTRAP
|
||||
|
||||
@@ -2415,6 +2415,52 @@ class NoPythonNamesThisSystemsCommand(unittest.TestCase):
|
||||
self.assertNotIn("apt", proc.stderr)
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("pwsh") and shutil.which("script"), "needs PowerShell and a terminal")
|
||||
class WindowsWithoutPythonIsOfferedWinget(unittest.TestCase):
|
||||
"""A Windows machine without Python was told to install it, open a new
|
||||
window and run the command again, while winget, present on Windows 10
|
||||
and 11, could do it from the bootstrap."""
|
||||
|
||||
def _folder(self) -> tuple[Path, Path]:
|
||||
folder = Path(_tempdir(self))
|
||||
shutil.copy(REPO_ROOT / "install.ps1", folder / "install.ps1")
|
||||
(folder / "install.py").write_text("print('installer ran')\n")
|
||||
fake_bin = folder / "bin"
|
||||
fake_bin.mkdir()
|
||||
python = shutil.which("python3") or sys.executable
|
||||
(fake_bin / "winget").write_text(
|
||||
f"#!/bin/sh\necho \"winget $*\"\n{shutil.which('ln')} -s {shlex.quote(python)} "
|
||||
f"{shlex.quote(str(fake_bin / 'python3'))}\n"
|
||||
)
|
||||
(fake_bin / "winget").chmod(0o755)
|
||||
return folder, fake_bin
|
||||
|
||||
def _run(self, folder: Path, fake_bin: Path, answer: str) -> subprocess.CompletedProcess:
|
||||
pwsh = shutil.which("pwsh")
|
||||
command = (f"{shlex.quote(pwsh)} -NoProfile -File "
|
||||
f"{shlex.quote(str(folder / 'install.ps1'))}")
|
||||
return subprocess.run(
|
||||
[shutil.which("script"), "-qec", command, "/dev/null"], input=answer,
|
||||
env={**os.environ, "PATH": f"{fake_bin}{os.pathsep}/usr/bin/nonexistent"},
|
||||
capture_output=True, text=True, timeout=120, check=False,
|
||||
)
|
||||
|
||||
def test_yes_installs_and_carries_on(self):
|
||||
folder, fake_bin = self._folder()
|
||||
proc = self._run(folder, fake_bin, "y\n")
|
||||
self.assertIn("Install Python 3.12 now with winget?", proc.stdout)
|
||||
self.assertIn("winget install --id Python.Python.3.12", proc.stdout)
|
||||
self.assertIn("installer ran", proc.stdout)
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout)
|
||||
|
||||
def test_no_names_the_command(self):
|
||||
folder, fake_bin = self._folder()
|
||||
proc = self._run(folder, fake_bin, "n\n")
|
||||
self.assertNotIn("installer ran", proc.stdout)
|
||||
self.assertIn("winget install Python.Python.3.12", proc.stdout)
|
||||
self.assertFalse((fake_bin / "python3").exists())
|
||||
|
||||
|
||||
class ThePowerShellWrapperPassesTheStatusOn(unittest.TestCase):
|
||||
"""Every non-zero exit became a red "installer failed" exception and
|
||||
code 1: a --check that found work read as a crash, 2 and 130 were lost."""
|
||||
|
||||
+7
-2
@@ -62,8 +62,13 @@ the same way: waiting five minutes settles it. Any change to `install.py`
|
||||
therefore means recomputing that pin in both wrappers.
|
||||
|
||||
They locate a Python 3.8 or newer interpreter (`python3` then `python` on
|
||||
POSIX, `py -3` then `python3` then `python` on Windows) and refuse to continue
|
||||
without one. `install.sh` needs `curl` or `wget` and `sha256sum` or `shasum`.
|
||||
POSIX, `py -3` then `python3` then `python` on Windows). Without one,
|
||||
`install.sh` names the command that installs it on the system it runs on
|
||||
(`pkg install python` in Termux, which it offers to run since Termux installs
|
||||
without root, `xcode-select --install` on macOS, apt, dnf, pacman, zypper or apk
|
||||
from `/etc/os-release`), and `install.ps1` at a console offers to run `winget
|
||||
install Python.Python.3.12`, then reads the new PATH and carries on.
|
||||
`install.sh` needs `curl` or `wget` (GNU or BusyBox) and `sha256sum` or `shasum`.
|
||||
Windows PowerShell 5.1 still negotiates SSL 3.0 and TLS 1.0 by default and
|
||||
GitHub refuses both, so `install.ps1` raises the floor to TLS 1.2 before
|
||||
downloading, and `install.sh` pins the same floor with `curl --tlsv1.2`.
|
||||
|
||||
Reference in new issue
Block a user