Repository navigation
Page past gh's 100-file cap when matching CODEOWNERS - #26
Merged
Merged
Conversation
`gh pr view --json files` truncates at 100 files with no warning, so large dependency bumps were labeled as if they touched nothing anyone owns. The ownerless go.mod/go.sum//vendor/ rules that enable Renovate automerge sort last in CODEOWNERS, so vendor churn fills the whole window and buries the first-party paths that carry the ownership signal -- yielding no domain/* labels and no reason comment even while GitHub requested the owning teams as reviewers. Re-fetch the full path list via a paged GraphQL query, but only when the embedded list is long enough to be suspect, so PRs under the cap keep their single API call. Co-authored-by: Cursor <cursoragent@cursor.com>
`gh api -F` coerces integer-looking values to JSON numbers, so an all-digit org or repo name would reach the query as an Int and fail its String! variable. `-f` sends them verbatim; the PR number stays on -F because the query types it as Int!. Co-authored-by: Cursor <cursoragent@cursor.com>
tylorreeves
approved these changes
Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The labeler silently skipped domain labeling on any PR touching more than 100 files.
fetch_prreads the changed-file list fromgh pr view --json files, which gh requests withfirst: 100and never pages, so the list comes back truncated with no warning. Still true as of gh 2.101.0.Truncation isn't a harmless approximation for CODEOWNERS matching. CODEOWNERS is last-match-wins, and the deliberately ownerless
go.mod/go.sum//vendor/rules that let Renovate automerge sort near the end of the file. A large dependency bump therefore fills the entire 100-file window with unowned vendor churn and buries the handful of first-party paths that carry the ownership signal, so the PR reads as owned by nobody: nodomain/*labels and no reason comment.Found via thog#7041 (since closed), a trufflehog digest bump. At the time it changed 351 files; the 11 owned ones under
vendor/github.com/trufflesecurity/trufflehog/sat at positions 340-350, well past the cutoff. GitHub itself requested Scanning and Integrations as reviewers (it evaluates every file) while the labeler computed zero owners -- that divergence is the bug.The fix re-fetches the full path list through a paged GraphQL query, gated behind a tripwire so the common case is untouched.
Why GraphQL rather than REST
pulls/{n}/filesREST embeds the full patch hunk for every file with no way to project it away. Measured on thog#7041: ~1.8MB transferred to learn 351 strings, versus ~25KB for the paths-only GraphQL query. Same wall time, ~70x the bytes.
Cost
Gated on the returned list being exactly 100 entries. Anything shorter is provably complete and keeps the existing single API call.
As of Oct 9, 5 of 243 open thog PRs (about 2%) hit the cap, so ~98% of runs are byte-for-byte identical in cost. This matters most in backfill mode, which fans out across every open PR; an ungated second call would have added one request per PR there.
Testing
pytest .github/scripts-- 155 passed (7 new acrossTestPrChangedFilesandTestFetchPrFilePaths), covering the fast path at 6 and 99 files, the re-fetch at exactly 100, a missingfileskey, owner/repo splitting into query variables (sent with-fso an all-digit name isn't coerced to an Int), and the$endCursor/hasNextPagepagination contract that--paginatedepends on.ruff checkandruff format --checkclean at the pinned 0.15.22.DRY_RUN=true) against every open thog PR over the cap, on bothmainand this branch:mainFollow-up after merge
None required. No open thog PR gets different labels; the only change a backfill would make today is refreshing #4928's reason comment, which its next push will do anyway.
Known limit
GitHub caps the files connection at 3000, the same ceiling REST and the "Files changed" tab enforce, so domain labeling stays best-effort beyond that. Noted in a comment on the query rather than worked around. Reaching that ceiling takes 30 page requests: on thog#4543 (a stale draft with 19,858 files) a labeler run goes from under a second to about 56 seconds.
Note
Low Risk
Scoped to PR labeler file discovery for CODEOWNERS; adds one extra GitHub API call only when the file list hits the 100-entry cap, with no change to labeling logic beyond using a complete file list.
Overview
Fixes silent wrong domain labeling on PRs with 100+ changed files. The labeler used
gh pr view --json files, which only returns the first 100 paths, so large Renovate/vendor bumps could show nodomain/*labels or review-reason comment even when GitHub requested the right teams.pr_changed_fileskeeps the existing list when it has fewer than 100 entries; at exactly 100 it re-fetches all paths via a paginated GraphQL query (fetch_pr_file_paths) that returns paths only (not full patches). Domain matching now calls this helper instead of readingpr["files"]directly.Adds unit tests for the fast path (6/99 files), the refetch at 100 files, GraphQL pagination variables, and query contract (
$endCursor/hasNextPage).Reviewed by Cursor Bugbot for commit 76e4ab4. Bugbot is set up for automated code reviews on this repo. Configure here.