-
-
Notifications
You must be signed in to change notification settings - Fork 1
432 lines (372 loc) · 13.4 KB
/
Copy pathci.yml
File metadata and controls
432 lines (372 loc) · 13.4 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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Lightweight structural validation (no Node required) ──────────────
validate:
name: Validate Structure
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
- name: Check shell script syntax
run: |
errors=0
for f in scripts/*.sh; do
if [ -f "$f" ]; then
bash -n "$f" || { echo "FAIL: $f"; errors=$((errors + 1)); }
fi
done
echo "Checked $(ls scripts/*.sh 2>/dev/null | wc -l) scripts, $errors failed"
exit $errors
- name: Validate JSON configs
run: |
errors=0
for f in .claude/pipeline.config.json package.json; do
if [ -f "$f" ]; then
python3 -m json.tool "$f" > /dev/null || { echo "FAIL: $f"; errors=$((errors + 1)); }
fi
done
for f in templates/**/*.json; do
if [ -f "$f" ]; then
python3 -m json.tool "$f" > /dev/null || { echo "FAIL: $f"; errors=$((errors + 1)); }
fi
done
echo "$errors JSON validation failures"
exit $errors
- name: Validate pipeline.config.json structure
run: |
# Verify required top-level keys exist
required_keys='["visualDiff","iterationLoop","tdd","e2e","qualityGate","appTypes","orchestration","caching"]'
python3 -c "
import json, sys
with open('.claude/pipeline.config.json') as f:
config = json.load(f)
required = json.loads('$required_keys')
missing = [k for k in required if k not in config]
if missing:
print(f'Missing required keys: {missing}')
sys.exit(1)
print(f'All {len(required)} required keys present')
"
- name: Check required files exist
run: |
exit_code=0
required_files=(
"CLAUDE.md"
"package.json"
".claude/pipeline.config.json"
"scripts/lint-and-format.sh"
"scripts/run-tests.sh"
"scripts/check-types.sh"
"scripts/visual-diff.js"
"scripts/verify-tokens.sh"
"scripts/check-security.sh"
)
for f in "${required_files[@]}"; do
if [ -f "$f" ]; then
echo " $f: OK"
else
echo " $f: MISSING"
exit_code=1
fi
done
exit $exit_code
- name: Validate agent frontmatter
run: |
errors=0
count=0
for f in .claude/agents/*.md; do
[ -f "$f" ] || continue
count=$((count + 1))
# Extract YAML frontmatter between --- delimiters
frontmatter=$(sed -n '/^---$/,/^---$/p' "$f" | sed '1d;$d')
if [ -z "$frontmatter" ]; then
echo "FAIL: $f — no YAML frontmatter found"
errors=$((errors + 1))
continue
fi
# Check required fields (tools is optional — omitted means "all tools")
for field in name description; do
if ! echo "$frontmatter" | grep -qE "^${field}:"; then
echo "FAIL: $f — missing required field: $field"
errors=$((errors + 1))
fi
done
done
echo "Checked $count agents, $errors failures"
exit $errors
- name: Validate skill structure
run: |
errors=0
count=0
for f in .claude/skills/*.md; do
[ -f "$f" ] || continue
[ "$(basename "$f")" = "README.md" ] && continue
count=$((count + 1))
frontmatter=$(sed -n '/^---$/,/^---$/p' "$f" | sed '1d;$d')
if [ -z "$frontmatter" ]; then
echo "FAIL: $f — no frontmatter found"
errors=$((errors + 1))
continue
fi
for field in name description; do
if ! echo "$frontmatter" | grep -qE "^${field}:"; then
echo "FAIL: $f — missing required field: $field"
errors=$((errors + 1))
fi
done
done
echo "Checked $count skills, $errors failures"
[ $count -eq 0 ] && echo "Note: no skill .md files found in .claude/skills/"
exit $errors
- name: Check doc counts match agents/skills on disk
run: bash scripts/check-doc-counts.sh
- name: Validate templates
run: |
errors=0
# Check template JSON files parse correctly
for f in templates/**/*.json; do
if [ -f "$f" ]; then
python3 -m json.tool "$f" > /dev/null 2>&1 || {
echo "FAIL: $f — invalid JSON"
errors=$((errors + 1))
}
fi
done
# Check key template directories exist
for dir in templates/shared templates/nextjs templates/vite; do
if [ -d "$dir" ]; then
echo " $dir: OK"
else
echo " $dir: MISSING"
errors=$((errors + 1))
fi
done
# Check shared configs exist
for f in templates/shared/eslint.config.js templates/shared/prettier.config.js templates/shared/tsconfig.json templates/shared/tailwind.config.ts; do
if [ -f "$f" ]; then
echo " $f: OK"
else
echo " $f: MISSING"
errors=$((errors + 1))
fi
done
echo "$errors template validation failures"
exit $errors
# ── Script test suite (needs Node + dependencies) ─────────────────────
script-tests:
name: Script Tests
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Validate pipeline.config.json against schema
run: node scripts/validate-pipeline-config.js
- name: Run script tests
run: pnpm vitest run --config scripts/__tests__/vitest.config.js scripts/__tests__/ --reporter=verbose
# ── Pipeline package (TypeScript: InDesign IDML parser + IR) ───────────
pipeline-tests:
name: Pipeline Package
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Type-check
run: pnpm --filter @aurelius/pipeline typecheck
- name: Run pipeline tests
run: pnpm --filter @aurelius/pipeline test
- name: Build (tsc emit + declarations)
run: pnpm --filter @aurelius/pipeline build
# ── Lint & format check (needs Node + project with eslint/prettier) ───
lint:
name: Lint & Format
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run lint and format check
run: bash scripts/lint-and-format.sh --check
# ── Token verification ────────────────────────────────────────────────
token-verification:
name: Verify Design Tokens
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
- name: Run token verification
run: |
if [ -f "scripts/verify-tokens.sh" ] && [ -d "app/src" ]; then
cd app && bash ../scripts/verify-tokens.sh
else
echo "No app source found — skipping token check"
fi
# ── Security scanning ─────────────────────────────────────────────────
security-scan:
name: Security Scan
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run pnpm audit
run: pnpm audit --audit-level moderate
continue-on-error: true
- name: Run Snyk security scan
uses: snyk/actions/node@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: Run security anti-pattern check
run: |
if [ -f "scripts/check-security.sh" ]; then
bash scripts/check-security.sh --no-fail
fi
- name: Upload Snyk report
uses: actions/upload-artifact@v4
if: always()
with:
name: snyk-report
path: snyk-report.json
if-no-files-found: ignore
retention-days: 30
# ── Visual regression (PR only) ───────────────────────────────────────
visual-regression:
name: Visual Regression
runs-on: ubuntu-latest
timeout-minutes: 5
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Check for baselines
id: baselines
run: |
BASELINE_COUNT=$(find .claude/visual-qa/baselines -name "*.png" 2>/dev/null | wc -l)
echo "count=$BASELINE_COUNT" >> "$GITHUB_OUTPUT"
if [ "$BASELINE_COUNT" -eq 0 ]; then
echo "No baselines found — skipping regression test"
else
echo "Found $BASELINE_COUNT baseline screenshots"
fi
- name: Install app dependencies
if: steps.baselines.outputs.count != '0' && hashFiles('app/package.json') != ''
working-directory: app
run: pnpm install --frozen-lockfile
- name: Build app
if: steps.baselines.outputs.count != '0' && hashFiles('app/package.json') != ''
working-directory: app
run: pnpm build
- name: Start app server
if: steps.baselines.outputs.count != '0' && hashFiles('app/package.json') != ''
working-directory: app
run: |
pnpm start &
sleep 5
- name: Run visual regression tests
if: steps.baselines.outputs.count != '0'
run: bash scripts/regression-test.sh http://localhost:3000 --json
continue-on-error: true
id: regression
- name: Upload diff artifacts
if: steps.baselines.outputs.count != '0' && always()
uses: actions/upload-artifact@v4
with:
name: visual-regression-diffs
path: |
.claude/visual-qa/diffs/regression/
.claude/visual-qa/regression-report.md
retention-days: 14
if-no-files-found: ignore
- name: Comment PR with regression results
if: steps.baselines.outputs.count != '0' && always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const reportPath = '.claude/visual-qa/regression-report.md';
let body = '## Visual Regression Results\n\nNo report generated.';
if (fs.existsSync(reportPath)) {
body = fs.readFileSync(reportPath, 'utf-8');
}
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('Visual Regression Results')
);
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}