Files
openswarm/.github/workflows/e2e.yml
T
Eric 2b4d90edaa [eric] ci: parallelize + cache the e2e gate to cut push wall-clock under ~10 min
- split the single windows e2e job into parallel jobs: gate (pure-node
  selftests, no build), verify (build + verify-all), playwright (build +
  renderer suite). verify and playwright run concurrently so wall-clock is
  max(verify, playwright), not their sum
- build the UNPACKED app on the gate path (electron-builder --dir via a new
  -DirOnly switch in build-app-win.ps1), skipping the ~2min NSIS LZMA
  compression. verify-all drives win-unpacked\OpenSwarm.exe; afterPack +
  locale paks still run in the pack phase, and verify-update-feed skips
  cleanly when no installer feed is present
- cache the heavy build inputs (bundled Python env, uv, MCP bundles) + npm
  across runs, keyed on their source manifests; the build script already skips
  any input already on disk, so warm builds are fast
- move the destructive installer cycle to its own job gated off routine pushes
  (runs on PR-to-main / dispatch; release-windows.yml covers v* tags)
- apply the same caching to dogfood (shares cache keys, so the two warm each other)
2026-05-28 23:55:32 -07:00

281 lines
10 KiB
YAML

name: E2E (packaged app, Windows)
# Fast-feedback packaged-app gate, Windows-only. Split into parallel jobs so the
# push wall-clock stays well under ~10 min:
# gate - cheap pure-node selftests (no build): mutation gate + preflight
# Layers 1-5. Fails the "tests that test the tests" fast.
# verify - build the UNPACKED app (electron-builder --dir, which skips the
# slow ~2min NSIS LZMA compression) and run the deterministic
# verify-all gate against win-unpacked\OpenSwarm.exe.
# playwright - build the unpacked app and run the renderer-level Playwright e2e.
# installer - full NSIS build + destructive install->verify->uninstall. The
# heaviest leg, so it runs only on PR-to-main / dispatch (NOT on
# routine pushes); release-windows.yml covers it on v* tags.
# verify + playwright run concurrently; both reuse cached heavy build inputs
# (bundled Python env, uv, MCP bundles, npm) so warm builds are fast - the build
# script skips any input already on disk.
#
# macOS legs were removed (runner starvation + untriageable mac-only failures);
# re-add when a Mac maintainer can own them. Real Win10 coverage still needs the
# SELF-HOSTED e2e-win10 job (repo var WIN10_SELF_HOSTED=true + a
# [self-hosted, windows, win10] runner).
on:
push:
branches: [eric/lock]
paths: &build-paths
- 'electron/**'
- 'frontend/**'
- 'backend/**'
- 'e2e/**'
- 'scripts/build-app**'
- 'scripts/build-python-env**'
- 'scripts/fetch-router**'
- 'scripts/ci/**'
- '.github/workflows/e2e.yml'
pull_request:
branches: [main]
paths: *build-paths
workflow_dispatch:
permissions:
contents: read
env:
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
GOOGLE_OAUTH_CLIENT_ID: 'e2e-placeholder.apps.googleusercontent.com'
GOOGLE_OAUTH_CLIENT_SECRET: 'e2e-placeholder-secret'
jobs:
# Pure-node, no build: cheap enough to always run and fail fast.
gate:
runs-on: windows-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.18.1'
- name: Gate selftest (mutation)
shell: bash
run: node scripts/ci/selftest-gate.js
- name: Preflight selftest (Layers 1+2)
shell: bash
run: node scripts/ci/selftest-preflight.js
- name: Preflight failure rigs (Layer 3)
shell: bash
run: node scripts/ci/verify-preflight-rigs.js
- name: Preflight race / cache (Layer 4)
shell: bash
run: node scripts/ci/verify-preflight-race.js
- name: Pairwise generator selftest (covering-array math)
shell: bash
run: node scripts/ci/selftest-pairwise.js
- name: Preflight matrix (normal + hostile-env scenarios)
shell: bash
run: |
node scripts/ci/verify-preflight.js
OPENSWARM_TEST_NETWORK=blocked node scripts/ci/verify-preflight.js
OPENSWARM_TEST_APPDATA=readonly node scripts/ci/verify-preflight.js
OPENSWARM_TEST_LANG=de-DE node scripts/ci/verify-preflight.js
# Build the unpacked app + run the deterministic gate.
verify:
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.18.1'
cache: npm
cache-dependency-path: |
electron/package-lock.json
frontend/package-lock.json
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Cache bundled Python env
uses: actions/cache@v4
with:
path: electron/python-env
key: pyenv-win-${{ hashFiles('scripts/build-python-env-win.ps1', 'backend/requirements.txt') }}
- name: Cache uv binaries
uses: actions/cache@v4
with:
path: backend/uv-bin
key: uvbin-win-${{ hashFiles('scripts/build-app-win.ps1') }}
- name: Cache MCP bundles
uses: actions/cache@v4
with:
path: backend/mcp-bundles
key: mcpbundles-win-${{ hashFiles('scripts/build-app-win.ps1') }}
- name: Build packaged app (unpacked, no installer)
shell: pwsh
run: pwsh -NoProfile -File scripts/build-app-win.ps1 -DirOnly
- name: Deterministic gate (verify-all)
shell: bash
run: node scripts/ci/verify-all.js
# Build the unpacked app + run the Playwright renderer suite, concurrently with verify.
playwright:
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.18.1'
cache: npm
cache-dependency-path: |
electron/package-lock.json
frontend/package-lock.json
e2e/package-lock.json
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Cache bundled Python env
uses: actions/cache@v4
with:
path: electron/python-env
key: pyenv-win-${{ hashFiles('scripts/build-python-env-win.ps1', 'backend/requirements.txt') }}
- name: Cache uv binaries
uses: actions/cache@v4
with:
path: backend/uv-bin
key: uvbin-win-${{ hashFiles('scripts/build-app-win.ps1') }}
- name: Cache MCP bundles
uses: actions/cache@v4
with:
path: backend/mcp-bundles
key: mcpbundles-win-${{ hashFiles('scripts/build-app-win.ps1') }}
- name: Build packaged app (unpacked, no installer)
shell: pwsh
run: pwsh -NoProfile -File scripts/build-app-win.ps1 -DirOnly
- name: Install e2e deps
shell: bash
working-directory: e2e
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
run: npm ci
- name: GUI hand selftest (MCP)
shell: bash
working-directory: e2e
run: node mcp/selftest.js
- name: Run E2E (Playwright)
shell: bash
working-directory: e2e
run: npm test
- name: Upload E2E results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results
path: e2e/results.json
if-no-files-found: ignore
retention-days: 14
# Per-test traces: playwright-trace.zip, events.jsonl, mousepath.jsonl,
# backend.log.tail - always uploaded so a failed run is debuggable.
- name: Upload E2E visibility traces
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-traces
path: e2e/traces/
if-no-files-found: ignore
retention-days: 14
# Heaviest leg: full NSIS installer + destructive install->verify->uninstall on
# a clean runner. Skip on routine pushes; run on PRs into main + manual dispatch.
installer:
if: github.event_name != 'push'
runs-on: windows-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.18.1'
cache: npm
cache-dependency-path: |
electron/package-lock.json
frontend/package-lock.json
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Cache bundled Python env
uses: actions/cache@v4
with:
path: electron/python-env
key: pyenv-win-${{ hashFiles('scripts/build-python-env-win.ps1', 'backend/requirements.txt') }}
- name: Cache uv binaries
uses: actions/cache@v4
with:
path: backend/uv-bin
key: uvbin-win-${{ hashFiles('scripts/build-app-win.ps1') }}
- name: Cache MCP bundles
uses: actions/cache@v4
with:
path: backend/mcp-bundles
key: mcpbundles-win-${{ hashFiles('scripts/build-app-win.ps1') }}
- name: Build packaged app (full NSIS installer)
shell: pwsh
run: pwsh -NoProfile -File scripts/build-app-win.ps1
- name: Installer cycle (clean runner)
shell: pwsh
run: node scripts/ci/verify-installer.js --destructive
# Real Windows 10 coverage. Skipped unless a self-hosted Win10 runner exists
# and WIN10_SELF_HOSTED=true (repo variable). Mirrors the windows steps above.
e2e-win10:
if: ${{ vars.WIN10_SELF_HOSTED == 'true' }}
runs-on: [self-hosted, windows, win10]
timeout-minutes: 90
env:
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
GOOGLE_OAUTH_CLIENT_ID: 'e2e-placeholder.apps.googleusercontent.com'
GOOGLE_OAUTH_CLIENT_SECRET: 'e2e-placeholder-secret'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.18.1'
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Gate selftest (mutation)
shell: bash
run: node scripts/ci/selftest-gate.js
- name: Preflight selftest + rigs + race + matrix (Layers 1-5)
shell: bash
run: |
node scripts/ci/selftest-preflight.js
node scripts/ci/verify-preflight-rigs.js
node scripts/ci/verify-preflight-race.js
node scripts/ci/verify-preflight.js
OPENSWARM_TEST_NETWORK=blocked node scripts/ci/verify-preflight.js
OPENSWARM_TEST_APPDATA=readonly node scripts/ci/verify-preflight.js
- name: Build packaged app (Windows)
shell: pwsh
run: pwsh -NoProfile -File scripts/build-app-win.ps1
- name: Deterministic gate (verify-all)
shell: bash
run: node scripts/ci/verify-all.js
- name: Install e2e deps
shell: bash
working-directory: e2e
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
run: npm ci
- name: GUI hand selftest (MCP)
shell: bash
working-directory: e2e
run: node mcp/selftest.js
- name: Run E2E (Playwright)
shell: bash
working-directory: e2e
run: npm test
- name: Installer cycle (clean runner)
shell: pwsh
run: node scripts/ci/verify-installer.js --destructive