@@ -193,11 +193,81 @@ jobs:
193193 - name : Checkout repository
194194 uses : actions/checkout@v4
195195 with :
196- fetch-depth : 50 # Optimized depth for most PR scenarios
196+ fetch-depth : 0 # Full history for reliable diff comparisons
197197 token : ${{ secrets.GITHUB_TOKEN }}
198198 # For issue_comment events, checkout the PR branch
199199 ref : ${{ github.event_name == 'issue_comment' && steps.pr-checkout-info.outputs.pr_head_ref || github.ref }}
200200
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+
201271 - name : Parse comment command
202272 id : parse-command
203273 timeout-minutes : 2
@@ -355,25 +425,163 @@ jobs:
355425 id : changes
356426 if : steps.parse-command.outputs.full_analysis == 'false'
357427 run : |
428+
429+ set -euo pipefail
430+
358431 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
359461
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
364483
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"
368501 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
370513 fi
371514
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
374527
528+ # Output for next steps
375529 echo "changed_files=$CHANGED_FILES_STR" >> $GITHUB_OUTPUT
376530 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
377585
378586 - name : Run Claude Code Review
379587 id : claude
@@ -382,18 +590,28 @@ jobs:
382590 with :
383591 claude_code_oauth_token : ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
384592
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 )"
387595
388596 # Dynamic prompt based on review mode
389597 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+
390607 ${{ steps.parse-command.outputs.full_analysis == 'false' && format('
391608 **IMPORTANT: Focus ONLY on the following changed files in this pull request:**
392609 Files changed: {0}
393610 Total files changed: {1}
611+ Diff detection: {2}
394612
395613 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' ) || '' }}
397615
398616 ${{ steps.parse-command.outputs.mode == 'hunt' && format('
399617 🕵️ BUG HUNT MODE - Find potential issues quickly:
@@ -444,8 +662,9 @@ jobs:
444662 Be constructive and helpful.
445663 ', steps.parse-command.outputs.focus, steps.parse-command.outputs.verbose) || '' }}
446664
447- # Use sticky comments for better UX
665+ # Use sticky comments with commit-specific cache invalidation
448666 use_sticky_comment : true
667+ # Note: The commit SHA and review ID in the prompt help ensure fresh analysis
449668
450669 - name : Workflow Summary
451670 if : always()
@@ -472,11 +691,34 @@ jobs:
472691 echo "**Full Analysis:** \`${{ steps.parse-command.outputs.full_analysis || 'false' }}\`" >> $GITHUB_STEP_SUMMARY
473692 if [ "${{ steps.parse-command.outputs.full_analysis }}" != "true" ]; then
474693 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
475695 fi
476696 echo "**PR Number:** \`${{ steps.pr-info.outputs.pr_number || 'N/A' }}\`" >> $GITHUB_STEP_SUMMARY
477697 echo "**Base Branch:** \`${{ steps.pr-info.outputs.base_ref || 'N/A' }}\`" >> $GITHUB_STEP_SUMMARY
478698 echo "" >> $GITHUB_STEP_SUMMARY
479699
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+
480722 # Available Commands
481723 echo "### 📝 Available Commands" >> $GITHUB_STEP_SUMMARY
482724 echo "Comment any of these in PRs to trigger specific review types:" >> $GITHUB_STEP_SUMMARY
@@ -506,8 +748,19 @@ jobs:
506748 # Performance & Reliability Info
507749 echo "" >> $GITHUB_STEP_SUMMARY
508750 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
511754 echo "- ⏱️ Timeout protection prevents runaway executions" >> $GITHUB_STEP_SUMMARY
512755 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