ci.yml 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. name: CI
  2. on:
  3. push:
  4. branches: [main, master]
  5. pull_request:
  6. workflow_dispatch:
  7. inputs:
  8. suite:
  9. description: Manual CI suite to run
  10. required: true
  11. default: serial-reference
  12. type: choice
  13. options:
  14. - serial-reference
  15. - larger-runner-benchmark
  16. - consolidated-runner-benchmark
  17. - sharded-reference
  18. - optimized-larger-runners
  19. concurrency:
  20. group: ${{ github.workflow }}-${{ github.ref }}
  21. cancel-in-progress: true
  22. permissions:
  23. contents: read
  24. env:
  25. PRIMARY_NODE_VERSION: '24'
  26. jobs:
  27. node-24-sharded:
  28. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'sharded-reference'
  29. runs-on: ${{ startsWith(matrix.lane, 'snapshot-') && 'dsh-ubuntu-24-04-8core' || 'dsh-ubuntu-24-04-4core' }}
  30. name: node 24 / ${{ matrix.lane }}
  31. env:
  32. DSH_GATE_CONCURRENCY: ${{ matrix.gate_concurrency }}
  33. DSH_PUBLINT_CONCURRENCY: ${{ matrix.publint_concurrency }}
  34. DSH_ARTIFACT_SHARD: ${{ matrix.artifact_shard }}
  35. DSH_COVERAGE_MAX_WORKERS: ${{ matrix.coverage_max_workers }}
  36. DSH_COVERAGE_SHARD: ${{ matrix.coverage_shard }}
  37. DSH_LINT_SHARD: ${{ matrix.lint_shard }}
  38. DSH_SNAPSHOT_LANE: ${{ matrix.snapshot_lane }}
  39. DSH_STATIC_SHARD: ${{ matrix.static_shard }}
  40. DSH_SNAPSHOT_MAX_CONCURRENCY: ${{ matrix.snapshot_max_concurrency }}
  41. DSH_SNAPSHOT_PREBUILT: ${{ matrix.snapshot_prebuilt }}
  42. DSH_ESLINT_CACHE: ${{ matrix.eslint_cache }}
  43. strategy:
  44. fail-fast: false
  45. matrix:
  46. include:
  47. - lane: static-foundation-projection
  48. command: pnpm run check:ci:static
  49. gate_concurrency: '4'
  50. static_shard: 'foundation,site-projection'
  51. - lane: static-doc-types
  52. command: pnpm run check:ci:static
  53. gate_concurrency: '1'
  54. static_shard: doc-types
  55. - lane: static-api-contracts
  56. command: pnpm run check:ci:static
  57. gate_concurrency: '4'
  58. static_shard: api-contracts
  59. - lane: static-catalogs
  60. command: pnpm run check:ci:static
  61. gate_concurrency: '4'
  62. static_shard: catalogs
  63. - lane: static-prose
  64. command: pnpm run check:ci:static
  65. gate_concurrency: '4'
  66. static_shard: prose
  67. - lane: static-site-mpa
  68. command: pnpm run check:ci:static
  69. gate_concurrency: '1'
  70. static_shard: site-build
  71. - lane: typecheck
  72. command: pnpm run typecheck
  73. - lane: lint-package-sources-a-c
  74. command: pnpm run check:ci:lint
  75. gate_concurrency: '1'
  76. eslint_cache: '1'
  77. lint_shard: package-sources-a-c
  78. - lane: lint-package-sources-d-m
  79. command: pnpm run check:ci:lint
  80. gate_concurrency: '1'
  81. eslint_cache: '1'
  82. lint_shard: package-sources-d-m
  83. - lane: lint-package-sources-n-s
  84. command: pnpm run check:ci:lint
  85. gate_concurrency: '1'
  86. eslint_cache: '1'
  87. lint_shard: package-sources-n-s
  88. - lane: lint-package-sources-t-z
  89. command: pnpm run check:ci:lint
  90. gate_concurrency: '1'
  91. eslint_cache: '1'
  92. lint_shard: package-sources-t-z
  93. - lane: lint-package-tests-a-c
  94. command: pnpm run check:ci:lint
  95. gate_concurrency: '1'
  96. eslint_cache: '1'
  97. lint_shard: package-tests-a-c
  98. - lane: lint-package-tests-d-m
  99. command: pnpm run check:ci:lint
  100. gate_concurrency: '1'
  101. eslint_cache: '1'
  102. lint_shard: package-tests-d-m
  103. - lane: lint-package-tests-n-s
  104. command: pnpm run check:ci:lint
  105. gate_concurrency: '1'
  106. eslint_cache: '1'
  107. lint_shard: package-tests-n-s
  108. - lane: lint-package-tests-t-z
  109. command: pnpm run check:ci:lint
  110. gate_concurrency: '1'
  111. eslint_cache: '1'
  112. lint_shard: package-tests-t-z
  113. - lane: lint-repository
  114. command: pnpm run check:ci:lint
  115. gate_concurrency: '1'
  116. eslint_cache: '1'
  117. lint_shard: repository
  118. - lane: coverage-core-loop
  119. command: pnpm run check:ci:coverage
  120. gate_concurrency: '1'
  121. coverage_max_workers: '4'
  122. coverage_shard: core-loop
  123. - lane: coverage-state-session
  124. command: pnpm run check:ci:coverage
  125. gate_concurrency: '1'
  126. coverage_max_workers: '4'
  127. coverage_shard: state-session
  128. - lane: coverage-session-title
  129. command: pnpm run check:ci:coverage
  130. gate_concurrency: '1'
  131. coverage_max_workers: '4'
  132. coverage_shard: session-title
  133. - lane: coverage-models
  134. command: pnpm run check:ci:coverage
  135. gate_concurrency: '1'
  136. coverage_max_workers: '4'
  137. coverage_shard: models
  138. - lane: coverage-integrations
  139. command: pnpm run check:ci:coverage
  140. gate_concurrency: '1'
  141. coverage_max_workers: '4'
  142. coverage_shard: integrations
  143. - lane: coverage-sdk-capabilities
  144. command: pnpm run check:ci:coverage
  145. gate_concurrency: '1'
  146. coverage_max_workers: '4'
  147. coverage_shard: sdk-capabilities
  148. - lane: coverage-interfaces
  149. command: pnpm run check:ci:coverage
  150. gate_concurrency: '1'
  151. coverage_max_workers: '4'
  152. coverage_shard: interfaces
  153. - lane: coverage-execution
  154. command: pnpm run check:ci:coverage
  155. gate_concurrency: '1'
  156. coverage_max_workers: '4'
  157. coverage_shard: execution
  158. - lane: coverage-workflow
  159. command: pnpm run check:ci:coverage
  160. gate_concurrency: '1'
  161. coverage_max_workers: '4'
  162. coverage_shard: workflow
  163. - lane: coverage-workflow-worker
  164. command: pnpm run check:ci:coverage
  165. gate_concurrency: '1'
  166. coverage_max_workers: '4'
  167. coverage_shard: workflow-worker
  168. - lane: coverage-delegation
  169. command: pnpm run check:ci:coverage
  170. gate_concurrency: '1'
  171. coverage_max_workers: '4'
  172. coverage_shard: delegation
  173. - lane: coverage-repository
  174. command: pnpm run check:ci:coverage
  175. gate_concurrency: '1'
  176. coverage_max_workers: '4'
  177. coverage_shard: repository
  178. - lane: snapshot-support
  179. command: pnpm run check:ci:snapshot
  180. gate_concurrency: '1'
  181. snapshot_lane: support
  182. snapshot_max_concurrency: '5'
  183. snapshot_prebuilt: '1'
  184. - lane: snapshot-agents
  185. command: pnpm run check:ci:snapshot
  186. gate_concurrency: '1'
  187. snapshot_lane: agents
  188. snapshot_max_concurrency: '5'
  189. snapshot_prebuilt: '1'
  190. - lane: snapshot-acp-1
  191. command: pnpm run check:ci:snapshot
  192. gate_concurrency: '1'
  193. snapshot_lane: acp-1
  194. snapshot_max_concurrency: '5'
  195. snapshot_prebuilt: '1'
  196. - lane: snapshot-acp-2
  197. command: pnpm run check:ci:snapshot
  198. gate_concurrency: '1'
  199. snapshot_lane: acp-2
  200. snapshot_max_concurrency: '5'
  201. snapshot_prebuilt: '1'
  202. - lane: snapshot-acp-3
  203. command: pnpm run check:ci:snapshot
  204. gate_concurrency: '1'
  205. snapshot_lane: acp-3
  206. snapshot_max_concurrency: '5'
  207. snapshot_prebuilt: '1'
  208. - lane: snapshot-acp-4
  209. command: pnpm run check:ci:snapshot
  210. gate_concurrency: '1'
  211. snapshot_lane: acp-4
  212. snapshot_max_concurrency: '5'
  213. snapshot_prebuilt: '1'
  214. - lane: snapshot-acp-5
  215. command: pnpm run check:ci:snapshot
  216. gate_concurrency: '1'
  217. snapshot_lane: acp-5
  218. snapshot_max_concurrency: '5'
  219. snapshot_prebuilt: '1'
  220. - lane: snapshot-acp-6
  221. command: pnpm run check:ci:snapshot
  222. gate_concurrency: '1'
  223. snapshot_lane: acp-6
  224. snapshot_max_concurrency: '5'
  225. snapshot_prebuilt: '1'
  226. - lane: snapshot-acp-7
  227. command: pnpm run check:ci:snapshot
  228. gate_concurrency: '1'
  229. snapshot_lane: acp-7
  230. snapshot_max_concurrency: '5'
  231. snapshot_prebuilt: '1'
  232. - lane: snapshot-acp-8
  233. command: pnpm run check:ci:snapshot
  234. gate_concurrency: '1'
  235. snapshot_lane: acp-8
  236. snapshot_max_concurrency: '5'
  237. snapshot_prebuilt: '1'
  238. - lane: artifacts-metadata
  239. command: pnpm run check:ci:artifacts
  240. gate_concurrency: '3'
  241. publint_concurrency: '8'
  242. artifact_shard: metadata
  243. - lane: artifacts-smoke
  244. command: pnpm run check:ci:artifacts
  245. gate_concurrency: '1'
  246. artifact_shard: smoke
  247. steps:
  248. - uses: actions/checkout@v6
  249. - uses: actions/setup-node@v6
  250. with:
  251. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  252. - name: Enable corepack and resolve pnpm store path
  253. id: pnpm-store
  254. run: |
  255. corepack enable
  256. echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
  257. - uses: actions/cache@v4
  258. with:
  259. path: ${{ steps.pnpm-store.outputs.path }}
  260. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  261. restore-keys: |
  262. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  263. - name: Install (immutable)
  264. if: ${{ ! startsWith(matrix.lane, 'snapshot-') }}
  265. run: pnpm install --frozen-lockfile
  266. # The snapshot lanes REPLAY the sandbox example's recorded scenarios,
  267. # re-executing their bash calls under a real runner. ubuntu-latest has
  268. # no bubblewrap preinstalled and no built Landlock launcher, so without
  269. # this the confined executions fail closed (SANDBOX_UNAVAILABLE). The
  270. # install retries after refreshing stale indexes and applies the Ubuntu
  271. # 24.04 AppArmor userns knob. Bubblewrap preparation is independent of
  272. # dependency installation and the build, so it runs beside both.
  273. - name: Install and prepare built snapshot runtime and bubblewrap
  274. if: startsWith(matrix.lane, 'snapshot-')
  275. run: |
  276. pnpm install --frozen-lockfile &
  277. install_pid=$!
  278. (
  279. if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then
  280. echo "initial bubblewrap install failed; refreshing APT indexes and retrying"
  281. sudo apt-get update -q
  282. sudo apt-get install -yq --no-install-recommends bubblewrap
  283. fi
  284. sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \
  285. || echo "apparmor userns knob absent — the functional probe decides"
  286. ) &
  287. sandbox_pid=$!
  288. install_status=0
  289. wait "$install_pid" || install_status=$?
  290. build_status=0
  291. if (( install_status == 0 )); then
  292. pnpm run build || build_status=$?
  293. fi
  294. sandbox_status=0
  295. wait "$sandbox_pid" || sandbox_status=$?
  296. if (( install_status != 0 )); then exit "$install_status"; fi
  297. if (( build_status != 0 )); then exit "$build_status"; fi
  298. exit "$sandbox_status"
  299. - uses: actions/cache@v4
  300. if: startsWith(matrix.lane, 'lint-')
  301. with:
  302. path: .cache/eslint
  303. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-${{ matrix.lint_shard }}-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
  304. restore-keys: |
  305. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-${{ matrix.lint_shard }}-
  306. - name: Run gates
  307. run: ${{ matrix.command }}
  308. # One large runner pays hosted setup once, then the repository scheduler
  309. # overlaps the unsharded primary inventory except the production site build.
  310. node-24:
  311. if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
  312. runs-on: dsh-ubuntu-24-04-96core
  313. name: node 24 / core
  314. env:
  315. # Thirty-two Vitest forks intermittently crash Node's CJS lexer on this image.
  316. DSH_COVERAGE_MAX_WORKERS: '16'
  317. DSH_ESLINT_CACHE: '1'
  318. DSH_ESLINT_CONCURRENCY: '32'
  319. DSH_GATE_CONCURRENCY: '32'
  320. DSH_PUBLINT_CONCURRENCY: '32'
  321. DSH_SNAPSHOT_MAX_CONCURRENCY: '32'
  322. steps:
  323. - uses: actions/checkout@v6
  324. - uses: actions/setup-node@v6
  325. with:
  326. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  327. - name: Enable corepack and resolve pnpm store path
  328. id: pnpm-store
  329. run: |
  330. corepack enable
  331. echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
  332. - uses: actions/cache@v4
  333. with:
  334. path: ${{ steps.pnpm-store.outputs.path }}
  335. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  336. restore-keys: |
  337. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  338. - uses: actions/cache@v4
  339. with:
  340. path: .cache/eslint
  341. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
  342. restore-keys: |
  343. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
  344. - name: Install and prepare bubblewrap
  345. run: |
  346. pnpm install --frozen-lockfile &
  347. install_pid=$!
  348. (
  349. if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then
  350. echo "initial bubblewrap install failed; refreshing APT indexes and retrying"
  351. sudo apt-get update -q
  352. sudo apt-get install -yq --no-install-recommends bubblewrap
  353. fi
  354. sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \
  355. || echo "apparmor userns knob absent — the functional probe decides"
  356. ) &
  357. sandbox_pid=$!
  358. install_status=0
  359. wait "$install_pid" || install_status=$?
  360. sandbox_status=0
  361. wait "$sandbox_pid" || sandbox_status=$?
  362. if (( install_status != 0 )); then exit "$install_status"; fi
  363. exit "$sandbox_status"
  364. - name: Run unsharded primary Node core CI concurrently
  365. run: pnpm run check:ci:large-runner
  366. # Keep only the longest independent Linux gate on a second coarse-grained
  367. # runner so cold install variance does not push the primary box over a minute.
  368. node-24-site:
  369. if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
  370. runs-on: dsh-ubuntu-24-04-16core
  371. name: node 24 / production site
  372. steps:
  373. - uses: actions/checkout@v6
  374. - uses: actions/setup-node@v6
  375. with:
  376. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  377. - name: Enable corepack and resolve pnpm store path
  378. id: pnpm-store
  379. run: |
  380. corepack enable
  381. echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
  382. - uses: actions/cache@v4
  383. with:
  384. path: ${{ steps.pnpm-store.outputs.path }}
  385. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  386. restore-keys: |
  387. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  388. - name: Install (immutable)
  389. run: pnpm install --frozen-lockfile
  390. - name: Build documentation site (production SPA)
  391. run: pnpm run docs:build
  392. node-compat:
  393. if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
  394. runs-on: ubuntu-latest
  395. name: node ${{ matrix.node }}
  396. env:
  397. DSH_GATE_CONCURRENCY: '2'
  398. DSH_NODE_COMPAT_SKIP_TYPECHECK: ${{ matrix.skip_typecheck }}
  399. strategy:
  400. fail-fast: false
  401. matrix:
  402. include:
  403. - node: '22.19'
  404. skip_typecheck: '1'
  405. - node: 24
  406. skip_typecheck: '1'
  407. - node: 26
  408. skip_typecheck: '1'
  409. steps:
  410. - uses: actions/checkout@v6
  411. - uses: actions/setup-node@v6
  412. with:
  413. node-version: ${{ matrix.node }}
  414. - name: Enable corepack and resolve pnpm store path
  415. id: pnpm-store
  416. run: |
  417. corepack enable
  418. echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
  419. - uses: actions/cache@v4
  420. with:
  421. path: ${{ steps.pnpm-store.outputs.path }}
  422. key: ${{ runner.os }}-node-${{ matrix.node }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  423. restore-keys: |
  424. ${{ runner.os }}-node-${{ matrix.node }}-pnpm-
  425. - name: Install (immutable)
  426. run: pnpm install --frozen-lockfile
  427. - name: Run compatibility gates
  428. run: pnpm run check:node-compat
  429. python-sdk:
  430. if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
  431. runs-on: ubuntu-latest
  432. name: python 3.10 / keyless SDK
  433. steps:
  434. - uses: actions/checkout@v6
  435. - uses: actions/setup-python@v6
  436. with:
  437. python-version: '3.10'
  438. cache: pip
  439. - name: Install uv
  440. run: python -m pip install uv==0.11.23
  441. - name: Run complete keyless Python suite
  442. run: uv run --python 3.10 --group test --project python/sdk pytest
  443. # Manual sharded Windows reference matching the former production topology.
  444. # Blocking build and production-site lanes keep the already-green
  445. # native outputs protected without putting both critical paths in one job.
  446. # The broader observational gate matrix below exposes the remaining
  447. # portability work without blocking mainline merges.
  448. windows-build-sharded:
  449. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'sharded-reference'
  450. runs-on: dsh-windows-2025-4core
  451. name: windows / build
  452. steps:
  453. - uses: actions/checkout@v6
  454. - uses: actions/setup-node@v6
  455. with:
  456. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  457. - name: Install (immutable)
  458. run: |
  459. corepack enable
  460. pnpm install --frozen-lockfile
  461. - name: Build (tsc -b + tsdown)
  462. run: pnpm run build
  463. windows-site-sharded:
  464. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'sharded-reference'
  465. runs-on: dsh-windows-2025-4core
  466. name: windows / production site
  467. steps:
  468. - uses: actions/checkout@v6
  469. - uses: actions/setup-node@v6
  470. with:
  471. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  472. - name: Install (immutable)
  473. run: |
  474. corepack enable
  475. pnpm install --frozen-lockfile
  476. - name: Build documentation site (production SPA)
  477. run: pnpm run docs:build
  478. # Observational, non-blocking Windows static, lint, and artifact lanes. Coverage
  479. # and snapshot stay Linux-only until their platform-specific runtime failures
  480. # have dedicated support. Run the gates from native PowerShell: an MSYS parent
  481. # would change the environment being measured. This job intentionally stays
  482. # out of all-checks-passed.needs.
  483. windows-gates-sharded:
  484. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'sharded-reference'
  485. continue-on-error: true
  486. runs-on: dsh-windows-2025-4core
  487. name: windows node 24 / ${{ matrix.lane }}
  488. env:
  489. DSH_GATE_CONCURRENCY: ${{ matrix.gate_concurrency }}
  490. DSH_PUBLINT_CONCURRENCY: ${{ matrix.publint_concurrency }}
  491. DSH_ARTIFACT_SHARD: ${{ matrix.artifact_shard }}
  492. DSH_LINT_SHARD: ${{ matrix.lint_shard }}
  493. DSH_STATIC_SHARD: ${{ matrix.static_shard }}
  494. DSH_ESLINT_CACHE: ${{ matrix.eslint_cache }}
  495. strategy:
  496. fail-fast: false
  497. matrix:
  498. include:
  499. - lane: static-general
  500. command: pnpm run check:ci:static
  501. gate_concurrency: '4'
  502. static_shard: 'foundation,catalogs,prose'
  503. - lane: static-contracts
  504. command: pnpm run check:ci:static
  505. gate_concurrency: '4'
  506. static_shard: 'doc-types,api-contracts'
  507. - lane: lint-package-sources
  508. command: pnpm run check:ci:lint
  509. gate_concurrency: '1'
  510. eslint_cache: '1'
  511. lint_shard: package-sources
  512. - lane: lint-package-tests
  513. command: pnpm run check:ci:lint
  514. gate_concurrency: '1'
  515. eslint_cache: '1'
  516. lint_shard: package-tests
  517. - lane: lint-repository
  518. command: pnpm run check:ci:lint
  519. gate_concurrency: '1'
  520. eslint_cache: '1'
  521. lint_shard: repository
  522. - lane: artifacts-metadata
  523. command: pnpm run check:ci:artifacts
  524. gate_concurrency: '3'
  525. publint_concurrency: '8'
  526. artifact_shard: metadata
  527. - lane: artifacts-smoke
  528. command: pnpm run check:ci:artifacts
  529. gate_concurrency: '1'
  530. artifact_shard: smoke
  531. steps:
  532. - uses: actions/checkout@v6
  533. - name: Enable Developer Mode (symlink support)
  534. shell: pwsh
  535. run: >-
  536. reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
  537. /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
  538. - uses: actions/setup-node@v6
  539. with:
  540. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  541. - name: Enable corepack and resolve pnpm store path
  542. id: pnpm-store
  543. shell: pwsh
  544. run: |
  545. corepack enable
  546. "path=$(pnpm store path --silent)" >> $env:GITHUB_OUTPUT
  547. - uses: actions/cache@v4
  548. with:
  549. path: ${{ steps.pnpm-store.outputs.path }}
  550. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  551. restore-keys: |
  552. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  553. - name: Install (immutable)
  554. shell: pwsh
  555. run: pnpm install --frozen-lockfile
  556. - uses: actions/cache@v4
  557. if: startsWith(matrix.lane, 'lint-')
  558. with:
  559. path: .cache/eslint
  560. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-${{ matrix.lint_shard }}-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
  561. restore-keys: |
  562. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-${{ matrix.lint_shard }}-
  563. - name: Run gates
  564. shell: pwsh
  565. run: ${{ matrix.command }}
  566. # One Windows box shares setup across the required build/site checks and the
  567. # complete observational portability inventory. run-gates reports failures
  568. # from observational gates without allowing them to fail the required job.
  569. windows:
  570. if: github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners'
  571. runs-on: dsh-windows-2025-32core
  572. name: windows node 24 / complete
  573. env:
  574. # Keep ESLint itself single-threaded: 16 ESLint workers took 174 seconds on
  575. # this image. The outer scheduler still overlaps lint with the other gates.
  576. DSH_ESLINT_CACHE: '1'
  577. DSH_GATE_CONCURRENCY: '32'
  578. DSH_PUBLINT_CONCURRENCY: '32'
  579. steps:
  580. - uses: actions/checkout@v6
  581. - name: Enable Developer Mode (symlink support)
  582. shell: pwsh
  583. run: >-
  584. reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
  585. /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
  586. - uses: actions/setup-node@v6
  587. with:
  588. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  589. - name: Enable corepack and resolve pnpm store path
  590. id: pnpm-store
  591. shell: pwsh
  592. run: |
  593. corepack enable
  594. "path=$(pnpm store path --silent)" >> $env:GITHUB_OUTPUT
  595. - uses: actions/cache@v4
  596. with:
  597. path: ${{ steps.pnpm-store.outputs.path }}
  598. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  599. restore-keys: |
  600. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  601. - uses: actions/cache@v4
  602. with:
  603. path: .cache/eslint
  604. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
  605. restore-keys: |
  606. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
  607. - name: Install (immutable)
  608. shell: pwsh
  609. run: pnpm install --frozen-lockfile
  610. - name: Run blocking and observational Windows gates concurrently
  611. shell: pwsh
  612. run: pnpm run check:ci:windows-complete
  613. # Manual reference runs deliberately avoid the optimized jobs above.
  614. # Each host executes the complete, unsharded primary Node aggregate with one
  615. # gate worker, giving reviewers a simple cross-platform oracle for completeness
  616. # and timing.
  617. serial-linux:
  618. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'serial-reference'
  619. name: serial / linux
  620. runs-on: ubuntu-latest
  621. steps:
  622. - uses: actions/checkout@v6
  623. - uses: actions/setup-node@v6
  624. with:
  625. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  626. - name: Enable corepack (pnpm)
  627. run: corepack enable
  628. - name: Install (immutable)
  629. run: pnpm install --frozen-lockfile
  630. - name: Install bubblewrap (unrestrict userns)
  631. run: |
  632. sudo apt-get update -q
  633. sudo apt-get install -yq --no-install-recommends bubblewrap
  634. sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \
  635. || echo "apparmor userns knob absent — the functional probe decides"
  636. - name: Run complete unsharded primary Node CI serially
  637. env:
  638. DSH_COVERAGE_MAX_WORKERS: '1'
  639. DSH_E2E_MAX_WORKERS: '1'
  640. DSH_GATE_CONCURRENCY: '1'
  641. DSH_PUBLINT_CONCURRENCY: '1'
  642. DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
  643. run: pnpm run check:ci
  644. serial-macos:
  645. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'serial-reference'
  646. name: serial / macos
  647. runs-on: macos-latest
  648. steps:
  649. - uses: actions/checkout@v6
  650. - uses: actions/setup-node@v6
  651. with:
  652. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  653. - name: Enable corepack (pnpm)
  654. run: corepack enable
  655. - name: Install (immutable)
  656. run: pnpm install --frozen-lockfile
  657. - name: Run complete unsharded primary Node CI serially
  658. env:
  659. DSH_COVERAGE_MAX_WORKERS: '1'
  660. DSH_E2E_MAX_WORKERS: '1'
  661. DSH_GATE_CONCURRENCY: '1'
  662. DSH_PUBLINT_CONCURRENCY: '1'
  663. DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
  664. run: pnpm run check:ci
  665. serial-windows:
  666. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'serial-reference'
  667. name: serial / windows
  668. runs-on: windows-2025
  669. steps:
  670. - uses: actions/checkout@v6
  671. - name: Enable Developer Mode (symlink support)
  672. shell: pwsh
  673. run: >-
  674. reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
  675. /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
  676. - uses: actions/setup-node@v6
  677. with:
  678. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  679. - name: Enable corepack (pnpm)
  680. shell: pwsh
  681. run: corepack enable
  682. - name: Install (immutable)
  683. shell: pwsh
  684. run: pnpm install --frozen-lockfile
  685. - name: Run complete unsharded primary Node CI serially
  686. shell: pwsh
  687. env:
  688. DSH_COVERAGE_MAX_WORKERS: '1'
  689. DSH_E2E_MAX_WORKERS: '1'
  690. DSH_GATE_CONCURRENCY: '1'
  691. DSH_PUBLINT_CONCURRENCY: '1'
  692. DSH_SNAPSHOT_MAX_CONCURRENCY: '1'
  693. run: pnpm run check:ci
  694. # Manual, bounded comparison of the actual critical Linux and Windows lanes.
  695. # The named pools are restricted at the organization level to this repository.
  696. larger-runner-benchmark:
  697. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'larger-runner-benchmark'
  698. name: larger runner / ${{ matrix.platform }} / ${{ matrix.cores }} cores / ${{ matrix.workload }}
  699. runs-on: ${{ matrix.runner }}
  700. timeout-minutes: 15
  701. strategy:
  702. fail-fast: false
  703. max-parallel: 12
  704. matrix:
  705. include:
  706. - platform: linux
  707. cores: '4'
  708. runner: dsh-ubuntu-24-04-4core
  709. workload: typecheck
  710. - platform: linux
  711. cores: '8'
  712. runner: dsh-ubuntu-24-04-8core
  713. workload: typecheck
  714. - platform: linux
  715. cores: '16'
  716. runner: dsh-ubuntu-24-04-16core
  717. workload: typecheck
  718. - platform: linux
  719. cores: '32'
  720. runner: dsh-ubuntu-24-04-32core
  721. workload: typecheck
  722. - platform: linux
  723. cores: '64'
  724. runner: dsh-ubuntu-24-04-64core
  725. workload: typecheck
  726. - platform: linux
  727. cores: '96'
  728. runner: dsh-ubuntu-24-04-96core
  729. workload: typecheck
  730. - platform: windows
  731. cores: '4'
  732. runner: dsh-windows-2025-4core
  733. workload: production-site
  734. - platform: windows
  735. cores: '8'
  736. runner: dsh-windows-2025-8core
  737. workload: production-site
  738. - platform: windows
  739. cores: '16'
  740. runner: dsh-windows-2025-16core
  741. workload: production-site
  742. - platform: windows
  743. cores: '32'
  744. runner: dsh-windows-2025-32core
  745. workload: production-site
  746. - platform: windows
  747. cores: '64'
  748. runner: dsh-windows-2025-64core
  749. workload: production-site
  750. - platform: windows
  751. cores: '96'
  752. runner: dsh-windows-2025-96core
  753. workload: production-site
  754. steps:
  755. - uses: actions/checkout@v6
  756. - uses: actions/setup-node@v6
  757. with:
  758. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  759. - name: Report runner capacity
  760. run: >-
  761. node -e "const os = require('node:os');
  762. console.log(JSON.stringify({ arch: process.arch, cpus: os.cpus().length,
  763. memoryGiB: Math.round(os.totalmem() / 2 ** 30) }))"
  764. - name: Enable corepack (pnpm)
  765. run: corepack enable
  766. - name: Resolve pnpm store path
  767. if: matrix.platform == 'linux'
  768. id: pnpm-store
  769. run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
  770. - uses: actions/cache@v4
  771. if: matrix.platform == 'linux'
  772. with:
  773. path: ${{ steps.pnpm-store.outputs.path }}
  774. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  775. restore-keys: |
  776. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  777. - name: Install (immutable)
  778. run: pnpm install --frozen-lockfile
  779. - name: Run critical Linux typecheck lane
  780. if: matrix.platform == 'linux'
  781. run: pnpm run typecheck
  782. - name: Run critical Windows production-site lane
  783. if: matrix.platform == 'windows'
  784. run: pnpm run docs:build
  785. # Manual comparison of the intended low-fanout topology. Linux runs the
  786. # complete unsharded primary aggregate with bounded in-runner parallelism;
  787. # Windows runs both blocking build surfaces concurrently through run-gates.
  788. consolidated-runner-benchmark:
  789. if: github.event_name == 'workflow_dispatch' && inputs.suite == 'consolidated-runner-benchmark'
  790. name: consolidated / ${{ matrix.platform }} / ${{ matrix.cores }} cores
  791. runs-on: ${{ matrix.runner }}
  792. timeout-minutes: 15
  793. strategy:
  794. fail-fast: false
  795. max-parallel: 12
  796. matrix:
  797. include:
  798. - platform: linux
  799. cores: '4'
  800. runner: dsh-ubuntu-24-04-4core
  801. workers: '4'
  802. - platform: linux
  803. cores: '8'
  804. runner: dsh-ubuntu-24-04-8core
  805. workers: '8'
  806. - platform: linux
  807. cores: '16'
  808. runner: dsh-ubuntu-24-04-16core
  809. workers: '16'
  810. - platform: linux
  811. cores: '32'
  812. runner: dsh-ubuntu-24-04-32core
  813. workers: '32'
  814. - platform: linux
  815. cores: '64'
  816. runner: dsh-ubuntu-24-04-64core
  817. workers: '32'
  818. - platform: linux
  819. cores: '96'
  820. runner: dsh-ubuntu-24-04-96core
  821. workers: '32'
  822. - platform: windows
  823. cores: '4'
  824. runner: dsh-windows-2025-4core
  825. workers: '2'
  826. - platform: windows
  827. cores: '8'
  828. runner: dsh-windows-2025-8core
  829. workers: '2'
  830. - platform: windows
  831. cores: '16'
  832. runner: dsh-windows-2025-16core
  833. workers: '2'
  834. - platform: windows
  835. cores: '32'
  836. runner: dsh-windows-2025-32core
  837. workers: '2'
  838. - platform: windows
  839. cores: '64'
  840. runner: dsh-windows-2025-64core
  841. workers: '2'
  842. - platform: windows
  843. cores: '96'
  844. runner: dsh-windows-2025-96core
  845. workers: '2'
  846. steps:
  847. - uses: actions/checkout@v6
  848. - uses: actions/setup-node@v6
  849. with:
  850. node-version: ${{ env.PRIMARY_NODE_VERSION }}
  851. - name: Report runner capacity
  852. run: >-
  853. node -e "const os = require('node:os');
  854. console.log(JSON.stringify({ arch: process.arch, cpus: os.cpus().length,
  855. memoryGiB: Math.round(os.totalmem() / 2 ** 30) }))"
  856. - name: Enable corepack (pnpm)
  857. run: corepack enable
  858. - name: Resolve pnpm store path (Linux)
  859. if: matrix.platform == 'linux'
  860. id: pnpm-store-linux
  861. run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
  862. - name: Resolve pnpm store path (Windows)
  863. if: matrix.platform == 'windows'
  864. id: pnpm-store-windows
  865. shell: pwsh
  866. run: '"path=$(pnpm store path --silent)" >> $env:GITHUB_OUTPUT'
  867. - uses: actions/cache@v4
  868. with:
  869. path: ${{ steps.pnpm-store-linux.outputs.path || steps.pnpm-store-windows.outputs.path }}
  870. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
  871. restore-keys: |
  872. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
  873. - uses: actions/cache@v4
  874. if: matrix.platform == 'linux'
  875. with:
  876. path: .cache/eslint
  877. key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.mjs', 'tsconfig.json', 'packages/*/*/tsconfig.json', 'examples/*/tsconfig.json') }}
  878. restore-keys: |
  879. ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full-
  880. - name: Install and prepare Linux
  881. if: matrix.platform == 'linux'
  882. run: |
  883. pnpm install --frozen-lockfile &
  884. install_pid=$!
  885. (
  886. if ! sudo apt-get install -yq --no-install-recommends bubblewrap; then
  887. echo "initial bubblewrap install failed; refreshing APT indexes and retrying"
  888. sudo apt-get update -q
  889. sudo apt-get install -yq --no-install-recommends bubblewrap
  890. fi
  891. sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 \
  892. || echo "apparmor userns knob absent — the functional probe decides"
  893. ) &
  894. sandbox_pid=$!
  895. install_status=0
  896. wait "$install_pid" || install_status=$?
  897. sandbox_status=0
  898. wait "$sandbox_pid" || sandbox_status=$?
  899. if (( install_status != 0 )); then exit "$install_status"; fi
  900. exit "$sandbox_status"
  901. - name: Install (immutable)
  902. if: matrix.platform == 'windows'
  903. shell: pwsh
  904. run: pnpm install --frozen-lockfile
  905. - name: Run complete unsharded primary Node CI concurrently
  906. if: matrix.platform == 'linux'
  907. env:
  908. DSH_COVERAGE_MAX_WORKERS: ${{ matrix.workers }}
  909. DSH_ESLINT_CACHE: '1'
  910. DSH_ESLINT_CONCURRENCY: ${{ matrix.workers }}
  911. DSH_GATE_CONCURRENCY: ${{ matrix.workers }}
  912. DSH_PUBLINT_CONCURRENCY: ${{ matrix.workers }}
  913. DSH_SNAPSHOT_MAX_CONCURRENCY: ${{ matrix.workers }}
  914. run: pnpm run check:ci
  915. - name: Run blocking Windows builds concurrently
  916. if: matrix.platform == 'windows'
  917. shell: pwsh
  918. env:
  919. DSH_GATE_CONCURRENCY: ${{ matrix.workers }}
  920. run: pnpm run check:ci:windows-blocking
  921. # Single stable required check for branch protection: require "all checks
  922. # passed" instead of enumerating matrix legs whose names change as lanes and
  923. # node versions evolve. Every blocking job in THIS workflow must be listed in
  924. # `needs`; observational Windows gates share the required Windows job but are
  925. # marked non-blocking inside run-gates. (`needs` cannot reach across workflow
  926. # files; e2e.yml stays its own check.)
  927. # `if: always()` is load-bearing: without it a failed dependency
  928. # would SKIP this job, and GitHub counts a skipped required check as passing
  929. # — so this job always runs and fails on any non-success result, including
  930. # 'cancelled' and 'skipped'.
  931. all-checks-passed:
  932. name: all checks passed
  933. runs-on: ubuntu-latest
  934. needs: [node-24, node-24-site, node-compat, python-sdk, windows]
  935. if: always() && (github.event_name != 'workflow_dispatch' || inputs.suite == 'optimized-larger-runners')
  936. steps:
  937. - name: Fail if any needed job did not succeed
  938. if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped')
  939. run: |
  940. echo "::error::Needed job results: ${{ join(needs.*.result, ', ') }}"
  941. exit 1
  942. - name: All checks passed
  943. run: echo "All needed jobs succeeded (${{ join(needs.*.result, ', ') }})"