diff --git a/.gitea/workflows/build-windows.yml b/.gitea/workflows/build-windows.yml new file mode 100644 index 0000000..b4a3df4 --- /dev/null +++ b/.gitea/workflows/build-windows.yml @@ -0,0 +1,148 @@ +name: Build Windows + +# 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. +# +# Notes for self-hosted Windows runners: +# - Uses `shell: powershell` (Windows PowerShell 5.1); `pwsh`/PowerShell 7 is +# not assumed to be installed. +# - actions/setup-python is avoided (its Windows tool-cache install hangs on +# self-hosted runners); Python 3.11 is used if present, else silently +# installed per-user. + +on: + workflow_dispatch: + push: + tags: + - 'v*' + +jobs: + # Fast smoke test so a broken runner fails in seconds, not minutes. + preflight: + runs-on: windows-latest + timeout-minutes: 5 + steps: + - name: Runner check + shell: powershell + run: | + Write-Host "Runner OK on $env:COMPUTERNAME" + Write-Host "OS: $([System.Environment]::OSVersion.VersionString)" + Write-Host "PowerShell: $($PSVersionTable.PSVersion)" + $py = Get-Command py -ErrorAction SilentlyContinue + if ($py) { & py -3.11 --version } else { Write-Host "No py launcher; build will self-install Python 3.11." } + + build-windows: + needs: [preflight] + runs-on: windows-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Ensure Python 3.11 + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + # 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" + + 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') { + $python = (& python -c "import sys; print(sys.executable)").Trim() + } + } catch {} + } + + 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 + + - name: Install dependencies + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + & $env:PYTHON -m pip install --upgrade pip + & $env:PYTHON -m pip install -e . + & $env:PYTHON -m pip install pyinstaller + + - name: Build Windows executable + shell: powershell + run: | + $ErrorActionPreference = 'Stop' + & $env:PYTHON -m PyInstaller --noconfirm macropad.spec + + - name: Verify build output + shell: powershell + run: | + if (-not (Test-Path dist/macropad.exe)) { + throw "Build failed: dist/macropad.exe not found" + } + Get-Item dist/macropad.exe | Format-List Name, Length, LastWriteTime + + - name: Upload build artifact + # v3 uses the artifact protocol Gitea supports; v4+ requires a backend + # Gitea (reported as GHES) does not provide. + uses: actions/upload-artifact@v3 + with: + name: macropad-windows + path: dist/macropad.exe + if-no-files-found: error + + - name: Publish release asset (tags only) + if: startsWith(github.ref, 'refs/tags/') + shell: powershell + env: + GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $ErrorActionPreference = 'Stop' + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $api = $env:GITHUB_API_URL + $repo = $env:GITHUB_REPOSITORY + $tag = $env:GITHUB_REF_NAME + $headers = @{ Authorization = "token $env:GITEA_TOKEN" } + + # Create the release for this tag, or fetch it if it already exists. + $body = @{ tag_name = $tag; name = $tag; draft = $false; prerelease = $false } | ConvertTo-Json + try { + $rel = Invoke-RestMethod -Method Post -Uri "$api/repos/$repo/releases" ` + -Headers $headers -ContentType 'application/json' -Body $body + } catch { + $rel = Invoke-RestMethod -Method Get -Uri "$api/repos/$repo/releases/tags/$tag" -Headers $headers + } + + # 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 ` + -Uri "$api/repos/$repo/releases/$($rel.id)/assets/$($existing.id)" + } + Invoke-RestMethod -Method Post -Headers $headers ` + -Uri "$api/repos/$repo/releases/$($rel.id)/assets?name=macropad.exe" ` + -InFile dist/macropad.exe -ContentType 'application/octet-stream' + Write-Host "Attached macropad.exe to release $tag" diff --git a/Macro Pad.ico b/Macro Pad.ico new file mode 100644 index 0000000..ac85435 Binary files /dev/null and b/Macro Pad.ico differ diff --git a/macropad.spec b/macropad.spec index 52c2283..2e9a08b 100644 --- a/macropad.spec +++ b/macropad.spec @@ -48,6 +48,9 @@ a = Analysis( 'netifaces', 'websockets', 'multipart', + # Relay client (imported lazily in the GUI, so declare explicitly) + 'relay_client', + 'aiohttp', ], hookspath=[], hooksconfig={}, @@ -78,5 +81,5 @@ exe = EXE( target_arch=None, codesign_identity=None, entitlements_file=None, - icon='Macro Pad.png', + icon='Macro Pad.ico', )