mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(triage): warn when fetch_open_prs hits the gh CLI 1000-row cap
Addresses greptile-apps P2 review note: 'gh pr list --limit 1000' is
the CLI's hard ceiling. If BerriAI/litellm ever exceeds 1000 open PRs,
fetch_open_prs silently returns a truncated list and the daily sweep
quietly misses everything beyond the cap.
Extract the cap value as GH_PR_LIST_LIMIT and emit a GitHub Actions
':⚠️:' annotation (to stderr, where Actions surfaces it in the
job log) whenever the result count reaches the cap. This is the minimal
defensive change — a real fix (paginated 'gh api' calls) is left as a
follow-up because the repo is nowhere near the cap today and an
unnecessary pagination loop would slow every run.
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
This commit is contained in:
parent
28ba9f7c6b
commit
d976504534
2 changed files with 53 additions and 2 deletions
20
.github/scripts/close_low_quality_prs.py
vendored
20
.github/scripts/close_low_quality_prs.py
vendored
|
|
@ -78,6 +78,12 @@ def gh(*args: str) -> str:
|
|||
return result.stdout
|
||||
|
||||
|
||||
# `gh pr list --limit` caps at 1000 (the CLI's documented hard ceiling).
|
||||
# Surface a warning if we ever hit that cap so the silent truncation is
|
||||
# visible in workflow logs instead of just being a missed close.
|
||||
GH_PR_LIST_LIMIT = 1000
|
||||
|
||||
|
||||
def fetch_open_prs(repo: str | None) -> list[dict]:
|
||||
"""Fetch all open PRs (number, createdAt, isDraft, labels, author).
|
||||
|
||||
|
|
@ -94,12 +100,22 @@ def fetch_open_prs(repo: str | None) -> list[dict]:
|
|||
"--state",
|
||||
"open",
|
||||
"--limit",
|
||||
"1000",
|
||||
str(GH_PR_LIST_LIMIT),
|
||||
"--json",
|
||||
fields,
|
||||
*repo_args,
|
||||
)
|
||||
return json.loads(raw)
|
||||
prs = json.loads(raw)
|
||||
if len(prs) >= GH_PR_LIST_LIMIT:
|
||||
# `gh pr list --limit N` returns at most N rows even if more exist;
|
||||
# log a GitHub Actions warning so the truncation isn't silent.
|
||||
message = (
|
||||
f"fetch_open_prs hit the gh CLI cap ({GH_PR_LIST_LIMIT}); "
|
||||
"the open-PR list is likely truncated. Switch to paginated "
|
||||
"`gh api` calls if the repo regularly exceeds this cap."
|
||||
)
|
||||
print(f"::warning::{message}", file=sys.stderr)
|
||||
return prs
|
||||
|
||||
|
||||
def fetch_pr_author_association(pr_number: int, repo: str | None) -> str:
|
||||
|
|
|
|||
|
|
@ -422,6 +422,41 @@ class TestMainOptoutLabelDefault:
|
|||
assert default not in captured["optout_labels"], default
|
||||
|
||||
|
||||
class TestFetchOpenPrsLimitWarning:
|
||||
"""`fetch_open_prs` must surface a warning when the gh CLI cap is hit."""
|
||||
|
||||
def test_should_warn_when_at_cap(self, closer_module, monkeypatch, capsys):
|
||||
# Pretend `gh pr list --limit 1000` returned exactly 1000 PRs —
|
||||
# this is the silent-truncation case the warning is meant to catch.
|
||||
cap = closer_module.GH_PR_LIST_LIMIT
|
||||
synthetic = [{"number": i} for i in range(cap)]
|
||||
import json as _json
|
||||
|
||||
monkeypatch.setattr(
|
||||
closer_module, "gh", lambda *a, **kw: _json.dumps(synthetic)
|
||||
)
|
||||
result = closer_module.fetch_open_prs(None)
|
||||
assert len(result) == cap
|
||||
captured = capsys.readouterr()
|
||||
# GitHub Actions `::warning::` annotations go to stderr by
|
||||
# convention; just check the marker appears somewhere visible.
|
||||
combined = captured.out + captured.err
|
||||
assert "::warning::" in combined
|
||||
assert str(cap) in combined
|
||||
|
||||
def test_should_not_warn_when_under_cap(self, closer_module, monkeypatch, capsys):
|
||||
synthetic = [{"number": i} for i in range(5)]
|
||||
import json as _json
|
||||
|
||||
monkeypatch.setattr(
|
||||
closer_module, "gh", lambda *a, **kw: _json.dumps(synthetic)
|
||||
)
|
||||
result = closer_module.fetch_open_prs(None)
|
||||
assert len(result) == 5
|
||||
captured = capsys.readouterr()
|
||||
assert "::warning::" not in (captured.out + captured.err)
|
||||
|
||||
|
||||
class TestHasOptoutLabel:
|
||||
def test_should_match_label_case_insensitively(self, closer_module):
|
||||
pr = {"labels": [{"name": "Do Not Close"}, {"name": "bug"}]}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue