Skip to content
Open
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
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

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

- name: Install dependencies
run: pip install -r requirements.txt

- name: Run tests
run: pytest -v

- name: Build package
run: python -m build

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: python-package
path: dist/
56 changes: 56 additions & 0 deletions .github/workflows/pr-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: AI Code Review

on:
pull_request:
branches: [main]

permissions:
contents: read
pull-requests: write

jobs:
review:
runs-on: ubuntu-latest

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: pip install -r requirements.txt

- name: Get PR diff
run: |
git diff origin/main...HEAD > pr_diff.txt

- name: Run AI review
id: ai-review
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
run: |
REVIEW=$(python scripts/ai_review.py pr_diff.txt)
echo "review<<EOF" >> $GITHUB_OUTPUT
echo "$REVIEW" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Post review comment
uses: actions/github-script@v7
env:
REVIEW: ${{ steps.ai-review.outputs.review }}
with:
script: |
const review = process.env.REVIEW;

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## 🤖 AI Code Review\n\n${review}\n\n---\n*Powered by Gemini AI*`
});
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ venv/
env/
.venv/

# Environment variables
.env
venv/
__pycache__/
*.pyc

# Distribution / packaging
dist/
build/
Expand Down
4 changes: 4 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ def is_even(n: int) -> bool:
def reverse_string(s: str) -> str:
"""Reverse a string."""
return s[::-1]

def multiply(a: int, b: int) -> int:
"""Multiply two numbers together."""
return a * b
6 changes: 4 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Production dependencies (none for this simple app)
# Production dependencies
requests>=2.31.0
python-dotenv>=1.0.0

# Development/testing dependencies
pytest>=7.0.0
build>=1.0.0
build>=1.0.0
78 changes: 78 additions & 0 deletions scripts/ai_review.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import requests
import sys
import os
import json
from dotenv import load_dotenv

# Load environment
load_dotenv(override=True)
api_key = os.environ.get('GEMINI_API_KEY')

if not api_key:
print("ERROR: GEMINI_API_KEY not found!")
sys.exit(1)

api_key = api_key.strip()
print(f"API Key loaded: {api_key[:10]}...{api_key[-4:]}")

def review_code(diff_text):
"""Send a code diff to Gemini for review."""
prompt = f"""You are an expert code reviewer. Review the following code diff and provide feedback.

Focus on:
1. Security vulnerabilities
2. Bug risks
3. Performance issues
4. Best practice violations

For each issue found, provide:
- Severity: HIGH / MEDIUM / LOW
- Description of the issue
- Suggested fix

If the code looks good, say so.

Code diff to review:

{diff_text}

Provide your review in a clear, structured format."""

# Use the correct model name from the list
url = f"https://generativelanguage.googleapis.com/v1/models/gemini-2.5-flash:generateContent?key={api_key}"

headers = {
'Content-Type': 'application/json'
}

data = {
"contents": [{
"parts": [{
"text": prompt
}]
}]
}

print("Sending request to Gemini API...")
response = requests.post(url, headers=headers, json=data)

if response.status_code != 200:
print(f"Error: {response.status_code}")
print(response.text)
sys.exit(1)

result = response.json()
return result['candidates'][0]['content']['parts'][0]['text']

if __name__ == "__main__":
if len(sys.argv) > 1:
diff_file = sys.argv[1]
with open(diff_file, "r") as f:
diff_content = f.read()
else:
diff_content = sys.stdin.read()

print("\nGenerating review...\n")
review = review_code(diff_content)
print("\n=== CODE REVIEW ===\n")
print(review)
17 changes: 17 additions & 0 deletions scripts/sample_diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
diff --git a/app.py b/app.py
index 1234567..abcdefg 100644
--- a/app.py
+++ b/app.py
@@ -1,5 +1,12 @@
"""Simple utility functions"""

+import sqlite3
+
+def get_user(username):
+ conn = sqlite3.connect("users.db")
+ query = f"SELECT * FROM users WHERE name = '{username}'"
+ return conn.execute(query).fetchone()
+
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
36 changes: 36 additions & 0 deletions test_mods.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import requests
import os
from dotenv import load_dotenv

load_dotenv(override=True)
api_key = os.environ.get('GEMINI_API_KEY').strip()

print(f"API Key: {api_key[:10]}...{api_key[-4:]}")
print("\nChecking available models...\n")

# Try v1 API
print("=== Trying v1 API ===")
url = f"https://generativelanguage.googleapis.com/v1/models?key={api_key}"
response = requests.get(url)
print(f"Status: {response.status_code}")
if response.status_code == 200:
models = response.json()
print("Available models:")
for model in models.get('models', []):
print(f" - {model.get('name')}")
print(f" Methods: {model.get('supportedGenerationMethods', [])}")
else:
print(response.text)

print("\n=== Trying v1beta API ===")
url = f"https://generativelanguage.googleapis.com/v1beta/models?key={api_key}"
response = requests.get(url)
print(f"Status: {response.status_code}")
if response.status_code == 200:
models = response.json()
print("Available models:")
for model in models.get('models', []):
print(f" - {model.get('name')}")
print(f" Methods: {model.get('supportedGenerationMethods', [])}")
else:
print(response.text)
20 changes: 19 additions & 1 deletion tests/test_app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tests for app.py - you'll add more!"""

from app import add, is_even, reverse_string
from app import add, is_even, reverse_string, multiply


class TestMath:
Expand All @@ -22,3 +22,21 @@ def test_reverse(self):
def test_is_even(self):
assert is_even(4) is True
assert is_even(3) is False

class TestMultiply:
"""Tests for the multiply function."""

def test_multiple(self):
"""Test multiplying two positive numbers."""
assert multiply(9, 9) == 81



def test_multiply_by_zero(self):
"""Test multiplying by zero."""
assert multiply(5, 0) == 0

def test_multiply_negative_numbers(self):
"""Test multiplying negative numbers."""
assert multiply(-2, 3) == -6