Repository navigation
docs: connect project metadata to public website #2013
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: Windows baseline | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| # Nightly full storage suite (PR/push keep curated OS-sensitive gates only). | |
| schedule: | |
| - cron: '17 6 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| full_storage: | |
| description: 'Run full packages/storage suite on Windows (slow, evidence lane)' | |
| type: boolean | |
| default: false | |
| concurrency: | |
| group: windows-baseline-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| windows: ${{ steps.plan.outputs.windows }} | |
| windows_runtime: ${{ steps.plan.outputs.windows_runtime }} | |
| windows_storage: ${{ steps.plan.outputs.windows_storage }} | |
| # Full suite is never selected by path plan — only schedule / dispatch. | |
| windows_storage_full: ${{ steps.full.outputs.windows_storage_full }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - id: plan | |
| name: Select affected Windows surfaces | |
| env: | |
| BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event_name == 'push' && github.sha || github.event.pull_request.head.sha }} | |
| run: | | |
| # schedule / manual dispatch have no meaningful PR base; use full | |
| # surface selection so the job still runs install+smoke+gates. | |
| if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| node scripts/ci-test-plan.mjs --full >> "$GITHUB_OUTPUT" | |
| elif [[ "$BASE_SHA" =~ ^0+$ ]] || ! git cat-file -e "${BASE_SHA}^{commit}"; then | |
| node scripts/ci-test-plan.mjs --full >> "$GITHUB_OUTPUT" | |
| else | |
| node scripts/ci-test-plan.mjs --base "$BASE_SHA" --head "$HEAD_SHA" >> "$GITHUB_OUTPUT" | |
| fi | |
| - id: full | |
| name: Decide full storage suite | |
| run: | | |
| if [[ "${{ github.event_name }}" == "schedule" ]]; then | |
| echo "windows_storage_full=true" >> "$GITHUB_OUTPUT" | |
| elif [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.full_storage }}" == "true" ]]; then | |
| echo "windows_storage_full=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "windows_storage_full=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| windows_baseline: | |
| needs: changes | |
| if: needs.changes.outputs.windows == 'true' | |
| 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: smoke | |
| name: Run CLI and Electron startup smoke | |
| if: always() && steps.build.outcome == 'success' | |
| continue-on-error: true | |
| shell: pwsh | |
| run: | | |
| npm.cmd --workspace @maka/desktop run build:smoke *> "$env:WINDOWS_BASELINE_LOG_DIR/smoke-build.log" | |
| $exitCode = $LASTEXITCODE | |
| Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/smoke-build.log" | |
| if ($exitCode -ne 0) { exit $exitCode } | |
| npm.cmd run smoke:windows:dist *> "$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' && needs.changes.outputs.windows_runtime == 'true' | |
| 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 | |
| # Full packages/storage test:dist is ~10 minutes on windows-latest and | |
| # mostly duplicates the Linux unit lane. Baseline keeps process/path/ | |
| # lock-sensitive gates here. Release-blocking crash evidence belongs to | |
| # windows-recovery.yml, so this diagnostic lane does not duplicate it. | |
| # Bump concurrency carefully — several suites spawn child processes and | |
| # fight for disk under high fan-out. | |
| - id: storage | |
| name: Run Windows storage path and lock gates | |
| if: always() && steps.build.outcome == 'success' && needs.changes.outputs.windows_storage == 'true' | |
| continue-on-error: true | |
| timeout-minutes: 12 | |
| shell: pwsh | |
| run: | | |
| $storageGates = @( | |
| 'packages/storage/dist/__tests__/root-authority.test.js', | |
| 'packages/storage/dist/__tests__/sqlite-recovery-concurrency.test.js', | |
| 'packages/storage/dist/__tests__/artifact-writer-lock.test.js', | |
| 'packages/storage/dist/__tests__/write-queue.test.js', | |
| 'packages/storage/dist/__tests__/marker-file.test.js', | |
| 'packages/storage/dist/__tests__/workspace-root.test.js', | |
| 'packages/storage/dist/__tests__/managed-workspace-owner.test.js', | |
| 'packages/storage/dist/__tests__/workspace-identity.test.js' | |
| ) | |
| node.exe --test --test-concurrency=2 --test-force-exit --test-timeout=60000 ` | |
| @storageGates ` | |
| 2>&1 | Tee-Object -FilePath "$env:WINDOWS_BASELINE_LOG_DIR/storage.log" | |
| exit $LASTEXITCODE | |
| # Full suite is evidence-only: nightly + manual dispatch. Do not run on | |
| # ordinary PRs — Linux unit already covers the package, and the full | |
| # Windows wall clock is ~10 minutes. | |
| - id: storage_full | |
| name: Capture full storage suite baseline | |
| if: always() && steps.build.outcome == 'success' && needs.changes.outputs.windows_storage_full == 'true' | |
| continue-on-error: true | |
| timeout-minutes: 25 | |
| shell: pwsh | |
| run: | | |
| node.exe scripts/run-workspace-tests-parallel.mjs --concurrency=1 --workspaces=packages/storage *> "$env:WINDOWS_BASELINE_LOG_DIR/storage-full.log" | |
| $exitCode = $LASTEXITCODE | |
| Get-Content "$env:WINDOWS_BASELINE_LOG_DIR/storage-full.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 }} | | |
| | CLI / Electron smoke | ${{ steps.smoke.outcome }} | | |
| | Storage path/lock gates | ${{ steps.storage.outcome }} | | |
| | Runtime PTY input | ${{ steps.runtime_pty_input.outcome }} | | |
| | Full storage suite (nightly/manual) | ${{ steps.storage_full.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 |