Files
orca/.github/workflows/wsl-orcad-e2e.yml
T
Neil fceb475e44 test(wsl): measure distro restart to first output for the WSL host and an old-bridge lower bound (#34864)
* test(wsl): measure reboot to first prompt for the WSL host and the old bridge

The real-WSL lane's phase 3 now terminates its distro and times, median of 3 each, how long a
WSL-host project takes to a working terminal (holder boot, orcad start, terminal spawn and an
answered command) against one wsl.exe into the login shell, as the old bridge does. Test-only.

* test(wsl): relabel the restart baseline honestly and raise the WSL2 job cap

The baseline measures distro restart (VM warm) to first command output; the old-bridge figure is a
non-PTY wsl.exe --exec lower bound, not ConPTY first paint. The job cap rises to 40 minutes so it
no longer undercuts phase 3's own 30-minute timeout.
2026-10-28 03:40:34 -07:00

418 lines
22 KiB
YAML

name: WSL orcad E2E
# WSL via orcad (step C10) on real WSL2 distros: a GitHub-hosted windows-2025 runner imports a
# pinned Ubuntu 24.04 rootfs twice (systemd off and on), plus Debian 12 and Fedora 41 (systemd off,
# rootfs exported from their container images), and runs
# src/main/wsl/wsl-orcad-real-distro-lane.test.ts against both: deploy and launch, the wsl.exe
# stdio bridge, runtime RPC, liveness recovery, holder survival, structured chat and teardown. Not a required check.
# Hosted WSL2 was proven in #27783: WSL 2.7.14, import 13-18 s, cold boot 8-14 s.
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
# This lane drives host RPC, not UI; pr.yml owns mobile bundle/render checks.
paths:
- 'src/main/wsl/**'
- 'src/main/runtime/**'
- 'src/main/native-chat/**'
- 'src/main/claude/**'
- 'src/main/claude-accounts/**'
- 'src/main/codex/**'
- 'src/main/acp/**'
- 'src/main/pi/**'
- 'src/main/orcad/**'
- 'src/main/server/**'
- 'src/main/persistence/**'
- 'src/main/ipc/*wsl*'
- 'src/shared/**'
- 'config/scripts/*orcad*'
- 'package.json'
- 'pnpm-lock.yaml'
- 'src/main/ssh/orcad-*'
- 'src/main/managed-server/**'
- 'src/packages/process-host/**'
# Unit tests never run in this lane; only the real distro test below does.
- '!**/*.test.ts'
- '!**/*.test.tsx'
- 'src/main/wsl/wsl-orcad-real-distro-lane.test.ts'
- 'src/main/persistence/moving-wsl-projects/wsl-move-real-pause.test.ts'
- 'src/main/wsl/wsl-orcad-real-distro-idle-release-lane.test.ts'
- '.github/workflows/wsl-orcad-e2e.yml'
- 'config/ci/wsl-lifecycle/**'
- 'config/ci/windows-lifecycle-receipt.ps1'
workflow_dispatch:
inputs:
qualification:
description: Distribution coverage (Ubuntu canary validates the CI policy)
type: choice
options: [full, ubuntu-canary]
default: full
schedule:
- cron: '45 11 * * *'
permissions:
contents: read
concurrency:
group: wsl-orcad-e2e-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
# wsl.exe prints UTF-16LE to pipes unless told otherwise.
WSL_UTF8: '1'
ROOTFS_URL: https://releases.ubuntu.com/24.04.4/ubuntu-24.04.4-wsl-amd64.wsl
ROOTFS_SHA256: 9b2f7730dc68227dd04a9f3e5eab86ad85caf556b8606ad94f1f29ff5c4fd3f5
WSL_MSI_URL: https://github.com/microsoft/WSL/releases/download/3.0.1/wsl.3.0.1.0.x64.msi
WSL_MSI_SHA256: 28b1a0d013640a2ac95898ea705fa186e5b4ff767a1c1b49257161bc106599c6
PLAIN_DISTRO: orca-lane-plain
jobs:
# The distro is linux-x64-glibc; its node-pty slot only builds on Linux, and nothing else is needed.
template:
if: github.event_name != 'pull_request' || github.event.pull_request.draft != true
name: Build the linux-x64-glibc orcad template
runs-on: ubuntu-24.04
timeout-minutes: 20
outputs:
families: ${{ steps.scope.outcome == 'success' && steps.scope.outputs.families || '' }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
persist-credentials: false
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
- name: Select WSL distribution qualification
id: scope
continue-on-error: true
shell: bash
env:
MANUAL_QUALIFICATION: ${{ inputs.qualification }}
run: |
if [ "$GITHUB_EVENT_NAME" = workflow_dispatch ] && [ "$MANUAL_QUALIFICATION" = ubuntu-canary ]; then
echo 'families=["ubuntu-24.04"]' >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$GITHUB_EVENT_NAME" != pull_request ]; then
echo 'Full WSL distribution reference' >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
git diff --name-only --no-renames -z HEAD^1 HEAD > "$RUNNER_TEMP/wsl-changed"
git diff --name-only --no-renames --diff-filter=M -z HEAD^1 HEAD > "$RUNNER_TEMP/wsl-modified"
node config/scripts/wsl-orcad-qualification.mjs "$RUNNER_TEMP/wsl-changed" "$RUNNER_TEMP/wsl-modified"
- uses: ./.github/actions/build-mobile-web-bundle
- name: Build the template
run: |
pnpm build:orcad-prebuilds
pnpm build:orcad-prebuilds --require-slots linux-x64-glibc
node config/scripts/build-orcad-template.mjs --targets linux-x64-glibc
- uses: actions/upload-artifact@v7
with:
name: wsl-orcad-template
path: out/orcad-template/
# The template carries dot-directories (`.claude-plugin`) its manifest checksums.
include-hidden-files: true
retention-days: 1
if-no-files-found: error
# Container images ship no systemd, so these families run the systemd-off distro only.
container-rootfs:
if: github.event_name != 'pull_request' || github.event.pull_request.draft != true
name: Export ${{ matrix.family }} rootfs
runs-on: ubuntu-24.04
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
include:
- family: debian-12
image: debian:12
- family: fedora-41
image: fedora:41
steps:
- name: Export the image filesystem
run: |
docker pull --platform linux/amd64 '${{ matrix.image }}'
echo "digest: $(docker image inspect --format '{{index .RepoDigests 0}}' '${{ matrix.image }}')"
id="$(docker create --platform linux/amd64 '${{ matrix.image }}')"
mkdir -p rootfs
docker export "$id" > rootfs/rootfs.tar
docker rm "$id" >/dev/null
ls -l rootfs/rootfs.tar
- uses: actions/upload-artifact@v7
with:
name: wsl-orcad-rootfs-${{ matrix.family }}
path: rootfs/rootfs.tar
retention-days: 1
if-no-files-found: error
wsl2:
name: WSL2 distro (${{ matrix.family }})
needs: [template, container-rootfs]
runs-on: windows-2025
# Healthy runs take 4-11 min plus the phase 3 restart baseline; a runner frozen during WSL2 setup is only reaped here (retried by wsl-orcad-e2e-frozen-runner-retry.yml).
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
family: ${{ fromJSON(needs.template.outputs.families || '["ubuntu-24.04","debian-12","fedora-41"]') }}
env:
ORCA_BACKGROUND_LAUNCH: '1'
ORCA_WSL_ORCAD_LANE_RECEIPTS: ${{ github.workspace }}\test-results\wsl-orcad-lane
SYSTEMD_DISTRO: ${{ matrix.family == 'ubuntu-24.04' && 'orca-lane-systemd' || '' }}
ORCA_WSL_ORCAD_LANE_SYSTEMD_DISTRO: ${{ matrix.family == 'ubuntu-24.04' && 'orca-lane-systemd' || '' }}
defaults:
run:
shell: pwsh
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
# WSL before 3.0.1 drops the VM-wide WSLInterop entry when a systemd distro stops
# (microsoft/WSL#13885). Images from 20261007 ship 3.0.1, where reinstalling the MSI fails
# with 1603, so install only when older; `wsl --update` gets rate-limited by the GitHub API.
- name: Check the installed WSL version
id: wsl-version
run: |
"ImageOS=$env:ImageOS ImageVersion=$env:ImageVersion"
$details = (wsl.exe --version) -join "`n"
$details
$match = [regex]::Match($details, '(?m)^WSL version:\s*(\d+(?:\.\d+){2,3})\s*$')
# Inbox WSL has no --version, so an unreadable version means the package is needed.
$installed = if ($LASTEXITCODE -eq 0 -and $match.Success) { [version]$match.Groups[1].Value } else { [version]'0.0.0' }
$needsInstall = $installed -lt [version]'3.0.1'
"Installed WSL $installed; minimum 3.0.1; install needed: $needsInstall"
"needs-install=$($needsInstall.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT
- name: Restore the pinned WSL package
id: wsl-msi-cache
if: steps.wsl-version.outputs.needs-install == 'true'
uses: actions/cache/restore@v5
with:
path: ${{ runner.temp }}/wsl-msi
key: wsl-orcad-msi-${{ env.WSL_MSI_SHA256 }}
- name: Install WSL 3.0.1
if: steps.wsl-version.outputs.needs-install == 'true'
timeout-minutes: 8
run: |
$dir = Join-Path $env:RUNNER_TEMP 'wsl-msi'
New-Item -ItemType Directory -Force $dir | Out-Null
$msi = Join-Path $dir 'wsl.msi'
if (-not (Test-Path $msi)) { & "$env:WINDIR\System32\curl.exe" --fail --location --silent --show-error --output $msi $env:WSL_MSI_URL }
if ((Get-FileHash $msi -Algorithm SHA256).Hash.ToLowerInvariant() -ne $env:WSL_MSI_SHA256) {
Remove-Item $msi; throw 'WSL package checksum mismatch'
}
# Kept beside the receipts so the always() upload carries it.
New-Item -ItemType Directory -Force $env:ORCA_WSL_ORCAD_LANE_RECEIPTS | Out-Null
$log = Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'wsl-msi-install.log'
$install = Start-Process msiexec.exe -ArgumentList @('/i', $msi, '/qn', '/norestart', '/l*v', $log) -Wait -PassThru
if ($install.ExitCode -notin 0, 3010) {
if (Test-Path $log) {
$lines = @(Get-Content $log)
$failed = ($lines | Select-String -SimpleMatch 'Return value 3' | Select-Object -First 1).LineNumber
if ($failed) { '--- msiexec log before the failing action'; $lines[[math]::Max(0, $failed - 40)..($failed - 1)] }
'--- msiexec log tail'; $lines | Select-Object -Last 60
}
throw "WSL install failed: $($install.ExitCode)"
}
wsl.exe --shutdown
- name: Save the pinned WSL package
if: steps.wsl-version.outputs.needs-install == 'true' && steps.wsl-msi-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ${{ runner.temp }}/wsl-msi
key: wsl-orcad-msi-${{ env.WSL_MSI_SHA256 }}
- name: Verify WSL is at least 3.0.1
run: |
$details = (wsl.exe --version) -join "`n"
if ($LASTEXITCODE -ne 0) { throw "Could not read the WSL version: $details" }
$details
$match = [regex]::Match($details, '(?m)^WSL version:\s*(\d+(?:\.\d+){2,3})\s*$')
if (-not $match.Success) { throw 'Could not parse the WSL version' }
if ([version]$match.Groups[1].Value -lt [version]'3.0.1') { throw "WSL $($match.Groups[1].Value) is older than 3.0.1" }
- name: Restore the pinned rootfs
id: rootfs-cache
if: ${{ matrix.family == 'ubuntu-24.04' }}
uses: actions/cache/restore@v5
with:
path: ${{ runner.temp }}/wsl-rootfs
key: wsl-orcad-rootfs-${{ env.ROOTFS_SHA256 }}
- uses: actions/download-artifact@v8
if: ${{ matrix.family != 'ubuntu-24.04' }}
with:
name: wsl-orcad-rootfs-${{ matrix.family }}
path: ${{ runner.temp }}/wsl-rootfs
- name: Verify WSL lifecycle receipts
run: ./config/ci/wsl-lifecycle/test-wsl-lifecycle-command.ps1
# Runs beside the dependency install; the lane waits for it below.
- name: Import the distros as WSL2
id: import
background: true
timeout-minutes: 10
run: |
. config/ci/wsl-lifecycle/invoke-wsl-lifecycle-command.ps1
New-Item -ItemType Directory -Force $env:ORCA_WSL_ORCAD_LANE_RECEIPTS | Out-Null
$lifecycle = Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'import-lifecycle.json'
$cache = Join-Path $env:RUNNER_TEMP 'wsl-rootfs'
New-Item -ItemType Directory -Force $cache | Out-Null
if ('${{ matrix.family != 'ubuntu-24.04' }}' -eq 'true') {
$rootfs = Join-Path $cache 'rootfs.tar'
} else {
$rootfs = Join-Path $cache 'ubuntu.wsl'
if (-not (Test-Path $rootfs)) { Invoke-WebRequest $env:ROOTFS_URL -OutFile $rootfs }
if ((Get-FileHash $rootfs -Algorithm SHA256).Hash.ToLowerInvariant() -ne $env:ROOTFS_SHA256) {
Remove-Item $rootfs; throw 'rootfs checksum mismatch'
}
}
$install = switch ('${{ matrix.family }}') {
'ubuntu-24.04' { 'command -v git >/dev/null || (apt-get update -qq && apt-get install -y -qq git)' }
'debian-12' { 'apt-get update -qq && apt-get install -y -qq git procps' }
'fedora-41' { 'dnf -y -q install git procps-ng shadow-utils util-linux findutils tar gzip' }
default { throw 'Unknown WSL distribution family' }
}
$distros = @(@{ Name = $env:PLAIN_DISTRO; Systemd = 'false' })
if ($env:SYSTEMD_DISTRO) { $distros += @{ Name = $env:SYSTEMD_DISTRO; Systemd = 'true' } }
foreach ($d in $distros) {
$sw = [Diagnostics.Stopwatch]::StartNew()
# No WSL1 fallback: the lane exists to prove WSL2.
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage import -Distro $d.Name -Arguments @('--import', $d.Name, (Join-Path $env:RUNNER_TEMP $d.Name), $rootfs, '--version', '2')
"$($d.Name) import_ms=$($sw.ElapsedMilliseconds)"
if (-not ((Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage verify-version -Distro $d.Name -Arguments @('--list', '--verbose')) -match "$($d.Name)\s+\S+\s+2\s*$")) { throw "$($d.Name) is not a WSL2 distro" }
# A non-root default user, as on a real install; git for the lane's repo.
# The env exports go to the file a login bash reads first; Fedora's skel ships .bash_profile.
$setup = "set -e; $install; id orca >/dev/null 2>&1 || useradd -m -s /bin/bash orca; " +
"printf '[boot]\nsystemd=$($d.Systemd)\n[user]\ndefault=orca\n' > /etc/wsl.conf; " +
"rc=/home/orca/.profile; for f in /home/orca/.bash_profile /home/orca/.bash_login; do " +
"if [ -f `$f ]; then rc=`$f; break; fi; done; " +
"printf 'export ORCA_BACKGROUND_LAUNCH=1\nexport USERPROFILE=`$HOME\nexport APPDATA=`$HOME/.config\nexport LOCALAPPDATA=`$HOME/.local/share\nexport XDG_CONFIG_HOME=`$HOME/.config\nexport XDG_DATA_HOME=`$HOME/.local/share\nexport XDG_CACHE_HOME=`$HOME/.cache\nexport XDG_STATE_HOME=`$HOME/.local/state\nexport CLAUDE_CONFIG_DIR=`$HOME/.claude\nexport CODEX_HOME=`$HOME/.codex\n' >> `$rc"
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage provision -Distro $d.Name -Arguments @('-d', $d.Name, '-u', 'root', '--exec', 'sh', '-c', $setup)
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage terminate -Distro $d.Name -Arguments @('--terminate', $d.Name)
$sw.Restart()
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage first-boot -Distro $d.Name -Arguments @('-d', $d.Name, '--exec', 'sh', '-c', 'echo "user=$(id -un) pid1=$(ps -p 1 -o comm=) kernel=$(uname -r)"')
"$($d.Name) boot_ms=$($sw.ElapsedMilliseconds)"
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage terminate -Distro $d.Name -Arguments @('--terminate', $d.Name)
}
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage list -Arguments @('--list', '--verbose')
- uses: ./.github/actions/install-node-dependencies
with:
native-runtime: node
- uses: actions/download-artifact@v8
with:
name: wsl-orcad-template
path: ${{ runner.temp }}/orcad-template
- wait: import
# Saved even when the lane fails; the import step verified its checksum.
- name: Save the pinned rootfs
if: ${{ matrix.family == 'ubuntu-24.04' && steps.rootfs-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v5
with:
path: ${{ runner.temp }}/wsl-rootfs
key: wsl-orcad-rootfs-${{ env.ROOTFS_SHA256 }}
- name: Isolate the desktop test home
run: |
New-Item -ItemType Directory -Force $env:ORCA_WSL_ORCAD_LANE_RECEIPTS | Out-Null
$files = @('.claude/settings.json', '.claude.json', '.codex/hooks.json', '.codex/config.toml') | ForEach-Object { Join-Path $env:USERPROFILE $_ }
$hashes = @($files | ForEach-Object {
@{ Path = $_; Hash = $(if (Test-Path $_) { (Get-FileHash $_ -Algorithm SHA256).Hash } else { 'absent' }) }
})
$hashes | ConvertTo-Json | Set-Content (Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'real-home-hashes.json')
$homeDir = Join-Path $env:RUNNER_TEMP 'wsl-orcad-desktop-home'
foreach ($name in @('HOME', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'CLAUDE_CONFIG_DIR', 'CODEX_HOME')) {
$path = Join-Path $homeDir $name
New-Item -ItemType Directory -Force $path | Out-Null
"$name=$path" >> $env:GITHUB_ENV
}
- name: 'Phase 1: deploy, RPC, kill orcad, terminate distro'
timeout-minutes: 10
env:
ORCA_RUN_WSL_ORCAD_LANE: '1'
ORCA_WSL_ORCAD_LANE_PHASE: launch
ORCA_ORCAD_TEMPLATE_PATH: ${{ runner.temp }}/orcad-template
run: |
New-Item -ItemType Directory -Force $env:ORCA_WSL_ORCAD_LANE_RECEIPTS | Out-Null
node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/wsl/wsl-orcad-real-distro-lane.test.ts --reporter=verbose 2>&1 | Tee-Object (Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'launch.log')
if ($global:LASTEXITCODE -ne 0) { throw "launch phase failed: $global:LASTEXITCODE" }
# A new process: the holders must have kept both distros Running since phase 1 exited.
- name: 'Phase 2: holders survive, chat, reconnect, teardown'
timeout-minutes: 8
env:
ORCA_RUN_WSL_ORCAD_LANE: '1'
ORCA_WSL_ORCAD_LANE_PHASE: survive
ORCA_ORCAD_TEMPLATE_PATH: ${{ runner.temp }}/orcad-template
run: |
node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/wsl/wsl-orcad-real-distro-lane.test.ts --reporter=verbose 2>&1 | Tee-Object (Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'survive.log')
if ($global:LASTEXITCODE -ne 0) { throw "survive phase failed: $global:LASTEXITCODE" }
# The WSL move's pause on real distro shells: Sleep ends them, the daemon proves the exit,
# the journal keeps each pane's folder, and a new shell opens there.
- name: 'WSL move: pause real distro terminals'
if: ${{ !cancelled() && steps.import.outcome == 'success' }}
timeout-minutes: 10
env:
ORCA_WSL_MOVE_REAL_PAUSE_DISTRO: ${{ env.PLAIN_DISTRO }}
run: |
New-Item -ItemType Directory -Force $env:ORCA_WSL_ORCAD_LANE_RECEIPTS | Out-Null
node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/persistence/moving-wsl-projects/wsl-move-real-pause.test.ts --reporter=verbose 2>&1 | Tee-Object (Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'wsl-move-real-pause.log')
if ($global:LASTEXITCODE -ne 0) { throw "WSL move real pause failed: $global:LASTEXITCODE" }
wsl.exe --terminate $env:PLAIN_DISTRO
# R40: with no terminal left, orcad idle-exits and the holder lets the distro stop on its own.
- name: 'Phase 3: idle orcad releases the distro; distro restart to first output baseline'
timeout-minutes: 30
env:
ORCA_RUN_WSL_ORCAD_LANE: '1'
ORCA_WSL_ORCAD_LANE_PHASE: idle-release
ORCA_ORCAD_TEMPLATE_PATH: ${{ runner.temp }}/orcad-template
run: |
node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/wsl/wsl-orcad-real-distro-idle-release-lane.test.ts --reporter=verbose 2>&1 | Tee-Object (Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'idle-release.log')
if ($global:LASTEXITCODE -ne 0) { throw "idle-release phase failed: $global:LASTEXITCODE" }
- name: Real desktop agent configuration is unchanged
if: always()
run: |
$baseline = Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'real-home-hashes.json'
if (Test-Path $baseline) {
foreach ($file in (Get-Content $baseline -Raw | ConvertFrom-Json)) {
$hash = if (Test-Path $file.Path) { (Get-FileHash $file.Path -Algorithm SHA256).Hash } else { 'absent' }
if ($hash -ne $file.Hash) { throw "Real agent configuration changed: $($file.Path)" }
}
}
# Belt and braces for the runner: a missing VM-wide interop entry is restored, then fails.
- name: WSLInterop is intact
if: ${{ !cancelled() && steps.import.outcome == 'success' }}
timeout-minutes: 3
run: |
. config/ci/wsl-lifecycle/invoke-wsl-lifecycle-command.ps1
$lifecycle = Join-Path $env:ORCA_WSL_ORCAD_LANE_RECEIPTS 'interop-lifecycle.json'
try {
$state = Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage interop-check -Distro $env:PLAIN_DISTRO -Arguments @('-d', $env:PLAIN_DISTRO, '-u', 'root', '--exec', 'sh', '-c', 'if [ -e /proc/sys/fs/binfmt_misc/WSLInterop ]; then echo present; else echo :WSLInterop:M::MZ::/init:P > /proc/sys/fs/binfmt_misc/register && echo restored; fi')
"WSLInterop: $state"
} finally {
Invoke-WslLifecycleCommand -Receipt $lifecycle -Stage terminate -Distro $env:PLAIN_DISTRO -Arguments @('--terminate', $env:PLAIN_DISTRO)
}
if ("$state".Trim() -ne 'present') { throw "WSLInterop was missing ($state)" }
- name: Collect orcad logs
if: failure()
timeout-minutes: 3
run: |
$out = $env:ORCA_WSL_ORCAD_LANE_RECEIPTS
New-Item -ItemType Directory -Force $out | Out-Null
foreach ($d in @($env:PLAIN_DISTRO, $env:SYSTEMD_DISTRO) | Where-Object { $_ }) {
wsl.exe -d $d --exec sh -c 'for f in $(find "$HOME/.orca-remote" -name "*.log*" -type f 2>/dev/null); do echo "=== $f"; tail -n 400 "$f"; done' > (Join-Path $out "$d-orcad-logs.txt") 2>&1
}
wsl.exe --list --verbose
- name: Summary
if: always()
run: |
$dir = $env:ORCA_WSL_ORCAD_LANE_RECEIPTS
$lines = @("### WSL orcad lane (${{ matrix.family }}) — $env:ImageOS $env:ImageVersion", '')
foreach ($phase in 'launch', 'survive', 'idle-release') {
$file = Join-Path $dir "$phase.json"
if (Test-Path $file) { $lines += "#### $phase", '```json', (Get-Content $file -Raw), '```' }
}
$lines | Add-Content $env:GITHUB_STEP_SUMMARY
- uses: actions/upload-artifact@v7
if: always()
with:
name: wsl-orcad-lane-receipts-${{ matrix.family }}
path: test-results/wsl-orcad-lane/
retention-days: 7
if-no-files-found: ignore