Repository navigation
388 lines (334 loc) · 14.2 KB
/
Copy pathquality.yml
File metadata and controls
388 lines (334 loc) · 14.2 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
name: quality
on:
push:
branches: [main]
pull_request:
# Superseded runs for the same branch/PR do not consume another full matrix.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
# Linux legs pin Ubuntu 24.04: 26.04 denies the sandbox's nested user namespace (docs/sandbox-profiles.md)
runs-on: ubuntu-24.04
outputs:
run: ${{ steps.paths.outputs.run }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 24.16.0
- name: Detect runtime changes
id: paths
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: node .github/scripts/changes.mjs
static-checks:
needs: changes
if: needs.changes.outputs.run == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-latest]
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24.16.0
cache: pnpm
- name: Install JavaScript dependencies
run: pnpm install --frozen-lockfile
- name: Test CI change classification
run: node --test .github/scripts/changes.test.mjs
- name: Lint server and frontend
run: pnpm lint
- name: Typecheck all packages
run: pnpm typecheck
- name: Check shell scripts
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
shellcheck -S warning scripts/*.sh scripts/fixtures/*.sh
- name: Check PowerShell scripts
if: runner.os == 'Windows'
shell: pwsh
run: |
Install-Module PSScriptAnalyzer -RequiredVersion 1.24.0 -Scope CurrentUser -Force -Repository PSGallery
$issues = @(Invoke-ScriptAnalyzer -Path scripts -Recurse -Settings .github/PSScriptAnalyzerSettings.psd1)
$issues | Format-Table -AutoSize
if ($issues.Count -gt 0) { exit 1 }
tests:
needs: changes
if: needs.changes.outputs.run == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# The real Linux sandbox needs a nested user namespace, and Ubuntu 26.04
# (ubuntu-latest since 2026-10-27) refuses the gid map write for it:
# "apply-seccomp: write /proc/self/setgroups ... Permission denied".
# Pin the image the sandbox is verified on until the runtime supports it.
os: [ubuntu-24.04, windows-latest]
# Native process tests remain serialized on Windows to avoid contention.
timeout-minutes: ${{ matrix.os == 'windows-latest' && 45 || 20 }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24.16.0
cache: pnpm
- name: Install required Linux sandbox dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y bubblewrap socat ripgrep
# Dedicated ephemeral runner: Bubblewrap requires user namespaces.
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Install JavaScript dependencies
run: pnpm install --frozen-lockfile
- name: Test native Windows file read boundary
if: runner.os == 'Windows'
run: pnpm --filter @pi-science/server exec vitest run src/runtime/sandbox/file-read.test.ts src/runtime/agent/worker/workspace-file-tool.test.ts
- name: Test environment diagnostics
run: node --test scripts/doctor.test.mjs scripts/check-http-health.test.mjs scripts/check-linux-sandbox.test.mjs scripts/process-state.test.mjs scripts/ci-runner-pins.test.mjs
- name: Test Windows process ownership
if: runner.os == 'Windows'
run: powershell -NoProfile -File scripts/test-process-identity.ps1
- name: Test POSIX launcher lifecycle
if: runner.os != 'Windows'
run: bash scripts/test-launcher.sh
- name: Test contracts, control plane, frontend and skills
if: runner.os != 'Windows'
# Each suite may use both runner cores. Running frontend and server
# together also competes with real child workers in the IPC tests.
run: |
pnpm --workspace-concurrency=1 --filter @pi-science/contracts --filter @pi-science/server --filter frontend run test --maxWorkers=2
pnpm test:skills
- name: Check Windows worker startup before the full IPC suite
if: runner.os == 'Windows'
run: node scripts/check-worker-startup.mjs
# Native process integration tests launch Node, bash and taskkill trees.
# Serializing server files on the two-core Windows runner prevents unrelated
# 500 ms/15 s characterization deadlines from competing for process slots.
- name: Test contracts, serialized control plane, frontend and skills
if: runner.os == 'Windows'
shell: pwsh
run: |
pnpm --filter @pi-science/contracts test
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
pnpm --filter @pi-science/server exec vitest run --fileParallelism=false
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
pnpm --filter frontend test
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
pnpm test:skills
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
build:
needs: changes
if: needs.changes.outputs.run == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# Same nested user namespace requirement as the tests job. The
# control-plane smoke test starts the sandbox, so it fails on 26.04.
os: [ubuntu-24.04, windows-latest]
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24.16.0
cache: pnpm
- name: Install required Linux sandbox dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y bubblewrap socat ripgrep
# Dedicated ephemeral runner: Bubblewrap requires user namespaces.
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Install JavaScript dependencies
run: pnpm install --frozen-lockfile
- name: Build all JavaScript packages
run: pnpm build
- name: Verify native web tools from the compiled server
run: node apps/server/scripts/smoke-web-access.mjs
- name: Enforce Settings backend performance budgets
if: runner.os == 'Linux'
run: |
node scripts/benchmark-settings.mjs > settings-performance.json
node scripts/benchmark-provider-credentials.mjs > provider-credential-performance.json
- name: Upload Settings backend performance metrics
if: always() && runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: settings-backend-performance
path: |
settings-performance.json
provider-credential-performance.json
if-no-files-found: ignore
- name: Enforce frontend bundle budget
run: pnpm --filter frontend test:bundle
- name: Run control-plane smoke test
if: runner.os != 'Windows'
run: bash scripts/smoke-control-plane.sh
- name: Share the Linux frontend build with browser checks
if: runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: frontend/dist
include-hidden-files: true
if-no-files-found: error
browser:
needs: [changes, build]
if: needs.changes.outputs.run == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24.16.0
cache: pnpm
- name: Install JavaScript dependencies
run: pnpm install --frozen-lockfile
- name: Restore the frontend build
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: frontend/dist
# Reuses the build above. The browser-level SSE budget is the only gate on
# the frontend's subscription lifecycle, so it must not stay local-only.
- name: Enforce frontend SSE connection budget
if: runner.os == 'Linux'
run: pnpm --filter frontend test:sse-budget
- name: Run frontend accessibility smoke
if: runner.os == 'Linux'
run: pnpm --filter frontend exec playwright test --config playwright.visual.config.ts --grep @accessibility
- name: Run workspace sidebar interaction tests
if: runner.os == 'Linux'
run: |
pnpm --filter frontend exec playwright test --config playwright.visual.config.ts sidebar.spec.ts --project desktop-light --project mobile --project tablet --output tests/visual/.artifacts/sidebar
pnpm --filter frontend exec playwright test --config playwright.visual.config.ts app-shell.visual.spec.ts --grep "collapsed context panel" --project desktop-light --project desktop-dark --project desktop-lg --project tablet --project wide --update-snapshots=none --output tests/visual/.artifacts/rail-baseline
- name: Upload workspace sidebar screenshots
if: always() && runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: workspace-sidebar-interactions
path: frontend/tests/visual/.artifacts
if-no-files-found: ignore
include-hidden-files: true
- name: Enforce Settings browser performance budgets
if: runner.os == 'Linux'
run: pnpm --filter frontend exec playwright test --config playwright.visual.config.ts settings-performance.spec.ts model-selection-performance.spec.ts --project desktop-light --project mobile
- name: Upload Settings browser performance metrics
if: always() && runner.os == 'Linux'
uses: actions/upload-artifact@v4
with:
name: settings-browser-performance
path: frontend/tests/visual/.artifacts
if-no-files-found: ignore
include-hidden-files: true
macos-launcher:
needs: changes
if: needs.changes.outputs.run == 'true'
name: macOS launcher smoke
runs-on: macos-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24.16.0
cache: pnpm
- name: Install JavaScript dependencies
run: pnpm install --frozen-lockfile
- name: Test native macOS file read boundary
run: pnpm --filter @pi-science/server exec vitest run src/runtime/sandbox/file-read.test.ts src/runtime/agent/worker/workspace-file-tool.test.ts
- name: Test macOS launcher lifecycle
run: bash scripts/test-launcher.sh
- name: Verify native web development and compiled entries on macOS
run: |
pnpm --filter @pi-science/contracts build
pnpm --filter @pi-science/server exec vitest run src/web/tools.test.ts src/web/deepseek.test.ts src/http/routes/settings-web-access.test.ts
pnpm --filter @pi-science/server build
node apps/server/scripts/smoke-web-access.mjs
production-install:
needs: changes
if: needs.changes.outputs.run == 'true'
name: production install (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-latest, macos-latest]
env:
PI_SCIENCE_BIN_DIR: ${{ github.workspace }}/work/production-install/pi-science-bin
PNPM_STORE_DIR: ${{ github.workspace }}/work/production-install/pi-science-store
PI_SCIENCE_HOME: ${{ github.workspace }}/work/production-install/pi-science-data
PI_SCIENCE_WORKSPACES: ${{ github.workspace }}/work/production-install/pi-science-workspaces
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24.16.0
- name: Install required Linux sandbox dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y bubblewrap socat ripgrep
# Dedicated ephemeral runner: Bubblewrap requires user namespaces.
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
# Intentionally omit pnpm setup: exercise the pinned bootstrap from npm.
- name: Install and build (POSIX)
if: runner.os != 'Windows'
run: bash scripts/install.sh
- name: Install and build (Windows PowerShell 5.1)
if: runner.os == 'Windows'
run: powershell -NoProfile -ExecutionPolicy Bypass -File scripts/install.ps1
- name: Test real production lifecycle
run: node scripts/smoke-production.mjs
verify:
name: verify (${{ matrix.os }})
needs: [changes, static-checks, tests, build, browser, macos-launcher, production-install]
if: always()
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04, windows-latest]
steps:
- name: Require all applicable checks to succeed
shell: bash
env:
CHECKS: ${{ toJSON(needs) }}
run: |
node -e 'const checks = JSON.parse(process.env.CHECKS); const allowed = checks.changes.outputs.run === "false" ? ["success", "skipped"] : ["success"]; if (checks.changes.result !== "success" || Object.values(checks).some(check => !allowed.includes(check.result))) process.exit(1);'