Skip to content

Commit 3657ac8

Browse files
committed
EX-319: fixed test/created logic for github
1 parent 59e3c97 commit 3657ac8

9 files changed

Lines changed: 298 additions & 92 deletions

File tree

‎.codereview.yml‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
llm:
2-
provider: 'openai'
3-
models:
4-
context_builder:
5-
name: 'gpt-4o'
6-
reviewer:
7-
name: 'gpt-4o'
8-
91
# llm:
10-
# provider: 'openrouter'
11-
# base_url: 'https://openrouter.ai/api/v1'
12-
2+
# provider: 'openai'
133
# models:
14-
# context_builder: 'google/gemini-flash-1.5'
15-
# reviewer: 'google/gemini-1.5-pro'
4+
# context_builder:
5+
# name: 'gpt-4o'
6+
# reviewer:
7+
# name: 'gpt-4o'
8+
9+
llm:
10+
provider: 'openrouter'
11+
base_url: 'https://openrouter.ai/api/v1'
12+
13+
models:
14+
context_builder: 'google/gemini-flash-1.5'
15+
reviewer: 'google/gemini-1.5-pro'
1616

1717
max_context_iterations: 3
1818
max_context_files: 25

‎.github/workflows/code_review.yml‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# .github/workflows/code_review.yml
2+
3+
name: AI Code Review
4+
5+
on:
6+
pull_request:
7+
types: [opened, synchronize]
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
13+
jobs:
14+
review:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout Pull Request Code
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
- name: Run AI Code Review Agent
22+
run: |
23+
IMAGE_NAME="umykhailo/codereviewagent:latest"
24+
docker run \
25+
--volume ${{ github.workspace }}:/repo \
26+
--workdir /repo \
27+
--env LLM_API_KEY=${{ secrets.LLM_API_KEY }} \
28+
--env GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
29+
--env GITHUB_PR_NUMBER=${{ github.event.pull_request.number }} \
30+
--env GITHUB_REPOSITORY=${{ github.repository }} \
31+
--env GITHUB_BASE_REF=${{ github.base_ref }} \
32+
--env GITHUB_HEAD_REF=${{ github.head_ref }} \
33+
$IMAGE_NAME \
34+
review --repo-path "." --base-ref "origin/${{ github.base_ref }}" --head-ref "${{ github.head_ref }}"

‎poetry.lock‎

Lines changed: 75 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pydantic = "^2.7.1"
2121
python-dotenv = "^1.0.1"
2222
tiktoken = "^0.9.0"
2323
atlassian-python-api = "^4.0.4"
24+
pygithub = "^2.6.1"
2425

2526

2627
[tool.poetry.group.dev.dependencies]

‎src/code_review_agent/bitbucket_client.py‎

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,52 @@
44
from collections import Counter
55
from .models import CodeIssue
66

7-
USERNAME = os.environ["BITBUCKET_USERNAME"]
8-
APP_PASSWORD = os.environ["BITBUCKET_APP_PASSWORD"]
9-
WORKSPACE = os.environ["BITBUCKET_WORKSPACE"]
10-
REPO_SLUG = os.environ["BITBUCKET_REPO_SLUG"]
11-
PR_ID = os.environ["BITBUCKET_PR_ID"]
127

13-
BASE_URL = f"https://api.bitbucket.org/2.0/repositories/{WORKSPACE}/{REPO_SLUG}/pullrequests/{PR_ID}"
14-
AUTH = HTTPBasicAuth(USERNAME, APP_PASSWORD)
15-
HEADERS = {
16-
"Content-Type": "application/json"
17-
}
8+
def _get_api_details():
9+
"""
10+
Helper function to get all necessary details from environment variables.
11+
This function should only be called when we know we are in a Bitbucket environment.
12+
"""
13+
try:
14+
username = os.environ["BITBUCKET_APP_USERNAME"]
15+
app_password = os.environ["BITBUCKET_APP_PASSWORD"]
16+
workspace = os.environ["BITBUCKET_WORKSPACE"]
17+
repo_slug = os.environ["BITBUCKET_REPO_SLUG"]
18+
pr_id = os.environ["BITBUCKET_PR_ID"]
19+
except KeyError as e:
20+
raise ValueError(f"Required Bitbucket environment variable is not set: {e}")
21+
22+
base_url = f"https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/pullrequests/{pr_id}"
23+
auth = HTTPBasicAuth(username, app_password)
24+
headers = {"Content-Type": "application/json"}
25+
26+
return base_url, auth, headers
1827

