From 80358aed9f020b30d978a24d99ee6aad8f0100a5 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:15:19 +0800 Subject: [PATCH] ci: parallelize github workflows --- .github/workflows/ci.yml | 300 +++++++++++++++++++++++++++++--------- .github/workflows/e2e.yml | 11 ++ 2 files changed, 239 insertions(+), 72 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4212717339..f8b7d4f41b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,14 +9,226 @@ 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-.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: ${{ 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 }} + + 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 + + - name: Build (tsc -b + tsdown bundles) + run: pnpm run build + + - 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 @@ -27,78 +239,22 @@ 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-${{ matrix.node }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node }}-pnpm- + - name: Install (immutable) run: pnpm install --frozen-lockfile - - name: Constraints - run: pnpm run constraints - - # 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 - # 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-.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 + - name: Unit tests + run: pnpm run test diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 7a8c9dc88c..ace789915b 100644 --- a/.github/workflows/e2e.yml +++ b/.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