Skip to content

Commit e3bc05d

Browse files
authored
Fix ciflow tag creation for cross-fork PRs (#7970)
For cross-fork PRs, GitHub's workflow_run webhook payload has an empty pull_requests array, so handleWorkflowRunEvent could never find the PR to create ciflow tags for. Fall back to searching for open PRs by the fork owner and branch name.
1 parent 64d0fe8 commit e3bc05d

2 files changed

Lines changed: 103 additions & 6 deletions

File tree

torchci/lib/bot/ciflowPushTrigger.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,34 @@ async function handleWorkflowRunEvent(context: Context<"workflow_run">) {
297297
return;
298298
}
299299

300-
const pullRequests = payload.workflow_run.pull_requests;
301-
if (!pullRequests || pullRequests.length === 0) {
300+
let prNumbers: number[] = (payload.workflow_run.pull_requests ?? []).map(
301+
(pr: any) => pr.number
302+
);
303+
304+
// For cross-fork PRs, the pull_requests array is empty.
305+
// Fall back to searching for PRs by the fork's owner and branch name.
306+
if (prNumbers.length === 0) {
307+
const headRepo = payload.workflow_run.head_repository;
308+
const headBranch = payload.workflow_run.head_branch;
309+
if (headRepo && headBranch) {
310+
const head = `${headRepo.owner.login}:${headBranch}`;
311+
context.log.info(
312+
`workflow_run has empty pull_requests, looking up PRs with head=${head}`
313+
);
314+
const prs = await context.octokit.pulls.list(
315+
context.repo({ head, state: "open" })
316+
);
317+
prNumbers = prs.data.map((pr) => pr.number);
318+
}
319+
}
320+
321+
if (prNumbers.length === 0) {
302322
return;
303323
}
304324

305-
for (const pr of pullRequests) {
325+
for (const prNum of prNumbers) {
306326
const prData = await context.octokit.pulls.get(
307-
context.repo({ pull_number: pr.number })
327+
context.repo({ pull_number: prNum })
308328
);
309329

310330
if (prData.data.state === "closed") {
@@ -320,13 +340,13 @@ async function handleWorkflowRunEvent(context: Context<"workflow_run">) {
320340
}
321341

322342
const headSha = prData.data.head.sha;
323-
const tags = ciflowLabels.map((l: string) => labelToTag(l, pr.number));
343+
const tags = ciflowLabels.map((l: string) => labelToTag(l, prNum));
324344
const promises = tags.map(
325345
async (tag: string) => await syncTag(context as any, tag, headSha)
326346
);
327347
await Promise.all(promises);
328348

329-
await resolvePendingComment(context as any, pr.number);
349+
await resolvePendingComment(context as any, prNum);
330350
}
331351
}
332352

torchci/test/ciflow-push-trigger.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,4 +517,81 @@ describe("Push trigger integration tests", () => {
517517
// No tag creation or label removal should happen
518518
await probot.receive({ name: "pull_request", id: "123", payload });
519519
});
520+
521+
test("workflow_run with empty pull_requests falls back to SHA lookup", async () => {
522+
const head_sha = "abc123def456";
523+
const prNum = 42;
524+
const repoFullName = "suo/actions-test";
525+
526+
const payload = {
527+
action: "requested",
528+
workflow_run: {
529+
event: "pull_request",
530+
head_sha: head_sha,
531+
head_branch: "feature-branch",
532+
head_repository: {
533+
owner: { login: "fork-user" },
534+
},
535+
pull_requests: [],
536+
},
537+
repository: {
538+
owner: { login: "suo" },
539+
name: "actions-test",
540+
full_name: repoFullName,
541+
},
542+
};
543+
544+
// Fall back: lookup PRs by fork owner and branch
545+
nock("https://api.github.com")
546+
.get(
547+
`/repos/${repoFullName}/pulls?head=${encodeURIComponent(
548+
"fork-user:feature-branch"
549+
)}&state=open`
550+
)
551+
.reply(200, [{ number: prNum }]);
552+
553+
// Fetch PR data
554+
nock("https://api.github.com")
555+
.get(`/repos/${repoFullName}/pulls/${prNum}`)
556+
.reply(200, {
557+
state: "open",
558+
head: { sha: head_sha },
559+
labels: [{ name: "ciflow/trunk" }],
560+
});
561+
562+
// syncTag: check existing tags
563+
nock("https://api.github.com")
564+
.get(
565+
`/repos/${repoFullName}/git/matching-refs/${encodeURIComponent(
566+
`tags/ciflow/trunk/${prNum}`
567+
)}`
568+
)
569+
.reply(200, []);
570+
571+
// syncTag: create tag
572+
nock("https://api.github.com")
573+
.post(`/repos/${repoFullName}/git/refs`, (body) => {
574+
expect(body).toMatchObject({
575+
ref: `refs/tags/ciflow/trunk/${prNum}`,
576+
sha: head_sha,
577+
});
578+
return true;
579+
})
580+
.reply(200);
581+
582+
// Resolve pending comment
583+
mockListComments(repoFullName, prNum, [
584+
{
585+
id: 99,
586+
body: "<!-- ciflow-pending -->\nWorkflows awaiting approval",
587+
},
588+
]);
589+
mockUpdateComment(repoFullName, 99, "CI has now been triggered");
590+
591+
await probot.receive({
592+
name: "workflow_run" as any,
593+
id: "456",
594+
payload: payload as any,
595+
});
596+
});
520597
});

0 commit comments

Comments
 (0)