blob: a530f0e1144e5ec54ad40aa44894a9ade1559fc0 [file]
name: Windows baseline
on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:
concurrency:
group: windows-baseline-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
windows_baseline:
name: windows_baseline (non-blocking)
runs-on: windows-latest
timeout-minutes: 45
continue-on-error: true
env:
WINDOWS_BASELINE_LOG_DIR: artifacts/windows-baseline
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
cache: npm
- name: Create log directory
shell: pwsh
run: New-Item -ItemType Directory -Force $env:WINDOWS_BASELINE_LOG_DIR | Out-Null
- name: Capture process baseline
shell: pwsh
run: |
$processes = @(
Get-CimInstance Win32_Process |
Where-Object { $_.Name -in @('node.exe', 'electron.exe') } |
ForEach-Object {
[pscustomobject]@{
ProcessId = $_.ProcessId
ParentProcessId = $_.ParentProcessId
Name = $_.Name
CreationDate = $_.CreationDate.ToUniversalTime().ToString('o')
CommandLine = $_.CommandLine
}
}
)
[pscustomobject]@{
CapturedAtUtc = [DateTime]::UtcNow.ToString('o')
Processes = $processes
} | ConvertTo-Json -Depth 4 |
Set-Content "$env:WINDOWS_BASELINE_LOG_DIR/process-baseline.json"
- id: install
name: Install dependencies
continue-on-error: true
shell: pwsh
run: |
npm.cmd ci *> "$env:WINDOWS_BASELINE_LOG_DIR/install.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/install.log"
exit $exitCode
- id: build
name: Build test artifacts
if: always() && steps.install.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
npm.cmd run build:test *> "$env:WINDOWS_BASELINE_LOG_DIR/build.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/build.log"
exit $exitCode
- id: inventory
name: Check Windows skip inventory
if: always() && steps.install.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
npm.cmd run windows:inventory *> "$env:WINDOWS_BASELINE_LOG_DIR/inventory.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/inventory.log"
exit $exitCode
- id: scripts
name: Run repository script tests
if: always() && steps.install.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
npm.cmd run test:scripts *> "$env:WINDOWS_BASELINE_LOG_DIR/script-tests.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/script-tests.log"
exit $exitCode
- id: smoke
name: Run CLI and Electron startup smoke
if: always() && steps.install.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
npm.cmd run smoke:windows *> "$env:WINDOWS_BASELINE_LOG_DIR/smoke.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/smoke.log"
exit $exitCode
- id: runtime_pty_input
name: Run Runtime PTY input gates
if: always() && steps.build.outcome == 'success'
continue-on-error: true
timeout-minutes: 5
shell: pwsh
run: |
node.exe --test --test-force-exit --test-timeout=15000 --test-reporter=tap --test-concurrency=1 --test-name-pattern="semantic text and Enter actions|terminal mode parsed before the control cut" packages/runtime/dist/__tests__/shell-run-manager.test.js 2>&1 |
Tee-Object -FilePath "$env:WINDOWS_BASELINE_LOG_DIR/runtime-pty-input.log"
$exitCode = $LASTEXITCODE
$output = Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/runtime-pty-input.log"
if ($exitCode -eq 0 -and ($output -notcontains '# tests 2' -or $output -notcontains '# pass 2')) {
Write-Error 'Runtime PTY input gate did not run exactly two passing tests'
exit 1
}
exit $exitCode
- id: storage
name: Capture storage test baseline
if: always() && steps.build.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
node.exe scripts/run-workspace-tests-parallel.mjs --concurrency=1 --workspaces=packages/storage *> "$env:WINDOWS_BASELINE_LOG_DIR/storage.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/storage.log"
exit $exitCode
- id: managed_workspace_crash
name: Run managed workspace crash recovery gates
if: always() && steps.build.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
$env:MAKA_STORAGE_STRESS = '1'
node.exe --test --test-concurrency=1 --test-name-pattern="real process crash|real crash|real-process crash|crash after baseline ref publication" packages/storage/dist/__tests__/managed-workspace-baseline.test.js packages/storage/dist/__tests__/git-workspace-service.test.js *> "$env:WINDOWS_BASELINE_LOG_DIR/managed-workspace-crash.log"
$exitCode = $LASTEXITCODE
Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/managed-workspace-crash.log"
exit $exitCode
- id: processes
name: Audit residual job processes
if: always()
continue-on-error: true
shell: pwsh
run: |
. ./scripts/windows-process-identity.ps1
$baseline = Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/process-baseline.json" -Raw |
ConvertFrom-Json
$baselineKeys = [System.Collections.Generic.HashSet[string]]::new(
[System.StringComparer]::Ordinal
)
foreach ($process in @($baseline.Processes)) {
[void]$baselineKeys.Add((Get-WindowsProcessIdentityKey $process))
}
$allProcesses = @(
Get-CimInstance Win32_Process |
ForEach-Object {
[pscustomobject]@{
ProcessId = $_.ProcessId
ParentProcessId = $_.ParentProcessId
Name = $_.Name
CreationDate = $_.CreationDate.ToUniversalTime().ToString('o')
CommandLine = $_.CommandLine
}
}
)
$leaks = @(
$allProcesses | Where-Object {
$_.Name -in @('node.exe', 'electron.exe') -and
-not $baselineKeys.Contains((Get-WindowsProcessIdentityKey $_))
}
)
ConvertTo-Json -InputObject $leaks -Depth 3 |
Set-Content "$env:WINDOWS_BASELINE_LOG_DIR/residual-processes.json"
if ($leaks.Count -eq 0) {
Write-Output 'No residual job-created Node or Electron processes.'
exit 0
}
$treeProcessIds = [System.Collections.Generic.HashSet[int]]::new()
foreach ($process in $leaks) {
[void]$treeProcessIds.Add($process.ProcessId)
}
do {
$addedDescendant = $false
foreach ($process in $allProcesses) {
if (
$treeProcessIds.Contains($process.ParentProcessId) -and
$treeProcessIds.Add($process.ProcessId)
) {
$addedDescendant = $true
}
}
} while ($addedDescendant)
$ownedTree = @(
$allProcesses | Where-Object { $treeProcessIds.Contains($_.ProcessId) }
)
ConvertTo-Json -InputObject $ownedTree -Depth 3 |
Set-Content "$env:WINDOWS_BASELINE_LOG_DIR/residual-process-tree.json"
$leaks | Format-List | Out-String | Tee-Object -FilePath "$env:WINDOWS_BASELINE_LOG_DIR/residual-processes.log"
foreach ($process in $leaks) {
& taskkill.exe /PID $process.ProcessId /T /F 2>&1 |
Add-Content "$env:WINDOWS_BASELINE_LOG_DIR/process-cleanup.log"
}
Start-Sleep -Seconds 1
$remainingById = @{}
Get-CimInstance Win32_Process |
ForEach-Object {
$key = Get-WindowsProcessIdentityKey $_
$remainingById[$key] = $_
}
$unreaped = @(
$ownedTree | Where-Object {
$remainingById.ContainsKey((Get-WindowsProcessIdentityKey $_))
}
)
ConvertTo-Json -InputObject $unreaped -Depth 3 |
Set-Content "$env:WINDOWS_BASELINE_LOG_DIR/residual-processes-after-cleanup.json"
if ($unreaped.Count -gt 0) {
Write-Error "Failed to reap $($unreaped.Count) process(es) from the residual job tree."
exit 1
}
Write-Error "Found $($leaks.Count) residual job root(s) and reaped their $($ownedTree.Count)-process tree."
exit 1
- name: Publish baseline summary
if: always()
shell: pwsh
run: |
@"
## Windows baseline
This lane is intentionally non-blocking while the Phase 1 backlog is being reduced.
| Check | Outcome |
|---|---|
| Install | ${{ steps.install.outcome }} |
| Build | ${{ steps.build.outcome }} |
| Skip inventory | ${{ steps.inventory.outcome }} |
| Script tests | ${{ steps.scripts.outcome }} |
| CLI / Electron smoke | ${{ steps.smoke.outcome }} |
| Storage suite | ${{ steps.storage.outcome }} |
| Runtime PTY input | ${{ steps.runtime_pty_input.outcome }} |
| Managed workspace crash recovery | ${{ steps.managed_workspace_crash.outcome }} |
| Residual processes | ${{ steps.processes.outcome }} |
Download the `windows-baseline` artifact for complete logs.
"@ >> $env:GITHUB_STEP_SUMMARY
- name: Upload Windows baseline logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: windows-baseline
path: artifacts/windows-baseline
if-no-files-found: warn
retention-days: 14