feat(fleet): resilient PR merge on ship (inline attempt + background retry)
The corporate proxy intermittently 407s GitHub's API, so a single gh pr merge can fail transiently. Try once inline (fast path), then retry in the background with backoff (3s/8s/20s/45s) without blocking the ship; mark prState=merged when one lands. Best-effort throughout.
This commit is contained in:
parent
740335a149
commit
2bd97791c9
@ -643,17 +643,44 @@ const execFileAsync = promisify(execFile);
|
||||
* authenticated (the coordinator host). Best-effort: a failure leaves the PR open
|
||||
* and never fails the ship. Marks the run prState=merged on success.
|
||||
*/
|
||||
async function ghMergePr(prUrl: string): Promise<boolean> {
|
||||
try {
|
||||
await execFileAsync('gh', ['pr', 'merge', prUrl, '--squash', '--delete-branch'], {
|
||||
timeout: 60_000,
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
// Network/proxy hiccup (e.g. intermittent 407), checks pending, or conflict.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function mergeRunPrOnShip(jobId: string, latest: FleetRunDoc | undefined): Promise<void> {
|
||||
if (process.env.FLEET_SHIP_MERGES_PR !== '1') return;
|
||||
if (!latest?.prUrl || latest.prState === 'merged') return;
|
||||
try {
|
||||
await execFileAsync('gh', ['pr', 'merge', latest.prUrl, '--squash', '--delete-branch'], {
|
||||
timeout: 60_000,
|
||||
});
|
||||
await repo.updateRun(latest.id, jobId, { prState: 'merged' });
|
||||
} catch {
|
||||
// best-effort — leave the PR open if the merge is blocked (checks, conflicts…)
|
||||
const { prUrl, id: runId } = latest;
|
||||
// Fast attempt inline (so a healthy proxy merges immediately without delaying ship).
|
||||
if (await ghMergePr(prUrl)) {
|
||||
await repo.updateRun(runId, jobId, { prState: 'merged' });
|
||||
return;
|
||||
}
|
||||
// The corporate proxy intermittently 407s GitHub's API. Retry in the BACKGROUND
|
||||
// with backoff so the ship response is never blocked; mark merged when one lands.
|
||||
void (async () => {
|
||||
for (const delayMs of [3_000, 8_000, 20_000, 45_000]) {
|
||||
await new Promise(r => setTimeout(r, delayMs));
|
||||
if (await ghMergePr(prUrl)) {
|
||||
try {
|
||||
await repo.updateRun(runId, jobId, { prState: 'merged' });
|
||||
} catch {
|
||||
/* run gone — ignore */
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
})().catch(() => {
|
||||
/* detached best-effort — never throws */
|
||||
});
|
||||
}
|
||||
|
||||
export async function patchJobFenced(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user