Bump peter-evans/create-or-update-comment from 4 to 5 in the actions group #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Gemini Issue Assistant | |
| on: | |
| issues: | |
| types: [opened, edited] | |
| issue_comment: | |
| types: [created, edited] | |
| jobs: | |
| analyze-issue: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '20' | |
| - name: Install Google AI SDK and Create Analysis Script | |
| run: | | |
| echo "🔧 Installing Google AI SDK..." | |
| npm install @google/generative-ai | |
| echo "📦 Creating Gemini analysis script..." | |
| cat > gemini-analyze.js << 'SCRIPT_EOF' | |
| const { GoogleGenerativeAI } = require('@google/generative-ai'); | |
| const fs = require('fs'); | |
| async function analyzeIssue() { | |
| try { | |
| const apiKey = process.env.GEMINI_API_KEY; | |
| if (!apiKey) { | |
| throw new Error('GEMINI_API_KEY environment variable not found'); | |
| } | |
| console.log('🔑 API key configured, length:', apiKey.length); | |
| console.log('🤖 Initializing Gemini AI...'); | |
| const genAI = new GoogleGenerativeAI(apiKey); | |
| // Try different model names prioritizing quality first, then fallback on rate limits | |
| // Order: 2.5 models (highest quality) -> 2.0 models (higher RPM) -> legacy | |
| const modelNames = [ | |
| "gemini-2.5-pro", // 5 RPM, 250K TPM - Highest quality | |
| "gemini-2.5-flash", // 10 RPM, 250K TPM - Best 2.5 balance | |
| "gemini-2.5-flash-preview", // 10 RPM, 250K TPM - Latest 2.5 features | |
| "gemini-2.5-flash-lite", // 15 RPM, 250K TPM - Faster 2.5 | |
| "gemini-2.5-flash-lite-preview", // 15 RPM, 250K TPM - Latest 2.5 lite | |
| "gemini-2.0-flash", // 15 RPM, 1M TPM - Good 2.0 balance | |
| "gemini-2.0-flash-lite", // 30 RPM, 1M TPM - Highest RPM fallback | |
| "gemini-1.5-flash", // 15 RPM, 250K TPM - DEPRECATED fallback | |
| "gemini-pro" // Legacy final fallback | |
| ]; | |
| let model = null; | |
| let modelUsed = null; | |
| for (const modelName of modelNames) { | |
| try { | |
| console.log('🔧 Trying model:', modelName); | |
| model = genAI.getGenerativeModel({ model: modelName }); | |
| // Test the model with a small request to check availability/rate limits | |
| console.log('🧪 Testing model availability...'); | |
| await model.generateContent("test"); | |
| modelUsed = modelName; | |
| console.log('✅ Successfully initialized and tested model:', modelName); | |
| break; | |
| } catch (modelError) { | |
| console.log('❌ Model', modelName, 'failed:', modelError.message); | |
| // Check for rate limit errors specifically | |
| if (modelError.message && ( | |
| modelError.message.includes('rate limit') || | |
| modelError.message.includes('quota') || | |
| modelError.message.includes('429') || | |
| modelError.status === 429 | |
| )) { | |
| console.log('⚠️ Rate limit detected, trying next model with higher RPM...'); | |
| } else if (modelError.message && modelError.message.includes('404')) { | |
| console.log('⚠️ Model not found, trying next available model...'); | |
| } | |
| continue; | |
| } | |
| } | |
| if (!model) { | |
| throw new Error('No supported Gemini model could be initialized'); | |
| } | |
| const prompt = fs.readFileSync('analysis_prompt.txt', 'utf8'); | |
| console.log('📝 Prompt loaded, size:', prompt.length, 'characters'); | |
| console.log('🚀 Generating analysis with model:', modelUsed); | |
| const result = await model.generateContent(prompt); | |
| const response = await result.response; | |
| const text = response.text(); | |
| fs.writeFileSync('gemini_response.txt', text); | |
| console.log('✅ Analysis completed successfully'); | |
| console.log('📄 Result size:', text.length, 'characters'); | |
| } catch (error) { | |
| console.error('❌ Gemini analysis failed:', error.message); | |
| console.error('🔍 Full error details:', error); | |
| const fallbackContent = [ | |
| '## 🤖 AI Analysis Status', | |
| '', | |
| 'The automated AI analysis encountered an issue: ' + error.message, | |
| '', | |
| 'This may be due to:', | |
| '- API key configuration issues', | |
| '- Network connectivity problems', | |
| '- Gemini API rate limits or service issues', | |
| '- Invalid prompt format or size', | |
| '', | |
| '### Manual Review Recommended', | |
| 'Please review this issue manually and check the repository for related code.', | |
| '' | |
| ].join('\n'); | |
| fs.writeFileSync('gemini_response.txt', fallbackContent); | |
| process.exit(1); | |
| } | |
| } | |
| analyzeIssue().catch(error => { | |
| console.error('Fatal error:', error); | |
| process.exit(1); | |
| }); | |
| SCRIPT_EOF | |
| echo "✅ Analysis script created successfully" | |
| - name: Determine analysis type | |
| id: analysis-type | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| ISSUE_TITLE: ${{ github.event.issue.title }} | |
| ISSUE_AUTHOR: ${{ github.event.issue.user.login }} | |
| COMMENT_AUTHOR: ${{ github.event.comment.user.login }} | |
| run: | | |
| if [ "$EVENT_NAME" = "issues" ]; then | |
| echo "type=issue-analysis" >> $GITHUB_OUTPUT | |
| echo "Issue: $ISSUE_TITLE" | |
| echo "Author: $ISSUE_AUTHOR" | |
| elif [ "$EVENT_NAME" = "issue_comment" ]; then | |
| echo "type=comment-analysis" >> $GITHUB_OUTPUT | |
| echo "Comment on issue: $ISSUE_TITLE" | |
| echo "Comment author: $COMMENT_AUTHOR" | |
| else | |
| echo "type=skip" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Scan Codebase for Context | |
| id: scan-code | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPO_FULL_NAME: ${{ github.repository }} | |
| run: | | |
| echo "📋 Scanning codebase for relevant context..." | |
| # Get recent commits for context with git as primary method | |
| echo "📜 Collecting recent commits..." | |
| if git log --oneline -5 > recent_commits.txt 2>git_error.log; then | |
| echo "✅ Recent commits collected via git" | |
| else | |
| echo "⚠️ Failed to get recent commits via git:" | |
| cat git_error.log 2>/dev/null || echo "No git error details" | |
| echo "No recent commits available" > recent_commits.txt | |
| fi | |
| # Get main plugin files for context using reliable file system operations | |
| echo "📁 Collecting plugin file structure..." | |
| echo "🔍 Main plugin files:" > codebase_context.txt | |
| # Use git ls-files as primary method (more reliable than find) | |
| echo "🔧 Using git ls-files (primary method)..." | |
| if git ls-files "*.php" | head -10 > found_files.txt 2>git_error.log; then | |
| echo "✅ Git ls-files successful" | |
| while read file; do | |
| if [ -f "$file" ] && [ -n "$file" ]; then | |
| echo "=== $file ===" >> codebase_context.txt | |
| echo "🔍 Processing: $file" | |
| if head -30 "$file" >> codebase_context.txt 2>/dev/null; then | |
| echo "✅ Added content from $file" | |
| else | |
| echo "⚠️ Failed to read $file" | |
| fi | |
| echo "" >> codebase_context.txt | |
| fi | |
| done < found_files.txt | |
| else | |
| echo "⚠️ Git ls-files failed, using find as fallback..." | |
| cat git_error.log 2>/dev/null || echo "No git error details" | |
| # Find as fallback method | |
| if find . -name "*.php" -path "./.*" -prune -o -name "*.php" -print 2>/dev/null | head -10 > found_files.txt; then | |
| echo "✅ Find fallback successful" | |
| while read file; do | |
| if [ -f "$file" ] && [ -n "$file" ]; then | |
| echo "=== $file ===" >> codebase_context.txt | |
| head -30 "$file" >> codebase_context.txt 2>/dev/null | |
| echo "" >> codebase_context.txt | |
| fi | |
| done < found_files.txt | |
| else | |
| echo "❌ Both git and find methods failed" | |
| echo "No PHP files found" >> codebase_context.txt | |
| fi | |
| fi | |
| # Add recent commits to context | |
| echo "" >> codebase_context.txt | |
| echo "📜 Recent Git History:" >> codebase_context.txt | |
| cat recent_commits.txt >> codebase_context.txt | |
| # Check if we collected context with better validation | |
| if [ -s codebase_context.txt ] && [ $(wc -l < codebase_context.txt) -gt 5 ]; then | |
| echo "✅ Codebase context collected: $(wc -l < codebase_context.txt) lines" | |
| echo "context-available=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Insufficient codebase context collected" | |
| echo "🔍 Context file size: $(wc -l < codebase_context.txt 2>/dev/null || echo '0') lines" | |
| echo "context-available=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create analysis prompt | |
| env: | |
| ANALYSIS_TYPE: ${{ steps.analysis-type.outputs.type }} | |
| ISSUE_TITLE: ${{ github.event.issue.title }} | |
| ISSUE_BODY: ${{ github.event.issue.body }} | |
| ISSUE_AUTHOR: ${{ github.event.issue.user.login }} | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| COMMENT_AUTHOR: ${{ github.event.comment.user.login }} | |
| CONTEXT_AVAILABLE: ${{ steps.scan-code.outputs.context-available }} | |
| run: | | |
| # Skip analysis if not relevant | |
| if [ "$ANALYSIS_TYPE" = "skip" ]; then | |
| echo "No relevant issue activity. Skipping analysis." > analysis_prompt.txt | |
| echo "analysis-skipped=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| elif [ "$ANALYSIS_TYPE" = "issue-analysis" ]; then | |
| # Create issue-focused prompt - FOCUS ON USER'S PROBLEM FIRST | |
| echo "You are an expert WordPress plugin developer helping users solve problems." > analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "CRITICAL INSTRUCTION: FOCUS FIRST ON UNDERSTANDING THE USER'S ISSUE." >> analysis_prompt.txt | |
| echo "Then scan the codebase to find potential solutions or identify code-related causes." >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "ISSUE DETAILS:" >> analysis_prompt.txt | |
| echo "Title: $ISSUE_TITLE" >> analysis_prompt.txt | |
| echo "Author: @$ISSUE_AUTHOR" >> analysis_prompt.txt | |
| echo "Description:" >> analysis_prompt.txt | |
| echo "$ISSUE_BODY" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "ANALYSIS APPROACH:" >> analysis_prompt.txt | |
| echo "1. Understand the user's problem/request thoroughly" >> analysis_prompt.txt | |
| echo "2. Scan the codebase for related functionality" >> analysis_prompt.txt | |
| echo "3. Identify potential code-based solutions or fixes" >> analysis_prompt.txt | |
| echo "4. Check for existing similar functionality" >> analysis_prompt.txt | |
| echo "5. Provide actionable recommendations" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "REPOSITORY CONTEXT: WordPress plugin project (WordPress 6.5+, PHP 7.4+)" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| elif [ "$ANALYSIS_TYPE" = "comment-analysis" ]; then | |
| # Create comment-focused prompt - FOCUS ON CONVERSATION CONTEXT | |
| echo "You are an expert WordPress plugin developer analyzing an issue conversation." > analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "CRITICAL INSTRUCTION: FOCUS ON THE CONVERSATION CONTEXT AND NEW INFORMATION." >> analysis_prompt.txt | |
| echo "Analyze the new comment in relation to the original issue and provide relevant insights." >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "ORIGINAL ISSUE:" >> analysis_prompt.txt | |
| echo "Title: $ISSUE_TITLE" >> analysis_prompt.txt | |
| echo "Description: $ISSUE_BODY" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "NEW COMMENT:" >> analysis_prompt.txt | |
| echo "Author: @$COMMENT_AUTHOR" >> analysis_prompt.txt | |
| echo "Content: $COMMENT_BODY" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "ANALYSIS FOCUS:" >> analysis_prompt.txt | |
| echo "1. How does this comment relate to the original issue?" >> analysis_prompt.txt | |
| echo "2. What new information or clarification is provided?" >> analysis_prompt.txt | |
| echo "3. Are there code implications from this comment?" >> analysis_prompt.txt | |
| echo "4. What follow-up actions are suggested?" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "REPOSITORY CONTEXT: WordPress plugin project (WordPress 6.5+, PHP 7.4+)" >> analysis_prompt.txt | |
| fi | |
| # Add codebase context if available | |
| if [ "$CONTEXT_AVAILABLE" = "true" ]; then | |
| echo "" >> analysis_prompt.txt | |
| echo "CODEBASE CONTEXT FOR REFERENCE:" >> analysis_prompt.txt | |
| cat codebase_context.txt >> analysis_prompt.txt | |
| fi | |
| - name: Run AI Analysis | |
| id: ai-analysis | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} | |
| ANALYSIS_SKIPPED: ${{ steps.analysis-type.outputs.analysis-skipped }} | |
| run: | | |
| if [ "$ANALYSIS_SKIPPED" = "true" ]; then | |
| echo "Analysis skipped" | |
| exit 0 | |
| fi | |
| echo "🤖 Starting AI issue analysis with official Google SDK..." | |
| echo "📝 Prompt file size: $(wc -c < analysis_prompt.txt) bytes" | |
| echo "🔑 API key status: $([ -n "$GEMINI_API_KEY" ] && echo "✅ Set" || echo "❌ Missing")" | |
| if node gemini-analyze.js; then | |
| echo "analysis-success=true" >> $GITHUB_OUTPUT | |
| echo "✅ AI analysis completed successfully" | |
| else | |
| echo "analysis-success=false" >> $GITHUB_OUTPUT | |
| echo "❌ AI analysis failed - check logs for details" | |
| fi | |
| # Format the response with enhanced error handling | |
| echo "## 🤖 Gemini Issue Analysis" > formatted_response.txt | |
| echo "" >> formatted_response.txt | |
| if [ -s gemini_response.txt ]; then | |
| echo "📄 Adding analysis results ($(wc -c < gemini_response.txt) characters)" | |
| cat gemini_response.txt >> formatted_response.txt | |
| else | |
| echo "⚠️ No analysis results found - adding fallback message" | |
| echo "Analysis completed but encountered issues. Please review the issue manually and check the repository for related functionality." >> formatted_response.txt | |
| fi | |
| echo "" >> formatted_response.txt | |
| echo "---" >> formatted_response.txt | |
| echo "*Analysis performed by Gemini AI on $(date)*" >> formatted_response.txt | |
| # Debug: Show final response size | |
| echo "📊 Final response size: $(wc -c < formatted_response.txt) characters" | |
| - name: Comment on Issue | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let response = ''; | |
| if (fs.existsSync('formatted_response.txt')) { | |
| response = fs.readFileSync('formatted_response.txt', 'utf8'); | |
| } else { | |
| response = '## 🤖 Gemini Issue Analysis\n\nAnalysis completed. Please review the codebase for potential solutions to this issue.'; | |
| } | |
| // Get the issue number | |
| const issueNumber = context.issue.number; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueNumber, | |
| body: response | |
| }); |