|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# Helper script to run code review using Claude Code's Task tool |
| 4 | +# |
| 5 | +# Usage: |
| 6 | +# ./test/review.sh # Review current branch vs origin/main |
| 7 | +# ./test/review.sh --pr 1234 # Review specific PR |
| 8 | +# ./test/review.sh --range HEAD~3 # Review last 3 commits |
| 9 | +# ./test/review.sh --files file1.go file2.go # Review specific files |
| 10 | + |
| 11 | +set -euo pipefail |
| 12 | + |
| 13 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 14 | +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" |
| 15 | + |
| 16 | +# Default values |
| 17 | +DIFF_RANGE="origin/main...HEAD" |
| 18 | +PR_NUMBER="" |
| 19 | +PR_REMOTE="${PR_REMOTE:-origin}" # Can be overridden via env var (e.g., PR_REMOTE=upstream) |
| 20 | +FILES=() |
| 21 | +OUTPUT_FILE="${SCRIPT_DIR}/review-output.json" |
| 22 | + |
| 23 | +function usage() { |
| 24 | + cat <<EOF |
| 25 | +Usage: $0 [OPTIONS] |
| 26 | +
|
| 27 | +Run automated code review using Claude Code agent. |
| 28 | +
|
| 29 | +Options: |
| 30 | + --pr NUMBER Review a specific pull request number |
| 31 | + --range RANGE Git diff range (default: origin/main...HEAD) |
| 32 | + --files FILE... Review only specific files |
| 33 | + --output FILE Output file for review results (default: test/review-output.json) |
| 34 | + -h, --help Show this help message |
| 35 | +
|
| 36 | +Environment Variables: |
| 37 | + PR_REMOTE Git remote to use for fetching PRs (default: origin) |
| 38 | + Set to 'upstream' if working from a fork |
| 39 | +
|
| 40 | +Examples: |
| 41 | + $0 # Review current changes vs main |
| 42 | + $0 --pr 1234 # Review PR #1234 (uses gh CLI if available) |
| 43 | + PR_REMOTE=upstream $0 --pr 1234 # Review PR from upstream remote |
| 44 | + $0 --range HEAD~5..HEAD # Review last 5 commits |
| 45 | + $0 --files pkg/cvo/cvo.go # Review specific file |
| 46 | +
|
| 47 | +Notes: |
| 48 | + - PR reviews work best with 'gh' CLI installed: https://cli.github.com/ |
| 49 | + - Without 'gh', falls back to git fetch (requires proper remote configuration) |
| 50 | + - When working from a fork, use PR_REMOTE=upstream to fetch from main repository |
| 51 | +EOF |
| 52 | +} |
| 53 | + |
| 54 | +# Parse arguments |
| 55 | +while [[ $# -gt 0 ]]; do |
| 56 | + case $1 in |
| 57 | + --pr) |
| 58 | + PR_NUMBER="$2" |
| 59 | + shift 2 |
| 60 | + ;; |
| 61 | + --range) |
| 62 | + DIFF_RANGE="$2" |
| 63 | + shift 2 |
| 64 | + ;; |
| 65 | + --files) |
| 66 | + shift |
| 67 | + while [[ $# -gt 0 ]] && [[ ! $1 =~ ^-- ]]; do |
| 68 | + FILES+=("$1") |
| 69 | + shift |
| 70 | + done |
| 71 | + ;; |
| 72 | + --output) |
| 73 | + OUTPUT_FILE="$2" |
| 74 | + shift 2 |
| 75 | + ;; |
| 76 | + -h|--help) |
| 77 | + usage |
| 78 | + exit 0 |
| 79 | + ;; |
| 80 | + *) |
| 81 | + echo "Unknown option: $1" |
| 82 | + usage |
| 83 | + exit 1 |
| 84 | + ;; |
| 85 | + esac |
| 86 | +done |
| 87 | + |
| 88 | +cd "$REPO_ROOT" |
| 89 | + |
| 90 | +# Get diff based on input |
| 91 | +if [[ -n "$PR_NUMBER" ]]; then |
| 92 | + echo "Fetching PR #${PR_NUMBER}..." |
| 93 | + |
| 94 | + if command -v gh &> /dev/null; then |
| 95 | + # Use gh CLI (preferred - shows PR metadata and handles authentication) |
| 96 | + echo "Using 'gh' CLI to fetch PR..." |
| 97 | + DIFF=$(gh pr diff "$PR_NUMBER" 2>&1) || { |
| 98 | + echo "Error: Failed to fetch PR #${PR_NUMBER} using 'gh' CLI" |
| 99 | + echo "Output: $DIFF" |
| 100 | + exit 1 |
| 101 | + } |
| 102 | + FILES_CHANGED=$(gh pr view "$PR_NUMBER" --json files -q '.files[].path' 2>&1) || { |
| 103 | + echo "Error: Failed to get PR file list" |
| 104 | + exit 1 |
| 105 | + } |
| 106 | + else |
| 107 | + # Fallback to git fetch (no gh CLI required) |
| 108 | + # Note: This requires 'origin' to be the upstream GitHub repo, not a fork |
| 109 | + echo "Note: 'gh' CLI not found. Using git fetch as fallback." |
| 110 | + echo "Install 'gh' for better PR integration: https://cli.github.com/" |
| 111 | + echo "" |
| 112 | + |
| 113 | + PR_REF="pull/${PR_NUMBER}/head" |
| 114 | + echo "Fetching ${PR_REF} from remote '${PR_REMOTE}'..." |
| 115 | + |
| 116 | + if ! git fetch "$PR_REMOTE" "$PR_REF" 2>/dev/null; then |
| 117 | + echo "" |
| 118 | + echo "Error: Could not fetch PR #${PR_NUMBER} from remote '${PR_REMOTE}'" |
| 119 | + echo "" |
| 120 | + echo "This can happen if:" |
| 121 | + echo " - Your 'origin' remote is a fork (not the upstream repo)" |
| 122 | + echo " - The PR doesn't exist or is closed" |
| 123 | + echo " - You don't have network access to the repository" |
| 124 | + echo "" |
| 125 | + echo "Possible solutions:" |
| 126 | + echo " 1. Install 'gh' CLI (recommended): https://cli.github.com/manual/installation" |
| 127 | + echo " 2. Add upstream remote: git remote add upstream https://github.com/openshift/cluster-version-operator.git" |
| 128 | + echo " Then use: PR_REMOTE=upstream ./test/review.sh --pr ${PR_NUMBER}" |
| 129 | + echo " 3. Manually fetch: git fetch origin pull/${PR_NUMBER}/head:pr-${PR_NUMBER}" |
| 130 | + echo " 4. Use git range instead: ./test/review.sh --range origin/main...HEAD" |
| 131 | + exit 1 |
| 132 | + fi |
| 133 | + |
| 134 | + # Get the base branch (usually main) |
| 135 | + BASE_BRANCH=$(git remote show "$PR_REMOTE" 2>/dev/null | grep 'HEAD branch' | cut -d' ' -f5) |
| 136 | + BASE_BRANCH=${BASE_BRANCH:-main} |
| 137 | + |
| 138 | + DIFF=$(git diff "${PR_REMOTE}/${BASE_BRANCH}...FETCH_HEAD") |
| 139 | + FILES_CHANGED=$(git diff --name-only "${PR_REMOTE}/${BASE_BRANCH}...FETCH_HEAD") |
| 140 | + |
| 141 | + echo "Successfully fetched PR #${PR_NUMBER}" |
| 142 | + fi |
| 143 | +elif [[ ${#FILES[@]} -gt 0 ]]; then |
| 144 | + echo "Reviewing specified files: ${FILES[*]}" |
| 145 | + DIFF=$(git diff "$DIFF_RANGE" -- "${FILES[@]}") |
| 146 | + FILES_CHANGED=$(printf '%s\n' "${FILES[@]}") |
| 147 | +else |
| 148 | + echo "Reviewing changes in range: $DIFF_RANGE" |
| 149 | + DIFF=$(git diff "$DIFF_RANGE") |
| 150 | + FILES_CHANGED=$(git diff --name-only "$DIFF_RANGE") |
| 151 | +fi |
| 152 | + |
| 153 | +if [[ -z "$DIFF" ]]; then |
| 154 | + echo "No changes found to review." |
| 155 | + exit 0 |
| 156 | +fi |
| 157 | + |
| 158 | +echo "Files changed:" |
| 159 | +echo "$FILES_CHANGED" |
| 160 | +echo "" |
| 161 | +echo "Generating code review..." |
| 162 | +echo "" |
| 163 | + |
| 164 | +# Create prompt for the agent |
| 165 | +REPO_NAME=$(git remote get-url origin | sed -e 's/.*[:/]\([^/]*\/[^/]*\)\.git$/\1/' -e 's/.*[:/]\([^/]*\/[^/]*\)$/\1/') |
| 166 | + |
| 167 | +REVIEW_PROMPT=$(cat <<EOF |
| 168 | +Please perform a code review for the following changes in the ${REPO_NAME} repository. |
| 169 | +
|
| 170 | +Files changed: |
| 171 | +${FILES_CHANGED} |
| 172 | +
|
| 173 | +Git diff: |
| 174 | +\`\`\`diff |
| 175 | +${DIFF} |
| 176 | +\`\`\` |
| 177 | +
|
| 178 | +Analyze these changes according to the code review checklist and provide your findings in JSON format as specified in your instructions. |
| 179 | +EOF |
| 180 | +) |
| 181 | + |
| 182 | +# Save prompt to temp file for claude CLI |
| 183 | +PROMPT_FILE=$(mktemp) |
| 184 | +echo "$REVIEW_PROMPT" > "$PROMPT_FILE" |
| 185 | + |
| 186 | +echo "=== CODE REVIEW RESULTS ===" |
| 187 | +echo "" |
| 188 | + |
| 189 | +# Check if claude CLI is available |
| 190 | +if command -v claude &> /dev/null; then |
| 191 | + # Use Claude Code CLI if available |
| 192 | + claude task --type general-purpose --prompt "@${PROMPT_FILE}" --output "$OUTPUT_FILE" |
| 193 | + echo "" |
| 194 | + echo "Review saved to: $OUTPUT_FILE" |
| 195 | + |
| 196 | + # Pretty print if jq is available |
| 197 | + if command -v jq &> /dev/null && [[ -f "$OUTPUT_FILE" ]]; then |
| 198 | + echo "" |
| 199 | + echo "=== SUMMARY ===" |
| 200 | + jq -r '.summary // "No summary available"' "$OUTPUT_FILE" |
| 201 | + echo "" |
| 202 | + echo "Verdict: $(jq -r '.verdict // "Unknown"' "$OUTPUT_FILE")" |
| 203 | + echo "" |
| 204 | + echo "Top Risks:" |
| 205 | + jq -r '.top_risks[]? // "None identified"' "$OUTPUT_FILE" | sed 's/^/ - /' |
| 206 | + fi |
| 207 | +else |
| 208 | + # Fallback: Print instructions for manual review |
| 209 | + echo "Claude Code CLI not found." |
| 210 | + echo "" |
| 211 | + echo "To perform the review, copy the following prompt to Claude Code:" |
| 212 | + echo "---------------------------------------------------------------" |
| 213 | + cat "$PROMPT_FILE" |
| 214 | + echo "---------------------------------------------------------------" |
| 215 | +fi |
| 216 | + |
| 217 | +rm -f "$PROMPT_FILE" |
0 commit comments