Skip to content

Commit a380049

Browse files
unarboscursoragent
andcommitted
ai-review prefetch: reconstruct oversized diffs from the local checkout, fail closed (mirrors PR #2845).
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent de52967 commit a380049

1 file changed

Lines changed: 26 additions & 39 deletions

File tree

.github/ai-review/prefetch.sh

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -86,52 +86,39 @@ gh_retry gh pr view "$PR_NUMBER" --repo "$REPO" --json files > "$OUTPUT_DIR/pr-f
8686
# number of files" and no amount of retrying clears it. Since the Skeptic is a
8787
# required check, that would leave the gate permanently red on big PRs. So try
8888
# `gh pr diff` first (canonical output, correct for the common case) and fall
89-
# back to reconstructing the unified diff from the paginated Files API, which
90-
# has no file-count cap.
91-
reconstruct_diff_from_files_api() {
92-
# The Files API returns per-file `patch` hunks (omitted for binary and
93-
# individually-oversized files). Rebuild a `diff --git` stream from them so
94-
# the personas see the same hunks a normal diff would show.
95-
gh api "repos/$REPO/pulls/$PR_NUMBER/files?per_page=100" --paginate \
96-
| jq -s 'add // []' \
97-
| jq -r '.[] | @base64' \
98-
| while read -r row; do
99-
_f() { printf '%s' "$row" | base64 --decode | jq -r "$1"; }
100-
local status filename previous patch old new
101-
status=$(_f '.status')
102-
filename=$(_f '.filename')
103-
previous=$(_f '.previous_filename // ""')
104-
patch=$(_f '.patch // ""')
105-
106-
old="a/${previous:-$filename}"
107-
new="b/$filename"
108-
printf 'diff --git %s %s\n' "$old" "$new"
109-
110-
case "$status" in
111-
added) printf -- '--- /dev/null\n+++ %s\n' "$new" ;;
112-
removed) printf -- '--- %s\n+++ /dev/null\n' "$old" ;;
113-
renamed)
114-
printf 'rename from %s\nrename to %s\n' "${previous:-$filename}" "$filename"
115-
printf -- '--- %s\n+++ %s\n' "$old" "$new"
116-
;;
117-
*) printf -- '--- %s\n+++ %s\n' "$old" "$new" ;;
118-
esac
119-
120-
if [[ -n "$patch" ]]; then
121-
printf '%s\n' "$patch"
122-
else
123-
printf '@@ (no textual patch — binary or file exceeded diff size limit) @@\n'
124-
fi
125-
done
89+
# back to computing the same diff from the local PR checkout, which has no
90+
# file-count cap.
91+
reconstruct_diff_locally() {
92+
# The Files API alternative is unsuitable: it omits `patch` for binary AND
93+
# individually-oversized textual files, and a placeholder there would be a
94+
# review blind spot (an attacker could oversize exactly the file they want
95+
# unreviewed). Instead, produce the complete diff with local git: the
96+
# workflow runs this script inside a fetch-depth-0 checkout of the PR head,
97+
# so the merge base is already present and no network or credentials are
98+
# needed (the checkout uses persist-credentials: false). Any failure aborts
99+
# the script (set -e), so this fails closed — the personas never see a
100+
# partial diff.
101+
local head_sha base_ref merge_base
102+
head_sha=$(jq -r '.headRefOid' "$OUTPUT_DIR/pr.json")
103+
base_ref=$(jq -r '.baseRefName' "$OUTPUT_DIR/pr.json")
104+
105+
git rev-parse --is-inside-work-tree >/dev/null \
106+
|| { echo "::error::not inside the PR checkout; cannot reconstruct diff" >&2; return 1; }
107+
[ "$(git rev-parse HEAD)" = "$head_sha" ] \
108+
|| { echo "::error::checkout HEAD does not match PR head $head_sha; cannot reconstruct diff" >&2; return 1; }
109+
110+
merge_base=$(git merge-base "origin/$base_ref" "$head_sha") \
111+
|| { echo "::error::could not resolve merge base of origin/$base_ref and $head_sha" >&2; return 1; }
112+
git diff "$merge_base" "$head_sha"
126113
}
127114

128115
if _gh_retry_inner gh pr diff "$PR_NUMBER" --repo "$REPO" > "$OUTPUT_DIR/pr-diff.patch"; then
129116
:
130117
else
131-
echo "::warning::gh pr diff failed (likely >300 files / HTTP 406); reconstructing from the Files API" >&2
118+
echo "::warning::gh pr diff failed (likely >300 files / HTTP 406); reconstructing from the local checkout" >&2
132119
cat /tmp/gh_retry.err >&2 2>/dev/null || true
133120
rm -f /tmp/gh_retry.err
134-
reconstruct_diff_from_files_api > "$OUTPUT_DIR/pr-diff.patch"
121+
reconstruct_diff_locally > "$OUTPUT_DIR/pr-diff.patch"
135122
fi
136123

137124
# All PR comments (issue-style). `--paginate` alone writes one JSON array per

0 commit comments

Comments
 (0)