[CRCR] Fix the way of searching PR - #8386
Conversation
|
@KarhouTam is attempting to deploy a commit to the Meta Open Source Team on Vercel. A member of the Team first needs to authorize it. |
subinz1
left a comment
There was a problem hiding this comment.
Reviewed the change — this is a clean, well-evidenced fix.
The listPullRequestsAssociatedWithCommit endpoint returning empty [] for cross-fork PRs is a known GitHub API limitation, and the Search API is the correct fallback here. The PR description's evidence with real SHA lookups makes the case clear.
A couple of minor notes (not blocking):
- Search API rate limit: 30 req/min vs. 5000 req/hr for REST. Under burst scenarios (multiple downstream repos failing simultaneously on the same PR), the 403 would be caught by the existing
try/catchand logged, so no functional impact — just worth being aware of. - Indexing delay: The Search API can lag a few seconds behind real-time indexing, but since CRCR check runs take minutes to hours, this shouldn't be a practical concern.
Tests are well-structured — the new cross-fork fallback test exercises the full happy path, and the existing "no PR" test is now properly mocking the Search API instead of relying on an unintentional nock connection error.
LGTM!
I think this is worth considering. 30 req/min could not be satisfied if there are more and more L3/L4 repos. Actually, we don't need this extra search. We can add the PR number when we create the check run output on the Lambda side, so we can directly extract the corresponding PR number from the check run; no need to search. WDYT? @KarhouTam |
|
Thx for your comments @subinz1 @can-gaa-hou . Updated and please have a look again! Keep the search API as backup. |
|
Thanks @subinz1! Fixed. |
|
Re-reviewed after the latest commit — all three issues from the previous round are addressed:
Tests are properly updated — the "no PR" case now mocks the Search API returning empty instead of relying on an unintentional error path, and the cross-fork test exercises the full output-regex happy path. LGTM! |
can-gaa-hou
left a comment
There was a problem hiding this comment.
Thanks @KarhouTam. Overall look good to me except some small nits. Please also update the description.
|
Hi @atalman. Sorry to pin you. Could you please review this PR when you have chance? Thanks! |
|
Thanks for digging into this — the diagnosis is well evidenced, and embedding the PR number at the source is the right call. It removes the lookup entirely and sidesteps the Search API rate ceiling @can-gaa-hou raised. Two things I'd like addressed before this merges. 1. The description doesn't match the implementationThe Worth fixing before merge, since the PR body becomes the squash commit message and the permanent record would describe an approach that never landed. It also means earlier review comments referring to the Search API as a backup tier, or to three lookup tiers, don't describe the current code. There are two: if (checkRun.pull_requests && checkRun.pull_requests.length > 0) {
prNumbers = checkRun.pull_requests.map((pr) => pr.number);
} else if (checkRun.output?.summary) {
const match = checkRun.output.summary.match(/for PR (\d+)/);
...
}2.
|
Thanks for the suggestion @atalman, that makes sense to me. We can use this approach to store both |
|
Hi @atalman. Thanks for your valuable review and comment! Already fixed them. Please re-review if you have chance. Thanks! |
b7d0323 to
8036d96
Compare
|
Hi @atalman @can-gaa-hou . Thanks for your reviews and comments. I've already fixed it. Please re-review it if you have chance! Thanks! |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary
Fix
crcrOncallBot's "commit SHA → PR number" resolution for cross-fork pull requests onpytorch/pytorch, where the bot was silently failing to comment on downstream CI failures for the majority of contributions.Problem
crcrOncallBotreacts tocheck_run.completedwebhook events and posts oncall-ping comments on the associated PR when downstream CI fails. To find which PR a check run belongs to, it uses a two-step strategy:checkRun.pull_requests— thecheck_runpayload'spull_requestsarraylistPullRequestsAssociatedWithCommit(GitHub Commits API) — whenpull_requestsis empty, which happens for cross-fork PRs (the code comment itself admits this)The problem: both paths are dead ends for cross-fork PRs on large repos like
pytorch/pytorch.checkRun.pull_requests[]— GitHub does not populate this for cross-fork check runsGET /repos/{owner}/{repo}/commits/{sha}/pulls[]— the commits-pulls endpoint does not return PRs from forked repositories on repos ofpytorch/pytorch's scaleThe commits-pulls API returns HTTP 200 with
[](not an error), so thetry/catchon the fallback doesn't fire — execution falls through to an empty-array check that silently returns without posting a comment. The bot was effectively completely broken for cross-fork PRs, which is the dominant contribution pattern forpytorch/pytorch.Evidence
Tested with two real open fork PRs on
pytorch/pytorch:Fork-branch PR (not functional): pytorch/pytorch#189246
Intra-branch PR (functional): pytorch/pytorch#191313
Fix
Core approach: embed PR number at the source
Instead of a server-side API lookup, the PR number is now embedded in the check run's
external_idfield at creation time — alongside the downstreamrun_idthat was already stored there. The bot reads it back directly with no external API call.Producer side (Python —
callback_handler.py,cleanup_handler.py,event_handler.py):Consumer side (
crcrOncallBot.ts):Read sites (
event_handler.py— check run rerequest handlers) parse out the run_id portion:Why
external_idinstead ofoutput.summaryoutput.summaryis user-facing display text rendered in the check run detail panel. Binding a machine contract to it means any future copy change silently breaks the bot, with no shared constant or cross-repo test between the Python (AWS Lambda) and TypeScript (Probot) deploy units.external_idis purpose-built for machine-readable data and was already used to storerun_idfor rerequest handling.Also fixed:
str(None)bugstr(pr_field.get("number", ""))produces the literal string"None"when thenumberkey exists but isnull— Python'sdict.get()only returns the default when the key is missing, not when it'sNone. Changed tostr(pr_field.get("number") or "")in all three call sites.