-
Notifications
You must be signed in to change notification settings - Fork 1
393 lines (338 loc) · 15.9 KB
/
Copy pathci.yml
File metadata and controls
393 lines (338 loc) · 15.9 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
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
build:
name: Node ${{ matrix.node }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [18, 20, 22]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js ${{ matrix.node }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ matrix.node }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Type-check
run: npm run typecheck
- name: Lint & format (Biome)
run: npm run lint:ci
- name: Generated artifacts are in sync
run: npm run check:drift
# Coverage is instrumented on ONE leg (Node 22) instead of in a job of its
# own, so it costs one instrumented run rather than a second `npm ci` + suite,
# and the 18/20 legs pay nothing at all. It is REPORT-ONLY: there are no
# thresholds and no step below can fail on a coverage number — `npm test`
# stays the single pass/fail signal.
- name: Test
if: matrix.node != 22
run: npm test
- name: Test (+ coverage, report-only)
if: matrix.node == 22
run: npm run test:coverage
- name: Coverage summary
if: matrix.node == 22
run: |
node --input-type=commonjs -e '
const t = JSON.parse(require("node:fs").readFileSync("coverage/coverage-summary.json", "utf8")).total;
const rows = ["lines", "statements", "functions", "branches"].map(
(k) => "| " + k + " | " + t[k].pct + "% | " + t[k].covered + " / " + t[k].total + " |",
);
console.log(
[
"## Coverage — `src/` (report-only, never gates)",
"",
"| Metric | % | Covered / Total |",
"| --- | --- | --- |",
...rows,
].join("\n"),
);
' >> "$GITHUB_STEP_SUMMARY"
# The totals above cannot show a module that fell to ZERO — it is one row among 111
# in the `text` reporter and invisible in a four-line total. This names them. It is
# ADVISORY in the strongest sense: the script always exits 0 (it catches its own
# errors, including a missing coverage file), so this step can never turn a green run
# red. No thresholds are added — see `docs/testing.md` §2, "a map, not a gate".
- name: Zero-coverage modules (advisory)
if: matrix.node == 22
run: node scripts/coverage-zero-report.mjs >> "$GITHUB_STEP_SUMMARY"
- name: Upload coverage summary
if: matrix.node == 22
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-summary
path: coverage/coverage-summary.json
if-no-files-found: error
- name: Authorability eval (offline golden regression guard)
run: npm run eval:ci
builds:
name: Workspace builds
runs-on: ubuntu-latest
# The core suite never compiles the downstream workspaces, so a change that
# breaks the docs site, the playground, the MCP shim or the VS Code extension
# used to reach `main` green. This job compiles all four.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
# Every workspace consumes the core's `dist/`, and the root
# `docs:build`/`playground:build`/`mcp:build` scripts each BEGIN with their
# own `npm run build`. Running those four here would rebuild the core four
# times, so the core is built once and CI calls the `:only` variants.
- name: Build core (once — every workspace below consumes dist/)
run: npm run build
# Fails fast, before the slower site builds. Covers the root dev tsconfig
# (src + test + eval + dataset + scripts + bench) plus the playground, the
# docs site (vue-tsc over .vitepress + every theme SFC, and tsc over the docs
# E2E specs), the MCP shim and the VS Code extension. The playground and docs
# legs both resolve the bare `archlang` specifier to ../dist/index.d.ts, hence
# after the build above; the docs leg also runs `sync-docs.mjs` first, since
# config.ts and CompileSeam.vue import its generated modules.
- name: Type-check every workspace
run: npm run typecheck:all
- name: Build docs site
run: npm run docs:build:only
- name: Build playground
run: npm run playground:build:only
- name: Build MCP shim
run: npm run mcp:build:only
- name: Build VS Code extension
run: npm run vscode:build:only
# The MCP shim bakes its context resources into dist/ at pack time — the
# 0.2.2 staleness incident shipped a v1.19 grammar against a current core.
# This proves every dist/ resource is byte-identical to its repo source.
- name: Check MCP dist resources are fresh
run: node packages/mcp/scripts/check-dist-resources.mjs
# The stdio smoke + bundle-freshness tests need the built extension
# (dist/server.js); the plain test matrix skips them by design.
- name: VS Code extension bundle tests
run: npx vitest run editors/vscode
# A `*.test.ts` outside vitest.config.ts's `test.include` list is not skipped
# and not reported — it is never collected, so it reads as coverage in the
# diff while `npm test` stays green. This proves every tracked test file is
# matched by a glob the suite actually runs (and that no glob has gone dead).
- name: Every test file is wired into the suite
run: npm run check:test-wiring
# (appended by later workstreams)
windows:
name: Windows (tests + drift)
runs-on: windows-latest
# Determinism is a core invariant, and the whole toolchain is developed on
# Windows — but CI only ever proved it on Linux. This leg catches the two
# things that actually differ: line endings (a golden/snapshot mismatch) and
# path handling in the generators.
#
# NOTE: git's line-ending settings are deliberately left at the RUNNER
# DEFAULTS (no core.autocrlf override). `.gitattributes` pins `* text=auto
# eol=lf`, and this job's whole point is to fail if that ever stops holding —
# forcing autocrlf here would hide exactly the regression it exists to catch.
#
# `eval:ci` and the benchmark are Linux-only on purpose: the eval is already
# covered by the matrix above and bench timings are runner-dependent.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Test
run: npm test
- name: Generated artifacts are in sync
run: npm run check:drift
bench:
name: Benchmark (informational)
runs-on: ubuntu-latest
# Timings are runner-dependent — this never gates the build; it posts a PR
# comment with the median per-stage deltas vs. bench/baseline.json.
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20
cache: npm
- run: npm ci
- name: Run benchmark
run: |
npx tsx bench/run.ts --json > bench-current.json
node bench/compare.mjs bench-current.json bench-comment.md
- name: Post benchmark comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('bench-comment.md', 'utf8');
const marker = '<!-- archlang-bench -->';
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number });
const existing = comments.find((c) => c.body.includes(marker));
const payload = `${marker}\n${body}`;
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: payload });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body: payload });
}
e2e-playground:
name: Playground E2E (Playwright)
runs-on: ubuntu-latest
# The unit suite proves the playground's pure logic and `builds` proves it
# COMPILES — neither ever opens the page. This job drives the real built
# bundle in a real Chromium: boot, edit, apply a diagnostic fix, download
# every format, and the chrome-less embed page.
#
# It earned its keep on the first run by finding a live bug: click-to-source
# never fired with a real mouse, because the pan/zoom controller's
# `setPointerCapture` retargets the click away from the drawn element (fixed
# in `interact.ts` by hit-testing the POINT instead of reading `e.target`).
# That class of fault is invisible to `tsc`, vitest and `vite build` alike,
# which is the whole argument for this leg.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
# `vite preview` (the config's `webServer`) only SERVES playground/dist/ —
# it never builds it. Both builds below are prerequisites, not
# conveniences: the core first, since the playground aliases the bare
# `archlang` specifier to ../dist/index.js, then the app itself.
- name: Build core
run: npm run build
- name: Build playground
run: npm run playground:build:only
# Keyed on the RESOLVED @playwright/test version: a browser build is pinned
# to its driver, so a version bump must miss the cache rather than reuse an
# incompatible download.
- name: Resolve Playwright version
id: pw
run: |
version=$(node -p "require('@playwright/test/package.json').version")
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.pw.outputs.version }}
# On a cache hit the browser binary is already there but the OS packages it
# links against are not (they live outside the cached path), so the two
# cases install different things.
- name: Install Chromium (browser + system deps)
if: steps.pw-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium
- name: Install Chromium system deps only (cached browser)
if: steps.pw-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium
- name: Run E2E specs
run: npx playwright test -c playground
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report
path: |
playground/playwright-report
playground/test-results
retention-days: 7
if-no-files-found: ignore
e2e-docs:
name: Docs site E2E (Playwright)
runs-on: ubuntu-latest
# `builds` proves the docs site COMPILES and `scripts/smoke.mjs` proves its routes
# exist AFTER a deploy — neither opens the site. This job serves the built output
# and checks the two contracts that are invisible to both:
#
# 1. The MACHINE routes llms.txt advertises — the raw `/<page>.md` copies (byte
# -for-byte the repo's canonical markdown), /llms-full.txt, both JSON schemas,
# /archlang.gbnf and one SVG per gallery example. A `srcExclude` or public/
# mistake turns any of them into an HTML 404 while the site still builds,
# still deploys and still looks perfect to a human.
# 2. `<ArchLive>` — the claim that every ```arch fence in the docs is a real
# compiler in the reader's tab: the SSR fallback swapping to a live editor,
# an edit recompiling, a hostile room label reaching the v-html'd SVG as TEXT,
# and "Open in Playground" minting a `#z=` hash that the PLAYGROUND's own
# decoder reads back. That last one is the runtime weld on the third copy of
# the share codec (test/share-codec.test.ts is the static one).
#
# Route lists are derived from docs-site/sync-docs.mjs, so a new page or example
# extends this job the day it lands.
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
# Node 22, not 18/20: archlive.spec.ts decodes the widget's `#z=` hash with
# `DecompressionStream("deflate-raw")`, which Node only gained in 21.2.
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
# `vitepress preview` (the config's `webServer`) only SERVES
# docs-site/.vitepress/dist/ — it never builds it. Both builds below are
# prerequisites, not conveniences: the core first, since the theme aliases the
# bare `archlang` specifier to ../dist/index.js (and sync-docs compiles every
# gallery example through it), then the site itself.
- name: Build core
run: npm run build
- name: Build docs site
run: npm run docs:build:only
# Keyed on the RESOLVED @playwright/test version: a browser build is pinned
# to its driver, so a version bump must miss the cache rather than reuse an
# incompatible download.
- name: Resolve Playwright version
id: pw
run: |
version=$(node -p "require('@playwright/test/package.json').version")
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
id: pw-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.pw.outputs.version }}
# On a cache hit the browser binary is already there but the OS packages it
# links against are not (they live outside the cached path), so the two
# cases install different things.
- name: Install Chromium (browser + system deps)
if: steps.pw-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium
- name: Install Chromium system deps only (cached browser)
if: steps.pw-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium
- name: Run E2E specs
run: npx playwright test -c docs-site
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report-docs
path: |
docs-site/playwright-report
docs-site/test-results
retention-days: 7
if-no-files-found: ignore