Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .llms/skills/release-copilot/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ Seven steps, the last five through `scripts/hermes-release.mjs`:
6. `hermes-push`: pushes that commit only, after checking it changes exactly the three
pin files and sits on the branch's current tip.
7. `hermes-next-bump`: opens the PR moving the Hermes branch to the next version.
Skippable, since the pin does not depend on it.
Required: the release is not finished until the branch names the next version.

`hermesReadyToCut` matters because `RN Build Static Hermes` has **no version input**
and reads `npm/hermes-compiler/package.json` verbatim. `hermesPublishable` checks the
Expand Down Expand Up @@ -366,8 +366,8 @@ captain to **retype the version or branch** rather than accept `y`.

Declining normally **stops the run**, because later steps assume earlier ones
happened. Side steps nothing later depends on are **skippable** and say so with a
`skip:` line: the nightly, the template branch, the board, the support-table PR, a
board Target Release and the Hermes next bump. Declining one skips it, the run carries
`skip:` line: the nightly, the template branch, the board, the support-table PR and a
board Target Release. Declining one skips it, the run carries
on and lists it at the end as still to do. On the 0.89 cut the broken nightly could
only be accepted (and fail) or declined (and stop), so it blocked the publish.

Expand Down
11 changes: 10 additions & 1 deletion .llms/skills/release-copilot/__tests__/copilot.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3284,7 +3284,7 @@ test('hermes phase: wait, verify, pin, push and next bump all go through hermes-
assert.equal(push.confirmToken, '0.89-stable');
const bump = one('hermes-next-bump');
assert.equal(tail(bump), 'next-bump 260318099.0.0-stable 260318099.0.5 260318099.0.6');
assert.equal(bump.skippable, true, 'the next bump does not block the pin');
assert.equal(bump.skippable, false, 'the next bump is part of finishing the release, so declining it stops the run');
for (const a of [one('hermes-monitor'), one('hermes-verify-tag'), one('hermes-pin'), push, bump]) {
assert.match(a.args[0], /hermes-release\.mjs$/);
}
Expand Down Expand Up @@ -3888,3 +3888,12 @@ test('template check: only a full pass on both platforms is recorded', async ()
assert.equal(templateResult(v).ok, true);
rmSync(join(TEMPLATE_RESULTS, `${v}.json`));
});

test('hermes wait: only a dispatch from the last two hours counts', async () => {
const {recentDispatch} = await import('../scripts/hermes-release.mjs');
const now = Date.parse('2026-11-07T20:14:00Z');
const list = [{databaseId: 42409982858, createdAt: '2026-11-07T20:13:06Z'}, {databaseId: 41524889130, createdAt: '2026-11-02T21:03:54Z'}];
assert.equal(recentDispatch(list, {now}).databaseId, 42409982858);
assert.equal(recentDispatch([list[1]], {now}), null, 'a run from days ago is not this cut');
assert.equal(recentDispatch([], {now}), null);
});
28 changes: 22 additions & 6 deletions .llms/skills/release-copilot/scripts/hermes-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,29 @@ export function judgeRun(run) {
return {ok: true, done: true, detail: `run ${run.databaseId} tagged and published: ${run.url}`};
}

export async function waitForRun(version, {branch, sinceMinutes = 120, log = console.log} = {}) {
const list = JSON.parse(
await must('gh', ['run', 'list', '--repo', HERMES_REPO, '--workflow', WORKFLOW, '--branch', branch, '--event', 'workflow_dispatch', '--limit', '5', '--json', 'databaseId,createdAt,url,status']),
);
const recent = list.find(r => Date.now() - Date.parse(r.createdAt) < sinceMinutes * 60000);
/** The newest dispatch on the branch from the last `sinceMinutes`, or null. */
export function recentDispatch(list, {sinceMinutes = 120, now = Date.now()} = {}) {
return (list ?? []).find(r => now - Date.parse(r.createdAt) < sinceMinutes * 60000) ?? null;
}

export async function waitForRun(version, {branch, sinceMinutes = 120, appearMinutes = 5, log = console.log} = {}) {
// A run is not listed until a few seconds after its dispatch returns. On
// 260318099.0.7 the wait asked straight after the cut, found nothing and
// stopped the phase while the run was building.
let recent = null;
const until = Date.now() + appearMinutes * 60000;
for (;;) {
const list = JSON.parse(
await must('gh', ['run', 'list', '--repo', HERMES_REPO, '--workflow', WORKFLOW, '--branch', branch, '--event', 'workflow_dispatch', '--limit', '5', '--json', 'databaseId,createdAt,url,status']),
);
recent = recentDispatch(list, {sinceMinutes});
if (recent || Date.now() >= until) {
break;
}
await sleep(10000);
}
if (!recent) {
throw new Error(`no ${WORKFLOW} dispatch on ${branch} in the last ${sinceMinutes} minutes. Did the cut step run?`);
throw new Error(`no ${WORKFLOW} dispatch on ${branch} in the last ${sinceMinutes} minutes, after waiting ${appearMinutes} for one to appear. Did the cut step run?`);
}
log(`Watching ${recent.url} for ${version}`);
for (;;) {
Expand Down
3 changes: 1 addition & 2 deletions .llms/skills/release-copilot/scripts/phases.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -952,12 +952,11 @@ export const PHASES = {
'without it merged, the next cut re-cuts the version just released',
],
reversible: 'yes, close the PR and delete the branch',
skippable: true,
}),
];
},
note:
'This is the tail of THIS release, not the head of the next one: the file must always name the next version to publish. IMPORT the PR once it is open, the import is what adds rn-oss and Hermes as reviewers. See reference/hermes.md.',
'This is the tail of THIS release, not the head of the next one: the file must always name the next version to publish. Open it as soon as the release is out, or it gets forgotten: the .0.3 release never did and the branch named a published version for weeks. Declining stops the run so it is not skipped quietly. IMPORT the PR once it is open, the import is what adds rn-oss and Hermes as reviewers. See reference/hermes.md.',
},
],
},
Expand Down
Loading