mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 01:57:57 +00:00
One entry point (and preview/start/cancel/listUnfinished IPC for D1b-9) plans the move, gates terminals on the hook server's agent status, the close-tab process probe, running automation runs and live dispatches, pauses and proves exit, stages, sends blobs, commits only from blobs-transferred (#29825), and finishes on the desktop. Any failure resets to zero or goes forward when the host committed. Restore folders and the automations to turn back on are kept in the journal; the host turns those automations back on after commit, retried until it confirms. A real-PTY test pauses daemon shells, and runs on a real distro in the WSL orcad lane.
376 lines
19 KiB
YAML
376 lines
19 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'
|
|
- '.github/workflows/wsl-orcad-e2e.yml'
|
|
workflow_dispatch:
|
|
|
|
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-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
persist-credentials: false
|
|
- uses: ./.github/actions/install-node-dependencies
|
|
with:
|
|
native-runtime: node
|
|
- 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-latest
|
|
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: ${{ matrix.name }}
|
|
needs: [template, container-rootfs]
|
|
runs-on: windows-2025
|
|
timeout-minutes: 45
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: WSL2 distros (systemd off and on)
|
|
family: ubuntu-24.04
|
|
container: false
|
|
systemd-distro: orca-lane-systemd
|
|
install: command -v git >/dev/null || (apt-get update -qq && apt-get install -y -qq git)
|
|
- name: WSL2 distro (debian-12)
|
|
family: debian-12
|
|
container: true
|
|
systemd-distro: ''
|
|
install: apt-get update -qq && apt-get install -y -qq git procps
|
|
- name: WSL2 distro (fedora-41)
|
|
family: fedora-41
|
|
container: true
|
|
systemd-distro: ''
|
|
install: dnf -y -q install git procps-ng shadow-utils util-linux findutils tar gzip
|
|
env:
|
|
ORCA_BACKGROUND_LAUNCH: '1'
|
|
ORCA_WSL_ORCAD_LANE_RECEIPTS: ${{ github.workspace }}\test-results\wsl-orcad-lane
|
|
SYSTEMD_DISTRO: ${{ matrix.systemd-distro }}
|
|
ORCA_WSL_ORCAD_LANE_SYSTEMD_DISTRO: ${{ matrix.systemd-distro }}
|
|
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.container }}
|
|
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.container }}
|
|
with:
|
|
name: wsl-orcad-rootfs-${{ matrix.family }}
|
|
path: ${{ runner.temp }}/wsl-rootfs
|
|
# 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: |
|
|
$cache = Join-Path $env:RUNNER_TEMP 'wsl-rootfs'
|
|
New-Item -ItemType Directory -Force $cache | Out-Null
|
|
if ('${{ matrix.container }}' -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'
|
|
}
|
|
}
|
|
$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.
|
|
wsl.exe --import $d.Name (Join-Path $env:RUNNER_TEMP $d.Name) $rootfs --version 2
|
|
if ($LASTEXITCODE -ne 0) { throw "WSL2 import of $($d.Name) failed: $LASTEXITCODE" }
|
|
"$($d.Name) import_ms=$($sw.ElapsedMilliseconds)"
|
|
if (-not ((wsl.exe --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; ${{ matrix.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"
|
|
wsl.exe -d $d.Name -u root --exec sh -c $setup
|
|
if ($LASTEXITCODE -ne 0) { throw "provisioning $($d.Name) failed: $LASTEXITCODE" }
|
|
wsl.exe --terminate $d.Name
|
|
$sw.Restart()
|
|
wsl.exe -d $d.Name --exec sh -c 'echo "user=$(id -un) pid1=$(ps -p 1 -o comm=) kernel=$(uname -r)"'
|
|
if ($LASTEXITCODE -ne 0) { throw "first boot of $($d.Name) failed: $LASTEXITCODE" }
|
|
"$($d.Name) boot_ms=$($sw.ElapsedMilliseconds)"
|
|
wsl.exe --terminate $d.Name
|
|
}
|
|
wsl.exe --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.container && 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: 20
|
|
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: 15
|
|
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
|
|
- 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: |
|
|
$state = wsl.exe -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"
|
|
wsl.exe --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') {
|
|
$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
|