@@ -193,11 +193,81 @@ jobs:
193
193
- name : Checkout repository
194
194
uses : actions/checkout@v4
195
195
with :
196
- fetch-depth : 50 # Optimized depth for most PR scenarios
196
+ fetch-depth : 0 # Full history for reliable diff comparisons
197
197
token : ${{ secrets.GITHUB_TOKEN }}
198
198
# For issue_comment events, checkout the PR branch
199
199
ref : ${{ github.event_name == 'issue_comment' && steps.pr-checkout-info.outputs.pr_head_ref || github.ref }}
200
200
201
+ - name : Refresh git state
202
+ id : git-refresh
203
+ timeout-minutes : 3
204
+ run : |
205
+ set -euo pipefail
206
+
207
+ echo "🔄 Refreshing git state to ensure latest changes are analyzed"
208
+
209
+ # Get current branch name
210
+ CURRENT_BRANCH=$(git branch --show-current || echo "")
211
+ CURRENT_SHA=$(git rev-parse HEAD)
212
+
213
+ echo "Current branch: $CURRENT_BRANCH"
214
+ echo "Current SHA: $CURRENT_SHA"
215
+
216
+ # Fetch all remote references to ensure we have the latest state
217
+ echo "Fetching all remote references..."
218
+ git fetch --all --prune
219
+
220
+ # For issue_comment events, ensure we're on the latest commit of the PR branch
221
+ if [ "${{ github.event_name }}" = "issue_comment" ]; then
222
+ EXPECTED_SHA="${{ steps.pr-checkout-info.outputs.pr_head_sha }}"
223
+ echo "Expected SHA from PR: $EXPECTED_SHA"
224
+
225
+ # Fetch the specific PR branch to get latest changes
226
+ PR_HEAD_REF="${{ steps.pr-checkout-info.outputs.pr_head_ref }}"
227
+ if [ -n "$PR_HEAD_REF" ]; then
228
+ echo "Fetching latest changes for PR branch: $PR_HEAD_REF"
229
+ git fetch origin "$PR_HEAD_REF:$PR_HEAD_REF" 2>/dev/null || true
230
+
231
+ # Reset to the latest commit on the PR branch
232
+ echo "Resetting to latest commit on $PR_HEAD_REF"
233
+ git reset --hard "origin/$PR_HEAD_REF"
234
+
235
+ NEW_SHA=$(git rev-parse HEAD)
236
+ echo "Updated to SHA: $NEW_SHA"
237
+
238
+ if [ "$NEW_SHA" != "$CURRENT_SHA" ]; then
239
+ echo "⚠️ SHA changed from $CURRENT_SHA to $NEW_SHA - analyzing latest changes"
240
+ else
241
+ echo "✅ SHA unchanged - no new commits since checkout"
242
+ fi
243
+ fi
244
+ else
245
+ # For PR events, ensure we have the latest changes
246
+ if [ -n "$CURRENT_BRANCH" ] && [ "$CURRENT_BRANCH" != "HEAD" ]; then
247
+ echo "Ensuring latest changes for branch: $CURRENT_BRANCH"
248
+ git fetch origin "$CURRENT_BRANCH" 2>/dev/null || true
249
+ git reset --hard "origin/$CURRENT_BRANCH" 2>/dev/null || git reset --hard HEAD
250
+
251
+ NEW_SHA=$(git rev-parse HEAD)
252
+ if [ "$NEW_SHA" != "$CURRENT_SHA" ]; then
253
+ echo "⚠️ SHA updated from $CURRENT_SHA to $NEW_SHA"
254
+ fi
255
+ fi
256
+ fi
257
+
258
+ # Final verification
259
+ FINAL_SHA=$(git rev-parse HEAD)
260
+ FINAL_BRANCH=$(git branch --show-current || echo "detached")
261
+
262
+ echo "✅ Git state refreshed successfully"
263
+ echo "Final branch: $FINAL_BRANCH"
264
+ echo "Final SHA: $FINAL_SHA"
265
+
266
+ # Output for use in subsequent steps
267
+ echo "current_sha=$FINAL_SHA" >> $GITHUB_OUTPUT
268
+ echo "current_branch=$FINAL_BRANCH" >> $GITHUB_OUTPUT
269
+
270
+
201
271
- name : Parse comment command
202
272
id : parse-command
203
273
timeout-minutes : 2
@@ -355,25 +425,163 @@ jobs:
355
425
id : changes
356
426
if : steps.parse-command.outputs.full_analysis == 'false'
357
427
run : |
428
+
429
+ set -euo pipefail
430
+
358
431
BASE_REF="${{ steps.pr-info.outputs.base_ref }}"
432
+ CURRENT_SHA="${{ steps.git-refresh.outputs.current_sha }}"
433
+ CURRENT_BRANCH="${{ steps.git-refresh.outputs.current_branch }}"
434
+
435
+ echo "🔍 Detecting changed files for analysis"
436
+ echo "Base branch: $BASE_REF"
437
+ echo "Current SHA: $CURRENT_SHA"
438
+ echo "Current branch: $CURRENT_BRANCH"
439
+
440
+ # Ensure we have the base branch reference
441
+ echo "Fetching base branch: $BASE_REF"
442
+ git fetch origin "$BASE_REF" 2>/dev/null || {
443
+ echo "⚠️ Failed to fetch $BASE_REF, trying fallback methods"
444
+ git fetch origin 2>/dev/null || true
445
+ }
446
+
447
+ # Multiple fallback strategies for getting changed files
448
+ CHANGED_FILES=""
449
+ DIFF_SUCCESS=false
450
+
451
+ # Strategy 1: Standard three-dot diff (preferred)
452
+ if [ "$DIFF_SUCCESS" = false ]; then
453
+ echo "Trying three-dot diff: origin/$BASE_REF...HEAD"
454
+ if CHANGED_FILES=$(git diff --name-only "origin/$BASE_REF...HEAD" 2>/dev/null); then
455
+ echo "✅ Three-dot diff successful"
456
+ DIFF_SUCCESS=true
457
+ else
458
+ echo "❌ Three-dot diff failed"
459
+ fi
460
+ fi
359
461
360
- # Simple git fetch and diff
361
- git fetch origin $BASE_REF 2>/dev/null || true
362
- CHANGED_FILES=$(git diff --name-only origin/$BASE_REF...HEAD 2>/dev/null || echo "")
363
- CHANGED_COUNT=$(echo "$CHANGED_FILES" | grep -c . 2>/dev/null || echo "0")
462
+ # Strategy 2: Two-dot diff fallback
463
+ if [ "$DIFF_SUCCESS" = false ]; then
464
+ echo "Trying two-dot diff: origin/$BASE_REF..HEAD"
465
+ if CHANGED_FILES=$(git diff --name-only "origin/$BASE_REF..HEAD" 2>/dev/null); then
466
+ echo "✅ Two-dot diff successful"
467
+ DIFF_SUCCESS=true
468
+ else
469
+ echo "❌ Two-dot diff failed"
470
+ fi
471
+ fi
472
+
473
+ # Strategy 3: Direct branch comparison
474
+ if [ "$DIFF_SUCCESS" = false ]; then
475
+ echo "Trying direct comparison: origin/$BASE_REF HEAD"
476
+ if CHANGED_FILES=$(git diff --name-only "origin/$BASE_REF" HEAD 2>/dev/null); then
477
+ echo "✅ Direct comparison successful"
478
+ DIFF_SUCCESS=true
479
+ else
480
+ echo "❌ Direct comparison failed"
481
+ fi
482
+ fi
364
483
365
- # Simple validation
366
- if [ "$CHANGED_COUNT" -eq 0 ]; then
367
- CHANGED_FILES_STR="No files changed"
484
+ # Strategy 4: Last resort - use merge-base
485
+ if [ "$DIFF_SUCCESS" = false ]; then
486
+ echo "Trying merge-base approach"
487
+ if MERGE_BASE=$(git merge-base "origin/$BASE_REF" HEAD 2>/dev/null); then
488
+ if CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" HEAD 2>/dev/null); then
489
+ echo "✅ Merge-base diff successful"
490
+ DIFF_SUCCESS=true
491
+ fi
492
+ fi
493
+ fi
494
+
495
+ # Validate and process results
496
+ if [ "$DIFF_SUCCESS" = false ]; then
497
+ echo "❌ All diff strategies failed - will analyze full codebase"
498
+ CHANGED_FILES=""
499
+ CHANGED_COUNT=0
500
+ CHANGED_FILES_STR="Unable to determine changed files - will analyze full codebase"
368
501
else
369
- CHANGED_FILES_STR=$(echo "$CHANGED_FILES" | tr '\n' ' ')
502
+ # Filter out empty lines and count
503
+ CHANGED_FILES=$(echo "$CHANGED_FILES" | grep -v '^$' || echo "")
504
+ CHANGED_COUNT=$(echo "$CHANGED_FILES" | grep -c . 2>/dev/null || echo "0")
505
+
506
+ if [ "$CHANGED_COUNT" -eq 0 ]; then
507
+ CHANGED_FILES_STR="No files changed"
508
+ echo "⚠️ No changed files detected - this might indicate an issue"
509
+ else
510
+ CHANGED_FILES_STR=$(echo "$CHANGED_FILES" | tr '\n' ' ' | sed 's/[[:space:]]*$//')
511
+ echo "✅ Found $CHANGED_COUNT changed files"
512
+ fi
370
513
fi
371
514
372
- echo "Changed files: $CHANGED_FILES_STR"
373
- echo "Total changed files: $CHANGED_COUNT"
515
+ # Debug output
516
+ echo "📋 Changed files summary:"
517
+ echo " Count: $CHANGED_COUNT"
518
+ echo " Files: $CHANGED_FILES_STR"
519
+
520
+
521
+ if [ "$CHANGED_COUNT" -gt 0 ]; then
522
+ echo "📄 Individual files:"
523
+ echo "$CHANGED_FILES" | while IFS= read -r file; do
524
+ [ -n "$file" ] && echo " - $file"
525
+ done
526
+ fi
374
527
528
+ # Output for next steps
375
529
echo "changed_files=$CHANGED_FILES_STR" >> $GITHUB_OUTPUT
376
530
echo "changed_count=$CHANGED_COUNT" >> $GITHUB_OUTPUT
531
+ echo "diff_successful=$DIFF_SUCCESS" >> $GITHUB_OUTPUT
532
+
533
+ - name : Verify commit SHA and git state
534
+ id : verify-state
535
+ timeout-minutes : 2
536
+ run : |
537
+ set -euo pipefail
538
+
539
+ echo "🔍 Verifying git state before analysis"
540
+
541
+ CURRENT_SHA="${{ steps.git-refresh.outputs.current_sha }}"
542
+ ACTUAL_SHA=$(git rev-parse HEAD)
543
+ CURRENT_BRANCH="${{ steps.git-refresh.outputs.current_branch }}"
544
+
545
+ # Verify SHA consistency
546
+ if [ "$CURRENT_SHA" != "$ACTUAL_SHA" ]; then
547
+ echo "⚠️ SHA mismatch detected!"
548
+ echo " Expected: $CURRENT_SHA"
549
+ echo " Actual: $ACTUAL_SHA"
550
+ echo " This indicates a git state issue - attempting to resolve..."
551
+
552
+ # Update the SHA for consistency
553
+ CURRENT_SHA="$ACTUAL_SHA"
554
+ echo "current_sha=$ACTUAL_SHA" >> $GITHUB_OUTPUT
555
+ else
556
+ echo "✅ SHA verification passed: $CURRENT_SHA"
557
+ echo "current_sha=$CURRENT_SHA" >> $GITHUB_OUTPUT
558
+ fi
559
+
560
+ # Get additional context for debugging
561
+ echo "📊 Git state summary:"
562
+ echo " Commit SHA: $CURRENT_SHA"
563
+ echo " Branch: $CURRENT_BRANCH"
564
+ echo " Repository: ${{ github.repository }}"
565
+ echo " Event: ${{ github.event_name }}"
566
+
567
+ # Get commit info for better context
568
+ COMMIT_MSG=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "Unable to get commit message")
569
+ COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an" 2>/dev/null || echo "Unknown")
570
+ COMMIT_DATE=$(git log -1 --pretty=format:"%ci" 2>/dev/null || echo "Unknown")
571
+
572
+ echo " Commit message: $COMMIT_MSG"
573
+ echo " Author: $COMMIT_AUTHOR"
574
+ echo " Date: $COMMIT_DATE"
575
+
576
+ # Create unique identifier for cache invalidation
577
+ REVIEW_ID="${CURRENT_SHA:0:8}-${{ github.event_name }}-$(date +%s)"
578
+ echo " Review ID: $REVIEW_ID"
579
+
580
+ # Output additional context
581
+ echo "commit_message=$COMMIT_MSG" >> $GITHUB_OUTPUT
582
+ echo "commit_author=$COMMIT_AUTHOR" >> $GITHUB_OUTPUT
583
+ echo "commit_date=$COMMIT_DATE" >> $GITHUB_OUTPUT
584
+ echo "review_id=$REVIEW_ID" >> $GITHUB_OUTPUT
377
585
378
586
- name : Run Claude Code Review
379
587
id : claude
@@ -382,18 +590,28 @@ jobs:
382
590
with :
383
591
claude_code_oauth_token : ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
384
592
385
- # Optional: Add specific tools for running tests or linting
386
- # allowed_tools: "Bash(npm run test ),Bash(npm run lint ),Bash(npm run typecheck )"
593
+ # Allow Bash permissions for pre-commit hooks and documentation updates
594
+ allowed_tools : " Bash(pre-commit run --files ),Bash(terraform fmt ),Bash(terraform validate),Bash(terraform-docs )"
387
595
388
596
# Dynamic prompt based on review mode
389
597
direct_prompt : |
598
+ 🔄 **COMMIT ANALYSIS CONTEXT**
599
+ - **Commit SHA**: `${{ steps.verify-state.outputs.current_sha }}`
600
+ - **Review ID**: `${{ steps.verify-state.outputs.review_id }}`
601
+ - **Branch**: `${{ steps.git-refresh.outputs.current_branch }}`
602
+ - **Last Commit**: "${{ steps.verify-state.outputs.commit_message }}" by ${{ steps.verify-state.outputs.commit_author }}
603
+ - **Date**: ${{ steps.verify-state.outputs.commit_date }}
604
+
605
+ ---
606
+
390
607
${{ steps.parse-command.outputs.full_analysis == 'false' && format('
391
608
**IMPORTANT: Focus ONLY on the following changed files in this pull request:**
392
609
Files changed: {0}
393
610
Total files changed: {1}
611
+ Diff detection: {2}
394
612
395
613
DO NOT review or comment on files outside this list unless they are directly impacted by changes in these files.
396
- ', steps.changes.outputs.changed_files || 'Unable to determine changed files', steps.changes.outputs.changed_count || '0') || '' }}
614
+ ', steps.changes.outputs.changed_files || 'Unable to determine changed files', steps.changes.outputs.changed_count || '0', steps.changes.outputs.diff_successful == 'true' && 'Successful' || 'Failed - analyzing full codebase' ) || '' }}
397
615
398
616
${{ steps.parse-command.outputs.mode == 'hunt' && format('
399
617
🕵️ BUG HUNT MODE - Find potential issues quickly:
@@ -444,8 +662,9 @@ jobs:
444
662
Be constructive and helpful.
445
663
', steps.parse-command.outputs.focus, steps.parse-command.outputs.verbose) || '' }}
446
664
447
- # Use sticky comments for better UX
665
+ # Use sticky comments with commit-specific cache invalidation
448
666
use_sticky_comment : true
667
+ # Note: The commit SHA and review ID in the prompt help ensure fresh analysis
449
668
450
669
- name : Workflow Summary
451
670
if : always()
@@ -472,11 +691,34 @@ jobs:
472
691
echo "**Full Analysis:** \`${{ steps.parse-command.outputs.full_analysis || 'false' }}\`" >> $GITHUB_STEP_SUMMARY
473
692
if [ "${{ steps.parse-command.outputs.full_analysis }}" != "true" ]; then
474
693
echo "**Changed Files:** \`${{ steps.changes.outputs.changed_count || '0' }}\` files" >> $GITHUB_STEP_SUMMARY
694
+ echo "**Diff Detection:** \`${{ steps.changes.outputs.diff_successful == 'true' && 'Successful' || 'Failed' }}\`" >> $GITHUB_STEP_SUMMARY
475
695
fi
476
696
echo "**PR Number:** \`${{ steps.pr-info.outputs.pr_number || 'N/A' }}\`" >> $GITHUB_STEP_SUMMARY
477
697
echo "**Base Branch:** \`${{ steps.pr-info.outputs.base_ref || 'N/A' }}\`" >> $GITHUB_STEP_SUMMARY
478
698
echo "" >> $GITHUB_STEP_SUMMARY
479
699
700
+ # Git State Information
701
+ echo "### 📊 Git State Analysis" >> $GITHUB_STEP_SUMMARY
702
+ echo "**Commit SHA:** \`${{ steps.verify-state.outputs.current_sha || steps.git-refresh.outputs.current_sha || 'Unknown' }}\`" >> $GITHUB_STEP_SUMMARY
703
+ echo "**Review ID:** \`${{ steps.verify-state.outputs.review_id || 'N/A' }}\`" >> $GITHUB_STEP_SUMMARY
704
+ echo "**Current Branch:** \`${{ steps.git-refresh.outputs.current_branch || 'Unknown' }}\`" >> $GITHUB_STEP_SUMMARY
705
+ echo "**Last Commit:** \"${{ steps.verify-state.outputs.commit_message || 'Unknown' }}\"" >> $GITHUB_STEP_SUMMARY
706
+ echo "**Author:** \`${{ steps.verify-state.outputs.commit_author || 'Unknown' }}\`" >> $GITHUB_STEP_SUMMARY
707
+ echo "**Date:** \`${{ steps.verify-state.outputs.commit_date || 'Unknown' }}\`" >> $GITHUB_STEP_SUMMARY
708
+ echo "" >> $GITHUB_STEP_SUMMARY
709
+
710
+ # Analysis Scope Details
711
+ if [ "${{ steps.parse-command.outputs.full_analysis }}" != "true" ] && [ "${{ steps.changes.outputs.changed_count || '0' }}" -gt "0" ]; then
712
+ echo "### 📄 Files Being Analyzed" >> $GITHUB_STEP_SUMMARY
713
+ echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
714
+ echo "${{ steps.changes.outputs.changed_files }}" | tr ' ' '\n' | grep -v '^$' | head -20 >> $GITHUB_STEP_SUMMARY
715
+ if [ "${{ steps.changes.outputs.changed_count }}" -gt "20" ]; then
716
+ echo "... and $((${{ steps.changes.outputs.changed_count }} - 20)) more files" >> $GITHUB_STEP_SUMMARY
717
+ fi
718
+ echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
719
+ echo "" >> $GITHUB_STEP_SUMMARY
720
+ fi
721
+
480
722
# Available Commands
481
723
echo "### 📝 Available Commands" >> $GITHUB_STEP_SUMMARY
482
724
echo "Comment any of these in PRs to trigger specific review types:" >> $GITHUB_STEP_SUMMARY
@@ -506,8 +748,19 @@ jobs:
506
748
# Performance & Reliability Info
507
749
echo "" >> $GITHUB_STEP_SUMMARY
508
750
echo "### ⚡ Performance & Reliability" >> $GITHUB_STEP_SUMMARY
509
- echo "- 🚀 Optimized git operations with configurable fetch depth" >> $GITHUB_STEP_SUMMARY
510
- echo "- 🔄 Automatic retry logic for network operations" >> $GITHUB_STEP_SUMMARY
751
+ echo "- 🚀 Full git history fetch for reliable diff comparisons" >> $GITHUB_STEP_SUMMARY
752
+ echo "- 🔄 Automatic git state refresh and commit SHA verification" >> $GITHUB_STEP_SUMMARY
753
+ echo "- 🎯 Multiple fallback strategies for change detection" >> $GITHUB_STEP_SUMMARY
511
754
echo "- ⏱️ Timeout protection prevents runaway executions" >> $GITHUB_STEP_SUMMARY
512
755
echo "- 🛡️ Comprehensive error handling and validation" >> $GITHUB_STEP_SUMMARY
513
- echo "- 📊 Enhanced logging for troubleshooting" >> $GITHUB_STEP_SUMMARY
756
+ echo "- 📊 Enhanced logging and debug information" >> $GITHUB_STEP_SUMMARY
757
+ echo "- 🔐 Cache invalidation via commit-specific review IDs" >> $GITHUB_STEP_SUMMARY
758
+
759
+ # Troubleshooting section
760
+ echo "" >> $GITHUB_STEP_SUMMARY
761
+ echo "### 🔧 Troubleshooting" >> $GITHUB_STEP_SUMMARY
762
+ echo "If the review doesn't detect recent changes:" >> $GITHUB_STEP_SUMMARY
763
+ echo "1. Check the commit SHA matches your latest changes" >> $GITHUB_STEP_SUMMARY
764
+ echo "2. Verify the diff detection was successful" >> $GITHUB_STEP_SUMMARY
765
+ echo "3. Review the git state analysis above" >> $GITHUB_STEP_SUMMARY
766
+ echo "4. Try \`codebot hunt --full\` to analyze the entire codebase" >> $GITHUB_STEP_SUMMARY
0 commit comments