CI: cleanup and fix broken job references

- Fix code-size -> code-analysis job name (5 jobs had wrong dependency)
- Remove useless install-check job (was no-op)
- Add explicit docs_only guard to release-check
- Remove dead submodule checkout steps (no submodules in repo)
- Rename detect-docs-only -> detect-docs-changes, add docs_changed output
- Reorder check script: format first for faster fail
- Fix billing error test (PR #12946 removed fallback detection but not test)
This commit is contained in:
quotentiroler
2026-02-09 17:52:51 -08:00
parent 64cf50dfc3
commit 039aaf176e
6 changed files with 81 additions and 100 deletions

View File

@@ -8,6 +8,9 @@ outputs:
docs_only:
description: "'true' if all changes are docs/markdown, 'false' otherwise"
value: ${{ steps.check.outputs.docs_only }}
docs_changed:
description: "'true' if any changed file is under docs/ or is markdown"
value: ${{ steps.check.outputs.docs_changed }}
runs:
using: composite
@@ -28,9 +31,18 @@ runs:
CHANGED=$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "UNKNOWN")
if [ "$CHANGED" = "UNKNOWN" ] || [ -z "$CHANGED" ]; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Check if any changed file is a doc
DOCS=$(echo "$CHANGED" | grep -E '^docs/|\.md$|\.mdx$' || true)
if [ -n "$DOCS" ]; then
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
else
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
fi
# Check if all changed files are docs or markdown
NON_DOCS=$(echo "$CHANGED" | grep -vE '^docs/|\.md$|\.mdx$' || true)
if [ -z "$NON_DOCS" ]; then

View File