1928

2029
def post_pr_comment(issue: CodeIssue, file_path: str):
2130
"""Posts a single review comment to a specific line in a Bitbucket Pull Request."""
22-
url = f"{BASE_URL}/comments"
23-
24-
comment_body = f"**[{issue.issue_type}]**\n\n{issue.comment}"
25-
if issue.suggestion:
26-
comment_body += f"\n\n**Suggestion:**\n```\n{issue.suggestion}\n```"
27-
28-
payload = {
29-
"content": {"raw": comment_body},
30-
"inline": {
31-
"path": file_path,
32-
"to": issue.line_number
31+
try:
32+
base_url, auth, headers = _get_api_details()
33+
url = f"{base_url}/comments"
34+
35+
comment_body = f"**[{issue.issue_type}]**\n\n{issue.comment}"
36+
if issue.suggestion:
37+
comment_body += f"\n\n**Suggestion:**\n```\n{issue.suggestion}\n```"
38+
39+
payload = {
40+
"content": {"raw": comment_body},
41+
"inline": {
42+
"path": file_path,
43+
"to": issue.line_number
44+
}
3345
}
34-
}
3546

36-
response = requests.post(url, headers=HEADERS, auth=AUTH, json=payload)
47+
response = requests.post(url, headers=headers, auth=auth, json=payload)
48+
response.raise_for_status()
3749

38-
if response.status_code == 201:
3950
print(f"✅ Successfully posted comment to PR on file {file_path}.")
40-
else:
41-
print(f"❌ Failed to post line comment: {response.status_code} — {response.text}")
51+
except (ValueError, requests.exceptions.RequestException) as e:
52+
print(f"❌ Failed to post line comment: {e}")
4253

4354

4455
def post_summary_comment(all_issues: list[CodeIssue]):
@@ -47,25 +58,26 @@ def post_summary_comment(all_issues: list[CodeIssue]):
4758
return
4859

4960
print("📝 Generating and posting summary comment to Bitbucket...")
61+
try:
62+
total_issues = len(all_issues)
63+
issue_counts = Counter(issue.issue_type for issue in all_issues)
64+
65+
summary_body = f"### 🤖 AI Code Review Summary\n\nFound **{total_issues} potential issue(s)**.\n\n"
66+
if issue_counts:
67+
summary_body += "**Issue Breakdown:**\n"
68+
for issue_type, count in issue_counts.items():
69+
summary_body += f"* **{issue_type}:** {count} issue(s)\n"
70+
summary_body += "\n---\n*Please see the detailed inline comments on the \"Diff\" tab for more context.*"
71+
72+
base_url, auth, headers = _get_api_details()
73+
url = f"{base_url}/comments"
74+
payload = {
75+
"content": {"raw": summary_body}
76+
}
5077

51-
total_issues = len(all_issues)
52-
issue_counts = Counter(issue.issue_type for issue in all_issues)
53-
54-
summary_body = f"### 🤖 AI Code Review Summary\n\nFound **{total_issues} potential issue(s)**.\n\n"
55-
if issue_counts:
56-
summary_body += "**Issue Breakdown:**\n"
57-
for issue_type, count in issue_counts.items():
58-
summary_body += f"* **{issue_type}:** {count} issue(s)\n"
59-
summary_body += "\n---\n*Please see the detailed inline comments on the \"Diff\" tab for more context.*"
60-
61-
url = f"{BASE_URL}/comments"
62-
payload = {
63-
"content": {"raw": summary_body}
64-
}
65-
66-
response = requests.post(url, headers=HEADERS, auth=AUTH, json=payload)
78+
response = requests.post(url, headers=headers, auth=auth, json=payload)
79+
response.raise_for_status()
6780

68-
if response.status_code == 201:
6981
print("✅ Successfully posted the summary comment to Bitbucket.")
70-
else:
71-
print(f"❌ Failed to post summary comment: {response.status_code} — {response.text}")
82+
except (ValueError, requests.exceptions.RequestException) as e:
83+
print(f"❌ Failed to post summary comment: {e}")

0 commit comments

Comments
 (0)