From 60029fd4204cddfcf4da81c734de23d02f4b967c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 05:54:07 +0000 Subject: [PATCH 1/3] ci: cut runner queueing and add caching across workflows - ci.yml: concurrency group cancels superseded PR runs; e2e no longer waits on Typecheck; cache Playwright browsers and the Docusaurus build. - desktop.yml: on PRs, golden runs on ubuntu only and perf is skipped, which frees macOS/Windows runners; main keeps the full matrix. - setup-desktop: Rust caches are saved only from main/tags so PR saves stop evicting them from the 10 GB quota; npm cache keyable on extra lockfiles. - cloud.yml: npm cache covers web/ and supabase/functions lockfiles; cache Deno modules. deploy-web.yml: cache the Docusaurus build. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017djARXypsDMLN1BvJ2GQva --- .github/actions/setup-desktop/action.yml | 9 ++++++- .github/workflows/ci.yml | 31 ++++++++++++++++++++++-- .github/workflows/cloud.yml | 11 +++++++++ .github/workflows/deploy-web.yml | 9 +++++++ .github/workflows/desktop.yml | 5 +++- docs/decisions/performance-budgets.md | 3 ++- docs/decisions/windows-support.md | 5 +++- 7 files changed, 67 insertions(+), 6 deletions(-) diff --git a/.github/actions/setup-desktop/action.yml b/.github/actions/setup-desktop/action.yml index 335db731..ae360eab 100644 --- a/.github/actions/setup-desktop/action.yml +++ b/.github/actions/setup-desktop/action.yml @@ -6,6 +6,9 @@ inputs: cache-key: description: Shared rust-cache key (jobs that build the same artifacts share one). default: desktop + npm-cache-paths: + description: Lockfiles that key the npm cache (add web/ or supabase/ ones when the job installs them). + default: package-lock.json runs: using: composite steps: @@ -13,7 +16,7 @@ runs: with: node-version: lts/* cache: npm - cache-dependency-path: package-lock.json + cache-dependency-path: ${{ inputs.npm-cache-paths }} - uses: dtolnay/rust-toolchain@stable @@ -21,6 +24,10 @@ runs: with: workspaces: src-tauri -> target shared-key: ${{ inputs.cache-key }} + # Only main writes Rust caches: PR saves (one per key per OS) evicted + # them from the 10 GB repo quota. PRs restore main's cache. + save-if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} + cache-on-failure: true - name: Install Linux system dependencies if: runner.os == 'Linux' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4153a1d2..9419f8d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,10 @@ on: permissions: contents: read +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: verify: name: Typecheck @@ -38,14 +42,15 @@ jobs: e2e: name: End-to-end tests runs-on: ubuntu-latest - needs: verify steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: lts/* cache: npm - cache-dependency-path: web/package-lock.json + cache-dependency-path: | + web/package-lock.json + supabase/functions/package-lock.json # Same CLI pin as cloud.yml. - uses: supabase/setup-cli@v1 with: @@ -67,12 +72,34 @@ jobs: - name: Install dependencies working-directory: web run: npm ci + # Docusaurus/webpack build cache; restored from the newest entry. + - name: Cache Docusaurus build + uses: actions/cache@v4 + with: + path: | + web/.docusaurus + web/node_modules/.cache + key: docusaurus-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-${{ github.sha }} + restore-keys: docusaurus-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}- - name: Build site working-directory: web run: npm run build + # Browser binaries are keyed by the lockfile (it pins the Playwright version). + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }} - name: Install Playwright browsers + if: steps.playwright-cache.outputs.cache-hit != 'true' working-directory: web run: npx playwright install --with-deps chromium + # Cached browsers still need the system libraries. + - name: Install Playwright system dependencies + if: steps.playwright-cache.outputs.cache-hit == 'true' + working-directory: web + run: npx playwright install-deps chromium - name: Run Playwright tests working-directory: web run: npm run test:e2e diff --git a/.github/workflows/cloud.yml b/.github/workflows/cloud.yml index eb2191b9..c234c115 100644 --- a/.github/workflows/cloud.yml +++ b/.github/workflows/cloud.yml @@ -62,6 +62,10 @@ jobs: - uses: ./.github/actions/setup-desktop with: cache-key: cloud + npm-cache-paths: | + package-lock.json + web/package-lock.json + supabase/functions/package-lock.json # Pinned to the CLI version the stack is developed against. A maintainer # should also pin this action to a commit SHA. - uses: supabase/setup-cli@v1 @@ -77,6 +81,13 @@ jobs: key=$(sed -n 's/^IXTABLE_CLOUD_PUBLIC_KEY_RAW=//p' supabase/functions/.env.local) test -n "$key" echo "IXTABLE_CLOUD_PUBLIC_KEY_RAW=$key" >> "$GITHUB_ENV" + # Deno module cache for the Edge Function type check and tests. + - name: Cache Deno modules + uses: actions/cache@v4 + with: + path: ~/.cache/deno + key: deno-${{ runner.os }}-${{ hashFiles('supabase/functions/deno.json', 'supabase/functions/deno.lock', 'scripts/cloud/deno-test.mjs') }} + restore-keys: deno-${{ runner.os }}- # Deno comes from npm (npx deno@) inside the script. - name: Edge Function type check and Deno unit tests run: npm run service-qa:deno diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index 78bd9db3..05b615ac 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -50,6 +50,15 @@ jobs: - name: Install dependencies run: npm ci + - name: Cache Docusaurus build + uses: actions/cache@v4 + with: + path: | + web/.docusaurus + web/node_modules/.cache + key: docusaurus-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-${{ github.sha }} + restore-keys: docusaurus-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}- + - name: Build web app run: npm run build diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index a250132e..8805fc21 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -227,12 +227,13 @@ jobs: if-no-files-found: ignore retention-days: 7 + # PRs run golden apps on ubuntu only to free macOS/Windows runners; main runs all three. golden: name: Golden apps (${{ matrix.os }}) strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: ${{ fromJSON(github.event_name == 'pull_request' && '["ubuntu-latest"]' || '["ubuntu-latest","macos-latest","windows-latest"]') }} runs-on: ${{ matrix.os }} steps: # Keep LF endings so fixtures and snapshots match on Windows. @@ -278,8 +279,10 @@ jobs: # Performance budgets (PRD §27.3, docs/decisions/performance-budgets.md). Report-only: # results go to the job summary and an artifact, and a budget miss never fails CI. + # Runs on main only: a report-only release build is not worth a PR runner. perf: name: Performance budgets (report-only) + if: github.event_name != 'pull_request' runs-on: ubuntu-latest continue-on-error: true services: diff --git a/docs/decisions/performance-budgets.md b/docs/decisions/performance-budgets.md index 6f0901ac..2213ea33 100644 --- a/docs/decisions/performance-budgets.md +++ b/docs/decisions/performance-budgets.md @@ -83,7 +83,8 @@ remove that build first when one exists. ### CI -The `perf` job in `.github/workflows/desktop.yml` runs both halves in release +The `perf` job in `.github/workflows/desktop.yml` runs on pushes to `main` +(not on pull requests, to keep PR runners free). It runs both halves in release mode on `ubuntu-latest` with a PostgreSQL 16 service, publishes the table to the job summary, and uploads `reports/perf/*.json` and `summary.md` as the `performance-budgets` artifact. The job has `continue-on-error: true` and no diff --git a/docs/decisions/windows-support.md b/docs/decisions/windows-support.md index 65c08e66..1ddcd0d5 100644 --- a/docs/decisions/windows-support.md +++ b/docs/decisions/windows-support.md @@ -2,7 +2,8 @@ Status: accepted, pending Windows CI. Windows is a release-blocking platform (PRD §6.1). Green Desktop CI on Windows, macOS, and Linux is the `green-ci-all-os` gate in -[the release gates](../release-checklist.md). +[the release gates](../release-checklist.md). Pull requests run the golden +suite on Linux only; `main` and the release workflow run it on all three. ## What failed @@ -93,3 +94,5 @@ The `dumpbin /imports` diagnostic step stays until the Windows jobs are green. unchanged. - 2026-10-05 (later): runners returned. Recorded the first Windows results and the fixes (global.db race, data gate starvation, basename, state dir cleanup). +- 2026-10-06: golden suite runs on Linux only for pull requests; `main` + and release still run it on Windows, macOS, and Linux. From 72a0e4066b5bcdbf858cbc6263749baea765e7f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 12:18:22 +0000 Subject: [PATCH 2/3] ci: run native PostgreSQL conformance (macOS/Windows) on main only Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017djARXypsDMLN1BvJ2GQva --- .github/workflows/desktop.yml | 2 ++ CLAUDE.md | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 8805fc21..623ec369 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -191,8 +191,10 @@ jobs: # Service containers are Linux-only, so macOS and Windows start PostgreSQL # natively (scripts/ci/start-postgres.mjs exports IXTABLE_TEST_POSTGRES_URL). + # Main only: PRs get PostgreSQL coverage from the ubuntu job, freeing macOS/Windows runners. postgres-native: name: PostgreSQL conformance (${{ matrix.os }}) + if: github.event_name != 'pull_request' strategy: fail-fast: false matrix: diff --git a/CLAUDE.md b/CLAUDE.md index 1f9a2c3a..91d50fa4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,9 +83,10 @@ the UI with `userEvent`, query by role/label, use `findBy*` with `.github/workflows/desktop.yml`: lint, then tests on ubuntu/macos/windows (with the 500 MB archive test, `IXTABLE_HEAVY_TESTS=1`), PostgreSQL -conformance on all three (ubuntu service container, native server elsewhere -via `scripts/ci/start-postgres.mjs`), golden suites -(`tests/integration/golden/`), and a debug `tauri build` on ubuntu. Shared +conformance (ubuntu service container; on `main` also macOS/Windows with a +native server via `scripts/ci/start-postgres.mjs`), golden suites +(`tests/integration/golden/`; PRs ubuntu only, `main` all three), perf +budgets (`main` only), and a debug `tauri build` on ubuntu. Shared setup is `.github/actions/setup-desktop`. Keep commands cross-platform: use Node scripts under `scripts/ci/` instead of POSIX shell in `package.json`. From 0096658e715a0be279dc8d0b660fb03df5338fc2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 12:30:47 +0000 Subject: [PATCH 3/3] release-gates: accept the PR-conditional golden matrix in three-os-ci Golden still runs on all three OSes on main; the gate only matched literal os: [...] lists. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017djARXypsDMLN1BvJ2GQva --- scripts/ci/release-gates.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/ci/release-gates.mjs b/scripts/ci/release-gates.mjs index 601d2a72..8245518c 100644 --- a/scripts/ci/release-gates.mjs +++ b/scripts/ci/release-gates.mjs @@ -250,7 +250,8 @@ export const GATES = [ check: (root) => { const problems = []; const desktop = file(root, ".github/workflows/desktop.yml"); - const matrices = desktop.match(/os: \[[^\]]+\]/g) ?? []; + // A literal `os: [...]` list, or the push branch of a PR-conditional fromJSON matrix. + const matrices = desktop.match(/os: \[[^\]]+\]|'\["ubuntu-latest","macos-latest","windows-latest"\]'/g) ?? []; if (matrices.filter((m) => THREE_OS.test(m)).length < 2) problems.push("desktop.yml test and golden jobs must both run on all three OSes"); if (