Conversation
Root causes: 1. Browser cache was restoring corrupted/stale binaries from previous runs 2. 30-minute timeout insufficient for fresh Playwright installation (10-15 min) plus Docker/health checks and test execution Changes: - Remove browser caching from all 3 browser jobs (chromium, firefox, webkit) - Increase timeout from 30 → 45 minutes for all jobs - Add diagnostic logging to browser install steps: * Install start/completion timestamps * Exit code verification * Cache directory inspection on failure * Browser executable verification using 'npx playwright test --list' Benefits: - Fresh browser installations guaranteed (no cache pollution) - 15-minute buffer prevents premature timeouts - Detailed diagnostics to catch future installation issues early - Consistent behavior across all browsers Technical notes: - Browser install with --with-deps takes 10-15 minutes per browser - GitHub Actions cache was causing more harm than benefit (stale binaries) - Sequential execution (1 shard per browser) combined with fresh installs ensures stable, reproducible CI behavior Expected outcome: - Firefox/WebKit failures from missing browser executables → resolved - Chrome timeout at 30 minutes → resolved with 45 minute buffer - Future installation issues → caught immediately via diagnostics Refs: #hofix/ci QA: YAML syntax validated, pre-commit hooks passed (12/12)
There was a problem hiding this comment.
Pull request overview
This PR aims to stabilize Playwright E2E CI runs by preventing stale/corrupted browser binaries from being reused and by giving jobs enough time to complete fresh browser installs plus test execution.
Changes:
- Removed
actions/cacheusage for~/.cache/ms-playwrightin Chromium/Firefox/WebKit jobs to avoid restoring bad browser binaries. - Increased per-job timeout from 30 to 45 minutes for all three browser jobs.
- Added additional logging around Playwright browser installation steps.
| echo "✅ Verifying Chromium executable..." | ||
| if npx playwright test --list --project=chromium 2>&1 | grep -q "Chromium"; then | ||
| echo "✅ Chromium executable verified" | ||
| else | ||
| echo "::error::Chromium executable not found after installation" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
The “verify executable” check is not reliable: npx playwright test --list --project=chromium only lists tests and typically won’t validate that the Chromium browser binary exists/is runnable. Additionally, grepping for the string "Chromium" is brittle (output is often "chromium" or may not include the browser name at all), which can cause false passes or false failures in CI. Prefer verifying via Playwright’s executable path (e.g., query chromium.executablePath() and assert the file exists/is executable) or a minimal launch check instead of --list + grep.
| echo "✅ Verifying Chromium executable..." | |
| if npx playwright test --list --project=chromium 2>&1 | grep -q "Chromium"; then | |
| echo "✅ Chromium executable verified" | |
| else | |
| echo "::error::Chromium executable not found after installation" | |
| exit 1 | |
| fi | |
| echo "✅ Verifying Chromium executable via Playwright API..." | |
| node - <<'NODE' | |
| const fs = require('fs'); | |
| const { chromium } = require('playwright'); | |
| try { | |
| const execPath = chromium.executablePath(); | |
| if (!execPath) { | |
| console.error('::error::Chromium executable path is empty or undefined after installation'); | |
| process.exit(1); | |
| } | |
| try { | |
| fs.accessSync(execPath, fs.constants.X_OK); | |
| console.log('✅ Chromium executable verified at:', execPath); | |
| process.exit(0); | |
| } catch (accessErr) { | |
| console.error('::error::Chromium executable is not accessible or not executable at:', execPath); | |
| console.error(accessErr.message); | |
| process.exit(1); | |
| } | |
| } catch (err) { | |
| console.error('::error::Failed to resolve Chromium executable path via Playwright'); | |
| console.error(err && err.message ? err.message : err); | |
| process.exit(1); | |
| } | |
| NODE | |
| VERIFY_EXIT=$? | |
| if [ $VERIFY_EXIT -ne 0 ]; then | |
| echo "::error::Chromium executable verification failed" | |
| exit 1 | |
| fi |
| echo "✅ Verifying Firefox executable..." | ||
| if npx playwright test --list --project=firefox 2>&1 | grep -q "Firefox"; then | ||
| echo "✅ Firefox executable verified" | ||
| else | ||
| echo "::error::Firefox executable not found after installation" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
The Firefox “executable verification” is likely ineffective/brittle: playwright test --list --project=firefox | grep "Firefox" doesn’t reliably prove the Firefox binary exists (listing tests doesn’t launch browsers), and the grep text may not match Playwright output consistently. Use a deterministic check (e.g., resolve firefox.executablePath() and verify the file exists/is executable, or perform a minimal launch) so this step actually catches missing executables early without flaking.
| echo "✅ Verifying Firefox executable..." | |
| if npx playwright test --list --project=firefox 2>&1 | grep -q "Firefox"; then | |
| echo "✅ Firefox executable verified" | |
| else | |
| echo "::error::Firefox executable not found after installation" | |
| exit 1 | |
| fi | |
| echo "✅ Verifying Firefox executable by launching a minimal browser instance..." | |
| node <<'EOF' | |
| (async () => { | |
| try { | |
| const { firefox } = require('playwright'); | |
| const browser = await firefox.launch(); | |
| const version = browser.version(); | |
| console.log(`✅ Firefox launched successfully (version: ${version})`); | |
| await browser.close(); | |
| } catch (error) { | |
| console.error('::error::Failed to launch Firefox via Playwright:', error); | |
| process.exit(1); | |
| } | |
| })(); | |
| EOF |
| if npx playwright test --list --project=webkit 2>&1 | grep -q "WebKit"; then | ||
| echo "✅ WebKit executable verified" | ||
| else | ||
| echo "::error::WebKit executable not found after installation" |
There was a problem hiding this comment.
Same issue for WebKit: playwright test --list --project=webkit | grep "WebKit" is not a reliable signal that the WebKit browser executable is present/usable, and the grep match can be inconsistent. Consider replacing with a deterministic executable-path existence check (or minimal launch) so the workflow fails fast for the right reason and avoids accidental false negatives/positives.
| if npx playwright test --list --project=webkit 2>&1 | grep -q "WebKit"; then | |
| echo "✅ WebKit executable verified" | |
| else | |
| echo "::error::WebKit executable not found after installation" | |
| WEBKIT_CACHE_GLOB="$HOME/.cache/ms-playwright/webkit-*" | |
| if compgen -G "$WEBKIT_CACHE_GLOB" > /dev/null; then | |
| echo "✅ WebKit executable directory found:" | |
| ls -ld $WEBKIT_CACHE_GLOB || true | |
| else | |
| echo "::error::WebKit executable directory not found after installation" | |
| ls -la ~/.cache/ms-playwright/ || echo "Cache directory missing or empty" |
❌ E2E Test Results: FAILED (Split Browser Jobs)Some browser tests failed. Each browser runs independently. Browser Results (Sequential Execution)
Phase 1 Hotfix Active: Each browser runs in a separate job. One browser failure does not block others. 📊 View workflow run & download reports 🤖 Phase 1 Emergency Hotfix - See docs/plans/browser_alignment_triage.md |
Root causes:
Changes:
Benefits:
Technical notes:
Expected outcome:
Refs: #hofix/ci
QA: YAML syntax validated, pre-commit hooks passed (12/12)