From 741ad85854d051953b592d7a2a9fc8880c9c1f01 Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 21 May 2026 16:23:01 -0500 Subject: [PATCH 1/7] ci(repo): dogfood snapi api checks --- .changeset/dogfood-snapi.md | 2 + .github/workflows/api-changes.yml | 100 ++++++++++++++++++++++++++++++ snapi.config.json | 7 +++ 3 files changed, 109 insertions(+) create mode 100644 .changeset/dogfood-snapi.md create mode 100644 .github/workflows/api-changes.yml create mode 100644 snapi.config.json diff --git a/.changeset/dogfood-snapi.md b/.changeset/dogfood-snapi.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/dogfood-snapi.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml new file mode 100644 index 00000000000..8edc16f7126 --- /dev/null +++ b/.github/workflows/api-changes.yml @@ -0,0 +1,100 @@ +name: API Changes + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: + - main + - release/v4 + - release/core-2 + paths: + - 'packages/clerk-js/**' + - 'packages/react/**' + - 'packages/shared/**' + - 'snapi.config.json' + - '.github/workflows/api-changes.yml' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + check-api: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} + name: API Changes + runs-on: 'blacksmith-8vcpu-ubuntu-2204' + continue-on-error: true + defaults: + run: + shell: bash + timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }} + + env: + SNAPI_PACKAGE: github:clerk/snapi#8c021a2796b313cdefa945a715df8f02e4f99867 + SNAPI_FILTERS: >- + --filter=@clerk/clerk-js + --filter=@clerk/react + --filter=@clerk/shared + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 100 + fetch-tags: false + filter: 'blob:none' + show-progress: false + + - name: Fetch base branch + run: git fetch origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}" --depth=100 + + - name: Setup + id: config + uses: ./.github/actions/init-blacksmith + with: + cache-enabled: true + turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} + turbo-team: ${{ vars.TURBO_TEAM }} + turbo-token: ${{ secrets.TURBO_TOKEN }} + + - name: Build current declarations + run: pnpm turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS + + - name: Generate current API snapshots + run: pnpm dlx --package "$SNAPI_PACKAGE" snapi snapshot --output .api-snapshots-current + + - name: Create baseline worktree + run: | + mkdir -p .worktrees + git worktree add .worktrees/snapi-baseline "origin/${GITHUB_BASE_REF}" + cp snapi.config.json .worktrees/snapi-baseline/snapi.config.json + + - name: Install baseline dependencies + run: pnpm --dir .worktrees/snapi-baseline install --frozen-lockfile + + - name: Build baseline declarations + run: pnpm --dir .worktrees/snapi-baseline turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS + + - name: Generate baseline API snapshots + run: | + pnpm --dir .worktrees/snapi-baseline dlx --package "$SNAPI_PACKAGE" snapi snapshot \ + --output "$GITHUB_WORKSPACE/.api-snapshots-baseline" + + - name: Detect API changes + run: | + pnpm dlx --package "$SNAPI_PACKAGE" snapi detect \ + --baseline .api-snapshots-baseline \ + --output api-changes-report.md \ + --fail-on-breaking + + - name: Upload API changes report + uses: actions/upload-artifact@v4 + if: always() + with: + name: api-changes-report + path: api-changes-report.md + if-no-files-found: ignore + retention-days: 5 diff --git a/snapi.config.json b/snapi.config.json new file mode 100644 index 00000000000..070a39e8d86 --- /dev/null +++ b/snapi.config.json @@ -0,0 +1,7 @@ +{ + "packages": ["packages/clerk-js", "packages/react", "packages/shared"], + "snapshotDir": ".api-snapshots", + "mainBranch": "main", + "checkVersionBump": true, + "outputFormat": "markdown" +} From dd6e88606240aa2e2951c841357f98e9c527bf44 Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 21 May 2026 20:30:37 -0500 Subject: [PATCH 2/7] ci(repo): drop redundant snapi current-snapshot step --- .github/workflows/api-changes.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml index 8edc16f7126..aab4ce91a66 100644 --- a/.github/workflows/api-changes.yml +++ b/.github/workflows/api-changes.yml @@ -63,9 +63,6 @@ jobs: - name: Build current declarations run: pnpm turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS - - name: Generate current API snapshots - run: pnpm dlx --package "$SNAPI_PACKAGE" snapi snapshot --output .api-snapshots-current - - name: Create baseline worktree run: | mkdir -p .worktrees From 7b226d2987b04d698cef789c23f3d52a5c9fbcbb Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 21 May 2026 22:35:31 -0500 Subject: [PATCH 3/7] ci(repo): cache snapi baselines and watch @clerk/ui Publish the API baseline to a GHA cache on push to main and the release branches, keyed by commit sha. PR runs restore from that cache with a prefix-match fallback, and only rebuild the baseline via worktree on a full miss. Add @clerk/ui to the watched package set with a tsc-based build:declarations task matching @clerk/shared's pattern. --- .github/workflows/api-changes.yml | 92 ++++++++++++++++++++++---- packages/ui/package.json | 1 + packages/ui/tsconfig.declarations.json | 21 ++++++ snapi.config.json | 2 +- 4 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 packages/ui/tsconfig.declarations.json diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml index aab4ce91a66..e054b6f4981 100644 --- a/.github/workflows/api-changes.yml +++ b/.github/workflows/api-changes.yml @@ -1,6 +1,18 @@ name: API Changes on: + push: + branches: + - main + - release/v4 + - release/core-2 + paths: + - 'packages/clerk-js/**' + - 'packages/react/**' + - 'packages/shared/**' + - 'packages/ui/**' + - 'snapi.config.json' + - '.github/workflows/api-changes.yml' pull_request: types: [opened, synchronize, reopened, ready_for_review] branches: @@ -11,6 +23,7 @@ on: - 'packages/clerk-js/**' - 'packages/react/**' - 'packages/shared/**' + - 'packages/ui/**' - 'snapi.config.json' - '.github/workflows/api-changes.yml' @@ -19,11 +32,58 @@ permissions: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + SNAPI_PACKAGE: github:clerk/snapi#8c021a2796b313cdefa945a715df8f02e4f99867 + SNAPI_FILTERS: >- + --filter=@clerk/clerk-js + --filter=@clerk/react + --filter=@clerk/shared + --filter=@clerk/ui jobs: + publish-baseline: + if: github.event_name == 'push' + name: Publish API Baseline + runs-on: 'blacksmith-8vcpu-ubuntu-2204' + continue-on-error: true + defaults: + run: + shell: bash + timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }} + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + filter: 'blob:none' + show-progress: false + + - name: Setup + uses: ./.github/actions/init-blacksmith + with: + cache-enabled: true + turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} + turbo-team: ${{ vars.TURBO_TEAM }} + turbo-token: ${{ secrets.TURBO_TOKEN }} + + - name: Build declarations + run: pnpm turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS + + - name: Generate API snapshot + run: | + pnpm dlx --package "$SNAPI_PACKAGE" snapi snapshot \ + --output "$GITHUB_WORKSPACE/.api-snapshots-baseline" + + - name: Save baseline to cache + uses: actions/cache/save@v4 + with: + path: .api-snapshots-baseline + key: snapi-baseline-${{ github.sha }} + check-api: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == false }} name: API Changes runs-on: 'blacksmith-8vcpu-ubuntu-2204' continue-on-error: true @@ -32,13 +92,6 @@ jobs: shell: bash timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }} - env: - SNAPI_PACKAGE: github:clerk/snapi#8c021a2796b313cdefa945a715df8f02e4f99867 - SNAPI_FILTERS: >- - --filter=@clerk/clerk-js - --filter=@clerk/react - --filter=@clerk/shared - steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -48,11 +101,7 @@ jobs: filter: 'blob:none' show-progress: false - - name: Fetch base branch - run: git fetch origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}" --depth=100 - - name: Setup - id: config uses: ./.github/actions/init-blacksmith with: cache-enabled: true @@ -60,22 +109,39 @@ jobs: turbo-team: ${{ vars.TURBO_TEAM }} turbo-token: ${{ secrets.TURBO_TOKEN }} + - name: Restore baseline from cache + id: baseline-cache + uses: actions/cache/restore@v4 + with: + path: .api-snapshots-baseline + key: snapi-baseline-${{ github.event.pull_request.base.sha }} + restore-keys: | + snapi-baseline- + - name: Build current declarations run: pnpm turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS + - name: Fetch base branch + if: steps.baseline-cache.outputs.cache-matched-key == '' + run: git fetch origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}" --depth=100 + - name: Create baseline worktree + if: steps.baseline-cache.outputs.cache-matched-key == '' run: | mkdir -p .worktrees git worktree add .worktrees/snapi-baseline "origin/${GITHUB_BASE_REF}" cp snapi.config.json .worktrees/snapi-baseline/snapi.config.json - name: Install baseline dependencies + if: steps.baseline-cache.outputs.cache-matched-key == '' run: pnpm --dir .worktrees/snapi-baseline install --frozen-lockfile - name: Build baseline declarations + if: steps.baseline-cache.outputs.cache-matched-key == '' run: pnpm --dir .worktrees/snapi-baseline turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS - name: Generate baseline API snapshots + if: steps.baseline-cache.outputs.cache-matched-key == '' run: | pnpm --dir .worktrees/snapi-baseline dlx --package "$SNAPI_PACKAGE" snapi snapshot \ --output "$GITHUB_WORKSPACE/.api-snapshots-baseline" diff --git a/packages/ui/package.json b/packages/ui/package.json index 256b30f6bfa..70945f0288d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -71,6 +71,7 @@ "scripts": { "build": "pnpm build:umd && pnpm build:esm && pnpm check:no-rhc && pnpm type-check", "build:analyze": "rspack build --config rspack.config.js --env production --env analyze --analyze", + "build:declarations": "tsc -p tsconfig.declarations.json", "build:esm": "tsdown", "build:rsdoctor": "RSDOCTOR=true rspack build --config rspack.config.js --env production", "build:umd": "rspack build --config rspack.config.js --env production", diff --git a/packages/ui/tsconfig.declarations.json b/packages/ui/tsconfig.declarations.json new file mode 100644 index 00000000000..a1fc0a91dee --- /dev/null +++ b/packages/ui/tsconfig.declarations.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "./dist", + "declarationMap": true, + "emitDeclarationOnly": true, + "skipLibCheck": true + }, + "exclude": [ + "node_modules", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/__tests__/**", + "**/__mocks__/**", + "**/test/**", + "**/tests/**" + ] +} diff --git a/snapi.config.json b/snapi.config.json index 070a39e8d86..cd6455ef9da 100644 --- a/snapi.config.json +++ b/snapi.config.json @@ -1,5 +1,5 @@ { - "packages": ["packages/clerk-js", "packages/react", "packages/shared"], + "packages": ["packages/clerk-js", "packages/react", "packages/shared", "packages/ui"], "snapshotDir": ".api-snapshots", "mainBranch": "main", "checkVersionBump": true, From b54cb5216c67550a4d2e7e2039415f598debe068 Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 22 May 2026 10:50:15 -0500 Subject: [PATCH 4/7] ci(repo): overlay HEAD's ui build wiring onto baseline worktree snapi's baseline worktree checks out the PR's base branch but uses HEAD's snapi config. When a watched package's build:declarations task is added in the same PR that starts watching it, the baseline tree doesn't yet have the task and the fallback build fails. Copy ui's package.json and tsconfig.declarations.json from HEAD into the worktree before installing, matching the existing pattern for snapi.config.json. --- .github/workflows/api-changes.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml index e054b6f4981..aab834ec5fc 100644 --- a/.github/workflows/api-changes.yml +++ b/.github/workflows/api-changes.yml @@ -131,6 +131,12 @@ jobs: mkdir -p .worktrees git worktree add .worktrees/snapi-baseline "origin/${GITHUB_BASE_REF}" cp snapi.config.json .worktrees/snapi-baseline/snapi.config.json + # Overlay HEAD's build:declarations wiring for watched packages onto + # the baseline. snapi watches a package set defined by HEAD, so the + # baseline must produce declarations the same way, even if a watched + # package only acquired its build:declarations task in this PR. + cp packages/ui/package.json .worktrees/snapi-baseline/packages/ui/package.json + cp packages/ui/tsconfig.declarations.json .worktrees/snapi-baseline/packages/ui/tsconfig.declarations.json - name: Install baseline dependencies if: steps.baseline-cache.outputs.cache-matched-key == '' From 41781d8238a79fa51ac32c46631c3af371053bdc Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 22 May 2026 10:58:53 -0500 Subject: [PATCH 5/7] Revert "ci(repo): overlay HEAD's ui build wiring onto baseline worktree" This reverts commit b54cb5216c67550a4d2e7e2039415f598debe068. --- .github/workflows/api-changes.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml index aab834ec5fc..e054b6f4981 100644 --- a/.github/workflows/api-changes.yml +++ b/.github/workflows/api-changes.yml @@ -131,12 +131,6 @@ jobs: mkdir -p .worktrees git worktree add .worktrees/snapi-baseline "origin/${GITHUB_BASE_REF}" cp snapi.config.json .worktrees/snapi-baseline/snapi.config.json - # Overlay HEAD's build:declarations wiring for watched packages onto - # the baseline. snapi watches a package set defined by HEAD, so the - # baseline must produce declarations the same way, even if a watched - # package only acquired its build:declarations task in this PR. - cp packages/ui/package.json .worktrees/snapi-baseline/packages/ui/package.json - cp packages/ui/tsconfig.declarations.json .worktrees/snapi-baseline/packages/ui/tsconfig.declarations.json - name: Install baseline dependencies if: steps.baseline-cache.outputs.cache-matched-key == '' From 139285a263db14579ace501547d121d8a9c1d4d4 Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 22 May 2026 12:01:22 -0500 Subject: [PATCH 6/7] ci(repo): use working-directory for baseline worktree steps pnpm 10 mishandles `pnpm --dir ...`, treating the path as the command to execute and erroring with ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL. Switching the worktree-fallback install/build/snapshot steps to GHA's `working-directory:` avoids the flag entirely. --- .github/workflows/api-changes.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml index e054b6f4981..5eb15889ed1 100644 --- a/.github/workflows/api-changes.yml +++ b/.github/workflows/api-changes.yml @@ -134,16 +134,19 @@ jobs: - name: Install baseline dependencies if: steps.baseline-cache.outputs.cache-matched-key == '' - run: pnpm --dir .worktrees/snapi-baseline install --frozen-lockfile + working-directory: .worktrees/snapi-baseline + run: pnpm install --frozen-lockfile - name: Build baseline declarations if: steps.baseline-cache.outputs.cache-matched-key == '' - run: pnpm --dir .worktrees/snapi-baseline turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS + working-directory: .worktrees/snapi-baseline + run: pnpm turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS - name: Generate baseline API snapshots if: steps.baseline-cache.outputs.cache-matched-key == '' + working-directory: .worktrees/snapi-baseline run: | - pnpm --dir .worktrees/snapi-baseline dlx --package "$SNAPI_PACKAGE" snapi snapshot \ + pnpm dlx --package "$SNAPI_PACKAGE" snapi snapshot \ --output "$GITHUB_WORKSPACE/.api-snapshots-baseline" - name: Detect API changes From 63fa1c88bd7283daee0af2bf977ee7f5dd39d125 Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 22 May 2026 12:47:11 -0500 Subject: [PATCH 7/7] ci(repo): fetch snapi from pkg.pr.new and pin baseline to base.sha clerk/snapi is private, so pnpm dlx --package "github:clerk/snapi#..." fails on Actions runners with no SSH key. Switch to the public pkg.pr.new tarball URL pinned to the latest snapi PR build. Also pin the worktree-fallback baseline to the immutable pull_request.base.sha instead of origin/; if the base branch advances during the run, this prevents drift between the cached baseline lookup key and what the worktree actually checks out. --- .github/workflows/api-changes.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/api-changes.yml b/.github/workflows/api-changes.yml index 5eb15889ed1..f30b1d54d6f 100644 --- a/.github/workflows/api-changes.yml +++ b/.github/workflows/api-changes.yml @@ -35,7 +35,7 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: - SNAPI_PACKAGE: github:clerk/snapi#8c021a2796b313cdefa945a715df8f02e4f99867 + SNAPI_PACKAGE: https://pkg.pr.new/clerk/snapi/@clerk/snapi@5b897f1c8be7692b3914e7f11d2720e91e216b34 SNAPI_FILTERS: >- --filter=@clerk/clerk-js --filter=@clerk/react @@ -121,15 +121,15 @@ jobs: - name: Build current declarations run: pnpm turbo build:declarations $TURBO_ARGS $SNAPI_FILTERS - - name: Fetch base branch + - name: Fetch base commit if: steps.baseline-cache.outputs.cache-matched-key == '' - run: git fetch origin "${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}" --depth=100 + run: git fetch origin "${{ github.event.pull_request.base.sha }}" --depth=1 - name: Create baseline worktree if: steps.baseline-cache.outputs.cache-matched-key == '' run: | mkdir -p .worktrees - git worktree add .worktrees/snapi-baseline "origin/${GITHUB_BASE_REF}" + git worktree add --detach .worktrees/snapi-baseline "${{ github.event.pull_request.base.sha }}" cp snapi.config.json .worktrees/snapi-baseline/snapi.config.json - name: Install baseline dependencies