Release DF / v0.51.134 — stage-batch16 (Windows path defaults align with agent) #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Native Windows startup | |
| # Runs on PRs that touch start.ps1 (or this workflow). Validates the | |
| # native-Windows launch script catches the bug classes the recent | |
| # Windows-only batch caught manually (#2805 WOW64 ProgramFiles redirect, | |
| # #2806 venv-portability claim, #2807 port-parse + finally-cleanup). | |
| # | |
| # Scope (per nesquena-hermes comment on #2811 — option 1, mock-only): | |
| # hermes-agent is not published to PyPI, so we cannot pip-install it on | |
| # the runner. Instead we stub a hermes_cli/ directory next to a sibling | |
| # hermes-agent/ folder — just enough for start.ps1's existence guard to | |
| # pass. The workflow then runs start.ps1 for a few seconds and asserts | |
| # that none of start.ps1's own Write-Error guards fired. Server-boot | |
| # regressions remain covered by the Linux jobs and docker-smoke.yml. | |
| on: | |
| pull_request: | |
| paths: | |
| - 'start.ps1' | |
| - '.github/workflows/native-windows-startup.yml' | |
| workflow_dispatch: | |
| jobs: | |
| native-windows-startup: | |
| name: start.ps1 path discovery (mock hermes-agent) | |
| runs-on: windows-latest | |
| timeout-minutes: 8 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Python 3.11 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| # Create the WebUI venv. start.ps1 prefers $AgentDir\venv if it | |
| # exists, then falls back to the python on PATH. We create a | |
| # WebUI-local venv to mirror the README's documented native path | |
| # and to give start.ps1 a real python.exe to invoke. | |
| - name: Create venv (README path) | |
| shell: pwsh | |
| run: | | |
| python -m venv venv | |
| if (-not (Test-Path venv\Scripts\python.exe)) { | |
| throw "venv\Scripts\python.exe missing after venv create" | |
| } | |
| # Mock-only hermes-agent provisioning. We can't pip-install | |
| # hermes-agent (not on PyPI), so we stub the minimum that | |
| # start.ps1's `Test-Path hermes_cli -PathType Container` guard | |
| # needs to pass. server.py would crash on this stub at import | |
| # time — we deliberately do NOT probe /health below. | |
| - name: Stub hermes-agent (mock hermes_cli only) | |
| shell: pwsh | |
| run: | | |
| $agentDir = Join-Path (Split-Path -Parent $PWD) 'hermes-agent' | |
| $cliDir = Join-Path $agentDir 'hermes_cli' | |
| New-Item -ItemType Directory -Force -Path $cliDir | Out-Null | |
| Set-Content -Path (Join-Path $cliDir '__init__.py') -Value '# stub for CI path-discovery test only' | |
| "HERMES_WEBUI_AGENT_DIR=$agentDir" >> $env:GITHUB_ENV | |
| Write-Host "Stub hermes-agent provisioned at $agentDir" | |
| # Run start.ps1 and verify it passes its own discovery guards | |
| # without erroring out. server.py will exit non-zero on the stub | |
| # (no real CLI code) — that's expected and not asserted against. | |
| # We only fail if start.ps1's own Write-Error guards fire. | |
| - name: Run start.ps1 + verify path discovery | |
| shell: pwsh | |
| run: | | |
| $stdout = Join-Path $env:RUNNER_TEMP 'start-ps1.out' | |
| $stderr = Join-Path $env:RUNNER_TEMP 'start-ps1.err' | |
| $proc = Start-Process -FilePath 'pwsh' ` | |
| -ArgumentList '-NoLogo','-File','.\start.ps1' ` | |
| -WorkingDirectory $PWD ` | |
| -PassThru ` | |
| -RedirectStandardOutput $stdout ` | |
| -RedirectStandardError $stderr | |
| "SERVER_PID=$($proc.Id)" >> $env:GITHUB_ENV | |
| Write-Host "Spawned start.ps1 wrapper PID $($proc.Id)" | |
| # Path discovery is sub-second; the 8s buffer lets the python | |
| # launch land in the logs (and immediately exit on the stub). | |
| Start-Sleep -Seconds 8 | |
| Write-Host "===== start.ps1 stdout =====" | |
| $stdoutContent = if (Test-Path $stdout) { Get-Content $stdout -Raw } else { '<empty>' } | |
| Write-Host $stdoutContent | |
| Write-Host "===== start.ps1 stderr =====" | |
| $stderrContent = if (Test-Path $stderr) { Get-Content $stderr -Raw } else { '<empty>' } | |
| Write-Host $stderrContent | |
| # Pattern set: every Write-Error message start.ps1 can emit on | |
| # its own discovery path. If any of these appear in stderr, | |
| # path discovery regressed and the job must fail. | |
| $guardErrors = @( | |
| 'Python 3 is required', | |
| 'hermes-agent not found', | |
| 'HERMES_WEBUI_AGENT_DIR is set to', | |
| 'is not a valid integer port', | |
| 'is out of TCP-port range', | |
| 'server.py not found' | |
| ) | |
| foreach ($msg in $guardErrors) { | |
| if ($stderrContent -and $stderrContent -match [regex]::Escape($msg)) { | |
| throw "REGRESSION: start.ps1 errored on guard '$msg' - path discovery failed." | |
| } | |
| } | |
| Write-Host "OK: start.ps1 path discovery - all guards passed." | |
| # taskkill /T walks the process tree, /F forces. taskkill returns | |
| # 128 ("process not found") if the PID is already gone — that's | |
| # the expected steady state for this mock-only workflow because | |
| # server.py exits immediately on the stub hermes_cli. Reset | |
| # $LASTEXITCODE so the step never fails on the cleanup itself. | |
| - name: Stop background server (tree-kill) | |
| if: always() | |
| shell: pwsh | |
| run: | | |
| if ($env:SERVER_PID) { | |
| & taskkill /PID $env:SERVER_PID /T /F 2>&1 | Out-Host | |
| $global:LASTEXITCODE = 0 | |
| } | |
| # Belt-and-suspenders: kill anything still bound to 8787. | |
| $hanging = Get-NetTCPConnection -LocalPort 8787 -State Listen -ErrorAction SilentlyContinue | |
| if ($hanging) { | |
| foreach ($c in $hanging) { | |
| try { Stop-Process -Id $c.OwningProcess -Force -ErrorAction Stop } catch {} | |
| } | |
| } | |
| exit 0 |