-
-
Notifications
You must be signed in to change notification settings - Fork 42
executable file
·397 lines (365 loc) · 14.8 KB
/
build.yaml
File metadata and controls
executable file
·397 lines (365 loc) · 14.8 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
397
name: Node.js CI
on:
pull_request:
push:
branches:
- master
schedule:
- cron: "0 0 * * 0" # weekly
jobs:
test:
runs-on: ubuntu-latest
name: Node.js ${{ matrix.node-version }}
strategy:
matrix:
node-version: [22.x, 24.x]
env:
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USER: root
DB_PASSWORD: root
ENCRYPTION_KEY: b097b390a68441cc3bb151dd0171f25c3aabc688c50eeb26dc5e13254b333911
SESSION_KEY: a73545a5f08d2906e39a4438014200303f9269f3ade9227525ffb141294f1b62
DB_CONNECTION_LIMIT: 5
SERVER_FOOTER_NAME: BanManagement
steps:
- uses: actions/checkout@v6
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Cache Node.js modules and Next.js build
uses: actions/cache@v5
with:
path: |
~/.npm
${{ github.workspace }}/.next/cache
# Generate a new cache whenever packages or source files change
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
# If source files changed but packages didn't, rebuild from a prior cache
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
- run: npm ci
- name: Set up MySQL
run: |
sudo systemctl start mysql.service
sleep 5 && mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';" -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }}
mysql -e "SET GLOBAL sql_require_primary_key = ON;" -uroot
- run: npm run lint
- run: node --expose-gc ./node_modules/.bin/jest --coverage
env:
DB_PASSWORD:
LOG_LEVEL: warn
PORT: 3001
- name: Setup E2E database
run: node cypress/setup.js
env:
DB_PASSWORD:
DB_NAME: bm_e2e_tests
- name: Build for E2E
run: npm run build
env:
DB_PASSWORD:
DB_NAME: bm_e2e_tests
NODE_ENV: production
- name: Cypress run
uses: cypress-io/github-action@f790eee7a50d9505912f50c2095510be7de06aa7 # v6.10.9
env:
DB_PASSWORD:
DB_NAME: bm_e2e_tests
LOG_LEVEL: warn
PORT: 3000
NODE_ENV: production
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CYPRESS_PROJECT_ID: ${{ secrets.PROJECT_ID }}
CONTACT_EMAIL: "admin@banmanagement.com"
NOTIFICATION_VAPID_PUBLIC_KEY: ${{ secrets.NOTIFICATION_VAPID_PUBLIC_KEY }}
NOTIFICATION_VAPID_PRIVATE_KEY: ${{ secrets.NOTIFICATION_VAPID_PRIVATE_KEY }}
with:
install: false
start: npm start
wait-on: "http://127.0.0.1:3000"
record: true
- name: Coveralls Parallel
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
flag-name: run-${{ matrix.test_number }}
parallel: true
setup_e2e:
runs-on: ubuntu-latest
name: Setup wizard E2E
env:
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USER: root
DB_PASSWORD: root
SETUP_PORT: 3001
SETUP_DOTENV_PATH: /tmp/bm-setup-test.env
SETUP_DB_NAME: bm_e2e_setup
SETUP_BM_DB_NAME: bm_e2e_setup_bm
steps:
- uses: actions/checkout@v6
- name: Set up Node.js 24
uses: actions/setup-node@v4
with:
node-version: 24.x
- name: Cache Node.js modules
uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-setup-e2e-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-setup-e2e-
- run: npm ci
- name: Set up MySQL
run: |
sudo systemctl start mysql.service
sleep 5 && mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';" -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }}
mysql -e "SET GLOBAL sql_require_primary_key = ON;" -uroot
- name: Cypress run (setup wizard)
uses: cypress-io/github-action@f790eee7a50d9505912f50c2095510be7de06aa7 # v6.10.9
env:
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USER: root
DB_PASSWORD:
SETUP_PORT: 3001
SETUP_DOTENV_PATH: /tmp/bm-setup-test.env
SETUP_DB_NAME: bm_e2e_setup
SETUP_BM_DB_NAME: bm_e2e_setup_bm
LOG_LEVEL: warn
CYPRESS_setup_db_password: ''
CYPRESS_setup_db_host: 127.0.0.1
CYPRESS_setup_db_port: '3306'
CYPRESS_setup_db_user: root
CYPRESS_setup_dotenv_path: /tmp/bm-setup-test.env
CYPRESS_setup_db_name: bm_e2e_setup
with:
install: false
start: npm run e2e:setup:server
wait-on: "http://127.0.0.1:3001/health"
wait-on-timeout: 60
command: npm run e2e:setup:run
build_docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: docker build . # TODO cache
smoke_docker:
name: Docker compose smoke test
runs-on: ubuntu-latest
needs: build_docker
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build webui image (load locally)
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: banmanagement/webui:smoke
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Start docker compose stack
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_PASSWORD: webuipassword
WEBUI_PORT: 3000
run: |
# Use the locally built image instead of pulling
sed -i 's|image: banmanagement/webui:latest|image: banmanagement/webui:smoke|' docker-compose.prod.yml
docker compose -f docker-compose.prod.yml up -d
- name: Wait for /health to report setup_required
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_PASSWORD: webuipassword
WEBUI_PORT: 3000
run: |
set -e
for attempt in $(seq 1 60); do
response=$(curl -fsS http://127.0.0.1:3000/health || true)
echo "attempt $attempt: $response"
if echo "$response" | grep -q '"status":"setup_required"' || echo "$response" | grep -q '"status":"ok"'; then
echo "Health endpoint is responding"
exit 0
fi
sleep 2
done
echo "Health endpoint never responded"
echo "===== docker ps ====="
docker ps -a
echo "===== webui logs ====="
docker logs banmanager-webui 2>&1 | tail -200 || true
echo "===== mysql logs ====="
docker logs banmanager-webui-mysql 2>&1 | tail -80 || true
exit 1
- name: Confirm setup mode signal
run: |
curl -fsS http://127.0.0.1:3000/health | tee /tmp/health.json
grep -q 'setup_required\|"status":"ok"' /tmp/health.json
# ---------------------------------------------------------------
# Cypress against the dockerised stack
#
# Catches outputFileTracingIncludes regressions and other
# standalone-vs-runtime mismatches that the host-only `test` and
# `setup_e2e` jobs (which run against `npm start` / `e2e:setup:server`)
# cannot see. We tear down the setup-mode stack, restart MySQL with
# port 3306 exposed via docker-compose.e2e-overlay.yml, seed the
# bm_e2e_tests database from the runner using cypress/setup.js, then
# boot the WebUI container with ENCRYPTION_KEY/SESSION_KEY pre-set
# so it skips setup mode entirely. The 4 specs cover:
# - login.spec.js -> argon2 + sessions
# - registration.spec.js -> registration + PIN flow
# - admin-server-lifecycle -> Apollo + knex + mysql2 CRUD
# - admin-webhook-lifecycle -> webhook flow (also exercises sharp)
# ---------------------------------------------------------------
- name: Tear down setup-mode stack so we can reseed for cypress
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_PASSWORD: webuipassword
run: docker compose -f docker-compose.prod.yml down -v
- name: Set up Node.js for cypress + seed script
uses: actions/setup-node@v4
with:
node-version: 24.x
- name: Cache Node.js modules and Cypress binary
uses: actions/cache@v5
with:
path: |
~/.npm
~/.cache/Cypress
key: ${{ runner.os }}-cypress-docker-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-cypress-docker-
- run: npm ci
- run: npx cypress install
- name: Start MySQL with host port exposed
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_PASSWORD: webuipassword
E2E_ENCRYPTION_KEY: b097b390a68441cc3bb151dd0171f25c3aabc688c50eeb26dc5e13254b333911
E2E_SESSION_KEY: a73545a5f08d2906e39a4438014200303f9269f3ade9227525ffb141294f1b62
run: |
docker compose -f docker-compose.prod.yml -f docker-compose.e2e-overlay.yml up -d mysql
- name: Wait for MySQL healthy
run: |
for attempt in $(seq 1 60); do
health=$(docker inspect --format '{{.State.Health.Status}}' banmanager-webui-mysql 2>/dev/null || echo missing)
echo "attempt $attempt: mysql=$health"
if [ "$health" = "healthy" ]; then exit 0; fi
sleep 2
done
echo "MySQL never reported healthy"
docker logs banmanager-webui-mysql --tail 80 || true
exit 1
# MySQL's docker healthcheck (mysqladmin ping) flips the container to
# `healthy` while the entrypoint is still running its temporary mysqld
# against a unix socket. The real mysqld on TCP 3306 only comes up a few
# seconds later, after the temp server is shut down. If the seed script
# connects in that window it gets a fresh socket to the temp server and
# is killed mid-query with PROTOCOL_CONNECTION_LOST when it stops. Retry
# the seed a handful of times to ride out that transition cleanly.
- name: Seed bm_e2e_tests from the runner (retry through MySQL warm-up)
env:
# Connection details the seed script uses from the runner.
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USER: root
DB_PASSWORD: rootpassword
# Connection details the WebUI container will use to reach MySQL.
# We have to disambiguate from DB_HOST because the host the seed
# script connects to (127.0.0.1, via the compose port mapping) is
# not the host the WebUI container reaches MySQL at (the compose
# service name `mysql` on the internal network). server fixture
# writes these into bm_web_servers so the GraphQL layer hits the
# right address at request time.
BM_DB_HOST: mysql
BM_DB_PORT: 3306
BM_DB_USER: root
ENCRYPTION_KEY: b097b390a68441cc3bb151dd0171f25c3aabc688c50eeb26dc5e13254b333911
LOG_LEVEL: warn
run: |
for attempt in $(seq 1 8); do
echo "::group::seed attempt $attempt"
if node cypress/setup.js; then
echo "::endgroup::"
echo "seed succeeded on attempt $attempt"
exit 0
fi
echo "::endgroup::"
echo "seed failed on attempt $attempt - waiting 5s before retry"
sleep 5
done
echo "seed never succeeded after 8 attempts"
docker logs banmanager-webui-mysql --tail 200 || true
exit 1
- name: Start WebUI container against seeded DB
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_PASSWORD: webuipassword
E2E_ENCRYPTION_KEY: b097b390a68441cc3bb151dd0171f25c3aabc688c50eeb26dc5e13254b333911
E2E_SESSION_KEY: a73545a5f08d2906e39a4438014200303f9269f3ade9227525ffb141294f1b62
run: |
docker compose -f docker-compose.prod.yml -f docker-compose.e2e-overlay.yml up -d webui
- name: Wait for WebUI /health to report ok
run: |
for attempt in $(seq 1 90); do
body=$(curl -fsS http://127.0.0.1:3000/health 2>/dev/null || true)
echo "attempt $attempt: $body"
if echo "$body" | grep -q '"status":"ok"'; then exit 0; fi
sleep 2
done
echo "/health never returned ok"
docker logs banmanager-webui --tail 200 || true
exit 1
- name: Cypress (against dockerised webui)
uses: cypress-io/github-action@f790eee7a50d9505912f50c2095510be7de06aa7 # v6.10.9
env:
PORT: 3000
CONTACT_EMAIL: admin@banmanagement.com
ENCRYPTION_KEY: b097b390a68441cc3bb151dd0171f25c3aabc688c50eeb26dc5e13254b333911
SESSION_KEY: a73545a5f08d2906e39a4438014200303f9269f3ade9227525ffb141294f1b62
# Cypress.env(*) values used by admin-server-lifecycle.spec.js when it
# types into the "Add Server" form. Defaults to 127.0.0.1 in the
# spec, which inside the WebUI container resolves to the container
# itself - so the createServer mutation's connection probe fails
# with ECONNREFUSED. Point at the compose service name instead so
# the new BM server row the test creates is reachable.
CYPRESS_DB_HOST: mysql
CYPRESS_DB_PORT: 3306
CYPRESS_DB_USER: root
CYPRESS_DB_PASSWORD: rootpassword
with:
install: false
spec: |
cypress/e2e/pages/login.spec.js
cypress/e2e/journeys/registration.spec.js
cypress/e2e/journeys/admin-server-lifecycle.spec.js
cypress/e2e/journeys/admin-webhook-lifecycle.spec.js
- name: Dump container logs on failure
if: failure()
run: |
echo "===== docker ps ====="
docker ps -a
echo "===== webui logs ====="
docker logs banmanager-webui --tail 300 || true
echo "===== mysql logs ====="
docker logs banmanager-webui-mysql --tail 80 || true
- name: Stop docker compose stack
if: always()
env:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_PASSWORD: webuipassword
run: docker compose -f docker-compose.prod.yml down -v
finish:
needs: test
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true