evOo-local/.github/workflows/playwright.yml
Cursor Agent d85a3f4c62
ci: serialize self-hosted image validate with Web E2E
PR image validate and Playwright shared evoo-ci runners; overlapping
docker builds left E2E signup flakes and hit the 30-minute job cap.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-10-07 22:40:50 +00:00

197 lines
8.0 KiB
YAML

name: Playwright
on:
workflow_dispatch:
inputs:
sandbox_provider:
description: Sandbox provider
required: true
type: choice
options:
- fake
- e2b
- daytona
- box
default: fake
workflow_call:
inputs:
sandbox_provider:
description: Sandbox provider
required: false
type: string
default: fake
publish_report:
description: Publish this run to the persistent visual dashboard
required: false
type: boolean
default: false
upload_artifacts:
description: Retain the report and diagnostics as GitHub artifacts
required: false
type: boolean
default: false
secrets:
S3_ACCESS_KEY_ID:
description: Access key used to publish visual reports
required: false
S3_SECRET_ACCESS_KEY:
description: Secret key used to publish visual reports
required: false
E2B_API_KEY:
description: E2B API key used only when the E2B sandbox is selected
required: false
DAYTONA_API_KEY:
description: Daytona API key used only when the Daytona sandbox is selected
required: false
BOX_API_KEY:
description: Box API key used only when the Box sandbox is selected
required: false
permissions:
contents: read
jobs:
playwright:
name: Web E2E
runs-on: [self-hosted, evoo-ci, Linux]
timeout-minutes: 30
concurrency:
group: ${{ inputs.publish_report && 'playwright-publication' || 'evoo-self-hosted-heavy' }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
id: pnpm-setup
with:
dest: ${{ runner.temp }}/setup-pnpm-${{ github.run_id }}-${{ github.job }}
- name: Isolate pnpm store and PATH
shell: bash
run: |
set -euo pipefail
bin="${{ steps.pnpm-setup.outputs.bin_dest }}"
dest="${{ steps.pnpm-setup.outputs.dest }}"
if [[ -z "$bin" || -z "$dest" ]]; then
echo "pnpm setup did not report install paths" >&2
exit 1
fi
echo "PNPM_HOME=${dest}" >> "$GITHUB_ENV"
echo "${bin}" >> "$GITHUB_PATH"
echo "${dest}" >> "$GITHUB_PATH"
store="${RUNNER_TOOL_CACHE}/pnpm-store"
mkdir -p "$store"
echo "npm_config_store_dir=${store}" >> "$GITHUB_ENV"
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Clear stale Vite dependency cache
run: rm -rf apps/web/node_modules/.vite node_modules/.vite
- uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Chromium
timeout-minutes: 5
# Skip --with-deps: evo Linux runners keep Chromium's OS libraries installed,
# and apt-get can stall until the job timeout on persistent self-hosted machines.
run: pnpm --filter @rakazo/web exec playwright install chromium
- name: Validate E2B credentials
if: inputs.sandbox_provider == 'e2b'
env:
E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
run: test -n "$E2B_API_KEY"
- name: Validate Daytona credentials
if: inputs.sandbox_provider == 'daytona'
env:
DAYTONA_API_KEY: ${{ secrets.DAYTONA_API_KEY }}
run: test -n "$DAYTONA_API_KEY"
- name: Validate Box credentials
if: inputs.sandbox_provider == 'box'
env:
BOX_API_KEY: ${{ secrets.BOX_API_KEY }}
run: test -n "$BOX_API_KEY"
- name: Pick E2E ports for this runner
# Several runners share one host. Each runner gets its own port set so the
# port-freeing steps and the harness never touch another job's servers. No runner
# uses the harness defaults: jobs from branches that still free the fixed ports
# cannot kill ours, and we cannot kill theirs.
run: |
set -euo pipefail
suffix="${RUNNER_NAME##*-}"
offset=100
if [[ "$suffix" =~ ^[0-9]+$ ]]; then
offset=$((suffix * 100))
fi
{
echo "API_PORT=$((3110 + offset))"
echo "WEB_PORT=$((5180 + offset))"
echo "WWW_PORT=$((4321 + offset))"
} >> "$GITHUB_ENV"
echo "E2E ports for ${RUNNER_NAME}: api $((3110 + offset)), web $((5180 + offset)), www $((4321 + offset))"
- name: Free stale E2E ports
run: bash scripts/free-e2e-ports.sh
- name: Run Playwright tests
id: playwright_tests
env:
SANDBOX_TEST_PROVIDER: ${{ inputs.sandbox_provider }}
E2B_API_KEY: ${{ inputs.sandbox_provider == 'e2b' && secrets.E2B_API_KEY || '' }}
DAYTONA_API_KEY: ${{ inputs.sandbox_provider == 'daytona' && secrets.DAYTONA_API_KEY || '' }}
BOX_API_KEY: ${{ inputs.sandbox_provider == 'box' && secrets.BOX_API_KEY || '' }}
run: pnpm test:e2e -- --sandbox="$SANDBOX_TEST_PROVIDER"
# Marketing homepage only: separate config so product chromium does not start Astro.
# Runs after product e2e so shared apps/web/test-results is not wiped by the product run.
- name: Free stale marketing E2E ports
if: always() && (steps.playwright_tests.outcome == 'success' || steps.playwright_tests.outcome == 'failure')
run: bash scripts/free-e2e-ports.sh
- name: Run marketing homepage E2E
id: www_playwright_tests
if: always() && (steps.playwright_tests.outcome == 'success' || steps.playwright_tests.outcome == 'failure')
run: pnpm --filter @rakazo/www e2e
- name: Record Playwright result
if: always() && inputs.upload_artifacts
env:
PLAYWRIGHT_RESULT: ${{ steps.playwright_tests.outcome }}
WWW_PLAYWRIGHT_RESULT: ${{ steps.www_playwright_tests.outcome }}
run: |
mkdir -p test-report/e2e
if [[ "$PLAYWRIGHT_RESULT" == "success" && "$WWW_PLAYWRIGHT_RESULT" == "success" ]]; then
printf 'success\n' > test-report/e2e/outcome.txt
elif [[ "$PLAYWRIGHT_RESULT" == "success" && ( "$WWW_PLAYWRIGHT_RESULT" == "skipped" || -z "$WWW_PLAYWRIGHT_RESULT" ) ]]; then
printf 'success\n' > test-report/e2e/outcome.txt
else
printf 'failure\n' > test-report/e2e/outcome.txt
fi
- name: Upload Playwright artifacts
if: always() && inputs.upload_artifacts
continue-on-error: true
uses: actions/upload-artifact@v4
with:
name: playwright-artifacts-${{ github.run_id }}-${{ github.run_attempt }}
path: |
playwright-report
playwright-report-www
apps/web/test-results
test-report/e2e
retention-days: 7
if-no-files-found: warn
- name: Publish Playwright visual report
if: always() && inputs.publish_report
env:
AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ vars.S3_REGION }}
AWS_EC2_METADATA_DISABLED: "true"
S3_BUCKET: ${{ vars.S3_BUCKET }}
S3_ENDPOINT: ${{ vars.S3_ENDPOINT }}
PLAYWRIGHT_PUBLIC_BASE_URL: ${{ vars.PLAYWRIGHT_PUBLIC_BASE_URL }}
PLAYWRIGHT_RESULT: ${{ steps.playwright_tests.outcome == 'success' && steps.www_playwright_tests.outcome == 'success' && 'success' || 'failure' }}
PLAYWRIGHT_RUN_ATTEMPT: ${{ github.run_attempt }}
PLAYWRIGHT_RUN_ID: ${{ github.run_id }}
PLAYWRIGHT_RUN_NUMBER: ${{ github.run_number }}
PLAYWRIGHT_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
PLAYWRIGHT_SHA: ${{ github.sha }}
PLAYWRIGHT_EVENT: ${{ github.event_name }}
PLAYWRIGHT_BRANCH: ${{ github.ref_name }}
run: bash scripts/publish-playwright-report.sh