name: Windows Testbox Probe on: workflow_dispatch: inputs: target_ref: description: "Git ref or SHA to check out" required: false default: "main" type: string runner_label: description: "Windows runner label" required: false default: "blacksmith-16vcpu-windows-2025" type: choice options: - blacksmith-16vcpu-windows-2025 - blacksmith-32vcpu-windows-2025 - windows-2025 keepalive_minutes: description: "Minutes to keep the Windows runner alive for SSH inspection" required: false default: "20" type: string require_wsl2: description: "Fail the run when WSL2 is unavailable" required: false default: false type: boolean import_ubuntu_wsl2: description: "Import a throwaway Ubuntu WSL2 distro when none is installed" required: false default: false type: boolean enable_wsl2_features: description: "Try enabling Windows WSL2/VM optional features before probing" required: false default: false type: boolean permissions: contents: read env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" jobs: probe: name: Windows probe runs-on: ${{ inputs.runner_label }} timeout-minutes: 75 defaults: run: shell: pwsh steps: - name: Checkout uses: actions/checkout@v6 with: ref: ${{ inputs.target_ref || github.ref }} persist-credentials: false submodules: false - name: Probe native Windows run: | $ErrorActionPreference = "Stop" Write-Host "runner=$env:RUNNER_NAME" Write-Host "machine=$env:COMPUTERNAME" Write-Host "workspace=$env:GITHUB_WORKSPACE" Write-Host "target_ref=${{ inputs.target_ref || github.ref }}" Write-Host ("os=" + [System.Environment]::OSVersion.VersionString) Write-Host ("arch=" + [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) Write-Host ("powershell=" + $PSVersionTable.PSVersion.ToString()) cmd.exe /c ver git --version - name: Probe WSL2 id: wsl2 env: ENABLE_WSL2_FEATURES: ${{ inputs.enable_wsl2_features }} IMPORT_UBUNTU_WSL2: ${{ inputs.import_ubuntu_wsl2 }} UBUNTU_WSL_ROOTFS_URL: https://cloud-images.ubuntu.com/wsl/releases/24.04/current/ubuntu-noble-wsl-amd64-wsl.rootfs.tar.gz run: | $ErrorActionPreference = "Continue" $ok = $false function Invoke-WslText { param([string[]] $Arguments) $output = & wsl.exe @Arguments 2>&1 $code = $LASTEXITCODE $text = (($output | ForEach-Object { "$_" }) -join "`n") -replace "`0", "" [pscustomobject]@{ Code = $code; Text = $text } } function Get-WslDistros { $result = Invoke-WslText -Arguments @("--list", "--quiet") $result.Text -split "\r?\n" | ForEach-Object { $_.Trim() } | Where-Object { $_ -and $_ -notmatch "Windows Subsystem for Linux has no installed distributions" -and $_ -notmatch "^Use 'wsl\.exe" -and $_ -notmatch "^and 'wsl\.exe" } } $wsl = Get-Command wsl.exe -ErrorAction SilentlyContinue if (-not $wsl) { Write-Warning "wsl.exe is not available on this runner." } else { Write-Host "wsl.exe=$($wsl.Source)" if ($env:ENABLE_WSL2_FEATURES -eq "true") { Write-Host "enable_wsl2_features=true" foreach ($feature in @("Microsoft-Windows-Subsystem-Linux", "VirtualMachinePlatform", "HypervisorPlatform", "Microsoft-Hyper-V-All")) { dism.exe /online /enable-feature /featurename:$feature /all /norestart Write-Host "enable_feature_${feature}_exit=$LASTEXITCODE" } } $status = Invoke-WslText -Arguments @("--status") Write-Host $status.Text Write-Host "wsl_status_exit=$($status.Code)" $list = Invoke-WslText -Arguments @("--list", "--verbose") Write-Host $list.Text Write-Host "wsl_list_exit=$($list.Code)" $distros = @(Get-WslDistros) if ($distros.Count -eq 0 -and $env:IMPORT_UBUNTU_WSL2 -eq "true") { Write-Host "import_ubuntu_wsl2=true" $wslRoot = "C:\wsl\UbuntuProbe" $rootfs = "C:\wsl\ubuntu-noble-wsl.rootfs.tar.gz" New-Item -ItemType Directory -Force -Path @((Split-Path -Parent $rootfs), $wslRoot) | Out-Null Invoke-WebRequest -Uri $env:UBUNTU_WSL_ROOTFS_URL -OutFile $rootfs -UseBasicParsing wsl.exe --import UbuntuProbe $wslRoot $rootfs --version 2 Write-Host "wsl_import_exit=$LASTEXITCODE" $list = Invoke-WslText -Arguments @("--list", "--verbose") Write-Host $list.Text Write-Host "wsl_list_after_import_exit=$($list.Code)" $distros = @(Get-WslDistros) } if ($distros.Count -gt 0) { $distro = $distros[0] Write-Host "wsl_probe_distro=$distro" wsl.exe -d $distro --exec bash -lc 'set -euo pipefail; uname -a; if [ -f /etc/os-release ]; then sed -n "1,8p" /etc/os-release; fi' } else { wsl.exe --exec bash -lc 'set -euo pipefail; uname -a; if [ -f /etc/os-release ]; then sed -n "1,8p" /etc/os-release; fi' } if ($LASTEXITCODE -eq 0) { $ok = $true } Write-Host "wsl_exec_exit=$LASTEXITCODE" } if ($ok) { "wsl2_ok=true" >> $env:GITHUB_OUTPUT "OPENCLAW_WSL2_PROBE_OK=true" >> $env:GITHUB_ENV Write-Host "wsl2_ok=true" } else { "wsl2_ok=false" >> $env:GITHUB_OUTPUT "OPENCLAW_WSL2_PROBE_OK=false" >> $env:GITHUB_ENV Write-Warning "wsl2_ok=false" } exit 0 - name: Keep runner alive for SSH inspection env: KEEPALIVE_MINUTES: ${{ inputs.keepalive_minutes }} run: | $ErrorActionPreference = "Stop" $minutes = 20 if ($env:KEEPALIVE_MINUTES -match '^\d+$') { $minutes = [int]$env:KEEPALIVE_MINUTES } $minutes = [Math]::Max(0, [Math]::Min($minutes, 60)) Write-Host "keepalive_minutes=$minutes" for ($i = 1; $i -le $minutes; $i++) { Write-Host "keepalive minute $i/$minutes" Start-Sleep -Seconds 60 } - name: Enforce WSL2 requirement if: ${{ inputs.require_wsl2 }} run: | if ($env:OPENCLAW_WSL2_PROBE_OK -ne "true") { Write-Error "WSL2 probe failed or WSL2 is unavailable on this Windows runner." exit 1 }