fix(clerk-js): Fix token cache refresh timer leak #26489
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: | |
| merge_group: | |
| pull_request: | |
| branches: | |
| - main | |
| - release/v4 | |
| - release/core-2 | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Check triggering actor permissions to prevent PRs from forks accessing secrets by default, preventing them from exfiltrating secrets for malicious purposes | |
| check-permissions: | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| defaults: | |
| run: | |
| shell: bash | |
| name: Check Permissions | |
| steps: | |
| - name: Skip permission check for same‑repo PRs | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} | |
| run: echo "Permission check not required." | |
| - name: Get User Permission | |
| if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} | |
| id: checkAccess | |
| uses: actions-cool/check-user-permission@v2 | |
| with: | |
| require: write | |
| username: ${{ github.triggering_actor }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check User Permission | |
| if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && steps.checkAccess.outputs.require-result == 'false' }} | |
| run: | | |
| echo "${{ github.triggering_actor }} does not have permissions on this repo." | |
| echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" | |
| echo "Job originally triggered by ${{ github.actor }}" | |
| exit 1 | |
| pre-checks: | |
| needs: [check-permissions] | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} | |
| name: Formatting | Dedupe | Changeset | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| defaults: | |
| run: | |
| shell: bash | |
| timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 5 }} | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 100 # Enough for changeset status comparison, much faster than full history | |
| fetch-tags: false | |
| filter: "blob:none" | |
| show-progress: false | |
| - name: Fetch main branch for changeset comparison | |
| run: git fetch origin main:refs/remotes/origin/main --depth=100 | |
| - name: Setup | |
| id: config | |
| uses: ./.github/actions/init-blacksmith | |
| with: | |
| turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | |
| turbo-team: ${{ vars.TURBO_TEAM }} | |
| turbo-token: ${{ secrets.TURBO_TOKEN }} | |
| - name: Verify lockfile is deduped | |
| run: pnpm dedupe --check | |
| - name: Check Formatting | |
| run: pnpm format:check | |
| - name: Require Changeset | |
| if: ${{ !(github.event_name == 'merge_group') }} | |
| run: | | |
| if [[ "${{ github.event.pull_request.user.login }}" = "clerk-cookie" || "${{ github.event.pull_request.user.login }}" = "renovate[bot]" ]]; then | |
| echo 'Skipping'; | |
| exit 0; | |
| else | |
| pnpm changeset status --since=origin/main; | |
| fi | |
| build-packages: | |
| needs: [check-permissions] | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} | |
| name: Build Packages | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| permissions: | |
| contents: read | |
| defaults: | |
| run: | |
| shell: bash | |
| timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }} | |
| env: | |
| TURBO_SUMMARIZE: false | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| fetch-tags: false | |
| filter: "blob:none" | |
| show-progress: false | |
| - name: Setup | |
| id: config | |
| uses: ./.github/actions/init-blacksmith | |
| with: | |
| turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | |
| turbo-summarize: ${{ env.TURBO_SUMMARIZE }} | |
| turbo-team: ${{ vars.TURBO_TEAM }} | |
| turbo-token: ${{ secrets.TURBO_TOKEN }} | |
| - name: Turbo Build | |
| run: pnpm turbo build $TURBO_ARGS --only | |
| - name: Upload Turbo Summary | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ env.TURBO_SUMMARIZE == 'true' }} | |
| continue-on-error: true | |
| with: | |
| name: turbo-summary-report-build-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: .turbo/runs | |
| retention-days: 5 | |
| static-analysis: | |
| needs: [check-permissions, build-packages] | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} | |
| name: Static analysis | |
| permissions: | |
| contents: read | |
| actions: write # needed for actions/upload-artifact | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| defaults: | |
| run: | |
| shell: bash | |
| timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }} | |
| env: | |
| TURBO_SUMMARIZE: false | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| fetch-tags: false | |
| filter: "blob:none" | |
| show-progress: false | |
| - name: Setup | |
| id: config | |
| uses: ./.github/actions/init-blacksmith | |
| with: | |
| turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | |
| turbo-summarize: ${{ env.TURBO_SUMMARIZE }} | |
| turbo-team: ${{ vars.TURBO_TEAM }} | |
| turbo-token: ${{ secrets.TURBO_TOKEN }} | |
| - name: Check size using bundlewatch | |
| continue-on-error: true | |
| env: | |
| BUNDLEWATCH_GITHUB_TOKEN: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} | |
| CI_BRANCH: ${{ github.ref }} | |
| CI_BRANCH_BASE: refs/heads/main | |
| CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} | |
| CI_REPO_NAME: ${{ vars.REPO_NAME }} | |
| CI_REPO_OWNER: ${{ vars.REPO_OWNER }} | |
| run: pnpm turbo bundlewatch $TURBO_ARGS | |
| - name: Lint packages using publint | |
| run: pnpm turbo lint:publint $TURBO_ARGS | |
| - name: Lint types using attw | |
| run: pnpm turbo lint:attw $TURBO_ARGS | |
| - name: Run lint | |
| run: pnpm turbo lint $TURBO_ARGS | |
| - name: Upload Turbo Summary | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ env.TURBO_SUMMARIZE == 'true' }} | |
| continue-on-error: true | |
| with: | |
| name: turbo-summary-report-lint-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: .turbo/runs | |
| retention-days: 5 | |
| unit-tests: | |
| needs: [check-permissions, build-packages] | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} | |
| name: Unit Tests (${{ matrix.filter-label }}) | |
| permissions: | |
| contents: read | |
| actions: write # needed for actions/upload-artifact | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| defaults: | |
| run: | |
| shell: bash | |
| timeout-minutes: ${{ vars.TIMEOUT_MINUTES_NORMAL && fromJSON(vars.TIMEOUT_MINUTES_NORMAL) || 10 }} | |
| env: | |
| TURBO_SUMMARIZE: false | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - node-version: 22 | |
| test-filter: "**" | |
| filter-label: "**" | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| fetch-tags: false | |
| filter: "blob:none" | |
| show-progress: false | |
| - name: Setup | |
| id: config | |
| uses: ./.github/actions/init-blacksmith | |
| with: | |
| # Ensures that all builds are cached appropriately with a consistent run name `Unit Tests (20)`. | |
| node-version: ${{ matrix.node-version }} | |
| turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | |
| turbo-summarize: ${{ env.TURBO_SUMMARIZE }} | |
| turbo-team: ${{ vars.TURBO_TEAM }} | |
| turbo-token: ${{ secrets.TURBO_TOKEN }} | |
| - name: Run tests in packages | |
| run: | | |
| if [ "${{ matrix.test-filter }}" = "**" ]; then | |
| echo "Running full test suite on Node ${{ matrix.node-version }}" | |
| pnpm turbo test $TURBO_ARGS | |
| else | |
| echo "Running tests: ${{ matrix.filter-label }}" | |
| pnpm turbo test $TURBO_ARGS ${{ matrix.test-filter }} | |
| fi | |
| env: | |
| NODE_VERSION: ${{ matrix.node-version }} | |
| - name: Run Typedoc tests | |
| run: | | |
| # Only run Typedoc tests for one matrix version and main test run | |
| if [ "${{ matrix.node-version }}" == "22" ] && [ "${{ matrix.test-filter }}" = "**" ]; then | |
| pnpm turbo run //#test:typedoc | |
| fi | |
| env: | |
| NODE_VERSION: ${{ matrix.node-version }} | |
| - name: Upload Turbo Summary | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ env.TURBO_SUMMARIZE == 'true' }} | |
| continue-on-error: true | |
| with: | |
| name: turbo-summary-report-unit-${{ github.run_id }}-${{ github.run_attempt }}-node-${{ matrix.node-version }} | |
| path: .turbo/runs | |
| retention-days: 5 | |
| integration-tests: | |
| needs: [check-permissions, build-packages] | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} | |
| name: Integration Tests (${{ matrix.test-name }}, ${{ matrix.test-project }}${{ matrix.next-version && format(', {0}', matrix.next-version) || '' }}) | |
| permissions: | |
| contents: read | |
| actions: write # needed for actions/upload-artifact | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| defaults: | |
| run: | |
| shell: bash | |
| timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| test-name: | |
| [ | |
| "generic", | |
| "express", | |
| "fastify", | |
| "ap-flows", | |
| "localhost", | |
| "sessions", | |
| "sessions:staging", | |
| "handshake", | |
| "handshake:staging", | |
| "astro", | |
| "tanstack-react-start", | |
| "vue", | |
| "nuxt", | |
| "react-router", | |
| "custom", | |
| "hono", | |
| "chrome-extension", | |
| ] | |
| test-project: ["chrome"] | |
| include: | |
| - test-name: "billing" | |
| test-project: "chrome" | |
| - test-name: "machine" | |
| test-project: "chrome" | |
| - test-name: "nextjs" | |
| test-project: "chrome" | |
| next-version: "15" | |
| - test-name: "nextjs" | |
| test-project: "chrome" | |
| next-version: "16" | |
| - test-name: "quickstart" | |
| test-project: "chrome" | |
| next-version: "15" | |
| - test-name: "quickstart" | |
| test-project: "chrome" | |
| next-version: "16" | |
| - test-name: "cache-components" | |
| test-project: "chrome" | |
| next-version: "16" | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| fetch-tags: false | |
| filter: "blob:none" | |
| show-progress: false | |
| - name: Setup | |
| id: config | |
| uses: ./.github/actions/init-blacksmith | |
| with: | |
| turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | |
| turbo-team: ${{ vars.TURBO_TEAM }} | |
| turbo-token: ${{ secrets.TURBO_TOKEN }} | |
| playwright-enabled: true | |
| - name: Verify jq is installed | |
| shell: bash | |
| run: | | |
| if ! command -v jq &> /dev/null; then | |
| echo "jq not found, installing..." | |
| sudo apt-get update && sudo apt-get install -y jq | |
| fi | |
| jq --version | |
| - name: Validate turbo task | |
| env: | |
| E2E_APP_CLERK_JS_DIR: ${{runner.temp}} | |
| E2E_APP_CLERK_UI_DIR: ${{runner.temp}} | |
| E2E_CLERK_JS_VERSION: "latest" | |
| E2E_CLERK_UI_VERSION: "latest" | |
| E2E_NEXTJS_VERSION: ${{ matrix.next-version }} | |
| E2E_PROJECT: ${{ matrix.test-project }} | |
| INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }} | |
| run: | | |
| # Validate the turbo task exists. Turbo's --affected flag is a no-op for | |
| # root-level tasks (//#) — they are always returned as affected. The previous | |
| # --affected check only served to silently skip tests when the task was missing | |
| # from turbo.json, hiding real configuration errors. | |
| TASK_NAME="test:integration:${{ matrix.test-name }}" | |
| TURBO_STDERR=$(mktemp) | |
| if ! TURBO_JSON=$(pnpm turbo run "$TASK_NAME" --dry=json 2>"$TURBO_STDERR"); then | |
| echo "::error::Turbo task '$TASK_NAME' failed validation" | |
| cat "$TURBO_STDERR" | |
| exit 1 | |
| fi | |
| if ! TASK_COUNT=$(jq -er '.tasks | length' <<< "$TURBO_JSON"); then | |
| echo "::error::Turbo task '$TASK_NAME' returned invalid JSON or missing .tasks" | |
| printf '%s\n' "$TURBO_JSON" | |
| exit 1 | |
| fi | |
| if [ "$TASK_COUNT" -eq 0 ]; then | |
| echo "::error::Turbo task '$TASK_NAME' returned 0 tasks" | |
| exit 1 | |
| fi | |
| echo "Task '$TASK_NAME' validated ($TASK_COUNT tasks in graph)" | |
| - name: Build packages | |
| run: pnpm turbo build $TURBO_ARGS --only | |
| - name: Publish to local registry | |
| run: pkglab pub --force | |
| - name: Edit .npmrc [link-workspace-packages=false] | |
| run: sed -i -E 's/link-workspace-packages=(deep|true)/link-workspace-packages=false/' .npmrc | |
| - name: Install @clerk/clerk-js in os temp | |
| working-directory: ${{runner.temp}} | |
| run: | | |
| mkdir clerk-js && cd clerk-js | |
| pnpm init | |
| pkglab add @clerk/clerk-js | |
| - name: Install @clerk/ui in os temp | |
| working-directory: ${{runner.temp}} | |
| run: | | |
| mkdir clerk-ui && cd clerk-ui | |
| pnpm init | |
| pkglab add @clerk/ui | |
| - name: Copy components @clerk/astro | |
| if: ${{ matrix.test-name == 'astro' }} | |
| run: cd packages/astro && pnpm copy:components | |
| - name: Write all ENV certificates to files in integration/certs | |
| uses: actions/github-script@v7 | |
| env: | |
| INTEGRATION_CERTS: "${{secrets.INTEGRATION_CERTS}}" | |
| INTEGRATION_ROOT_CA: "${{secrets.INTEGRATION_ROOT_CA}}" | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const rootCa = process.env.INTEGRATION_ROOT_CA; | |
| console.log('rootCa', rootCa); | |
| fs.writeFileSync(path.join(process.env.GITHUB_WORKSPACE, 'integration/certs', 'rootCA.pem'), rootCa); | |
| const certs = JSON.parse(process.env.INTEGRATION_CERTS); | |
| for (const [name, cert] of Object.entries(certs)) { | |
| fs.writeFileSync(path.join(process.env.GITHUB_WORKSPACE, 'integration/certs', name), cert); | |
| } | |
| - name: LS certs | |
| working-directory: ./integration/certs | |
| run: ls -la && pwd | |
| - name: Run Integration Tests | |
| id: integration-tests | |
| timeout-minutes: 25 | |
| run: pnpm turbo test:integration:${{ matrix.test-name }} $TURBO_ARGS | |
| env: | |
| E2E_DEBUG: "1" | |
| E2E_APP_CLERK_JS_DIR: ${{runner.temp}} | |
| E2E_APP_CLERK_UI_DIR: ${{runner.temp}} | |
| E2E_CLERK_JS_VERSION: "latest" | |
| E2E_CLERK_UI_VERSION: "latest" | |
| E2E_NEXTJS_VERSION: ${{ matrix.next-version }} | |
| E2E_PROJECT: ${{ matrix.test-project }} | |
| E2E_CLERK_ENCRYPTION_KEY: ${{ matrix.clerk-encryption-key }} | |
| INTEGRATION_INSTANCE_KEYS: ${{ secrets.INTEGRATION_INSTANCE_KEYS }} | |
| NODE_EXTRA_CA_CERTS: ${{ github.workspace }}/integration/certs/rootCA.pem | |
| VERCEL_AUTOMATION_BYPASS_SECRET: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }} | |
| - name: Upload test-results | |
| if: ${{ cancelled() || failure() }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-traces-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.test-name }}${{ matrix.next-version && format('-next{0}', matrix.next-version) || '' }} | |
| path: integration/test-results | |
| retention-days: 1 | |
| pkg-pr-new: | |
| name: Publish with pkg-pr-new | |
| if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} | |
| needs: [check-permissions, build-packages] | |
| runs-on: "blacksmith-8vcpu-ubuntu-2204" | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| TURBO_SUMMARIZE: false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| fetch-tags: false | |
| filter: "blob:none" | |
| show-progress: false | |
| - name: Setup Node | |
| uses: ./.github/actions/init-blacksmith | |
| with: | |
| turbo-enabled: true | |
| node-version: 22 | |
| turbo-signature: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} | |
| turbo-summarize: ${{ env.TURBO_SUMMARIZE }} | |
| turbo-team: ${{ vars.TURBO_TEAM }} | |
| turbo-token: ${{ secrets.TURBO_TOKEN }} | |
| - name: Publish with pkg-pr-new | |
| run: pnpm run build && pnpx pkg-pr-new@${{ vars.PKG_PR_NEW_VERSION || '0.0.49' }} publish --compact --pnpm './packages/*' |