@@ -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
128115if _gh_retry_inner gh pr diff " $PR_NUMBER " --repo " $REPO " > " $OUTPUT_DIR /pr-diff.patch" ; then
129116 :
130117else
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"
135122fi
136123
137124# All PR comments (issue-style). `--paginate` alone writes one JSON array per
0 commit comments