|
8 | 8 | # ///
|
9 | 9 | import json
|
10 | 10 | import os
|
| 11 | +from collections import Counter |
11 | 12 |
|
12 | 13 | # pylint: disable=no-name-in-module
|
13 | 14 | import google.generativeai as genai
|
@@ -219,6 +220,82 @@ def add_labels_to_pr(labels):
|
219 | 220 | print(f"Response: {e.response.text}")
|
220 | 221 |
|
221 | 222 |
|
| 223 | +def add_reviewers_to_pr(reviewers): |
| 224 | + """Add reviewers to the current pull request""" |
| 225 | + if not reviewers: |
| 226 | + print("No reviewers to add") |
| 227 | + return |
| 228 | + |
| 229 | + url = f"https://api.github.com/repos/{os.environ['REPO_OWNER']}/{os.environ['REPO_NAME']}/pulls/{os.environ['PR_NUMBER']}/requested_reviewers" |
| 230 | + headers = { |
| 231 | + "Authorization": f"token {os.environ['GITHUB_TOKEN']}", |
| 232 | + "Accept": "application/vnd.github.v3+json", |
| 233 | + } |
| 234 | + data = {"reviewers": reviewers} |
| 235 | + |
| 236 | + try: |
| 237 | + response = requests.post(url, headers=headers, json=data, timeout=300) |
| 238 | + response.raise_for_status() |
| 239 | + print(f"Successfully added reviewers: {reviewers}") |
| 240 | + except requests.exceptions.RequestException as e: |
| 241 | + print(f"Error adding reviewers: {e}") |
| 242 | + if hasattr(e, "response") and e.response: |
| 243 | + print(f"Response: {e.response.text}") |
| 244 | + |
| 245 | + |
| 246 | +def handle_reviewer_suggestions(pr_info, files_changed): |
| 247 | + """Suggests and adds reviewers based on file change history if none are assigned""" |
| 248 | + if pr_info.get("requested_reviewers"): |
| 249 | + assigned_reviewers = [r["login"] for r in pr_info["requested_reviewers"]] |
| 250 | + print(f"PR already has reviewers assigned: {assigned_reviewers}") |
| 251 | + return |
| 252 | + |
| 253 | + print("No reviewers assigned, attempting to suggest one...") |
| 254 | + pr_author = pr_info.get("user", {}).get("login") |
| 255 | + headers = { |
| 256 | + "Authorization": f"token {os.environ['GITHUB_TOKEN']}", |
| 257 | + "Accept": "application/vnd.github.v3+json", |
| 258 | + } |
| 259 | + |
| 260 | + # Filter out vendor files |
| 261 | + non_vendor_files = [ |
| 262 | + f for f in files_changed if not f.split("\t")[1].startswith("vendor/") |
| 263 | + ] |
| 264 | + |
| 265 | + # Limit to the first 50 files |
| 266 | + files_to_process = non_vendor_files |
| 267 | + if len(non_vendor_files) > 50: |
| 268 | + print( |
| 269 | + "PR has more than 50 non-vendor files, analyzing the first 50 for reviewer suggestion." |
| 270 | + ) |
| 271 | + files_to_process = non_vendor_files[:50] |
| 272 | + |
| 273 | + contributor_counts = Counter() |
| 274 | + for file_info in files_to_process: |
| 275 | + filename = file_info.split("\t")[1] |
| 276 | + # Limit to 20 commits per file |
| 277 | + commits_url = f"https://api.github.com/repos/{os.environ['REPO_OWNER']}/{os.environ['REPO_NAME']}/commits?path={filename}&per_page=20" |
| 278 | + try: |
| 279 | + # Use a direct request instead of pagination for only the first page |
| 280 | + response = requests.get(commits_url, headers=headers, timeout=300) |
| 281 | + response.raise_for_status() |
| 282 | + commits_data = response.json() |
| 283 | + for commit in commits_data: |
| 284 | + if commit.get("author") and commit["author"].get("login"): |
| 285 | + author = commit["author"]["login"] |
| 286 | + if author != pr_author: |
| 287 | + contributor_counts[author] += 1 |
| 288 | + except requests.exceptions.RequestException as e: |
| 289 | + print(f"Could not fetch commits for {filename}: {e}") |
| 290 | + continue |
| 291 | + if not contributor_counts: |
| 292 | + print("Could not identify any potential reviewers from file history.") |
| 293 | + else: |
| 294 | + top_reviewer = contributor_counts.most_common(1)[0][0] |
| 295 | + print(f"Suggested reviewer based on file history: {top_reviewer}") |
| 296 | + add_reviewers_to_pr([top_reviewer]) |
| 297 | + |
| 298 | + |
222 | 299 | def validate_environment():
|
223 | 300 | """Validate all required environment variables are set"""
|
224 | 301 | required_vars = [
|
@@ -256,6 +333,10 @@ def main():
|
256 | 333 |
|
257 | 334 | print(f"Analyzing PR #{os.environ['PR_NUMBER']}: {pr_title}")
|
258 | 335 |
|
| 336 | + # --- Suggest and add reviewers --- |
| 337 | + handle_reviewer_suggestions(pr_info, files_changed) |
| 338 | + |
| 339 | + # --- ORIGINAL LABELING LOGIC (UNCHANGED) --- |
259 | 340 | # Show which Gemini model is being used
|
260 | 341 | model_name = os.environ.get("GEMINI_MODEL", DEFAULT_MODEL)
|
261 | 342 | print(f"Using Gemini model: {model_name}")
|
|
0 commit comments