Workflows #6
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
| # Gemini AI-Powered Code Analysis | |
| # Analyzes code changes in PRs, pushes, and branches - FOCUSES ON CODE CHANGES | |
| name: AI Code Analysis | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| push: | |
| branches: [main, develop, 'feature/*', 'bugfix/*'] | |
| workflow_dispatch: | |
| # Cancel previous workflow runs for the same context | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| ai-analysis: | |
| name: AI Analysis & Assistant | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js for Google AI SDK | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: '20' | |
| - name: Get Code Changes | |
| id: get-changes | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| BASE_SHA: ${{ github.event.before || '' }} | |
| HEAD_SHA: ${{ github.sha }} | |
| REPO_FULL_NAME: ${{ github.repository }} | |
| run: | | |
| echo "📋 Collecting code changes for analysis..." | |
| echo "🔍 Debug info: BASE_SHA=$BASE_SHA, HEAD_SHA=$HEAD_SHA" | |
| if [ "$EVENT_NAME" = "pull_request" ]; then | |
| # For PRs, use git diff as primary method | |
| PR_BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}" | |
| echo "🔍 PR Debug: BASE=$PR_BASE_SHA, HEAD=$PR_HEAD_SHA" | |
| # Use git diff as PRIMARY method (more reliable) | |
| echo "🔧 Using git diff (primary method)..." | |
| if git diff "$PR_BASE_SHA".."$PR_HEAD_SHA" > code_changes.diff 2>git_error.log; then | |
| echo "✅ Git diff successful - collected $(wc -l < code_changes.diff) lines" | |
| else | |
| echo "❌ Git diff failed, trying GitHub API as fallback..." | |
| cat git_error.log 2>/dev/null || echo "No git error details" | |
| # GitHub API as fallback | |
| curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github.v3.diff" \ | |
| "https://api.github.com/repos/$REPO_FULL_NAME/compare/$PR_BASE_SHA..$PR_HEAD_SHA" \ | |
| > code_changes.diff | |
| if [ -s code_changes.diff ]; then | |
| echo "✅ GitHub API fallback successful" | |
| else | |
| echo "❌ Both methods failed for PR" | |
| fi | |
| fi | |
| elif [ "$EVENT_NAME" = "push" ]; then | |
| # For pushes, get the diff from the previous commit | |
| if [ -n "$BASE_SHA" ] && [ "$BASE_SHA" != "0000000000000000000000000000000000000000" ]; then | |
| echo "🔍 Push Debug: Comparing $BASE_SHA to $HEAD_SHA" | |
| # Use git diff as PRIMARY method (more reliable) | |
| echo "🔧 Using git diff (primary method)..." | |
| if git diff "$BASE_SHA".."$HEAD_SHA" > code_changes.diff 2>git_error.log; then | |
| echo "✅ Git diff successful - collected $(wc -l < code_changes.diff) lines" | |
| else | |
| echo "❌ Git diff failed, trying GitHub API as fallback..." | |
| cat git_error.log 2>/dev/null || echo "No git error details" | |
| # GitHub API as fallback only | |
| echo "🌐 Attempting GitHub API diff as fallback..." | |
| echo "🔍 URL: https://api.github.com/repos/$REPO_FULL_NAME/compare/$BASE_SHA..$HEAD_SHA" | |
| HTTP_CODE=$(curl -s -w "%{http_code}" \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| -H "Accept: application/vnd.github.v3.diff" \ | |
| "https://api.github.com/repos/$REPO_FULL_NAME/compare/$BASE_SHA..$HEAD_SHA" \ | |
| -o code_changes.diff 2>api_error.log) | |
| if [ "$HTTP_CODE" = "200" ] && [ -s code_changes.diff ]; then | |
| echo "✅ GitHub API fallback successful" | |
| else | |
| echo "❌ GitHub API also failed (HTTP $HTTP_CODE)" | |
| fi | |
| fi | |
| # Final fallback: git show if no diff available | |
| if [ ! -s code_changes.diff ]; then | |
| echo "⚠️ No diff available, showing recent commit changes..." | |
| echo "🔧 Running: git show --stat $HEAD_SHA" | |
| git show --stat "$HEAD_SHA" > code_changes.diff 2>>git_error.log | |
| echo "" >> code_changes.diff | |
| git show "$HEAD_SHA" >> code_changes.diff 2>>git_error.log | |
| if [ -s code_changes.diff ]; then | |
| echo "✅ Git show successful as final fallback" | |
| else | |
| echo "❌ All methods failed - no code changes available" | |
| fi | |
| fi | |
| else | |
| echo "📄 Initial commit or no previous commit - showing current files..." | |
| git show --name-only $HEAD_SHA | head -10 | while read file; do | |
| if [ -f "$file" ]; then | |
| echo "=== $file ===" >> code_changes.diff | |
| head -50 "$file" >> code_changes.diff | |
| echo "" >> code_changes.diff | |
| fi | |
| done | |
| fi | |
| else | |
| echo "No code changes available for this event type" > code_changes.diff | |
| fi | |
| # Check if we got changes | |
| if [ -s code_changes.diff ]; then | |
| echo "✅ Code changes collected: $(wc -l < code_changes.diff) lines" | |
| echo "changes-available=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "⚠️ No code changes found" | |
| echo "changes-available=false" >> $GITHUB_OUTPUT | |
| fi | |
| - 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 analyzeCode() { | |
| 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); | |
| const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); | |
| const prompt = fs.readFileSync('analysis_prompt.txt', 'utf8'); | |
| console.log('📝 Prompt loaded, size:', prompt.length, 'characters'); | |
| console.log('🚀 Generating analysis...'); | |
| const result = await model.generateContent(prompt); | |
| const response = await result.response; | |
| const text = response.text(); | |
| fs.writeFileSync('ai_analysis_result.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 conduct a manual code review focusing on:', | |
| '- WordPress security best practices', | |
| '- Coding standards compliance', | |
| '- Performance considerations', | |
| '- Plugin-specific requirements', | |
| '', | |
| 'The PR can still be reviewed and merged based on manual inspection.' | |
| ].join('\n'); | |
| fs.writeFileSync('ai_analysis_result.txt', fallbackContent); | |
| process.exit(1); | |
| } | |
| } | |
| analyzeCode().catch(error => { | |
| console.error('Fatal error:', error); | |
| process.exit(1); | |
| }); | |
| SCRIPT_EOF | |
| echo "✅ Analysis script created successfully" | |
| - name: Create Analysis Prompt | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title || format('Push Analysis - {0}', github.ref_name) }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login || github.actor }} | |
| CHANGES_AVAILABLE: ${{ steps.get-changes.outputs.changes-available }} | |
| run: | | |
| echo "📝 Creating analysis prompt..." | |
| echo "You are an expert WordPress plugin developer and security consultant." > analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "CRITICAL INSTRUCTION: FOCUS ON THE CODE CHANGES AND PROVIDE SECURITY ANALYSIS." >> analysis_prompt.txt | |
| echo "Analyze what was changed, added, or removed and review those specific modifications." >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "Context: $PR_TITLE by @$PR_AUTHOR" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "Please analyze this WordPress plugin code for:" >> analysis_prompt.txt | |
| echo "1. Security vulnerabilities" >> analysis_prompt.txt | |
| echo "2. WordPress coding standards compliance" >> analysis_prompt.txt | |
| echo "3. Performance considerations" >> analysis_prompt.txt | |
| echo "4. Best practice recommendations" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| echo "Provide specific, actionable feedback." >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| # Add the actual code changes | |
| if [ "$CHANGES_AVAILABLE" = "true" ]; then | |
| echo "Here are the code changes to analyze:" >> analysis_prompt.txt | |
| echo "" >> analysis_prompt.txt | |
| cat code_changes.diff >> analysis_prompt.txt | |
| else | |
| echo "No code changes were detected in this commit." >> analysis_prompt.txt | |
| fi | |
| - name: Run AI Analysis | |
| id: ai-analysis | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} | |
| run: | | |
| echo "🤖 Starting AI analysis with official Google SDK..." | |
| echo "📝 Prompt file size: $(wc -c < analysis_prompt.txt) bytes" | |
| 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 | |
| - name: Output Analysis Results | |
| env: | |
| PR_NUMBER: ${{ github.event.number || '' }} | |
| IS_PR: ${{ github.event_name == 'pull_request' }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| run: | | |
| echo "📊 Analysis Results for $EVENT_NAME event:" | |
| echo "============================================================" | |
| if [ -f ai_analysis_result.txt ]; then | |
| cat ai_analysis_result.txt | |
| else | |
| echo "❌ Analysis result file not found" | |
| fi | |
| echo "============================================================" | |
| echo "✅ Analysis output complete" |