Self-hosted runs can compile Vite and warm the API slowly after image validate; extend health, dev-server, signup, and job timeouts so Product CI is not cancelled mid-suite. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
197 lines
8.0 KiB
YAML
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: 45
|
|
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: 15
|
|
# 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
|