Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 212 additions & 0 deletions .github/workflows/llm-code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
name: LLM Code Review

on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- '**.py'
- '**.sql'
- '**.yaml'
- '**.yml'

jobs:
code-review:
name: AI Code Review
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .

- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v44
with:
files: |
**.py
**.sql
**.yaml
**.yml

- name: Run code review
id: review
if: steps.changed-files.outputs.any_changed == 'true'
run: |
python - << 'EOF'
import os
import sys
from pathlib import Path

# Add astroml to path
sys.path.insert(0, '/home/runner/work/astroml/astroml')

from astroml.llm.code_review import CodeReviewer

# Initialize reviewer
reviewer = CodeReviewer(
enable_llm=False,
max_review_time=120,
ignored_rules=set()
)

# Get changed files
changed_files = os.getenv('CHANGED_FILES', '').split()

if not changed_files:
print("No changed files to review")
sys.exit(0)

print(f"Reviewing {len(changed_files)} file(s)...")

all_suggestions = []
files_reviewed = 0

for file_path in changed_files:
if not os.path.exists(file_path):
continue

print(f"Reviewing: {file_path}")
result = reviewer.review_file(file_path)

if result.status.value == 'completed':
all_suggestions.extend(result.suggestions)
files_reviewed += 1
else:
print(f"Error reviewing {file_path}: {result.error}")

# Generate review comment
if all_suggestions:
print("## 🤖 AI Code Review Results")
print(f"\nReviewed {files_reviewed} file(s)")
print(f"Found {len(all_suggestions)} issue(s):\n")

# Group by category
from astroml.llm.code_review.suggestions import SuggestionCategory
from collections import defaultdict

grouped = defaultdict(list)
for suggestion in all_suggestions:
grouped[suggestion.category].append(suggestion)

for category in [SuggestionCategory.SECURITY, SuggestionCategory.PERFORMANCE,
SuggestionCategory.CORRECTNESS, SuggestionCategory.STYLE,
SuggestionCategory.TESTING, SuggestionCategory.DOCUMENTATION,
SuggestionCategory.COMPLEXITY]:
if category in grouped and grouped[category]:
print(f"## {category.value}")
for suggestion in grouped[category]:
print(suggestion.format_markdown())
print("")
else:
print("## 🤖 AI Code Review Results")
print("\nNo issues found! 🎉")

# Save suggestions for GitHub comment
with open('/tmp/review_output.txt', 'w') as f:
if all_suggestions:
f.write("## 🤖 AI Code Review Results\n\n")
f.write(f"Reviewed {files_reviewed} file(s)\n")
f.write(f"Found {len(all_suggestions)} issue(s):\n\n")

grouped = defaultdict(list)
for suggestion in all_suggestions:
grouped[suggestion.category].append(suggestion)

for category in [SuggestionCategory.SECURITY, SuggestionCategory.PERFORMANCE,
SuggestionCategory.CORRECTNESS, SuggestionCategory.STYLE,
SuggestionCategory.TESTING, SuggestionCategory.DOCUMENTATION,
SuggestionCategory.COMPLEXITY]:
if category in grouped and grouped[category]:
f.write(f"## {category.value}\n")
for suggestion in grouped[category]:
f.write(suggestion.format_markdown() + "\n")
f.write("\n")
else:
f.write("## 🤖 AI Code Review Results\n\n")
f.write("No issues found! 🎉\n")

EOF
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}

- name: Post review comment
if: steps.review.outcome == 'success'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let reviewComment = '';

try {
reviewComment = fs.readFileSync('/tmp/review_output.txt', 'utf8');
} catch (error) {
console.log('No review output found');
return;
}

// Find existing review comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('🤖 AI Code Review Results')
);

if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: reviewComment
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: reviewComment
});
}

- name: Check for critical issues
if: steps.review.outcome == 'success'
run: |
python - << 'EOF'
import sys
sys.path.insert(0, '/home/runner/work/astroml/astroml')

from astroml.llm.code_review.suggestions import SuggestionSeverity

# Read review output
with open('/tmp/review_output.txt', 'r') as f:
content = f.read()

# Check for HIGH severity security issues
if 'HIGH' in content and 'Security' in content:
print("⚠️ Critical security issues found!")
print("Please review and address before merging.")
sys.exit(1)
else:
print("No critical security issues detected.")
sys.exit(0)

EOF
Loading
Loading