Skip to content

Commit 67cd6b9

Browse files
authored
fix(repo): use Promise.allSettled for release downstream dispatches (#8102)
1 parent 8ccfbca commit 67cd6b9

2 files changed

Lines changed: 114 additions & 39 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.github/workflows/release.yml

Lines changed: 112 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ jobs:
8181

8282
- name: Trigger workflows on related repos
8383
if: steps.changesets.outputs.published == 'true'
84+
continue-on-error: true
8485
uses: actions/github-script@v7
8586
with:
8687
result-encoding: string
@@ -94,33 +95,107 @@ jobs:
9495
const clerkUiVersion = require('./packages/ui/package.json').version;
9596
const nextjsVersion = require('./packages/nextjs/package.json').version;
9697
97-
const dispatches = [
98-
github.rest.actions.createWorkflowDispatch({
99-
owner: 'clerk',
100-
repo: 'sdk-infra-workers',
101-
workflow_id: 'update-pkg-versions.yml',
102-
ref: 'main',
103-
inputs: { clerkjsVersion, clerkUiVersion }
104-
}),
105-
github.rest.actions.createWorkflowDispatch({
106-
owner: 'clerk',
107-
repo: 'dashboard',
108-
workflow_id: 'prepare-nextjs-sdk-update.yml',
109-
ref: 'main',
110-
inputs: { version: nextjsVersion }
111-
}),
112-
github.rest.actions.createWorkflowDispatch({
113-
owner: 'clerk',
114-
repo: 'clerk-docs',
115-
workflow_id: 'typedoc.yml',
116-
ref: 'main',
117-
}),
98+
// NOTE: Keep in sync with the `targets` array in the "Recover downstream notifications" step below.
99+
const targets = [
100+
{ repo: 'sdk-infra-workers', workflow_id: 'update-pkg-versions.yml', inputs: { clerkjsVersion, clerkUiVersion } },
101+
{ repo: 'dashboard', workflow_id: 'prepare-nextjs-sdk-update.yml', inputs: { version: nextjsVersion } },
102+
{ repo: 'clerk-docs', workflow_id: 'typedoc.yml' },
118103
];
119-
await Promise.all(dispatches);
104+
const results = await Promise.allSettled(
105+
targets.map(t => github.rest.actions.createWorkflowDispatch({ owner: 'clerk', ref: 'main', ...t }))
106+
);
107+
const failures = results
108+
.map((r, i) => r.status === 'rejected' ? { target: targets[i], reason: r.reason } : null)
109+
.filter(Boolean);
110+
if (failures.length) {
111+
failures.forEach(f => core.error(`Dispatch to ${f.target.repo}/${f.target.workflow_id} failed: ${f.reason?.message ?? f.reason}`));
112+
core.setFailed(`${failures.length} downstream dispatch(es) failed`);
113+
}
120114
} else{
121115
core.warning("Changeset in pre-mode should not prepare a ClerkJS production release")
122116
}
123117
118+
# Recovery: if the changesets action published to npm but then failed
119+
# (e.g. git push --follow-tags error), the `published` output is never
120+
# set and downstream repos are not notified. This step detects that
121+
# scenario by checking npm for the local package version and dispatches
122+
# if the packages are already live.
123+
- name: Recover downstream notifications
124+
if: always() && steps.changesets.conclusion == 'failure'
125+
continue-on-error: true
126+
uses: actions/github-script@v7
127+
with:
128+
result-encoding: string
129+
retries: 3
130+
retry-exempt-status-codes: 400,401
131+
github-token: ${{ secrets.CLERK_COOKIE_PAT }}
132+
script: |
133+
const { execSync } = require('child_process');
134+
135+
const clerkjsVersion = require('./packages/clerk-js/package.json').version;
136+
const clerkUiVersion = require('./packages/ui/package.json').version;
137+
138+
// Only recover stable releases
139+
const preReleases = [
140+
clerkjsVersion.includes('-') && `@clerk/clerk-js@${clerkjsVersion}`,
141+
clerkUiVersion.includes('-') && `@clerk/ui@${clerkUiVersion}`,
142+
].filter(Boolean);
143+
if (preReleases.length > 0) {
144+
console.log(`Skipping recovery: ${preReleases.join(', ')} is a pre-release`);
145+
return;
146+
}
147+
148+
const preMode = require("fs").existsSync("./.changeset/pre.json");
149+
if (preMode) {
150+
core.warning("Changeset in pre-mode, skipping recovery dispatch");
151+
return;
152+
}
153+
154+
// Check if either version was actually published to npm
155+
function isPublished(name, version) {
156+
try {
157+
return execSync(`npm view ${name}@${version} version`, { encoding: 'utf8' }).trim() === version;
158+
} catch (e) {
159+
console.log(`npm view ${name}@${version} failed: ${e.message}`);
160+
return false;
161+
}
162+
}
163+
164+
const clerkjsPublished = isPublished('@clerk/clerk-js', clerkjsVersion);
165+
const clerkUiPublished = isPublished('@clerk/ui', clerkUiVersion);
166+
167+
if (!clerkjsPublished && !clerkUiPublished) {
168+
console.log('Neither @clerk/clerk-js nor @clerk/ui were published to npm, no recovery needed');
169+
return;
170+
}
171+
172+
const published = [
173+
clerkjsPublished && `@clerk/clerk-js@${clerkjsVersion}`,
174+
clerkUiPublished && `@clerk/ui@${clerkUiVersion}`,
175+
].filter(Boolean).join(', ');
176+
core.warning(`Recovery: ${published} published to npm but downstream repos were not notified. Dispatching now.`);
177+
178+
const nextjsVersion = require('./packages/nextjs/package.json').version;
179+
180+
// NOTE: Keep in sync with the `targets` array in the "Trigger workflows on related repos" step above.
181+
const targets = [
182+
{ repo: 'sdk-infra-workers', workflow_id: 'update-pkg-versions.yml', inputs: { clerkjsVersion, clerkUiVersion } },
183+
{ repo: 'dashboard', workflow_id: 'prepare-nextjs-sdk-update.yml', inputs: { version: nextjsVersion } },
184+
{ repo: 'clerk-docs', workflow_id: 'typedoc.yml' },
185+
];
186+
const results = await Promise.allSettled(
187+
targets.map(t => github.rest.actions.createWorkflowDispatch({ owner: 'clerk', ref: 'main', ...t }))
188+
);
189+
const failures = results
190+
.map((r, i) => r.status === 'rejected' ? { target: targets[i], reason: r.reason } : null)
191+
.filter(Boolean);
192+
if (failures.length) {
193+
failures.forEach(f => core.error(`Recovery dispatch to ${f.target.repo}/${f.target.workflow_id} failed: ${f.reason?.message ?? f.reason}`));
194+
core.setFailed(`${failures.length} recovery dispatch(es) failed`);
195+
} else {
196+
core.notice('Recovery dispatch completed successfully');
197+
}
198+
124199
- name: Generate notification payload
125200
id: notification
126201
if: steps.changesets.outputs.published == 'true'
@@ -205,6 +280,7 @@ jobs:
205280

206281
- name: Trigger workflows on related repos
207282
if: steps.publish.outcome == 'success'
283+
continue-on-error: true
208284
uses: actions/github-script@v7
209285
with:
210286
result-encoding: string
@@ -216,30 +292,27 @@ jobs:
216292
const clerkUiVersion = require('./packages/ui/package.json').version;
217293
const nextjsVersion = require('./packages/nextjs/package.json').version;
218294
219-
const dispatches = [
220-
github.rest.actions.createWorkflowDispatch({
221-
owner: 'clerk',
222-
repo: 'sdk-infra-workers',
223-
workflow_id: 'update-pkg-versions.yml',
224-
ref: 'main',
225-
inputs: { clerkjsVersion, clerkUiVersion, sourceCommit: context.sha }
226-
}),
295+
const targets = [
296+
{ repo: 'sdk-infra-workers', workflow_id: 'update-pkg-versions.yml', inputs: { clerkjsVersion, clerkUiVersion, sourceCommit: context.sha } },
227297
];
228298
229299
if (nextjsVersion.includes('canary')) {
230300
console.log('clerk/nextjs changed, will notify clerk/accounts');
231-
dispatches.push(
232-
github.rest.actions.createWorkflowDispatch({
233-
owner: 'clerk',
234-
repo: 'accounts',
235-
workflow_id: 'release-staging.yml',
236-
ref: 'main',
237-
inputs: { version: nextjsVersion }
238-
}),
301+
targets.push(
302+
{ repo: 'accounts', workflow_id: 'release-staging.yml', inputs: { version: nextjsVersion } },
239303
);
240304
}
241305
242-
await Promise.all(dispatches);
306+
const results = await Promise.allSettled(
307+
targets.map(t => github.rest.actions.createWorkflowDispatch({ owner: 'clerk', ref: 'main', ...t }))
308+
);
309+
const failures = results
310+
.map((r, i) => r.status === 'rejected' ? { target: targets[i], reason: r.reason } : null)
311+
.filter(Boolean);
312+
if (failures.length) {
313+
failures.forEach(f => core.error(`Dispatch to ${f.target.repo}/${f.target.workflow_id} failed: ${f.reason?.message ?? f.reason}`));
314+
core.setFailed(`${failures.length} downstream dispatch(es) failed`);
315+
}
243316
244317
- name: Notify Slack on failure
245318
if: ${{ always() && steps.publish.outcome == 'failure' }}

0 commit comments

Comments
 (0)