@@ -16,6 +16,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
docs_changed: ${{ steps.check.outputs.docs_changed }}
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -25,7 +26,7 @@ jobs:
- name: Detect docs-only changes
id: check
uses: ./.github/actions/detect-docs-only
uses: ./.github/actions/detect-docs-changes
# Detect which heavy areas are touched so PRs can skip unrelated expensive jobs.
# Push to main keeps broad coverage.
@@ -120,7 +121,7 @@ jobs:
# Build dist once for Node-relevant changes and share it with downstream jobs.
build-artifacts:
needs: [docs-scope, changed-scope, code-size, check-lint]
needs: [docs-scope, changed-scope, code-analysis, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
@@ -144,9 +145,10 @@ jobs:
path: dist/
retention-days: 1
install-check:
needs: [docs-scope, changed-scope, code-size, check-lint]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
# Validate npm pack contents after build.
release-check:
needs: [docs-scope, build-artifacts]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
@@ -159,8 +161,17 @@ jobs:
with:
install-bun: "false"
- name: Download dist artifact
uses: actions/download-artifact@v4
with:
name: dist-build
path: dist/
- name: Check release contents
run: pnpm release:check
checks:
needs: [docs-scope, changed-scope, code-size, check-lint]
needs: [docs-scope, changed-scope, code-analysis, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
@@ -188,9 +199,9 @@ jobs:
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
run: ${{ matrix.command }}
# Format check — cheapest gate (~43s). Always runs, even on docs-only changes.
check-format:
name: "check: format"
# Types, lint, and format check.
check:
name: "check"
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
@@ -201,31 +212,30 @@ jobs:
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
- name: Check formatting
run: pnpm format:check
# Lint check — runs after format passes for cleaner output.
check-lint:
name: "check: lint"
needs: [check-format]
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
- name: Check types and lint
- name: Check types and lint and oxfmt
run: pnpm check
# Validate docs (format, lint, broken links) only when docs files changed.
check-docs:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_changed == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
- name: Check docs
run: pnpm check:docs
# Check for files that grew past LOC threshold in this PR (delta-only).
# On push events, all steps are skipped and the job passes (no-op).
# Heavy downstream jobs depend on this to fail fast on violations.
code-size:
needs: [check-format]
code-analysis:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
@@ -279,7 +289,7 @@ jobs:
fi
checks-windows:
needs: [docs-scope, changed-scope, build-artifacts, code-size, check-lint]
needs: [docs-scope, changed-scope, build-artifacts, code-analysis, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-4vcpu-windows-2025
env:
@@ -328,19 +338,6 @@ jobs:
Write-Warning "Failed to apply Defender exclusions, continuing. $($_.Exception.Message)"
}
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Download dist artifact (lint lane)
if: matrix.task == 'lint'
uses: actions/download-artifact@v4
@@ -400,7 +397,7 @@ jobs:
# running 4 separate jobs per PR (as before) starved the queue. One job
# per PR allows 5 PRs to run macOS checks simultaneously.
macos:
needs: [docs-scope, changed-scope, code-size, check-lint]
needs: [docs-scope, changed-scope, code-analysis, check]
if: github.event_name == 'pull_request' && needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_macos == 'true'
runs-on: macos-latest
steps:
@@ -481,19 +478,6 @@ jobs:
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Select Xcode 26.1
run: |
sudo xcode-select -s /Applications/Xcode_26.1.app
@@ -646,7 +630,7 @@ jobs:
PY
android:
needs: [docs-scope, changed-scope, code-size, check-lint]
needs: [docs-scope, changed-scope, code-analysis, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_android == 'true')
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
@@ -663,19 +647,6 @@ jobs:
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Java
uses: actions/setup-java@v4
with:

View File

@@ -23,7 +23,7 @@ jobs:
- name: Detect docs-only changes
id: check
uses: ./.github/actions/detect-docs-only
uses: ./.github/actions/detect-docs-changes
install-smoke:
needs: [docs-scope]

View File

@@ -19,10 +19,10 @@ Tier 1 — Cheapest gates (parallel, ~43 s)
check-format secrets
Tier 2 — After format (parallel, ~2 min)
check-lint code-size
check-lint code-analysis
Tier 3 — Build (~3 min)
build-artifacts install-check
build-artifacts release-check
Tier 4 — Tests (~5 min)
checks (node tsgo / test / protocol, bun test)
@@ -39,8 +39,8 @@ Tier 5 — Platform (most expensive)
```
docs-scope ──► changed-scope ──┐
check-format ──► check-lint ──►├──► build-artifacts ──► checks-windows
├─► code-size ──►├──► install-check
check-format ──► check-lint ──►├──► build-artifacts ──► release-check
├─► code-analysis ►│ └──► checks-windows
├──► checks
├──► macos
└──► android
@@ -65,37 +65,37 @@ secrets (independent)
### Tier 2 — After Format
| Job | Runner | Depends on | Purpose |
| ------------ | ----------------- | -------------- | ----------------------------------------------------------- |
| `check-lint` | Blacksmith 4 vCPU | `check-format` | Runs `pnpm lint` — cleaner output after format passes |
| `code-size` | Blacksmith 4 vCPU | `check-format` | Checks LOC thresholds — accurate counts need formatted code |
| Job | Runner | Depends on | Purpose |
| --------------- | ----------------- | -------------- | ----------------------------------------------------------- |
| `check-lint` | Blacksmith 4 vCPU | `check-format` | Runs `pnpm lint` — cleaner output after format passes |
| `code-analysis` | Blacksmith 4 vCPU | `check-format` | Checks LOC thresholds — accurate counts need formatted code |
### Tier 3 — Build
| Job | Runner | Depends on | Purpose |
| ----------------- | ----------------- | ------------------------- | ------------------------------------- |
| `build-artifacts` | Blacksmith 4 vCPU | `check-lint`, `code-size` | Builds dist and uploads artifact |
| `install-check` | Blacksmith 4 vCPU | `check-lint`, `code-size` | Verifies `pnpm install` works cleanly |
| Job | Runner | Depends on | Purpose |
| ----------------- | ----------------- | ----------------------------- | -------------------------------- |
| `build-artifacts` | Blacksmith 4 vCPU | `check-lint`, `code-analysis` | Builds dist and uploads artifact |
| `release-check` | Blacksmith 4 vCPU | `build-artifacts` | Validates npm pack contents |
### Tier 4+ — Tests and Platform
| Job | Runner | Depends on | Purpose |
| ---------------- | ------------------ | -------------------------------------------- | ------------------------------------------------------ |
| `checks` | Blacksmith 4 vCPU | `check-lint`, `code-size` | TypeScript checks, tests (Node + Bun), protocol checks |
| `checks-windows` | Blacksmith Windows | `build-artifacts`, `check-lint`, `code-size` | Windows-specific lint, tests, protocol checks |
| `macos` | `macos-latest` | `check-lint`, `code-size` | TS tests + Swift lint/build/test (PR only) |
| `android` | Blacksmith 4 vCPU | `check-lint`, `code-size` | Gradle test + build |
| Job | Runner | Depends on | Purpose |
| ---------------- | ------------------ | ------------------------------------------------ | ------------------------------------------------------ |
| `checks` | Blacksmith 4 vCPU | `check-lint`, `code-analysis` | TypeScript checks, tests (Node + Bun), protocol checks |
| `checks-windows` | Blacksmith Windows | `build-artifacts`, `check-lint`, `code-analysis` | Windows-specific lint, tests, protocol checks |
| `macos` | `macos-latest` | `check-lint`, `code-analysis` | TS tests + Swift lint/build/test (PR only) |
| `android` | Blacksmith 4 vCPU | `check-lint`, `code-analysis` | Gradle test + build |
## Code-Size Gate
## Code-Analysis Gate
The `code-size` job runs `scripts/analyze_code_files.py` on PRs to catch:
The `code-analysis` job runs `scripts/analyze_code_files.py` on PRs to catch:
1. **Threshold crossings** — files that grew past 1000 lines in the PR
2. **Already-large files growing** — files already over 1000 lines that got bigger
3. **Duplicate function regressions** — new duplicate functions introduced by the PR
When `--strict` is set, any violation fails the job and blocks all downstream
work. On push to `main`, the code-size steps are skipped (the job passes as a
work. On push to `main`, the code-analysis steps are skipped (the job passes as a
no-op) so pushes still run the full test suite.
### Excluded Directories
@@ -109,26 +109,25 @@ The analysis skips: `node_modules`, `dist`, `vendor`, `.git`, `coverage`,
**Bad PR (formatting violations):**
- `check-format` fails at ~43 s
- `check-lint`, `code-size`, and all downstream jobs never start
- `check-lint`, `code-analysis`, and all downstream jobs never start
- Total cost: ~1 runner-minute
**Bad PR (lint or LOC violations, good format):**
- `check-format` passes → `check-lint` and `code-size` run in parallel
- `check-format` passes → `check-lint` and `code-analysis` run in parallel
- One or both fail → all downstream jobs skipped
- Total cost: ~3 runner-minutes
**Good PR:**
- Critical path: `check-format` (43 s) → `check-lint` (1m 46 s) → `build-artifacts``checks`
- `code-size` runs in parallel with `check-lint`, adding no latency
- `code-analysis` runs in parallel with `check-lint`, adding no latency
## Composite Action
The `setup-node-env` composite action (`.github/actions/setup-node-env/`)
handles the shared setup boilerplate:
- Submodule init/update with retry (5 attempts, exponential backoff)
- Node.js 22 setup
- pnpm via corepack + store cache
- Optional Bun install
@@ -141,7 +140,7 @@ This eliminates ~40 lines of duplicated YAML per job.
## Push vs PR Behavior
| Trigger | `code-size` | Downstream jobs |
| Trigger | `code-analysis` | Downstream jobs |
| -------------- | ----------------------------- | --------------------- |
| Push to `main` | Steps skipped (job passes) | Run normally |
| Pull request | Full analysis with `--strict` | Blocked on violations |

View File

@@ -35,7 +35,7 @@
"build": "pnpm canvas:a2ui:bundle && tsdown && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-compat.ts",
"build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json",
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
"check": "pnpm tsgo && pnpm lint && pnpm format:check",
"check": "pnpm format:check && pnpm tsgo && pnpm lint",
"check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:build",
"check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500",
"dev": "node scripts/run-node.mjs",

View File

@@ -17,7 +17,6 @@ describe("isBillingErrorMessage", () => {
"Payment Required",
"HTTP 402 Payment Required",
"plans & billing",
"billing: please upgrade your plan",
];
for (const sample of samples) {
expect(isBillingErrorMessage(sample)).toBe(true);