From 9f06b45322459c09a65a35e60171fdc68e9c692d Mon Sep 17 00:00:00 2001 From: MarcoPad Dev Date: Fri, 17 Jul 2026 19:53:39 -0700 Subject: [PATCH] ci: make Windows build robust on self-hosted runner actions/setup-python@v5 hangs inside its Windows tool-cache install script on the self-hosted runner. Replace it with an "Ensure Python 3.11" step that uses an existing py -3.11 / PATH python if present, otherwise silently installs Python per-user (no elevation, cannot prompt). Invoke pip/PyInstaller via the resolved interpreter path, and add a 30-minute job timeout so a hang can no longer run indefinitely. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/build-windows.yml | 69 ++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/build-windows.yml b/.gitea/workflows/build-windows.yml index a5e0420..88d87b1 100644 --- a/.gitea/workflows/build-windows.yml +++ b/.gitea/workflows/build-windows.yml @@ -1,8 +1,13 @@ name: Build Windows -# Windows-only build. Runs on the self-hosted org runner labelled -# "windows-latest". Produces dist/macropad.exe as a build artifact, and on a -# version tag (v*) creates/updates a Gitea release and attaches the exe. +# Windows-only build for the self-hosted org runner labelled "windows-latest". +# Produces dist/macropad.exe as a build artifact, and on a version tag (v*) +# creates/updates a Gitea release and attaches the exe. +# +# Note: actions/setup-python is intentionally NOT used — its Windows +# tool-cache install script hangs on self-hosted runners. Instead we use an +# existing Python 3.11 if the VM has one, otherwise silently install it +# per-user (no elevation, no prompts). on: workflow_dispatch: @@ -13,25 +18,64 @@ on: jobs: build-windows: runs-on: windows-latest + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v4 - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' + - name: Ensure Python 3.11 + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $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 {} + + # 2) A python on PATH that is 3.11.x + if (-not $python) { + try { + $v = & python --version 2>&1 + if ($LASTEXITCODE -eq 0 -and "$v" -match '3\.11') { + $python = (& python -c "import sys; print(sys.executable)").Trim() + } + } 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 -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)) { throw "Python not found at '$python'" } + & $python --version + "PYTHON=$python" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 - name: Install dependencies shell: pwsh run: | - python -m pip install --upgrade pip - pip install -e . - pip install pyinstaller + $ErrorActionPreference = 'Stop' + & $env:PYTHON -m pip install --upgrade pip + & $env:PYTHON -m pip install -e . + & $env:PYTHON -m pip install pyinstaller - name: Build Windows executable shell: pwsh - run: pyinstaller --noconfirm macropad.spec + run: | + $ErrorActionPreference = 'Stop' + & $env:PYTHON -m PyInstaller --noconfirm macropad.spec - name: Verify build output shell: pwsh @@ -54,6 +98,7 @@ jobs: env: GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + $ErrorActionPreference = 'Stop' $api = $env:GITHUB_API_URL $repo = $env:GITHUB_REPOSITORY $tag = $env:GITHUB_REF_NAME @@ -68,7 +113,7 @@ jobs: $rel = Invoke-RestMethod -Method Get -Uri "$api/repos/$repo/releases/tags/$tag" -Headers $headers } - # Attach the executable (delete a prior asset of the same name first). + # Attach the executable (replace a prior asset of the same name). $existing = $rel.assets | Where-Object { $_.name -eq 'macropad.exe' } if ($existing) { Invoke-RestMethod -Method Delete -Headers $headers `