Browse Source

ci: parallelize github workflows

Tianyi Cui 2 months ago
parent
commit
80358aed9f
2 changed files with 238 additions and 71 deletions
  1. 227 71
      .github/workflows/ci.yml
  2. 11 0
      .github/workflows/e2e.yml

+ 227 - 71
.github/workflows/ci.yml

@@ -9,96 +9,252 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+permissions:
+  contents: read
+
+env:
+  PRIMARY_NODE_VERSION: '24'
+
 jobs:
-  checks:
+  quality:
     runs-on: ubuntu-latest
+    name: quality / ${{ matrix.name }}
     strategy:
       fail-fast: false
       matrix:
-        node: [24, 26]
-    name: node ${{ matrix.node }}
+        include:
+          - name: constraints
+            command: pnpm run constraints
+          - name: typecheck
+            command: pnpm run typecheck
+          - name: lint
+            command: |
+              # Root typecheck validates the package/vendor reference graph and
+              # refreshes TSC intermediates before type-aware ESLint loads the
+              # project graph.
+              pnpm run typecheck
+              NODE_OPTIONS=--max-old-space-size=8192 pnpm run lint
+          - name: coverage
+            command: pnpm run test:coverage
+          - name: snapshots
+            command: pnpm run test:snapshot
+          - name: demo smoke
+            command: |
+              set -euo pipefail
+              out=$(printf 'echo ci smoke\n' | timeout 60 pnpm run demo:echo 2>&1)
+              echo "$out"
+              echo "$out" | grep -q '\[tool call\] echo({"text":"ci smoke"})'
+              echo "$out" | grep -q '\[tool result\] ECHO: CI SMOKE'
+              # The JSONL backend (root ./.sessions, no cwd -> _no-cwd bucket)
+              # writes a per-run session log named main-session-<uuid>.jsonl.
+              ls .sessions/_no-cwd/main-session-*.jsonl >/dev/null
+              rm -rf .sessions
     steps:
       - uses: actions/checkout@v6
 
       - uses: actions/setup-node@v6
         with:
-          node-version: ${{ matrix.node }}
+          node-version: ${{ env.PRIMARY_NODE_VERSION }}
 
       - name: Enable corepack (pnpm)
         run: corepack enable
 
+      - name: Resolve pnpm store path
+        id: pnpm-store
+        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/cache@v4
+        with:
+          path: ${{ steps.pnpm-store.outputs.path }}
+          key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
+
       - name: Install (immutable)
         run: pnpm install --frozen-lockfile
 
-      - name: Constraints
-        run: pnpm run constraints
+      - name: Run ${{ matrix.name }}
+        run: ${{ matrix.command }}
 
