-
Notifications
You must be signed in to change notification settings - Fork 0
243 lines (204 loc) · 8.71 KB
/
ci.yml
File metadata and controls
243 lines (204 loc) · 8.71 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
name: CI
on:
push:
branches: [main]
pull_request:
# Default to the least-privileged scope. Individual jobs widen as needed.
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
name: Lint, Typecheck, Test, Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'
- name: Install root devDeps (biome)
run: npm ci --no-audit --no-fund
- name: Install TS runtime deps
run: npm --prefix runtimes/typescript ci --no-audit --no-fund
- name: Install web app deps
run: npm --prefix apps/web ci --no-audit --no-fund
- name: Install aiw-cli deps
run: npm --prefix packages/aiw-cli ci --no-audit --no-fund
- name: Lint (Biome, root)
run: npm run lint
- name: Typecheck (TS runtime)
run: npm run typecheck
- name: Typecheck (web)
run: npm --prefix apps/web run typecheck
- name: Test (TS runtime, includes conformance drift guard)
run: npm test
- name: Coverage (TS runtime)
run: npm --prefix runtimes/typescript run test:coverage
- name: Test (web, vitest + jsdom)
run: npm --prefix apps/web test
- name: Coverage (web lib boundary)
run: npm --prefix apps/web run test:coverage
- name: Build (TS runtime)
run: npm run build
- name: Build (web)
run: npm --prefix apps/web run build
- name: Build (aiw-cli)
# Build before test so the subprocess smoke spec can exercise
# the compiled `dist/cli.js` (it skips itself when absent).
run: npm --prefix packages/aiw-cli run build
- name: Test (aiw-cli, vitest + subprocess smoke)
run: npm --prefix packages/aiw-cli test
# The web client's typed API surface
# (`apps/web/src/lib/api-types.generated.ts`) is generated from
# the runtime's OpenAPI document and committed. Schema drift —
# e.g. a new route added in the runtime without regenerating —
# is silent because the file is committed. Re-run the generator
# and fail if the working tree changes.
- name: Verify generated API types match OpenAPI
run: |
npm --prefix apps/web run gen:types
if ! git diff --quiet apps/web/src/lib/api-types.generated.ts; then
echo "::error::apps/web/src/lib/api-types.generated.ts is out of date."
echo "Run 'npm --prefix apps/web run gen:types' and commit the result."
git --no-pager diff apps/web/src/lib/api-types.generated.ts | head -120
exit 1
fi
docker:
name: Docker Build
runs-on: ubuntu-latest
# `docker/build-push-action` writes to the GitHub Actions cache
# (cache-to: type=gha), which requires `actions: write`.
permissions:
contents: read
actions: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Build TS runtime + UI image
id: build
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
file: runtimes/typescript/Dockerfile
push: false
load: true
tags: ai-workbench:ci
cache-from: type=gha
cache-to: type=gha,mode=max
# `provenance` is intentionally OFF here. Buildx's provenance
# attestation produces a multi-platform manifest list, which
# the local docker daemon cannot accept via `--load` without
# the containerd image store. The SBOM step below still
# records the bill of materials for this image; SLSA
# provenance + cosign signing land alongside the registry
# push in a follow-up PR.
provenance: false
build-args: |
APP_VERSION=ci
APP_COMMIT=${{ github.sha }}
- name: Smoke test image
run: |
docker run --rm -d --name wb -p 8080:8080 ai-workbench:ci
for i in $(seq 1 20); do
if curl -fsS http://localhost:8080/healthz >/dev/null; then
echo "healthz ok"
break
fi
sleep 1
done
curl -fsS http://localhost:8080/healthz
curl -fsS http://localhost:8080/readyz
curl -fsS http://localhost:8080/api/v1/workspaces
curl -fsS http://localhost:8080/api/v1/openapi.json | head -c 200
echo
curl -fsS -o /dev/null -w 'docs HTTP %{http_code}\n' http://localhost:8080/docs
# Verify the embedded UI: `/` should now return the SPA
# shell instead of the operator banner.
curl -fsS -H 'accept: text/html' http://localhost:8080/ | grep -q '<div id="root"' \
|| (echo "UI shell missing from /" && exit 1)
curl -fsS -H 'accept: text/html' http://localhost:8080/onboarding | grep -q '<div id="root"' \
|| (echo "SPA fallback missing for /onboarding" && exit 1)
docker stop wb
# SBOM via syft. Generated against the local CI image so
# the artifact reflects exactly what we just built and smoke-
# tested. Uploaded as a workflow artifact so it's available
# for reviewers even before a registry push exists.
- name: Generate SBOM (syft, SPDX JSON)
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
image: ai-workbench:ci
format: spdx-json
artifact-name: ai-workbench-sbom.spdx.json
output-file: ai-workbench-sbom.spdx.json
upload-artifact: true
upload-release-assets: false
- name: Vulnerability scan SBOM (grype, fail on high+)
uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
with:
sbom: ai-workbench-sbom.spdx.json
severity-cutoff: high
fail-build: true
output-format: table
audit:
name: npm audit (production deps)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'
# `npm audit` is run on production deps only — devDependencies
# have a higher tolerance for advisories (build-only tooling) and
# noisier output blocks signal. `--audit-level=high` matches the
# severity bar SECURITY.md commits to.
- name: Audit (TS runtime, prod)
working-directory: runtimes/typescript
run: npm audit --omit=dev --audit-level=high
- name: Audit (web app, prod)
working-directory: apps/web
run: npm audit --omit=dev --audit-level=high
e2e:
name: Web E2E (Playwright)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: 'npm'
- name: Install TS runtime deps
run: npm --prefix runtimes/typescript ci --no-audit --no-fund
- name: Install web app deps
run: npm --prefix apps/web ci --no-audit --no-fund
# Cache the Playwright browser bundle keyed off the resolved
# @playwright/test version. A miss falls through to the explicit
# install below; a hit skips the ~150 MB download.
- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('apps/web/package-lock.json') }}
- name: Install Playwright browsers
if: steps.pw-cache.outputs.cache-hit != 'true'
run: npm --prefix apps/web run e2e:install
- name: Run Playwright golden-path spec
run: npm --prefix apps/web run test:e2e
env:
CI: 'true'
- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report
path: apps/web/playwright-report/
retention-days: 14