name: Electron E2E on: workflow_dispatch: pull_request: branches: - main push: branches: - main jobs: electron-e2e: name: Electron E2E (${{ matrix.os }}) runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: fail-fast: false matrix: os: - ubuntu-latest - macos-latest - windows-latest env: CI: 'true' # Linux runners cannot use Electron's setuid chrome-sandbox; harmless on macOS/Windows. ELECTRON_DISABLE_SANDBOX: '1' steps: - name: Checkout code uses: actions/checkout@v6 - name: Setup pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'pnpm' - name: Prefer HTTPS for public GitHub git dependencies run: | git config --global "url.https://github.com/.insteadOf" "git@github.com:" git config --global --add "url.https://github.com/.insteadOf" "ssh://git@github.com/" - name: Install dependencies shell: bash run: | echo "side-effects-cache=false" >> .npmrc pnpm install --frozen-lockfile # electron/install.js and extract-zip both leave dist half-extracted on CI # (114MB zip downloads OK; only LICENSE + LICENSES.chromium.html land in dist). # Download with @electron/get, extract with the OS unzip tool instead. - name: Install Electron binary (Unix) if: runner.os != 'Windows' shell: bash env: force_no_cache: 'true' run: | set -euo pipefail unset ELECTRON_SKIP_BINARY_DOWNLOAD ELECTRON_DIR="$(node -p "require('path').dirname(require.resolve('electron/package.json'))")" echo "Electron package dir: $ELECTRON_DIR" rm -rf "$ELECTRON_DIR/dist" "$ELECTRON_DIR/path.txt" mkdir -p "$ELECTRON_DIR/dist" ZIP="$(cd "$ELECTRON_DIR" && node -e " const { downloadArtifact } = require('@electron/get'); const { version } = require('./package.json'); downloadArtifact({ version, artifactName: 'electron', force: true }) .then((z) => { process.stdout.write(z); process.exit(0); }) .catch((e) => { console.error(e); process.exit(1); }); ")" ZIP_SIZE="$(stat -c%s "$ZIP" 2>/dev/null || stat -f%z "$ZIP")" echo "Downloaded zip: $ZIP ($ZIP_SIZE bytes)" unzip -oq "$ZIP" -d "$ELECTRON_DIR/dist" echo "Extracted top-level entries: $(ls -1 "$ELECTRON_DIR/dist" | wc -l | tr -d ' ')" if [ -f "$ELECTRON_DIR/dist/electron.d.ts" ]; then mv "$ELECTRON_DIR/dist/electron.d.ts" "$ELECTRON_DIR/electron.d.ts" fi if [ "$(uname -s)" = "Darwin" ]; then PLATFORM_PATH='Electron.app/Contents/MacOS/Electron' test -f "$ELECTRON_DIR/dist/Electron.app/Contents/MacOS/Electron" else PLATFORM_PATH='electron' test -f "$ELECTRON_DIR/dist/electron" chmod +x "$ELECTRON_DIR/dist/electron" fi printf '%s' "$PLATFORM_PATH" > "$ELECTRON_DIR/path.txt" echo "path.txt: $(cat "$ELECTRON_DIR/path.txt")" - name: Install Electron binary (Windows) if: runner.os == 'Windows' shell: pwsh env: force_no_cache: 'true' run: | $ErrorActionPreference = 'Stop' Remove-Item Env:ELECTRON_SKIP_BINARY_DOWNLOAD -ErrorAction SilentlyContinue $electronDir = node -p "require('path').dirname(require.resolve('electron/package.json'))" Write-Host "Electron package dir: $electronDir" Remove-Item -Recurse -Force "$electronDir\dist", "$electronDir\path.txt" -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path "$electronDir\dist" | Out-Null Push-Location $electronDir try { $zip = node -e "const { downloadArtifact } = require('@electron/get'); const { version } = require('./package.json'); downloadArtifact({ version, artifactName: 'electron', force: true }).then((z) => { process.stdout.write(z); process.exit(0); }).catch((e) => { console.error(e); process.exit(1); });" } finally { Pop-Location } $zipSize = (Get-Item -LiteralPath $zip).Length Write-Host "Downloaded zip: $zip ($zipSize bytes)" Expand-Archive -LiteralPath $zip -DestinationPath "$electronDir\dist" -Force $entryCount = (Get-ChildItem -LiteralPath "$electronDir\dist").Count Write-Host "Extracted top-level entries: $entryCount" $typeDef = Join-Path $electronDir 'dist\electron.d.ts' if (Test-Path -LiteralPath $typeDef) { Move-Item -LiteralPath $typeDef -Destination (Join-Path $electronDir 'electron.d.ts') -Force } $exe = Join-Path $electronDir 'dist\electron.exe' if (-not (Test-Path -LiteralPath $exe)) { throw "electron.exe missing after extract: $exe" } Set-Content -LiteralPath (Join-Path $electronDir 'path.txt') -Value 'electron.exe' -NoNewline Write-Host "path.txt: electron.exe" - name: Verify Electron binary run: pnpm exec electron --version - name: Generate extension bridge run: pnpm run ext:bridge - name: Run Electron E2E on Linux if: runner.os == 'Linux' run: xvfb-run -a pnpm run test:e2e - name: Run Electron E2E on macOS if: runner.os == 'macOS' run: pnpm run test:e2e - name: Run Electron E2E on Windows if: runner.os == 'Windows' run: pnpm run test:e2e - name: Upload Playwright artifacts if: always() uses: actions/upload-artifact@v4 with: name: playwright-artifacts-${{ matrix.os }} path: | playwright-report test-results if-no-files-found: warn retention-days: 7