-      # Before lint: root typecheck validates the package/vendor reference graph
-      # and refreshes TSC intermediates so type-aware ESLint sees the same project
-      # boundaries as the build.
-      - name: Typecheck (src + tests + examples)
-        run: pnpm run typecheck
+  docs:
+    runs-on: ubuntu-latest
+    name: docs / ${{ matrix.name }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: doc-typecheck
+            command: pnpm run doc-typecheck
+          - name: cordis catalog
+            command: pnpm run verify-cordis-catalog
+          - name: tool catalog
+            command: pnpm run verify-tool-catalog
+          - name: persistence catalog
+            command: pnpm run verify-persistence-catalog
+          - name: doc graphs
+            command: pnpm run verify-doc-graphs
+          - name: markdown wrap
+            command: pnpm run verify-md-wrap
+          - name: markdown links
+            command: pnpm run verify-md-links
+          - name: doc refs
+            command: pnpm run verify-doc-refs
+          - name: package paths
+            command: pnpm run verify-package-paths
+          - name: mermaid
+            command: pnpm run verify-mermaid
+          - name: rfc classification
+            command: pnpm run verify-rfc-classification
+          - name: type equivalence
+            command: pnpm run verify-type-equiv
+          - name: translation pairing
+            command: pnpm run verify-translation-pairing
+          - name: doc budgets
+            command: pnpm run verify-doc-budgets
+          - name: module graph
+            command: pnpm run verify-module-graph
+    steps:
+      - uses: actions/checkout@v6
+
+      - uses: actions/setup-node@v6
+        with:
+          node-version: ${{ env.PRIMARY_NODE_VERSION }}
+
+      - name: Enable corepack (pnpm)
+        run: corepack enable
+
+      - name: Resolve pnpm store path
+        id: pnpm-store
+        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/cache@v4
+        with:
+          path: ${{ steps.pnpm-store.outputs.path }}
+          key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
+
+      - name: Install (immutable)
+        run: pnpm install --frozen-lockfile
+
+      - name: Run ${{ matrix.name }}
+        run: ${{ matrix.command }}
+
+  build:
+    runs-on: ubuntu-latest
+    name: build
+    steps:
+      - uses: actions/checkout@v6
+
+      - uses: actions/setup-node@v6
+        with:
+          node-version: ${{ env.PRIMARY_NODE_VERSION }}
+
+      - name: Enable corepack (pnpm)
+        run: corepack enable
+
+      - name: Resolve pnpm store path
+        id: pnpm-store
+        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/cache@v4
+        with:
+          path: ${{ steps.pnpm-store.outputs.path }}
+          key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
+
+      - name: Install (immutable)
+        run: pnpm install --frozen-lockfile
 
-      # Type-aware ESLint loads every package tsconfig through the project
-      # service and peaks at ~3.4GB; the default V8 old-space ceiling (~2GB)
-      # OOMs it (exit 134). Raise the ceiling well above the peak.
-      - name: Lint
-        run: pnpm run lint
-        env:
-          NODE_OPTIONS: --max-old-space-size=8192
-
-      # Doc-sync gates (doc-sync-enforcement RFC). doc-typecheck compiles the
-      # fenced ts blocks against the root project-reference graph. The cordis
-      # catalog freshness check, type-equiv check, Mermaid syntax check, and
-      # markdown wrap/link checks only read source. Same `doc-sync` script the pre-push hook runs
-      # (quality-gates RFC: one source of truth).
-      - name: Doc-sync gates (doc code blocks + catalogs + mermaid + markdown)
-        run: pnpm run doc-sync
-
-      # Module-graph freshness: regenerate docs/module-graph.md from the
-      # packages' peerDependencies and fail if it differs from the committed
-      # file. Only reads source package.json — no build needed.
-      - name: Module-graph freshness
-        run: pnpm run verify-module-graph
-
-      - name: Tests with coverage gate (per-file 100%)
-        run: pnpm run test:coverage
-
-      # ACP snapshot tests (acp-snapshot-tests RFC): boot the real acp-agent
-      # subprocess and replay recorded session-log fixtures, diffing the
-      # normalized stdout transcript + re-persisted log against committed
-      # goldens. KEYLESS by design — the same `test:snapshot` script the pre-push
-      # hook runs (one source of truth), so the full-transcript regression net
-      # is part of every PR gate, not just local pre-push.
-      - name: Snapshot tests (ACP transcript replay)
-        run: pnpm run test:snapshot
-
-      # Before hygiene: publint validates the packed artifacts (lib/index.js),
-      # which only the tsdown bundling step emits, and verify-node-next-types
-      # validates the built declarations.
       - name: Build (tsc -b + tsdown bundles)
         run: pnpm run build
 
-      - name: Hygiene (knip + publint + constraints + NodeNext types)
-        run: pnpm run hygiene
-
-      - name: Demo smoke test
-        run: |
-          set -euo pipefail
-          out=$(printf 'echo ci smoke\n' | timeout 60 pnpm run demo:echo 2>&1)
-          echo "$out"
-          echo "$out" | grep -q '\[tool call\] echo({"text":"ci smoke"})'
-          echo "$out" | grep -q '\[tool result\] ECHO: CI SMOKE'
-          # The JSONL backend (root ./.sessions, no cwd → _no-cwd bucket) writes a
-          # per-run session log named main-session-<uuid>.jsonl. Assert one exists.
-          ls .sessions/_no-cwd/main-session-*.jsonl >/dev/null
-          rm -rf .sessions
-
-      # The published `bin` is `lib/bin.js`, run under plain `node` by a real
-      # consumer — NOT the tsx dev path the demo smoke and demo:* scripts use.
-      # These keyless smokes boot the BUILT bins (this step runs AFTER the build)
-      # in a temp dir that mirrors a real install, catching a regression in the
-      # published artifact that tsx would mask. They self-skip if lib/ is absent,
-      # so the e2e job (which does not build) does not run them.
-      - name: Built-bin smoke test (published lib/bin.js under node)
-        run: pnpm exec vitest run --config vitest.e2e.config.ts packages/ui/stdio-agent/tests/built-bin.e2e.ts packages/ui/acp-agent/tests/built-bin.e2e.ts
+      - uses: actions/upload-artifact@v4
+        with:
+          name: build-output
+          path: |
+            packages/*/*/lib
+            vendor/*/lib
+          if-no-files-found: error
+          retention-days: 1
+
+  artifact-gate:
+    runs-on: ubuntu-latest
+    name: artifact / ${{ matrix.name }}
+    needs: build
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: hygiene
+            command: pnpm run hygiene
+          - name: built-bin smoke
+            command: pnpm exec vitest run --config vitest.e2e.config.ts packages/ui/stdio-agent/tests/built-bin.e2e.ts packages/ui/acp-agent/tests/built-bin.e2e.ts
+    steps:
+      - uses: actions/checkout@v6
+
+      - uses: actions/setup-node@v6
+        with:
+          node-version: ${{ env.PRIMARY_NODE_VERSION }}
+
+      - name: Enable corepack (pnpm)
+        run: corepack enable
+
+      - name: Resolve pnpm store path
+        id: pnpm-store
+        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/cache@v4
+        with:
+          path: ${{ steps.pnpm-store.outputs.path }}
+          key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
+
+      - name: Install (immutable)
+        run: pnpm install --frozen-lockfile
+
+      - uses: actions/download-artifact@v4
+        with:
+          name: build-output
+
+      - name: Run ${{ matrix.name }}
+        run: ${{ matrix.command }}
+
+  node-compat:
+    runs-on: ubuntu-latest
+    name: node ${{ matrix.node }} compatibility
+    strategy:
+      fail-fast: false
+      matrix:
+        node: [26]
+    steps:
+      - uses: actions/checkout@v6
+
+      - uses: actions/setup-node@v6
+        with:
+          node-version: ${{ matrix.node }}
+
+      - name: Enable corepack (pnpm)
+        run: corepack enable
+
+      - name: Resolve pnpm store path
+        id: pnpm-store
+        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/cache@v4
+        with:
+          path: ${{ steps.pnpm-store.outputs.path }}
+          key: ${{ runner.os }}-node-${{ matrix.node }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-node-${{ matrix.node }}-pnpm-
+
+      - name: Install (immutable)
+        run: pnpm install --frozen-lockfile
+
+      - name: Typecheck (src + tests + examples)
+        run: pnpm run typecheck
+
+      - name: Unit tests
+        run: pnpm run test

+ 11 - 0
.github/workflows/e2e.yml

@@ -67,6 +67,17 @@ jobs:
       - name: Enable corepack (pnpm)
         run: corepack enable
 
+      - name: Resolve pnpm store path
+        id: pnpm-store
+        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/cache@v4
+        with:
+          path: ${{ steps.pnpm-store.outputs.path }}
+          key: ${{ runner.os }}-node-24-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-node-24-pnpm-
+
       - name: Install (immutable)
         run: pnpm install --frozen-lockfile