-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgitlab-ci.yml
More file actions
396 lines (356 loc) · 14.6 KB
/
gitlab-ci.yml
File metadata and controls
396 lines (356 loc) · 14.6 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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# =============================================================================
# AI-DDTK: WordPress E2E Tests — GitLab CI Pipeline
# =============================================================================
#
# This pipeline demonstrates production-ready CI/CD integration for WordPress
# E2E testing using AI-DDTK (pw-auth + Playwright).
#
# Key design decisions:
# - Auth state is NOT cached between runs (ephemeral CI environments)
# - pw-auth login --force is called on every run to always re-authenticate
# - dev-login-cli.php is installed via a Docker service container
# - Test results and screenshots are uploaded as GitLab CI artifacts
# - node_modules is cached to speed up subsequent runs
#
# Required GitLab CI/CD Variables (Settings → CI/CD → Variables):
# WP_ADMIN_USER — WordPress admin username (e.g. "admin") [masked]
# WP_ADMIN_PASS — WordPress admin password [masked]
# WP_SITE_URL — WordPress site URL in CI (e.g. "http://wordpress")
#
# =============================================================================
# ─── DEFAULT CONFIGURATION ───────────────────────────────────────────────────
# These defaults apply to all jobs unless overridden at the job level.
default:
image: node:20
# Retry each job once on infrastructure failures (runner lost, network hiccup)
retry:
max: 1
when:
- runner_system_failure
- stuck_or_timeout_failure
# Limit all jobs to 30 minutes; override per-job if needed
timeout: 30 minutes
interruptible: true
# ─── PIPELINE-WIDE VARIABLES ─────────────────────────────────────────────────
# Default values; secrets override these in GitLab CI/CD variable settings.
variables:
# WordPress site URL — must be reachable from the runner.
# Override these with masked CI/CD variables in GitLab project settings.
WP_SITE_URL: "http://wordpress"
WP_ADMIN_USER: "admin"
# Postgres/MySQL — used by the wordpress service container
MYSQL_ROOT_PASSWORD: "rootpassword"
MYSQL_DATABASE: "wordpress"
MYSQL_USER: "wordpress"
MYSQL_PASSWORD: "wordpresspassword"
# Playwright report directory
PLAYWRIGHT_OUTPUT_DIR: "playwright-report"
# Disable Playwright telemetry in CI
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "0"
# npm cache directory — used by the node_modules cache key
npm_config_cache: "$CI_PROJECT_DIR/.npm"
# ─── PIPELINE STAGES ─────────────────────────────────────────────────────────
stages:
- setup # Install dependencies and browsers
- auth # Authenticate via pw-auth (produces auth artifact)
- test # Run Playwright tests
- report # Aggregate and publish results
# =============================================================================
# CACHE CONFIGURATION
# =============================================================================
# GitLab CI caches node_modules so npm ci doesn't re-download packages.
# Key includes package-lock.json hash — auto-invalidates on dependency changes.
.node_modules_cache: &node_modules_cache
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
policy: pull-push
# Read-only variant — used in jobs that only consume the cache
.node_modules_cache_pull: &node_modules_cache_pull
cache:
key:
files:
- package-lock.json
paths:
- .npm/
- node_modules/
policy: pull
# =============================================================================
# SERVICE CONTAINER ANCHOR
# =============================================================================
# Reusable service definitions attached to jobs that need WordPress.
# Services run as separate containers on the same Docker network.
# All jobs on the same runner share the network and can reach services by name.
.wordpress_services: &wordpress_services
services:
# MySQL 8.0 — WordPress database backend
- name: mysql:8.0
alias: mysql
variables:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
MYSQL_DATABASE: "${MYSQL_DATABASE}"
MYSQL_USER: "${MYSQL_USER}"
MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
# WordPress + Apache — available at http://wordpress on port 80
- name: wordpress:latest
alias: wordpress
variables:
WORDPRESS_DB_HOST: "mysql:3306"
WORDPRESS_DB_NAME: "${MYSQL_DATABASE}"
WORDPRESS_DB_USER: "${MYSQL_USER}"
WORDPRESS_DB_PASSWORD: "${MYSQL_PASSWORD}"
# Allow dev-login-cli.php (must not be "production")
WP_ENVIRONMENT_TYPE: "development"
WORDPRESS_DEBUG: "1"
# =============================================================================
# STAGE: setup
# =============================================================================
# Install npm dependencies and Playwright browser binaries.
# Resulting node_modules is written to cache for downstream jobs.
install:
stage: setup
<<: *node_modules_cache
script:
# Install Node.js dependencies (reproducible, locked-version install)
- npm ci --prefer-offline
# Install Playwright browsers with system-level OS dependencies.
# The --with-deps flag installs libnss3, libgbm1, etc. required by Chromium.
- npx playwright install --with-deps chromium
- echo "Dependencies and browsers installed"
# =============================================================================
# STAGE: auth
# =============================================================================
# Run pw-auth login to create an authenticated Playwright session.
# The auth state JSON file is uploaded as a GitLab artifact so the `test`
# stage can download and reuse it without re-authenticating.
#
# IMPORTANT: In CI, always use --force to bypass cache checks.
# Ephemeral runners have no persistent auth files from previous runs.
authenticate:
stage: auth
<<: [*node_modules_cache_pull, *wordpress_services]
needs:
- install
before_script:
# Install WP-CLI — required by pw-auth to generate one-time login URLs
- curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
- chmod +x wp-cli.phar
- mv wp-cli.phar /usr/local/bin/wp
- wp --info
# Set WP_ENVIRONMENT_TYPE=development so dev-login-cli.php is allowed to run
- export WP_ENVIRONMENT_TYPE=development
script:
# Wait for WordPress to be fully initialized before attempting auth.
# The service container starts early but WP's init takes a few seconds.
- |
echo "Waiting for WordPress at $WP_SITE_URL..."
MAX_ATTEMPTS=30
ATTEMPT=0
until curl -sf "$WP_SITE_URL/wp-login.php" -o /dev/null; do
ATTEMPT=$((ATTEMPT + 1))
if [ "$ATTEMPT" -ge "$MAX_ATTEMPTS" ]; then
echo "ERROR: WordPress did not become ready after ${MAX_ATTEMPTS} attempts"
exit 1
fi
echo " Attempt $ATTEMPT/$MAX_ATTEMPTS — waiting 5s..."
sleep 5
done
echo "WordPress is ready"
# Complete WordPress installation so admin user exists
# (the Docker image creates DB tables but does not run the WP installer)
- |
wp core install \
--url="$WP_SITE_URL" \
--title="CI Test Site" \
--admin_user="$WP_ADMIN_USER" \
--admin_password="$WP_ADMIN_PASS" \
--admin_email="ci@example.com" \
--skip-email \
--path=/var/www/html \
--allow-root \
|| echo "WordPress already installed, continuing..."
# Install dev-login-cli.php mu-plugin into the WordPress container.
# This plugin is required by pw-auth to generate one-time login tokens.
- mkdir -p /var/www/html/wp-content/mu-plugins
- cp templates/dev-login-cli.php /var/www/html/wp-content/mu-plugins/dev-login-cli.php
- echo "dev-login-cli.php installed"
# Create auth directory for pw-auth output
- mkdir -p temp/playwright/.auth
# Authenticate — use --force to always re-authenticate (ephemeral CI)
- |
pw-auth login \
--site-url "$WP_SITE_URL" \
--user "$WP_ADMIN_USER" \
--force
# Verify auth state file was created
- |
if [ ! -f "temp/playwright/.auth/admin.json" ]; then
echo "ERROR: Auth state file not found — login failed"
exit 1
fi
echo "Auth state created: temp/playwright/.auth/admin.json"
pw-auth status
# Upload auth state as an artifact so the test stage can download it.
# Limit retention to 1 hour — auth tokens are short-lived.
artifacts:
name: "auth-state-${CI_JOB_ID}"
paths:
- temp/playwright/.auth/
expire_in: 1 hour
when: on_success
# =============================================================================
# STAGE: test
# =============================================================================
# Run Playwright tests using the auth state produced by the `authenticate` job.
e2e-tests:
stage: test
<<: [*node_modules_cache_pull, *wordpress_services]
needs:
- job: authenticate
# Download the auth state artifact from the authenticate job
artifacts: true
before_script:
# Install WP-CLI (needed if any tests call wp commands)
- curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
- chmod +x wp-cli.phar
- mv wp-cli.phar /usr/local/bin/wp
# Playwright in containers requires --no-sandbox.
# Set this via the PLAYWRIGHT_LAUNCH_OPTIONS env var or a playwright.config.js.
- export PLAYWRIGHT_CHROMIUM_LAUNCH_ARGS="--no-sandbox --disable-setuid-sandbox"
script:
# Wait for WordPress service (services restart for each job)
- |
echo "Waiting for WordPress..."
until curl -sf "$WP_SITE_URL/wp-login.php" -o /dev/null; do sleep 3; done
echo "WordPress is ready"
# Validate auth state exists before running tests
- |
if [ ! -f "temp/playwright/.auth/admin.json" ]; then
echo "ERROR: Auth state not found. Check the authenticate job."
exit 1
fi
pw-auth status
# Run Playwright tests
# The auth state at temp/playwright/.auth/admin.json is automatically
# picked up by tests that load storageState from that path.
- |
npx playwright test \
--reporter=html,list,junit \
--output="${PLAYWRIGHT_OUTPUT_DIR}"
env:
WP_SITE_URL: $WP_SITE_URL
PLAYWRIGHT_CHROMIUM_LAUNCH_ARGS: "--no-sandbox --disable-setuid-sandbox"
# Upload test results as artifacts
artifacts:
name: "playwright-report-${CI_JOB_ID}"
when: always # Upload even if tests fail — critical for debugging
paths:
- playwright-report/
- test-results/ # Playwright's raw JSON results
reports:
# Parse JUnit XML for GitLab's built-in test report UI
junit: playwright-report/results.xml
expire_in: 14 days
# =============================================================================
# STAGE: test (parallel matrix)
# =============================================================================
# This job demonstrates running tests in parallel across multiple shards.
# GitLab CI creates one job per matrix entry; each shard runs independently
# on its own runner with its own WordPress service container.
#
# Adjust TOTAL_SHARDS to match your test suite size and runner availability.
e2e-tests-parallel:
stage: test
<<: [*node_modules_cache_pull, *wordpress_services]
# Only run on default branch or tags — avoids doubling MR CI time
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
needs:
- install # Don't wait for authenticate — each shard re-auths itself
# GitLab parallel matrix: creates one job per combination of variables.
# With 3 shards you get e2e-tests-parallel: [1/3], [2/3], [3/3].
parallel:
matrix:
- SHARD_INDEX: ["1", "2", "3"]
TOTAL_SHARDS: ["3"]
variables:
# Each shard job knows its index and total shard count
SHARD: "${SHARD_INDEX}/${TOTAL_SHARDS}"
before_script:
- curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
- chmod +x wp-cli.phar
- mv wp-cli.phar /usr/local/bin/wp
- export WP_ENVIRONMENT_TYPE=development
- export PLAYWRIGHT_CHROMIUM_LAUNCH_ARGS="--no-sandbox --disable-setuid-sandbox"
script:
# Wait for WordPress
- |
echo "Waiting for WordPress (shard ${SHARD})..."
until curl -sf "$WP_SITE_URL/wp-login.php" -o /dev/null; do sleep 3; done
# Run WordPress installer
- |
wp core install \
--url="$WP_SITE_URL" \
--title="CI Test Site" \
--admin_user="$WP_ADMIN_USER" \
--admin_password="$WP_ADMIN_PASS" \
--admin_email="ci@example.com" \
--skip-email \
--path=/var/www/html \
--allow-root \
|| true
# Install dev-login-cli.php
- mkdir -p /var/www/html/wp-content/mu-plugins
- cp templates/dev-login-cli.php /var/www/html/wp-content/mu-plugins/dev-login-cli.php
# Each shard re-authenticates independently (no shared state in matrix)
- mkdir -p temp/playwright/.auth
- |
pw-auth login \
--site-url "$WP_SITE_URL" \
--user "$WP_ADMIN_USER" \
--force
# Run this shard's subset of tests
- |
npx playwright test \
--shard="${SHARD}" \
--reporter=html,list,junit \
--output="${PLAYWRIGHT_OUTPUT_DIR}-shard-${SHARD_INDEX}"
artifacts:
name: "playwright-report-shard-${SHARD_INDEX}"
when: always
paths:
- playwright-report-shard-${SHARD_INDEX}/
reports:
junit: playwright-report-shard-${SHARD_INDEX}/results.xml
expire_in: 14 days
# =============================================================================
# STAGE: report
# =============================================================================
# Merge per-shard HTML reports into a single report for easy review.
# This step is optional; remove it if you don't run the parallel job.
merge-reports:
stage: report
needs:
- e2e-tests-parallel
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
script:
# Merge HTML reports from all shards using the Playwright merge utility
- |
npx playwright merge-reports \
--reporter=html \
playwright-report-shard-*/blob-report/ \
--output=merged-playwright-report/ \
|| echo "Merge skipped (blob reports not available in all runners)"
artifacts:
name: "merged-playwright-report"
when: always
paths:
- merged-playwright-report/
expire_in: 30 days