fix(scripts): ignore superseded target dispatch checks (#128704)

This commit is contained in:
Peter Steinberger
2026-08-24 05:09:28 -07:00
committed by GitHub
parent 41050b2f3b
commit e45e4ca69f
2 changed files with 104 additions and 8 deletions
+32 -8
View File
@@ -32,6 +32,7 @@ const RollupCheckSchema = z.object({
workflowRun: optionalNullable(
z.object({
databaseId: optionalNumber,
event: optionalString,
workflow: optional(z.object({ databaseId: optionalNumber })),
}),
),
@@ -66,7 +67,7 @@ const RollupResponseSchema = z.object({
repository: z.object({ pullRequest: RollupPageSchema.nullish() }).nullish(),
}),
});
const RunListItemSchema = z.object({ id: z.number(), created_at: z.string() });
const RunListItemSchema = z.object({ id: z.number(), workflow_id: optionalNumber });
const RunListSchema = z
.object({ workflow_runs: validArray(RunListItemSchema) })
.transform((response) => response.workflow_runs)
@@ -89,7 +90,7 @@ const FAILURE_CONCLUSIONS = new Set([
"STALE",
"TIMED_OUT",
]);
const ROLLUP_QUERY = `query($owner:String!,$name:String!,$pr:Int!,$cursor:String){repository(owner:$owner,name:$name){pullRequest(number:$pr){state mergeable headRefOid statusCheckRollup{state contexts(first:100,after:$cursor){totalCount pageInfo{hasNextPage endCursor} nodes{kind:__typename ... on CheckRun{name status conclusion databaseId checkSuite{workflowRun{databaseId workflow{databaseId}}}} ... on StatusContext{context state}}}}}}}`;
const ROLLUP_QUERY = `query($owner:String!,$name:String!,$pr:Int!,$cursor:String){repository(owner:$owner,name:$name){pullRequest(number:$pr){state mergeable headRefOid statusCheckRollup{state contexts(first:100,after:$cursor){totalCount pageInfo{hasNextPage endCursor} nodes{kind:__typename ... on CheckRun{name status conclusion databaseId checkSuite{workflowRun{databaseId event workflow{databaseId}}}} ... on StatusContext{context state}}}}}}}`;
const GH_READ_OPTIONS = {
stdio: ["ignore", "pipe", "pipe"],
timeout: 60_000,
@@ -192,7 +193,7 @@ function checkRunIdentity(check: RollupCheck) {
const newerJob = (a: JobIdentity, b: JobIdentity) =>
a.runId !== b.runId ? a.runId > b.runId : a.checkId > b.checkId;
export function classifyRollup(rollup: RollupPayload | null | undefined) {
export function classifyRollup(rollup: RollupPayload | null | undefined, runs: RunListItem[] = []) {
const rawNodes = rollup?.contexts?.nodes ?? [];
const hiddenContextCount = Math.max(
0,
@@ -221,8 +222,8 @@ export function classifyRollup(rollup: RollupPayload | null | undefined) {
let supersededCount = 0;
// Re-triggers leave every prior run's check runs on the SHA forever and GitHub's aggregate
// counts them. A check is superseded when a newer same-workflow check shares its name
// (GitHub's latest-name-wins semantics), or when it was cancelled and its workflow has a
// newer run (draft->ready cancels the old run before the replacement posts check runs).
// (GitHub's latest-name-wins semantics), or when its cancelled workflow has a newer run.
// Actions run metadata also proves target-run supersession before a newer run posts jobs.
// Older-run checks with unique names stay visible so distinct invocations are not dropped.
const nodes = rawNodes.filter((check) => {
const identity = checkRunIdentity(check);
@@ -236,8 +237,13 @@ export function classifyRollup(rollup: RollupPayload | null | undefined) {
return false;
}
}
const newestRun = newestRunByWorkflow.get(identity.workflowId);
if (check.conclusion === "CANCELLED" && newestRun !== undefined && newestRun > identity.runId) {
const newestRun = newestRunByWorkflow.get(identity.workflowId) ?? identity.runId;
if (
check.conclusion === "CANCELLED" &&
(newestRun > identity.runId ||
(check.checkSuite?.workflowRun?.event === "pull_request_target" &&
runs.some((run) => run.workflow_id === identity.workflowId && run.id > identity.runId)))
) {
supersededCount += 1;
return false;
}
@@ -310,6 +316,13 @@ const findRun = (repo: string, sha: string, after?: number) =>
RunListSchema.parse(execGhJson(buildFindRunArgs(repo, sha), GH_READ_OPTIONS)),
after,
);
const findTargetRuns = (repo: string, sha: string) =>
RunListSchema.parse(
execGhJson(
["api", `repos/${repo}/actions/runs?event=pull_request_target&head_sha=${sha}&per_page=100`],
GH_READ_OPTIONS,
),
);
const readRun = (repo: string, runId: number) =>
RunStatusSchema.parse(
execGhJson(
@@ -541,7 +554,18 @@ async function main(argv = process.argv.slice(2)) {
if (blocked !== null) {
return blocked;
}
const result = classifyRollup(pr.statusCheckRollup);
let result = classifyRollup(pr.statusCheckRollup);
if (
result.verdict === "FAILING" &&
pr.statusCheckRollup?.contexts?.nodes?.some(
(check) =>
check.conclusion === "CANCELLED" &&
check.checkSuite?.workflowRun?.event === "pull_request_target" &&
checkRunIdentity(check),
)
) {
result = classifyRollup(pr.statusCheckRollup, findTargetRuns(args.repo, args.headSha));
}
lastState = pr.statusCheckRollup?.state ?? "NONE";
lastPending = result.pendingCount;
console.log(