Skip to content

Yurii bart feat add retryable policy 01 #67

Yurii bart feat add retryable policy 01

Yurii bart feat add retryable policy 01 #67

name: AI Code Risk & Compatibility Analysis (Gemini)
on:
pull_request:
branches:
- master
- main
jobs:
gemini-analysis:
runs-on: ubuntu-latest
steps:
# 1. Checkout PR code
- name: Checkout PR branch
uses: actions/checkout@v3
with:
fetch-depth: 0
# 2. Get changed C# files and generate diff
- name: Get changed files and diff
id: changed
run: |
BASE_BRANCH="${{ github.event.pull_request.base.ref }}"
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
echo "Base branch: $BASE_BRANCH"
echo "Base SHA: $BASE_SHA"
echo "Head SHA: $HEAD_SHA"
# Fetch the base branch explicitly to ensure it's available
git fetch origin "$BASE_BRANCH" || true
# Get changed C# files comparing base SHA to head SHA
# Fallback to branch comparison if SHA comparison fails
CHANGED_FILES=$(git diff --name-only "$BASE_SHA"..."$HEAD_SHA" 2>/dev/null | grep '\.cs$' || \
git diff --name-only "origin/$BASE_BRANCH"...HEAD 2>/dev/null | grep '\.cs$' || \
git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep '\.cs$' || true)
if [ -z "$CHANGED_FILES" ]; then
echo "No C# files changed in this PR"
echo "changed_files=" >> $GITHUB_OUTPUT
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "Changed C# files:"
echo "$CHANGED_FILES"
# Convert newlines to spaces for GitHub Actions output (multiline values cause issues)
CHANGED_FILES_SPACE=$(echo "$CHANGED_FILES" | tr '\n' ' ' | xargs)
echo "changed_files=$CHANGED_FILES_SPACE" >> $GITHUB_OUTPUT
echo "has_changes=true" >> $GITHUB_OUTPUT
# Generate unified diff for all changed C# files
# Use the same fallback logic for diff generation
# Try three-dot syntax first, then two-dot syntax
git diff "$BASE_SHA"..."$HEAD_SHA" -- $CHANGED_FILES_SPACE > code_diff.txt 2>/dev/null || \
git diff "$BASE_SHA..$HEAD_SHA" -- $CHANGED_FILES_SPACE > code_diff.txt 2>/dev/null || \
git diff "origin/$BASE_BRANCH"...HEAD -- $CHANGED_FILES_SPACE > code_diff.txt 2>/dev/null || \
git diff "origin/$BASE_BRANCH..HEAD" -- $CHANGED_FILES_SPACE > code_diff.txt 2>/dev/null || \
git diff "$BASE_BRANCH"...HEAD -- $CHANGED_FILES_SPACE > code_diff.txt 2>/dev/null || \
git diff "$BASE_BRANCH..HEAD" -- $CHANGED_FILES_SPACE > code_diff.txt 2>/dev/null || true
if [ -s code_diff.txt ]; then
echo "Generated diff file with $(wc -l < code_diff.txt) lines"
echo "diff_file=code_diff.txt" >> $GITHUB_OUTPUT
else
echo "WARNING: Could not generate diff file"
echo "Attempting alternative diff method..."
# Try diffing each file individually and concatenating
> code_diff.txt
for file in $CHANGED_FILES_SPACE; do
if [ -f "$file" ]; then
echo "=== $file ===" >> code_diff.txt
git diff "$BASE_SHA" "$HEAD_SHA" -- "$file" >> code_diff.txt 2>/dev/null || \
git diff "origin/$BASE_BRANCH" HEAD -- "$file" >> code_diff.txt 2>/dev/null || \
git diff "$BASE_BRANCH" HEAD -- "$file" >> code_diff.txt 2>/dev/null || true
echo "" >> code_diff.txt
fi
done
if [ -s code_diff.txt ]; then
echo "Generated diff file with $(wc -l < code_diff.txt) lines (alternative method)"
echo "diff_file=code_diff.txt" >> $GITHUB_OUTPUT
else
echo "ERROR: Still could not generate diff file"
echo "diff_file=" >> $GITHUB_OUTPUT
fi
fi
fi
# 3. Install Gemini CLI
- name: Install Gemini CLI
run: npm install -g @google/gemini-cli
# 4. Run Gemini AI
- name: Run Gemini Analysis
id: gemini
if: steps.changed.outputs.has_changes == 'true' && steps.changed.outputs.diff_file != ''
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
run: |
DIFF_FILE="${{ steps.changed.outputs.diff_file }}"
CHANGED_FILES="${{ steps.changed.outputs.changed_files }}"
if [ ! -f "$DIFF_FILE" ] || [ ! -s "$DIFF_FILE" ]; then
echo "ERROR: Diff file not found or empty"
echo "output_file=" >> $GITHUB_OUTPUT
echo "exit_code=1" >> $GITHUB_OUTPUT
exit 1
fi
# Validate API key is set
if [ -z "$GEMINI_API_KEY" ]; then
echo "ERROR: GEMINI_API_KEY is not set"
echo "output_file=" >> $GITHUB_OUTPUT
echo "exit_code=1" >> $GITHUB_OUTPUT
exit 1
fi
echo "Analyzing diff for files: $CHANGED_FILES"
echo "Diff file size: $(wc -l < "$DIFF_FILE") lines"
# Create prompt file with diff content to avoid command line length issues
{
echo "Analyze the following code changes (unified diff) in C# files for: Security and performance risks, Deprecated API usage, Coding standard violations, Potential backward compatibility risks (API signature changes, Data contract changes, Removal/renaming of public members, Behavior changes affecting clients). Focus only on the changed lines (lines starting with + or -), not the entire files. Always provide severity (Low/Medium/High) and improvement suggestions as summary. Do not use tables for rendering - use plain text, bullet points, or code blocks instead."
echo ""
echo "Code changes (diff):"
echo "\`\`\`diff"
cat "$DIFF_FILE"
echo "\`\`\`"
} > gemini_prompt.txt
# Pass prompt file to Gemini (read from stdin or file if supported)
# If Gemini CLI doesn't support file input, we'll use the file content
gemini "$(cat gemini_prompt.txt)" > gemini_output.txt 2>&1
GEMINI_EXIT_CODE=$?
echo "Gemini CLI exit code: $GEMINI_EXIT_CODE"
# Filter out potential API key exposure from output (security measure)
if [ -f gemini_output.txt ]; then
# Remove lines that might contain exposed API keys in error messages
# Match patterns like: "api-key: xxx", "GEMINI_API_KEY=xxx", or long alphanumeric strings that look like keys
sed -i.bak -E '/api[_-]?key[=:]\s*[a-zA-Z0-9_-]{20,}|GEMINI_API_KEY[=:]\s*[a-zA-Z0-9_-]{20,}/Id' gemini_output.txt 2>/dev/null || true
rm -f gemini_output.txt.bak 2>/dev/null || true
fi
if [ $GEMINI_EXIT_CODE -ne 0 ]; then
echo "WARNING: Gemini CLI failed"
echo "" >> gemini_output.txt
echo "--- Error Details ---" >> gemini_output.txt
echo "Exit code: $GEMINI_EXIT_CODE" >> gemini_output.txt
echo "Diff file: $DIFF_FILE ($(wc -l < "$DIFF_FILE") lines)" >> gemini_output.txt
fi
echo "output_file=gemini_output.txt" >> $GITHUB_OUTPUT
echo "exit_code=$GEMINI_EXIT_CODE" >> $GITHUB_OUTPUT
# 5. Post results to PR comment
- name: Comment on PR with Gemini Findings
if: steps.changed.outputs.changed_files != '' && steps.gemini.outputs.output_file != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const outputFile = '${{ steps.gemini.outputs.output_file }}';
const exitCode = '${{ steps.gemini.outputs.exit_code }}';
let analysisOutput = '';
let statusIcon = '[OK]';
let statusText = 'Analysis completed successfully';
if (fs.existsSync(outputFile)) {
analysisOutput = fs.readFileSync(outputFile, 'utf8');
if (!analysisOutput.trim()) {
analysisOutput = 'No analysis output was generated.';
statusIcon = '[WARNING]';
statusText = 'No output generated';
} else if (exitCode && exitCode !== '0') {
statusIcon = '[ERROR]';
statusText = `Analysis failed with exit code ${exitCode}`;
}
} else {
analysisOutput = 'Analysis output file was not found.';
statusIcon = '[ERROR]';
statusText = 'Output file not found';
}
// Format files as bullet point list
const changedFiles = '${{ steps.changed.outputs.changed_files }}';
const filesList = changedFiles
.split(' ')
.filter(file => file.trim())
.map(file => `- \`${file.trim()}\``)
.join('\n');
const body = `## Gemini AI Code Risk & Compatibility Analysis
**Status:** ${statusIcon} ${statusText}
**Files analyzed:**
${filesList}
### Analysis Results
${analysisOutput}
`;
github.rest.issues.createComment({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});