fix: surface GitHub rate limits from find_pr_by_branch#532
fix: surface GitHub rate limits from find_pr_by_branch#532mattmillerai wants to merge 2 commits into
Conversation
find_pr_by_branch lacked the 403/429 -> handle_github_rate_limit check that get_latest_release and fetch_pr_info both have. A rate-limited 403 raised requests.HTTPError from raise_for_status(), which the function's own `except requests.RequestException: return None` swallowed, so callers reported "PR not found" instead of the real rate-limit message. Extract the shared request logic into a module-private _github_get() helper that performs the token auth, the rate-limit check and raise_for_status() in one place, plus _pr_info_from_github() for the PRInfo construction duplicated between fetch_pr_info and find_pr_by_branch. Each caller keeps its own try/except so their divergent error semantics are unchanged. _github_get attaches the user's GITHUB_TOKEN, so it rejects any URL outside https://api.github.com/ to keep the token from leaking to another host. GitHubRateLimitError is a plain Exception, so it now propagates out of find_pr_by_branch; both call sites already wrap the call in `except Exception` and print the message correctly.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @mattmillerai.
Found 2 finding(s).
| Severity | Count |
|---|---|
| 🟠 High | 1 |
| 🟢 Low | 1 |
Panel: 6/8 reviewers contributed findings.
Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)
Both issues pre-date this PR (identical code on main) but were flagged by review on the lines this PR moved into _pr_info_from_github, and both are one-liners in that helper, so fix them here. - head.repo/base.repo come back null once a PR's source fork is deleted. Indexing them raised an opaque TypeError that escaped both callers (they only catch requests.RequestException). Guard and raise a ValueError naming the reason; both call sites already catch Exception and print it, matching this PR's thesis that real failures shouldn't be mislabeled "PR not found". Installing such PRs via the base repo's refs/pull/N/head is a follow-up. - mergeable is null (not absent) while GitHub is still computing it, which rendered a mergeable PR as '✗'. Treat null and absent alike as unknown and keep the pre-existing optimistic True default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Addressed both cursor-review findings in 7990d0c ( On the two red checks — both are pre-existing and unrelated to this diffThis PR touches only
The 9th (
Verified green locally on the code this PR actually changes: |
|
🤖 The reviews loop filed Linear follow-up ticket(s) for review thread(s) deferred as out of scope for this PR:
|
CI triage: both red checks are pre-existing repo-wide breakages, not from this diffThis PR touches only
Root cause is
The same 9 failures reproduce on
Real cause is earlier in the log: This PR's own signal is green: I'm deliberately not fixing either breakage here to avoid conflating unrelated concerns with a rate-limit fix. This should go green once #533/#536 and #535 land. 🤖 automated review-resolution pass |
ELI-5
When you ask the CLI to install a PR by
user:branch, it asks GitHub "which PR is this?". If GitHub is rate-limiting you, it answers with a 403 error. The code caught that error and quietly turned it into "no PR found" — so you'd get told "PR not found" and go hunting for a typo in a branch name that was perfectly fine. Now it tells you the truth: you're rate-limited, here's when to retry.The bug
find_pr_by_branchwas missing the403/429 → handle_github_rate_limitcheck that its two sibling functions (get_latest_release,fetch_pr_info) both had:raise_for_status()→requests.HTTPErrorexcept requests.RequestException: return NoneNone→ printPR not found/Frontend PR not found.What changed
Two module-private helpers in
install.py, no new public surface:_github_get(url, *, params=None, timeout)— one place that does theGITHUB_TOKENauth, the 403/429 rate-limit check, andraise_for_status(). The three duplicated token blocks are gone;os.getenv("GITHUB_TOKEN")now appears exactly once in the file (was 3×)._pr_info_from_github(data)— the 8-fieldPRInfoconstruction that was duplicated byte-for-byte betweenfetch_pr_infoandfind_pr_by_branch.The three functions keep their own
try/exceptand their divergent error semantics exactly (get_latest_release→None+ message;fetch_pr_info→ re-raise wrapped;find_pr_by_branch→None).Behavior change (intended, the point of the PR)
GitHubRateLimitErroris a plainException, not aRequestException, so it now propagates out offind_pr_by_branchinstead of being swallowed. Both call sites already wrap the call inexcept Exception as eand printe, so they render the rate-limit message correctly with no call-site changes. I grepped for other callers — there are none outside this file and its tests.A genuine network error still returns
None(test_find_pr_by_branch_error, unmodified, still passes).Security note
_github_getattaches the user'sGITHUB_TOKENas aBearerheader, so it rejects any URL outsidehttps://api.github.com/. The trailing slash is load-bearing: it rejectshttps://api.github.com.evil.com/. It's kept module-private so the guard can't be bypassed by a new caller elsewhere.Tests
Two added, both to the existing file in its existing Mock/patch style:
test_find_pr_by_branch_rate_limit— 403 + exhausted-limit headers now raisesGitHubRateLimitError(previously returnedNone; this test fails onmain).test_github_get_rejects_non_github_urls— a non-GitHub URL raisesValueErrorand assertsrequests.getwas never called, i.e. the token never leaves the process.All pre-existing tests pass unmodified — the test-file diff is
21 insertions, 0 deletions, so nothing existing was touched or re-asserted.Verification
Run in a dedicated venv inside the worktree (verified
comfy_cli.command.install.__file__resolves to the worktree copy, so this isn't a false pass against the main checkout):pytest tests/comfy_cli/command/github/test_pr.py→ 80 passed (78 collected onmain+ my 2).ruff check .→ clean.ruff format --check .→ clean.origin/mainworktree: identical, 9 on both sides, zero unique to this branch. All pre-existing and unrelated (registry/test_config_parser.py+test_node_init_strips_credentials).Judgment calls / notes for review
_github_getreturnsdict | list(the pulls-list endpoint returns a list, the others a dict) and_pr_info_from_githubtakesdict, so a strict type checker would want a narrow/cast at thefetch_pr_infocall. I kept the prescribed signature: there is no pyright/mypy gate in CI (gates are pytest + ruff, both green) and adding a cast seemed like more noise than the looseness costs. Happy to narrow it if reviewers prefer.if response is Nonecheck infetch_pr_infois gone —requests.getnever returnsNone, so it was unreachable; it disappears naturally with the rewrite._github_getsits next tohandle_github_rate_limit; I put_pr_info_from_githubnext to its two callers rather than beside_github_get, which read better.raise ValueErrordeny path, so I checked whether it denies anything real. It doesn't. TheValueErrorguard is on a module-private helper reachable only from three hardcodedhttps://api.github.com/...URLs (verified every call site), so no user-reachable path hits it. And raising on a rate limit isn't denying the find-PR capability — the HTTP request had already failed; the change only stops the failure from being mislabeled "PR not found". Every success path still works, proven bytest_find_pr_by_branch_successand the unmodified baseline suite.