From ae1691a3685d7ff4817593adab7741b4ce884784 Mon Sep 17 00:00:00 2001 From: sabril <5334504+saturninoabril@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:51:18 +0800 Subject: [PATCH] fix(pr-analysis): on large diff and reduce gh pr calls (#35734) * fix(pr-analysis): on large diff and reduce gh pr calls * address comments --- .github/workflows/pr-test-analysis.yml | 61 +++++++++++++++++--------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pr-test-analysis.yml b/.github/workflows/pr-test-analysis.yml index fb4648c33f7..2101d587a41 100644 --- a/.github/workflows/pr-test-analysis.yml +++ b/.github/workflows/pr-test-analysis.yml @@ -50,25 +50,40 @@ jobs: id: pr-info env: GH_TOKEN: ${{ github.token }} + # Pass event data via env vars to avoid script injection from PR body + EVENT_PR_NUMBER: ${{ github.event.pull_request.number }} + EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + EVENT_REPO_OWNER: ${{ github.repository_owner }} + EVENT_REPO_NAME: ${{ github.event.repository.name }} + EVENT_PR_BODY: ${{ github.event.pull_request.body }} + EVENT_BASE_REF: ${{ github.event.pull_request.base.ref }} run: | if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then PR_NUMBER="${{ inputs.pr_number }}" - PR_JSON=$(gh pr view "$PR_NUMBER" --repo "${{ env.REPO }}" --json headRefOid,headRepository,headRepositoryOwner --jq '{sha: .headRefOid, repo: .headRepository.name, owner: .headRepositoryOwner.login}') - HEAD_SHA=$(echo "$PR_JSON" | jq -r '.sha') - HEAD_REPO_OWNER=$(echo "$PR_JSON" | jq -r '.owner') - HEAD_REPO_NAME=$(echo "$PR_JSON" | jq -r '.repo') + PR_JSON=$(gh pr view "$PR_NUMBER" --repo "${{ env.REPO }}" --json headRefOid,headRepository,headRepositoryOwner,body,baseRefName) + HEAD_SHA=$(echo "$PR_JSON" | jq -r '.headRefOid') + HEAD_REPO_OWNER=$(echo "$PR_JSON" | jq -r '.headRepositoryOwner.login') + HEAD_REPO_NAME=$(echo "$PR_JSON" | jq -r '.headRepository.name') + PR_BODY=$(echo "$PR_JSON" | jq -r '.body // ""') + BASE_BRANCH=$(echo "$PR_JSON" | jq -r '.baseRefName') else - PR_NUMBER="${{ github.event.pull_request.number }}" - HEAD_SHA="${{ github.event.pull_request.head.sha }}" - HEAD_REPO_OWNER="${{ github.repository_owner }}" - HEAD_REPO_NAME="${{ github.event.repository.name }}" + PR_NUMBER="$EVENT_PR_NUMBER" + HEAD_SHA="$EVENT_HEAD_SHA" + HEAD_REPO_OWNER="$EVENT_REPO_OWNER" + HEAD_REPO_NAME="$EVENT_REPO_NAME" + PR_BODY="$EVENT_PR_BODY" + BASE_BRANCH="$EVENT_BASE_REF" fi { echo "pr_number=${PR_NUMBER}" echo "head_sha=${HEAD_SHA}" echo "head_repo=${HEAD_REPO_OWNER}/${HEAD_REPO_NAME}" + echo "base_branch=${BASE_BRANCH}" } >> "$GITHUB_OUTPUT" + # Write PR body to a temp path that survives checkout + printf '%s\n' "$PR_BODY" > "$RUNNER_TEMP/pr_body.txt" + # Required: claude-code-action needs a git directory to initialize. - name: ci/checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -77,15 +92,24 @@ jobs: - name: ci/prep-analysis-data id: prep - env: - GH_TOKEN: ${{ github.token }} run: | PR_NUM=${{ steps.pr-info.outputs.pr_number }} - # Fetch core data - gh pr view "$PR_NUM" --repo "$REPO" --json body --jq '.body' > pr_body.txt + BASE_BRANCH=${{ steps.pr-info.outputs.base_branch }} - # Get all changed files - gh pr diff "$PR_NUM" --repo "$REPO" --name-only > all_files.txt + # Copy PR body from temp path (written before checkout) + cp "$RUNNER_TEMP/pr_body.txt" pr_body.txt + + # Fetch the PR head and base branch, then diff locally (no API size limits) + git fetch origin "$BASE_BRANCH" "pull/$PR_NUM/head:pr-$PR_NUM" --depth=50 + if ! MERGE_BASE=$(git merge-base "origin/$BASE_BRANCH" "pr-$PR_NUM" 2>/dev/null); then + echo "Merge base not found in shallow history — fetching full history" + git fetch --unshallow origin "$BASE_BRANCH" "pull/$PR_NUM/head:pr-$PR_NUM" + MERGE_BASE=$(git merge-base "origin/$BASE_BRANCH" "pr-$PR_NUM") + fi + git diff "$MERGE_BASE" "pr-$PR_NUM" > full_diff.diff + + # Derive file list from the diff (avoids a second API call) + grep '^diff --git' full_diff.diff | sed 's|.*b/||' > all_files.txt # Extract Test Files (based on your conventions) grep -E "_test\.go|\.test\.(ts|tsx|js)|e2e-tests/|detox/|e2e/" all_files.txt > test_files.txt || true @@ -97,10 +121,7 @@ jobs: cat prod_files.txt test_files.txt | sort | uniq > relevant_files.txt if [ -s relevant_files.txt ]; then - # Fetch the full diff, then filter to keep only hunks for relevant files - gh pr diff "$PR_NUM" --repo "$REPO" > full_diff.diff - - # Use awk to extract only diff sections whose header matches a relevant file + # Filter the already-fetched diff to keep only hunks for relevant files awk ' BEGIN { while ((getline line < "relevant_files.txt") > 0) files[line]=1 } /^diff --git/ { @@ -109,12 +130,12 @@ jobs: } printing { print } ' full_diff.diff > filtered_diff.diff - - rm -f full_diff.diff else touch filtered_diff.diff fi + rm -f full_diff.diff + - name: ci/analyze-tests id: ai-analysis continue-on-error: true