ci: provision Python via nuget (idempotent, no registry side effects)

The per-user MSI installer is stateful across runs on a persistent VM: once a
version is registered, a later /quiet install no-ops and never lands at the new
TargetDir, so python.exe went missing. Switch to a standalone CPython from
nuget cached under LOCALAPPDATA — no registry/PATH changes, idempotent, reused
across runs. A pre-existing Python 3.11 on PATH is still honoured.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 20:44:47 -07:00
parent fc01f8e995
commit 0d0786e222
+20 -20
View File
@@ -45,16 +45,17 @@ jobs:
run: |
$ErrorActionPreference = 'Stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$python = $null
# 1) Existing "py -3.11" launcher
try {
$exe = & py -3.11 -c "import sys; print(sys.executable)" 2>$null
if ($LASTEXITCODE -eq 0 -and $exe) { $python = $exe.Trim() }
} catch {}
# Persistent, side-effect-free Python for this builder. We use a
# standalone CPython from nuget (no registry/PATH changes, idempotent)
# cached under LOCALAPPDATA so subsequent runs reuse it. A pre-existing
# Python 3.11 on PATH is honoured if present.
$pyVersion = "3.11.9"
$root = Join-Path $env:LOCALAPPDATA "MacroPadBuild"
$python = Join-Path $root "python.$pyVersion\tools\python.exe"
# 2) A python on PATH that is 3.11.x
if (-not $python) {
if (-not (Test-Path $python)) {
# Honour an existing 3.11 on PATH (e.g. if the VM is pre-provisioned).
try {
$v = & python --version 2>&1
if ($LASTEXITCODE -eq 0 -and "$v" -match '3\.11') {
@@ -63,21 +64,20 @@ jobs:
} catch {}
}
# 3) Install per-user, silent (no elevation, cannot prompt)
if (-not $python) {
Write-Host "Python 3.11 not found - installing per-user..."
$tmp = if ($env:RUNNER_TEMP) { $env:RUNNER_TEMP } else { $env:TEMP }
$installer = Join-Path $tmp "python-3.11.9-amd64.exe"
Invoke-WebRequest -UseBasicParsing -Uri "https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe" -OutFile $installer
$dir = Join-Path $tmp "Python311"
$proc = Start-Process -FilePath $installer -Wait -PassThru -NoNewWindow -ArgumentList @(
"/quiet","InstallAllUsers=0","PrependPath=0","Include_launcher=0","Include_test=0","TargetDir=$dir"
)
if ($proc.ExitCode -ne 0) { throw "Python installer exited with $($proc.ExitCode)" }
$python = Join-Path $dir "python.exe"
if (-not (Test-Path $python)) {
Write-Host "Provisioning standalone Python $pyVersion via nuget..."
New-Item -ItemType Directory -Force -Path $root | Out-Null
$nuget = Join-Path $root "nuget.exe"
if (-not (Test-Path $nuget)) {
Invoke-WebRequest -UseBasicParsing -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile $nuget
}
& $nuget install python -Version $pyVersion -OutputDirectory $root -Source "https://api.nuget.org/v3/index.json" -NonInteractive
if ($LASTEXITCODE -ne 0) { throw "nuget install python exited with $LASTEXITCODE" }
$python = Join-Path $root "python.$pyVersion\tools\python.exe"
}
if (-not (Test-Path $python)) { throw "Python not found at '$python'" }
& $python -m ensurepip --upgrade 2>$null | Out-Null
& $python --version
# Write to GITHUB_ENV without a BOM (ascii) so the value parses cleanly.
Add-Content -Path $env:GITHUB_ENV -Value "PYTHON=$python" -Encoding ascii