From 4e795a932f4fea88bebcc0b105da9ef98a9ce3da Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 19:26:06 +0000 Subject: [PATCH 1/3] ci: Shorten the CircleCI setup job, report coverage sooner, speed up the website check - setup no longer copies the yarn cache into the workspace; only jobs that re-resolve dependencies restore it. The cache key now hashes the committed manifests, so it hits exactly instead of always falling back. - codecov reports as soon as coverage is processed instead of waiting for every CI job. - The website check builds the site directly with a cached webpack build, builds only the packages it imports, and fails on build warnings. Broken links now log as warnings. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0174ShyuST1krJS2ZqzuLi9B --- .circleci/config.yml | 36 ++++++++++++++++++++++++---- .cursor/rules/ci-config.mdc | 2 ++ .github/workflows/site-preview.yml | 38 +++++++++++++++++++++++------- codecov.yml | 6 +++++ website/docusaurus.config.ts | 4 ++-- 5 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 codecov.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 58465fd2d66d..d7f83a1b1732 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -45,6 +45,16 @@ commands: echo "No relevance flag found; failing open (continuing job)." fi + restore-yarn-cache: + # The workspace skips the yarn cache (~130 MiB) to keep setup's upload + # short; only jobs that re-resolve dependencies (`yarn up`/`add`) need it. + # The newest entry is almost always the one setup just saved. Run before + # attach_workspace so the workspace's install state wins. + steps: + - restore_cache: + keys: + - v15-dependencies- + jobs: setup: executor: node @@ -132,11 +142,16 @@ jobs: npm pkg set 'workspaces[]'='examples/todo-app' npm pkg set 'workspaces[]'='examples/github-app' npm pkg set 'workspaces[]'='examples/normalizr-relationships' + - run: + # Hash the manifests before trimming and `yarn up` rewrite them, + # so the same key restores and saves. + name: Compute dependency cache key + command: | + git show HEAD:package.json HEAD:yarn.lock HEAD:examples/todo-app/package.json HEAD:examples/github-app/package.json HEAD:examples/normalizr-relationships/package.json | sha256sum > .ci-deps-key - restore_cache: keys: - - v14-dependencies-{{ checksum "yarn.lock" }}-{{ checksum "examples/github-app/package.json" }}-{{ checksum "examples/todo-app/package.json" }} - # fallback to using the latest cache if no exact match is found - - v14-dependencies- + - v15-dependencies-{{ checksum ".ci-deps-key" }} + - v15-dependencies- - run: name: yarn install command: | @@ -145,7 +160,7 @@ jobs: paths: - .yarn/cache - .yarn/install-state.gz - key: v14-dependencies-{{ checksum "yarn.lock" }}-{{ checksum "examples/github-app/package.json" }}-{{ checksum "examples/todo-app/package.json" }} + key: v15-dependencies-{{ checksum ".ci-deps-key" }} - run: # These are independent (babel/rollup compile from src, not tsc output), # so run them concurrently to shorten the critical path. Legacy types @@ -162,7 +177,10 @@ jobs: root: ~/ paths: # explicitly list so we can ignore some directories that are not needed - - project/.yarn + # .yarn/cache is left out; see restore-yarn-cache + - project/.yarn/releases + - project/.yarn/plugins + - project/.yarn/install-state.gz - project/__tests__ - project/examples/todo-app - project/examples/github-app @@ -218,6 +236,12 @@ jobs: steps: - halt-unless-relevant-change: flag: tests + - unless: + # `latest` uses the installed React as-is + condition: + equal: ["latest", << parameters.react-version >>] + steps: + - restore-yarn-cache - attach_workspace: at: ~/ - run: @@ -274,6 +298,7 @@ jobs: steps: - halt-unless-relevant-change: flag: tests + - restore-yarn-cache - attach_workspace: at: ~/ - run: @@ -299,6 +324,7 @@ jobs: steps: - halt-unless-relevant-change: flag: esmodule + - restore-yarn-cache - attach_workspace: at: ~/ - run: diff --git a/.cursor/rules/ci-config.mdc b/.cursor/rules/ci-config.mdc index c3a1f356b2d0..141d3170f996 100644 --- a/.cursor/rules/ci-config.mdc +++ b/.cursor/rules/ci-config.mdc @@ -21,12 +21,14 @@ alwaysApply: false - `typecheck` also runs `yarn check:typeperf` ([scripts/typeperf](../../scripts/typeperf/README.md)), which reads the `ci:build:types` output from `setup`'s workspace. - Never `git fetch --depth` the base branch in the relevance check: a shallow fetch severs the merge base and the three-dot diff fails. - Changing root `package.json` `workspaces` requires updating the `setup` job's workspace trimming step. +- The yarn cache (`.yarn/cache`) stays out of `setup`'s workspace to keep its upload short; only jobs that re-resolve dependencies (`yarn up`/`add`: `unit_tests` except `latest`, `node_matrix`, `esmodule-types`) run `restore-yarn-cache`, before `attach_workspace` so the workspace's `install-state.gz` wins. `setup` keys it on the manifests as committed (before trimming and `yarn up` rewrite them), so restore and save use the same key; downstream jobs restore the newest entry by prefix. ## GitHub Actions (`.github/workflows/`) - Workflows install only needed workspaces via `./scripts/ci-install.sh [extra-workspace ...]`. - `skills.yml` `paths` must cover every input of `website/framework-docs/skillReferences.mjs` (docs, skill manifests, the generator and its deps). - `editor-types.yml` reruns `yarn copy:websitetypes` and fails if `website/src/components/Playground/editor-types` changes. It needs the `website` workspace (for deps like `bignumber.js`), which CircleCI's `setup` drops. Its `paths` must cover every input of `scripts/copywebsitetypes.sh`. +- `site-preview.yml` `build` builds the site directly (no Vercel CLI, only the packages it imports, `VERCEL_ENV=preview` to include drafts), caches Docusaurus' webpack cache, and fails on any `[WARNING]`/`[ERROR]` line. Broken links are `warn` in `docusaurus.config.ts` so this check catches them without failing Vercel deploys. - `site-preview.yml`/`site-release.yml` `paths` (`website/**`, `docs/{core,rest,graphql}/**`) must match `SITE_PATHS` in `website/scripts/vercel-ignore.sh`. - `benchmark-react.yml` caches Playwright browsers keyed on the resolved `playwright` version from `examples/benchmark-react`; bumping playwright invalidates the cache automatically. - Benchmark workflows (`benchmark.yml`, `benchmark-react.yml`) tune the host (CPU governor, swapoff) and pin CPUs with `taskset` — they must run directly on the runner, not in a `container:`. diff --git a/.github/workflows/site-preview.yml b/.github/workflows/site-preview.yml index 431c6c962923..4546154d48ba 100644 --- a/.github/workflows/site-preview.yml +++ b/.github/workflows/site-preview.yml @@ -38,17 +38,37 @@ jobs: build: runs-on: ubuntu-latest - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} steps: - name: Checkout uses: actions/checkout@v7 with: fetch-depth: 1 - - name: Install Vercel CLI - run: npm install --global vercel@latest - - name: Pull Vercel Environment Information - run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} - - name: Build Project Artifacts - run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + - uses: actions/setup-node@v6 + with: + node-version: '26' + cache: 'yarn' + - name: Install packages + run: ./scripts/ci-install.sh website + - name: Build packages the website imports + run: yarn workspaces foreach -WptivR --from rdc-website --no-private run build:lib + # Docusaurus' webpack cache (~600 MiB): one entry per PR, saved on its + # first build and reused by later pushes (stale entries just miss) + - uses: actions/cache@v6 + with: + path: website/node_modules/.cache + key: website-build-${{ hashFiles('yarn.lock') }}-${{ github.head_ref }} + restore-keys: website-build-${{ hashFiles('yarn.lock') }}- + - name: Build website + env: + # Build drafts like the Vercel preview does + VERCEL_ENV: preview + # explicit `shell: bash` adds pipefail, so a failed build isn't masked by tee + shell: bash + run: | + yarn workspace rdc-website build 2>&1 | tee build.log + # Warnings (broken links, webpack warnings) fail the check, but not + # the Vercel deploy, which only stops on errors + if grep -E '\[(WARNING|ERROR)\]' build.log; then + echo "::error::Website build logged warnings (listed above)" + exit 1 + fi diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000000..406d10542cd2 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,6 @@ +codecov: + # Report as soon as the coverage upload is processed, instead of holding + # the status until every other CI job has finished. + require_ci_to_pass: false + notify: + wait_for_ci: false diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 4c51fb78c658..ad066e44d0d2 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -54,7 +54,7 @@ const config: Config = { markdown: { mermaid: true, hooks: { - onBrokenMarkdownLinks: 'log', + onBrokenMarkdownLinks: 'warn', }, // Vercel preview deploys publish `draft: true` pages so PRs can review them; // production (VERCEL_ENV=production) and local builds still drop them. @@ -225,7 +225,7 @@ const config: Config = { customFields: { repoUrl: 'https://github.com/reactive/data-client', }, - onBrokenLinks: 'log', + onBrokenLinks: 'warn', future: { // Generated Vue mirror pages have no git history; read their source's experimental_vcs: { From c86294793d49e1b87db6229541ad4de44f2e9485 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 19:45:33 +0000 Subject: [PATCH 2/3] ci: Restore the yarn cache by exact key; build the website in one job - Downstream jobs restore the dependency cache by the same key setup saved (carried in the workspace) instead of whichever entry is newest. The cache no longer holds install-state.gz, so restoring after attach_workspace is safe. - The website typecheck and build share one job and one install; ci:build:website builds types and the packages the site imports in parallel. - The webpack cache key drops head_ref: caches are already scoped per PR. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0174ShyuST1krJS2ZqzuLi9B --- .circleci/config.yml | 22 +++++++++----------- .cursor/rules/ci-config.mdc | 4 ++-- .github/workflows/site-preview.yml | 32 +++++++++--------------------- package.json | 2 ++ 4 files changed, 22 insertions(+), 38 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d7f83a1b1732..250ef2f5abe3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,12 +48,11 @@ commands: restore-yarn-cache: # The workspace skips the yarn cache (~130 MiB) to keep setup's upload # short; only jobs that re-resolve dependencies (`yarn up`/`add`) need it. - # The newest entry is almost always the one setup just saved. Run before - # attach_workspace so the workspace's install state wins. steps: - restore_cache: keys: - - v15-dependencies- + - v16-dependencies-{{ checksum ".ci-deps-key" }} + - v16-dependencies- jobs: setup: @@ -148,10 +147,7 @@ jobs: name: Compute dependency cache key command: | git show HEAD:package.json HEAD:yarn.lock HEAD:examples/todo-app/package.json HEAD:examples/github-app/package.json HEAD:examples/normalizr-relationships/package.json | sha256sum > .ci-deps-key - - restore_cache: - keys: - - v15-dependencies-{{ checksum ".ci-deps-key" }} - - v15-dependencies- + - restore-yarn-cache - run: name: yarn install command: | @@ -159,8 +155,7 @@ jobs: - save_cache: paths: - .yarn/cache - - .yarn/install-state.gz - key: v15-dependencies-{{ checksum ".ci-deps-key" }} + key: v16-dependencies-{{ checksum ".ci-deps-key" }} - run: # These are independent (babel/rollup compile from src, not tsc output), # so run them concurrently to shorten the critical path. Legacy types @@ -181,6 +176,7 @@ jobs: - project/.yarn/releases - project/.yarn/plugins - project/.yarn/install-state.gz + - project/.ci-deps-key - project/__tests__ - project/examples/todo-app - project/examples/github-app @@ -236,14 +232,14 @@ jobs: steps: - halt-unless-relevant-change: flag: tests + - attach_workspace: + at: ~/ - unless: # `latest` uses the installed React as-is condition: equal: ["latest", << parameters.react-version >>] steps: - restore-yarn-cache - - attach_workspace: - at: ~/ - run: name: Installing React Version command: | @@ -298,9 +294,9 @@ jobs: steps: - halt-unless-relevant-change: flag: tests - - restore-yarn-cache - attach_workspace: at: ~/ + - restore-yarn-cache - run: command: | node --version @@ -324,9 +320,9 @@ jobs: steps: - halt-unless-relevant-change: flag: esmodule - - restore-yarn-cache - attach_workspace: at: ~/ + - restore-yarn-cache - run: name: Install TypeScript Version command: | diff --git a/.cursor/rules/ci-config.mdc b/.cursor/rules/ci-config.mdc index 141d3170f996..865159d10b90 100644 --- a/.cursor/rules/ci-config.mdc +++ b/.cursor/rules/ci-config.mdc @@ -21,14 +21,14 @@ alwaysApply: false - `typecheck` also runs `yarn check:typeperf` ([scripts/typeperf](../../scripts/typeperf/README.md)), which reads the `ci:build:types` output from `setup`'s workspace. - Never `git fetch --depth` the base branch in the relevance check: a shallow fetch severs the merge base and the three-dot diff fails. - Changing root `package.json` `workspaces` requires updating the `setup` job's workspace trimming step. -- The yarn cache (`.yarn/cache`) stays out of `setup`'s workspace to keep its upload short; only jobs that re-resolve dependencies (`yarn up`/`add`: `unit_tests` except `latest`, `node_matrix`, `esmodule-types`) run `restore-yarn-cache`, before `attach_workspace` so the workspace's `install-state.gz` wins. `setup` keys it on the manifests as committed (before trimming and `yarn up` rewrite them), so restore and save use the same key; downstream jobs restore the newest entry by prefix. +- `setup`'s workspace leaves out the yarn cache (`.yarn/cache`) to keep its upload short. Jobs that re-resolve dependencies (`yarn up`/`add`) run `restore-yarn-cache`, keyed on `.ci-deps-key`, a hash of the manifests as committed (taken before trimming and `yarn up` rewrite them). ## GitHub Actions (`.github/workflows/`) - Workflows install only needed workspaces via `./scripts/ci-install.sh [extra-workspace ...]`. - `skills.yml` `paths` must cover every input of `website/framework-docs/skillReferences.mjs` (docs, skill manifests, the generator and its deps). - `editor-types.yml` reruns `yarn copy:websitetypes` and fails if `website/src/components/Playground/editor-types` changes. It needs the `website` workspace (for deps like `bignumber.js`), which CircleCI's `setup` drops. Its `paths` must cover every input of `scripts/copywebsitetypes.sh`. -- `site-preview.yml` `build` builds the site directly (no Vercel CLI, only the packages it imports, `VERCEL_ENV=preview` to include drafts), caches Docusaurus' webpack cache, and fails on any `[WARNING]`/`[ERROR]` line. Broken links are `warn` in `docusaurus.config.ts` so this check catches them without failing Vercel deploys. +- `site-preview.yml` runs one `build` job (one install for the typecheck and the build). It builds the site directly (no Vercel CLI, only the packages it imports via `ci:build:website`, `VERCEL_ENV=preview` to include drafts), caches Docusaurus' webpack cache, and fails on any `[WARNING]`/`[ERROR]` line. Broken links are `warn` in `docusaurus.config.ts` so this check catches them without failing Vercel deploys. - `site-preview.yml`/`site-release.yml` `paths` (`website/**`, `docs/{core,rest,graphql}/**`) must match `SITE_PATHS` in `website/scripts/vercel-ignore.sh`. - `benchmark-react.yml` caches Playwright browsers keyed on the resolved `playwright` version from `examples/benchmark-react`; bumping playwright invalidates the cache automatically. - Benchmark workflows (`benchmark.yml`, `benchmark-react.yml`) tune the host (CPU governor, swapoff) and pin CPUs with `taskset` — they must run directly on the runner, not in a `container:`. diff --git a/.github/workflows/site-preview.yml b/.github/workflows/site-preview.yml index 4546154d48ba..b917a4313155 100644 --- a/.github/workflows/site-preview.yml +++ b/.github/workflows/site-preview.yml @@ -16,7 +16,8 @@ concurrency: cancel-in-progress: true jobs: - typecheck: + # One job so the install happens once: the typecheck is short next to it + build: runs-on: ubuntu-latest steps: - name: Checkout @@ -31,33 +32,18 @@ jobs: cache: 'yarn' - name: Install packages run: ./scripts/ci-install.sh website - - name: Build package types - run: yarn ci:build:types + - name: Build package types and the packages the website imports + run: yarn ci:build:website - name: Typecheck website run: yarn workspace rdc-website typecheck - - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - fetch-depth: 1 - - uses: actions/setup-node@v6 - with: - node-version: '26' - cache: 'yarn' - - name: Install packages - run: ./scripts/ci-install.sh website - - name: Build packages the website imports - run: yarn workspaces foreach -WptivR --from rdc-website --no-private run build:lib - # Docusaurus' webpack cache (~600 MiB): one entry per PR, saved on its - # first build and reused by later pushes (stale entries just miss) + # Docusaurus' webpack cache (~600 MiB). Caches are scoped to the PR, so + # its first build saves one entry that later pushes reuse; stale modules + # just miss. - uses: actions/cache@v6 with: path: website/node_modules/.cache - key: website-build-${{ hashFiles('yarn.lock') }}-${{ github.head_ref }} - restore-keys: website-build-${{ hashFiles('yarn.lock') }}- + key: website-build-${{ hashFiles('yarn.lock') }} + restore-keys: website-build- - name: Build website env: # Build drafts like the Vercel preview does diff --git a/package.json b/package.json index 1eeff5aa957e..bae491c170d9 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "ci:build:types": "yarn build:copy:ambient && tsc --build --builders 1", "ci:build:legacy-types": "yarn workspaces foreach -Wpiv --include @data-client/endpoint --include @data-client/normalizr --include @data-client/rest run build:legacy-types", "ci:build-test-lib": "yarn workspace @data-client/core run build:lib && yarn workspace @data-client/test run build:lib && yarn workspace @data-client/test run build:bundle", + "ci:build:website": "run-p ci:build:types ci:build:website-lib", + "ci:build:website-lib": "yarn workspaces foreach -WptivR --from rdc-website --no-private run build:lib", "ci:build:esmodule": "yarn workspaces foreach -WptivR --from @data-client/react --from @data-client/rest --from @data-client/graphql run build:lib && yarn workspace @data-client/normalizr run build:js:node && yarn workspace @data-client/endpoint run build:js:node", "ci:build:bundlesize": "yarn workspaces foreach -Wptiv --no-private run build:lib && yarn workspace test-bundlesize run build:sizecompare", "build:benchmark": "yarn workspaces foreach -WptivR --from @data-client/core --from @data-client/endpoint --from @data-client/normalizr run build:lib && yarn workspace example-benchmark run build", From fb190693d358661977e21d66b3640f1b19893d78 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 19:53:35 +0000 Subject: [PATCH 3/3] ci: Save the website build cache from master; PRs only restore it Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0174ShyuST1krJS2ZqzuLi9B --- .cursor/rules/ci-config.mdc | 2 +- .github/workflows/site-preview.yml | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.cursor/rules/ci-config.mdc b/.cursor/rules/ci-config.mdc index 865159d10b90..9c18a97e2be6 100644 --- a/.cursor/rules/ci-config.mdc +++ b/.cursor/rules/ci-config.mdc @@ -28,7 +28,7 @@ alwaysApply: false - Workflows install only needed workspaces via `./scripts/ci-install.sh [extra-workspace ...]`. - `skills.yml` `paths` must cover every input of `website/framework-docs/skillReferences.mjs` (docs, skill manifests, the generator and its deps). - `editor-types.yml` reruns `yarn copy:websitetypes` and fails if `website/src/components/Playground/editor-types` changes. It needs the `website` workspace (for deps like `bignumber.js`), which CircleCI's `setup` drops. Its `paths` must cover every input of `scripts/copywebsitetypes.sh`. -- `site-preview.yml` runs one `build` job (one install for the typecheck and the build). It builds the site directly (no Vercel CLI, only the packages it imports via `ci:build:website`, `VERCEL_ENV=preview` to include drafts), caches Docusaurus' webpack cache, and fails on any `[WARNING]`/`[ERROR]` line. Broken links are `warn` in `docusaurus.config.ts` so this check catches them without failing Vercel deploys. +- `site-preview.yml` runs one `build` job (one install for the typecheck and the build). It builds the site directly (no Vercel CLI, only the packages it imports via `ci:build:website`, `VERCEL_ENV=preview` to include drafts), restores Docusaurus' webpack cache (only master pushes save it, so PRs share one entry), and fails on any `[WARNING]`/`[ERROR]` line. Broken links are `warn` in `docusaurus.config.ts` so this check catches them without failing Vercel deploys. - `site-preview.yml`/`site-release.yml` `paths` (`website/**`, `docs/{core,rest,graphql}/**`) must match `SITE_PATHS` in `website/scripts/vercel-ignore.sh`. - `benchmark-react.yml` caches Playwright browsers keyed on the resolved `playwright` version from `examples/benchmark-react`; bumping playwright invalidates the cache automatically. - Benchmark workflows (`benchmark.yml`, `benchmark-react.yml`) tune the host (CPU governor, swapoff) and pin CPUs with `taskset` — they must run directly on the runner, not in a `container:`. diff --git a/.github/workflows/site-preview.yml b/.github/workflows/site-preview.yml index b917a4313155..3aaf10c6e148 100644 --- a/.github/workflows/site-preview.yml +++ b/.github/workflows/site-preview.yml @@ -10,6 +10,16 @@ on: - 'docs/rest/**' - 'docs/graphql/**' - '.github/workflows/site-preview.yml' + # Refreshes the webpack cache that PRs restore from + push: + branches: + - master + paths: + - 'website/**' + - 'docs/core/**' + - 'docs/rest/**' + - 'docs/graphql/**' + - '.github/workflows/site-preview.yml' concurrency: group: site-preview-${{ github.head_ref || github.ref }} @@ -36,14 +46,16 @@ jobs: run: yarn ci:build:website - name: Typecheck website run: yarn workspace rdc-website typecheck - # Docusaurus' webpack cache (~600 MiB). Caches are scoped to the PR, so - # its first build saves one entry that later pushes reuse; stale modules - # just miss. - - uses: actions/cache@v6 + # Docusaurus' webpack cache (~73 MiB compressed). Only master saves it, so + # PRs share one fresh entry instead of each storing their own; stale + # modules just miss. + - uses: actions/cache/restore@v6 with: path: website/node_modules/.cache key: website-build-${{ hashFiles('yarn.lock') }} - restore-keys: website-build- + restore-keys: | + website-build-${{ hashFiles('yarn.lock') }}- + website-build- - name: Build website env: # Build drafts like the Vercel preview does @@ -58,3 +70,9 @@ jobs: echo "::error::Website build logged warnings (listed above)" exit 1 fi + - if: github.event_name == 'push' + uses: actions/cache/save@v6 + with: + path: website/node_modules/.cache + # Unique per run: an existing key can't be overwritten + key: website-build-${{ hashFiles('yarn.lock') }}-${{ github.run_id }}