Files
openclaw/scripts/github/pr-ci-sweeper.d.mts
Peter Steinberger 53ad69c6ee ci: hourly sweeper re-fires dropped pull_request CI runs (#110889)
* ci: add hourly PR CI sweeper for dropped pull_request runs

Fresh PRs race GitHub's merge-ref computation: the open-event CI run can
drop entirely or be created as an un-rerunnable startup_failure (~10-16
runs daily). The sweeper lists recently updated open PRs hourly, finds
heads whose only pull_request-event CI runs are startup failures (or
missing), and re-fires the event by close/reopen with the Barnacle app
token (GITHUB_TOKEN events would not trigger workflows).

Safety: 10-minute quiet window, 24h lookback, skips drafts, merge
conflicts, pending mergeability, and auto-merge PRs (close cancels
auto-merge); revalidates state, head, and CI attachment immediately
before mutating; per-PR budget of two sweeper closes and a per-sweep cap
of ten; reopen-on-unknown ownership so a stranded close (silent) always
loses to a spurious reopen (visible); manual dispatch supports dry_run.

Accepted tradeoffs are documented inline: shared-SHA PR topologies can
mask a dropped run (skip-only miss; run.pull_requests matching would
break fork PRs), and app-auth failover at worst doubles the close budget.

* test(ci): exercise pr-ci-sweeper runner with a faked client

* fix(test): lint-clean pr-ci-sweeper runner fakes
2026-07-18 20:18:57 +01:00

20 lines
652 B
TypeScript

export function classifyPrForSweep(params: {
pr: {
draft?: boolean;
created_at: string;
updated_at: string;
mergeable?: boolean | null;
auto_merge?: object | null;
};
ciRuns: Array<{ conclusion: string | null }>;
botCloseCount: number;
now: number;
}): { action: "refire" | "skip"; reason: string };
export function runPrCiSweeper(params: {
github: Record<string, unknown>;
context: Record<string, unknown>;
core: Pick<Console, "info"> & { setFailed: (message: string) => void };
dryRun?: boolean;
appSlug?: string;
}): Promise<Array<{ number: number; sha: string; action: "refire" | "skip"; reason: string }>>;