-
Notifications
You must be signed in to change notification settings - Fork 1
150 lines (136 loc) · 6.24 KB
/
Copy pathe2e.yml
File metadata and controls
150 lines (136 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
name: E2E
# Playwright end-to-end tests. Boots the full docker-compose stack
# (backend + frontend + runner image), then drives the app via headless
# Chromium. OpenAI is mocked at the network layer — no real API hits from CI.
#
# Kept as a separate workflow (not a job in ci.yml) because this one needs
# Docker + the full runner image build, which is materially slower than the
# lint/typecheck/unit jobs. Keeping them apart means a broken e2e run does
# not block the fast-feedback signal on unit tests.
on:
push:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
- '.github/ISSUE_TEMPLATE/**'
- 'LICENSE'
pull_request:
paths-ignore:
- '**.md'
- 'docs/**'
- '.github/ISSUE_TEMPLATE/**'
- 'LICENSE'
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e:
name: Playwright (chromium)
runs-on: ubuntu-latest
# Sharding splits the spec set across N parallel jobs (each its
# own ubuntu-latest runner with its own docker-compose stack), so
# we get effective N×2 = 8 parallelism without saturating a single
# runner. Larger runners aren't free on the Free plan even for
# public repos, so this is the right shape for our cost ceiling.
# Each shard's wall-clock should land ~10-12 min instead of the
# 25-min ceiling 2 workers were pushing.
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
# Bumped from 15 → 25 in Phase 22C: full e2e sweep grew past the 15-min
# ceiling once the marketing specs landed. Sharding (Phase 27-v2.2)
# means each shard runs ~1/4 of the suite, so 25 stays generous.
timeout-minutes: 25
# Phase 18d: cloud-only Supabase model. These feed both docker compose
# (which reads process env for ${VAR} interpolation in docker-compose.yml)
# and the Playwright specs (fixtures + auth.spec.ts read process.env).
# Values come from the codetutor-dev project — see docs/DEVELOPMENT.md.
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
BYOK_ENCRYPTION_KEY: ${{ secrets.BYOK_ENCRYPTION_KEY }}
# Phase 20-P3: e2e-only bearer for /api/metrics. Not a real secret —
# the backend validates against this exact string, and the metrics
# spec uses it to prove the authed path works. Local runs can set
# METRICS_TOKEN in .env to the same value, or leave it blank (in
# which case the spec's token branch skips).
METRICS_TOKEN: e2e-test-metrics-token
# Per-shard test-user email namespace — see e2e/fixtures/auth.ts
# `workerEmail`. Without this, shard 1's `e2e-w0-${pid}@…` could
# collide with shard 2's worker 0 if they happen to roll the
# same suffix. Pinning to the matrix shard guarantees each
# shard's auth-teardown scope is its own.
E2E_USER_SUFFIX: shard-${{ matrix.shard }}
# Phase 20-P3: Playwright cycles test pages faster than `pagehide`-driven
# session cleanup reaches the backend, so prod-tight caps (2/user) block
# the next test for the full sweeper interval (~45 s) and manifest as
# "Run Code button disabled 30 s" flakes that pass on retry. The auth
# fixture now explicitly ends each test's session in afterEach (see
# e2e/fixtures/auth.ts), which drops the per-user leak rate dramatically;
# the loose cap below is belt-and-suspenders for any session id the
# sniffer misses (early-failed tests, mid-flight rebinds). Prod posture
# (config.ts defaults 2/20/8) is unaffected.
MAX_SESSIONS_PER_USER: "60"
MAX_SESSIONS_GLOBAL: "200"
DOCKER_EXEC_CONCURRENCY: "16"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
cache: npm
cache-dependency-path: e2e/package-lock.json
- name: Boot docker-compose stack
run: docker compose up -d backend frontend
- name: Install e2e deps
working-directory: e2e
run: npm ci
- name: Cache Playwright browsers
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('e2e/package-lock.json') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright browsers
working-directory: e2e
run: npx playwright install --with-deps chromium
- name: Run Playwright tests
working-directory: e2e
# --shard partitions specs across the matrix — each runner
# picks up ~1/4 of the suite and runs it in parallel with the
# other shards. Each shard has its own docker-compose stack +
# 2 Playwright workers, so the effective parallelism is 8
# without any single runner being starved.
run: npx playwright test --shard=${{ matrix.shard }}/4
- name: Dump docker-compose logs on failure
if: failure()
run: docker compose logs --no-color --tail=300
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
# Shard-scoped artifact name — actions/upload-artifact@v4
# rejects duplicate names across matrix jobs, so each shard
# uploads its own report blob.
name: playwright-report-shard-${{ matrix.shard }}
path: e2e/playwright-report
retention-days: 14
if-no-files-found: ignore
- name: Upload Playwright traces/videos
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-traces-shard-${{ matrix.shard }}
path: e2e/test-results
retention-days: 14
if-no-files-found: ignore
- name: Tear down docker-compose
if: always()
run: docker compose down -v