diff --git a/.changeset/release-dispatch-recovery.md b/.changeset/release-dispatch-recovery.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/release-dispatch-recovery.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b5a527b8e0..76568532782 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,6 +73,7 @@ jobs: NPM_CONFIG_PROVENANCE: true - name: Trigger workflows on related repos + id: trigger if: steps.changesets.outputs.published == 'true' uses: actions/github-script@v7 with: @@ -114,6 +115,87 @@ jobs: core.warning("Changeset in pre-mode should not prepare a ClerkJS production release") } + # Recovery: if the changesets action published to npm but then failed + # (e.g. git push --follow-tags error), the `published` output is never + # set and downstream repos are not notified. This step detects that + # scenario by checking npm for the local package version and dispatches + # if the packages are already live. + - name: Recover downstream notifications + if: always() && steps.changesets.conclusion == 'failure' + uses: actions/github-script@v7 + with: + result-encoding: string + retries: 3 + retry-exempt-status-codes: 400,401 + github-token: ${{ secrets.CLERK_COOKIE_PAT }} + script: | + const { execSync } = require('child_process'); + + const clerkjsVersion = require('./packages/clerk-js/package.json').version; + const clerkUiVersion = require('./packages/ui/package.json').version; + + // Only recover stable releases + if (clerkjsVersion.includes('-')) { + console.log(`Skipping recovery: ${clerkjsVersion} is a pre-release`); + return; + } + + // Check if either version was actually published to npm + function isPublished(name, version) { + try { + return execSync(`npm view ${name}@${version} version`, { encoding: 'utf8' }).trim() === version; + } catch { + return false; + } + } + + const clerkjsPublished = isPublished('@clerk/clerk-js', clerkjsVersion); + const clerkUiPublished = isPublished('@clerk/ui', clerkUiVersion); + + if (!clerkjsPublished && !clerkUiPublished) { + console.log('Neither @clerk/clerk-js nor @clerk/ui were published to npm, no recovery needed'); + return; + } + + const published = [ + clerkjsPublished && `@clerk/clerk-js@${clerkjsVersion}`, + clerkUiPublished && `@clerk/ui@${clerkUiVersion}`, + ].filter(Boolean).join(', '); + core.warning(`Recovery: ${published} published to npm but downstream repos were not notified. Dispatching now.`); + + const preMode = require("fs").existsSync("./.changeset/pre.json"); + if (preMode) { + core.warning("Changeset in pre-mode, skipping recovery dispatch"); + return; + } + + const nextjsVersion = require('./packages/nextjs/package.json').version; + + const dispatches = [ + github.rest.actions.createWorkflowDispatch({ + owner: 'clerk', + repo: 'sdk-infra-workers', + workflow_id: 'update-pkg-versions.yml', + ref: 'main', + inputs: { clerkjsVersion, clerkUiVersion } + }), + github.rest.actions.createWorkflowDispatch({ + owner: 'clerk', + repo: 'dashboard', + workflow_id: 'prepare-nextjs-sdk-update.yml', + ref: 'main', + inputs: { version: nextjsVersion } + }), + github.rest.actions.createWorkflowDispatch({ + owner: 'clerk', + repo: 'clerk-docs', + workflow_id: 'typedoc.yml', + ref: 'main', + }), + ]; + await Promise.all(dispatches); + core.notice('Recovery dispatch completed successfully'); + - name: Generate notification payload id: notification if: steps.changesets.outputs.published == 'true'