Repository navigation
fix(ci): align Node requirements and dependency versions #1075
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, master, feature/**] | |
| pull_request: | |
| branches: [main, master, feature/**, cursor/vite-plus-toolchain-cdbd] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| statuses: read | |
| jobs: | |
| coverage: | |
| name: Coverage generators and drift checks | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .tool-versions | |
| - name: Test coverage generators and verify generated artifacts | |
| run: npm run test:coverage | |
| toolchain: | |
| name: Vite+ monorepo check | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .tool-versions | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install workspace dependencies | |
| run: npm ci | |
| - name: Check agent trust guards and generated OpenAPI artifacts | |
| run: npm run check:agent-trust | |
| - name: Check root and every workspace | |
| run: npx vp run check | |
| - name: Test every workspace | |
| run: npx vp run test | |
| - name: Build every workspace | |
| run: npx vp run build | |
| # Consumer compatibility: the SDK on every supported Node version. npm ci | |
| # from the SDK directory installs from the root workspace lockfile. | |
| sdk: | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node-version: [20, 22, 24] | |
| defaults: | |
| run: | |
| working-directory: sdks/typescript-sdk | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install SDK deps | |
| run: npm ci | |
| - name: Build SDK | |
| run: npm run build | |
| - name: Type-check SDK | |
| run: npm run type-check | |
| - name: Import the built SDK | |
| run: node --input-type=module -e "const sdk = await import('@terminal49/sdk'); if (typeof sdk.Terminal49Client !== 'function') throw new Error('Terminal49Client export missing');" | |
| # Vitest 5 and Vite+ 1.x support Node 22.12+ only, so the Node 20 leg | |
| # proves install, build, types, and import, and leaves tests to 22+. | |
| - name: Test SDK | |
| if: matrix.node-version != 20 | |
| run: npm test -- --run | |
| # Development tooling: root workspace lockfile on the development runtime. | |
| # Lint/format/docs are static checks, so one modern Node version suffices. | |
| sdk-tooling: | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .tool-versions | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install workspace deps | |
| run: npm ci | |
| - name: Build SDK | |
| run: npm run build --workspace @terminal49/sdk | |
| - name: Generate SDK docs | |
| run: npm run docs --workspace @terminal49/sdk | |
| - name: Check generated SDK docs are up to date | |
| run: | | |
| git diff --exit-code -- docs/sdk/reference | |
| test -z "$(git status --porcelain -- docs/sdk/reference)" | |
| - name: Check SDK (Vite+ and anti-slop) | |
| run: npm run lint --workspace @terminal49/sdk | |
| cli: | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node-version: [20, 22, 24] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install workspace deps | |
| run: npm ci | |
| - name: Build SDK (workspace dependency) | |
| run: npm run build --workspace @terminal49/sdk | |
| - name: Build CLI | |
| run: npm run build --workspace @terminal49/cli | |
| - name: Type-check CLI | |
| run: npm run type-check --workspace @terminal49/cli | |
| - name: Run the built CLI | |
| run: node sdks/typescript-sdk-cli/dist/bin/t49.js --help | |
| # Vitest 5 and Vite+ 1.x support Node 22.12+ only, so the Node 20 leg | |
| # proves install, build, types, and the binary, and leaves tests to 22+. | |
| - name: Test CLI | |
| if: matrix.node-version != 20 | |
| run: npm run test --workspace @terminal49/cli | |
| # Vite+ linting is static, so it only needs the development runtime. | |
| - name: Check CLI (Vite+ and anti-slop) | |
| if: matrix.node-version == 24 | |
| run: npm run lint --workspace @terminal49/cli | |
| mcp: | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .tool-versions | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install MCP deps (published SDK) | |
| run: npm ci | |
| - name: Build SDK (workspace dependency) | |
| run: npm run build --workspace @terminal49/sdk | |
| - name: Verify generated MCP filter schemas | |
| run: | | |
| npm run generate:filters --workspace @terminal49/mcp | |
| git diff --exit-code -- packages/mcp/src/generated | |
| test -z "$(git status --porcelain -- packages/mcp/src/generated)" | |
| - name: Build MCP | |
| run: npm run build --workspace @terminal49/mcp | |
| - name: Type-check SDK + MCP | |
| run: | | |
| npm run type-check --workspace @terminal49/sdk | |
| npm run type-check --workspace @terminal49/mcp | |
| - name: Type-check MCP eval suite | |
| run: npm run eval:check --workspace @terminal49/mcp | |
| - name: Test MCP | |
| run: npm run test --workspace @terminal49/mcp -- --run --coverage | |
| - name: Eval MCP tools against production (live; skips without MCP_EVAL_TOKEN secret) | |
| if: github.event_name == 'push' | |
| env: | |
| MCP_EVAL_TOKEN: ${{ secrets.MCP_EVAL_TOKEN }} | |
| run: npm run eval --workspace @terminal49/mcp | |
| - name: Check MCP (Vite+ and anti-slop) | |
| run: npm run lint --workspace @terminal49/mcp | |
| - name: Check API gateway (Vite+ and anti-slop) | |
| run: npm run lint:api | |
| mcp-protocol-compat: | |
| name: MCP protocol ${{ matrix.protocol-version }} | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| protocol-version: | |
| - '2026-07-28' | |
| - '2025-11-25' | |
| - '2025-06-18' | |
| - '2025-03-26' | |
| - '2024-11-05' | |
| - '2024-10-07' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .tool-versions | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install workspace dependencies | |
| run: npm ci | |
| - name: Build SDK dependency | |
| run: npm run build --workspace @terminal49/sdk | |
| - name: Build MCP server | |
| run: npm run build --workspace @terminal49/mcp | |
| - name: POST protocol handshake to built MCP server | |
| env: | |
| MCP_PROTOCOL_VERSION: ${{ matrix.protocol-version }} | |
| run: npm run test:protocol --workspace @terminal49/mcp | |
| mcp-preview-protocol: | |
| name: MCP preview ${{ matrix.protocol-version }} | |
| if: >- | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.full_name == github.repository && | |
| github.actor != 'dependabot[bot]' | |
| needs: [mcp, mcp-protocol-compat] | |
| runs-on: ${{ (startsWith(vars.CI_RUNNER, 'blacksmith-') && vars.CI_RUNNER) || 'blacksmith-4vcpu-ubuntu-2404' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| protocol-version: | |
| - '2026-07-28' | |
| - '2025-11-25' | |
| steps: | |
| - name: Check preview credential availability | |
| id: credential | |
| env: | |
| MCP_EVAL_TOKEN: ${{ secrets.MCP_EVAL_TOKEN }} | |
| run: | | |
| if [[ -n "$MCP_EVAL_TOKEN" ]]; then | |
| echo "available=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "available=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Skipping authenticated preview smoke because MCP_EVAL_TOKEN is unavailable" | |
| fi | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v6 | |
| if: steps.credential.outputs.available == 'true' | |
| with: | |
| node-version-file: .tool-versions | |
| cache: 'npm' | |
| cache-dependency-path: package-lock.json | |
| - name: Install workspace dependencies | |
| if: steps.credential.outputs.available == 'true' | |
| run: npm ci | |
| - name: Wait for this commit's Vercel preview | |
| id: vercel | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PREVIEW_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| state=pending | |
| for attempt in {1..60}; do | |
| state="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${PREVIEW_SHA}/status" \ | |
| --jq '[.statuses[] | select(.context == "Vercel")][0].state // "pending"')" | |
| if [[ "$state" == "success" ]]; then | |
| break | |
| fi | |
| if [[ "$state" == "failure" || "$state" == "error" ]]; then | |
| echo "Vercel preview failed for ${PREVIEW_SHA}" | |
| exit 1 | |
| fi | |
| sleep 10 | |
| done | |
| if [[ "$state" != "success" ]]; then | |
| echo "Timed out waiting for Vercel preview for ${PREVIEW_SHA}" | |
| exit 1 | |
| fi | |
| inspector_url="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${PREVIEW_SHA}/status" \ | |
| --jq '[.statuses[] | select(.context == "Vercel")][0].target_url // empty')" | |
| if [[ -z "$inspector_url" ]]; then | |
| echo "Vercel status for ${PREVIEW_SHA} has no deployment URL" | |
| exit 1 | |
| fi | |
| echo "inspector-url=${inspector_url}" >> "$GITHUB_OUTPUT" | |
| - name: Resolve Vercel preview endpoint | |
| id: preview | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| VERCEL_INSPECTOR_URL: ${{ steps.vercel.outputs.inspector-url }} | |
| run: | | |
| preview_url="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ | |
| --paginate --slurp | jq -r --arg inspector "$VERCEL_INSPECTOR_URL" \ | |
| 'flatten | [.[] | select((.user.login == "vercel[bot]" or .user.login == "vercel") and (.body | contains($inspector)))][-1].body // "" | (try capture("\\[Preview\\]\\((?<url>https://[^)]+\\.vercel\\.app)\\)") catch {}) | .url // empty')" | |
| if [[ -z "$preview_url" ]]; then | |
| echo "Could not resolve a Vercel preview URL for ${VERCEL_INSPECTOR_URL}" | |
| exit 1 | |
| fi | |
| echo "endpoint=${preview_url}/mcp" >> "$GITHUB_OUTPUT" | |
| - name: Verify authorization server advertises refresh support | |
| if: matrix.protocol-version == '2026-07-28' | |
| env: | |
| AUTHORIZATION_SERVER_METADATA_URL: https://mcp.terminal49.com/.well-known/oauth-authorization-server | |
| run: | | |
| curl --fail --silent --show-error --location --max-time 30 \ | |
| "$AUTHORIZATION_SERVER_METADATA_URL" \ | |
| | jq --exit-status ' | |
| (.grant_types_supported // [] | index("refresh_token")) != null | |
| and | |
| (.scopes_supported // [] | index("offline_access")) != null | |
| ' > /dev/null | |
| - name: POST handshake and tools/list to Vercel preview | |
| if: steps.credential.outputs.available == 'true' | |
| env: | |
| MCP_HTTP_ENDPOINT: ${{ steps.preview.outputs.endpoint }} | |
| MCP_HTTP_TOKEN: ${{ secrets.MCP_EVAL_TOKEN }} | |
| MCP_PROTOCOL_VERSION: ${{ matrix.protocol-version }} | |
| run: npm run test:http-protocol --workspace @terminal49/mcp | |
| - name: Eval MCP tools against Vercel preview | |
| if: steps.credential.outputs.available == 'true' && matrix.protocol-version == '2026-07-28' | |
| env: | |
| MCP_EVAL_ENDPOINT: ${{ steps.preview.outputs.endpoint }} | |
| MCP_EVAL_TOKEN: ${{ secrets.MCP_EVAL_TOKEN }} | |
| run: npm run eval --workspace @terminal49/mcp | |
| - name: Verify preview still belongs to this commit | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| VERCEL_INSPECTOR_URL: ${{ steps.vercel.outputs.inspector-url }} | |
| run: | | |
| matches="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ | |
| --paginate --slurp | jq -r --arg inspector "$VERCEL_INSPECTOR_URL" \ | |
| 'flatten | [.[] | select((.user.login == "vercel[bot]" or .user.login == "vercel") and (.body | contains($inspector)))] | length')" | |
| if [[ "$matches" != "1" ]]; then | |
| echo "Vercel preview changed while the smoke test was running" | |
| exit 1 | |
| fi |