Skip to content

Commit be61f2c

Browse files
authored
Merge pull request #74 from GGHansome/main
让AI CodeReview支持GitHub的Push和Pull Request事件
2 parents db192ee + 95b265c commit be61f2c

13 files changed

+621
-83
lines changed

api.py

Lines changed: 84 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from flask import Flask, request, jsonify
1212

1313
from biz.gitlab.webhook_handler import slugify_url
14-
from biz.queue.worker import handle_merge_request_event, handle_push_event
14+
from biz.queue.worker import handle_merge_request_event, handle_push_event, handle_github_pull_request_event, handle_github_push_event
1515
from biz.service.review_service import ReviewService
1616
from biz.utils.im import notifier
1717
from biz.utils.log import logger
@@ -108,56 +108,92 @@ def handle_webhook():
108108
if not data:
109109
return jsonify({"error": "Invalid JSON"}), 400
110110

111-
object_kind = data.get("object_kind")
112-
113-
# 优先从请求头获取,如果没有,则从环境变量获取,如果没有,则从推送事件中获取
114-
gitlab_url = os.getenv('GITLAB_URL') or request.headers.get('X-Gitlab-Instance')
115-
if not gitlab_url:
116-
repository = data.get('repository')
117-
if not repository:
118-
return jsonify({'message': 'Missing GitLab URL'}), 400
119-
homepage = repository.get("homepage")
120-
if not homepage:
121-
return jsonify({'message': 'Missing GitLab URL'}), 400
122-
try:
123-
parsed_url = urlparse(homepage)
124-
gitlab_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
125-
except Exception as e:
126-
return jsonify({"error": f"Failed to parse homepage URL: {str(e)}"}), 400
127-
128-
# 优先从环境变量获取,如果没有,则从请求头获取
129-
gitlab_token = os.getenv('GITLAB_ACCESS_TOKEN') or request.headers.get('X-Gitlab-Token')
130-
# 如果gitlab_token为空,返回错误
131-
if not gitlab_token:
132-
return jsonify({'message': 'Missing GitLab access token'}), 400
133-
134-
gitlab_url_slug = slugify_url(gitlab_url)
135-
136-
# 打印整个payload数据,或根据需求进行处理
137-
logger.info(f'Received event: {object_kind}')
138-
logger.info(f'Payload: {json.dumps(data)}')
139-
140-
# 处理Merge Request Hook
141-
if object_kind == "merge_request":
142-
# 创建一个新进程进行异步处理
143-
handle_queue(handle_merge_request_event, data, gitlab_token, gitlab_url, gitlab_url_slug)
144-
# 立马返回响应
145-
return jsonify(
146-
{'message': f'Request received(object_kind={object_kind}), will process asynchronously.'}), 200
147-
elif object_kind == "push":
148-
# 创建一个新进程进行异步处理
149-
# TODO check if PUSH_REVIEW_ENABLED is needed here
150-
handle_queue(handle_push_event, data, gitlab_token, gitlab_url, gitlab_url_slug)
151-
# 立马返回响应
152-
return jsonify(
153-
{'message': f'Request received(object_kind={object_kind}), will process asynchronously.'}), 200
154-
else:
155-
error_message = f'Only merge_request and push events are supported (both Webhook and System Hook), but received: {object_kind}.'
156-
logger.error(error_message)
157-
return jsonify(error_message), 400
111+
# 判断是GitLab还是GitHub的webhook
112+
webhook_source = request.headers.get('X-GitHub-Event')
113+
114+
if webhook_source: # GitHub webhook
115+
return handle_github_webhook(webhook_source, data)
116+
else: # GitLab webhook
117+
return handle_gitlab_webhook(data)
158118
else:
159119
return jsonify({'message': 'Invalid data format'}), 400
160120

121+
def handle_github_webhook(event_type, data):
122+
# 获取GitHub配置
123+
github_token = os.getenv('GITHUB_ACCESS_TOKEN') or request.headers.get('X-GitHub-Token')
124+
if not github_token:
125+
return jsonify({'message': 'Missing GitHub access token'}), 400
126+
127+
github_url = os.getenv('GITHUB_URL') or 'https://github.com'
128+
github_url_slug = slugify_url(github_url)
129+
130+
# 打印整个payload数据
131+
logger.info(f'Received GitHub event: {event_type}')
132+
logger.info(f'Payload: {json.dumps(data)}')
133+
134+
if event_type == "pull_request":
135+
# 使用handle_queue进行异步处理
136+
handle_queue(handle_github_pull_request_event, data, github_token, github_url, github_url_slug)
137+
# 立马返回响应
138+
return jsonify({'message': f'GitHub request received(event_type={event_type}), will process asynchronously.'}), 200
139+
elif event_type == "push":
140+
# 使用handle_queue进行异步处理
141+
handle_queue(handle_github_push_event, data, github_token, github_url, github_url_slug)
142+
# 立马返回响应
143+
return jsonify({'message': f'GitHub request received(event_type={event_type}), will process asynchronously.'}), 200
144+
else:
145+
error_message = f'Only pull_request and push events are supported for GitHub webhook, but received: {event_type}.'
146+
logger.error(error_message)
147+
return jsonify(error_message), 400
148+
149+
def handle_gitlab_webhook(data):
150+
object_kind = data.get("object_kind")
151+
152+
# 优先从请求头获取,如果没有,则从环境变量获取,如果没有,则从推送事件中获取
153+
gitlab_url = os.getenv('GITLAB_URL') or request.headers.get('X-Gitlab-Instance')
154+
if not gitlab_url:
155+
repository = data.get('repository')
156+
if not repository:
157+
return jsonify({'message': 'Missing GitLab URL'}), 400
158+
homepage = repository.get("homepage")
159+
if not homepage:
160+
return jsonify({'message': 'Missing GitLab URL'}), 400
161+
try:
162+
parsed_url = urlparse(homepage)
163+
gitlab_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
164+
except Exception as e:
165+
return jsonify({"error": f"Failed to parse homepage URL: {str(e)}"}), 400
166+
167+
# 优先从环境变量获取,如果没有,则从请求头获取
168+
gitlab_token = os.getenv('GITLAB_ACCESS_TOKEN') or request.headers.get('X-Gitlab-Token')
169+
# 如果gitlab_token为空,返回错误
170+
if not gitlab_token:
171+
return jsonify({'message': 'Missing GitLab access token'}), 400
172+
173+
gitlab_url_slug = slugify_url(gitlab_url)
174+
175+
# 打印整个payload数据,或根据需求进行处理
176+
logger.info(f'Received event: {object_kind}')
177+
logger.info(f'Payload: {json.dumps(data)}')
178+
179+
# 处理Merge Request Hook
180+
if object_kind == "merge_request":
181+
# 创建一个新进程进行异步处理
182+
handle_queue(handle_merge_request_event, data, gitlab_token, gitlab_url, gitlab_url_slug)
183+
# 立马返回响应
184+
return jsonify(
185+
{'message': f'Request received(object_kind={object_kind}), will process asynchronously.'}), 200
186+
elif object_kind == "push":
187+
# 创建一个新进程进行异步处理
188+
# TODO check if PUSH_REVIEW_ENABLED is needed here
189+
handle_queue(handle_push_event, data, gitlab_token, gitlab_url, gitlab_url_slug)
190+
# 立马返回响应
191+
return jsonify(
192+
{'message': f'Request received(object_kind={object_kind}), will process asynchronously.'}), 200
193+
else:
194+
error_message = f'Only merge_request and push events are supported (both Webhook and System Hook), but received: {object_kind}.'
195+
logger.error(error_message)
196+
return jsonify(error_message), 400
161197

162198
if __name__ == '__main__':
163199
# 启动定时任务调度器

biz/entity/review_entity.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class MergeRequestReviewEntity:
22
def __init__(self, project_name: str, author: str, source_branch: str, target_branch: str, updated_at: int,
3-
commits: list, score: float, url: str, review_result: str, gitlab_url_slug: str):
3+
commits: list, score: float, url: str, review_result: str, url_slug: str):
44
self.project_name = project_name
55
self.author = author
66
self.source_branch = source_branch
@@ -10,7 +10,7 @@ def __init__(self, project_name: str, author: str, source_branch: str, target_br
1010
self.score = score
1111
self.url = url
1212
self.review_result = review_result
13-
self.gitlab_url_slug = gitlab_url_slug
13+
self.url_slug = url_slug
1414

1515
@property
1616
def commit_messages(self):
@@ -20,15 +20,15 @@ def commit_messages(self):
2020

2121
class PushReviewEntity:
2222
def __init__(self, project_name: str, author: str, branch: str, updated_at: int, commits: list, score: float,
23-
review_result: str, gitlab_url_slug: str):
23+
review_result: str, url_slug: str):
2424
self.project_name = project_name
2525
self.author = author
2626
self.branch = branch
2727
self.updated_at = updated_at
2828
self.commits = commits
2929
self.score = score
3030
self.review_result = review_result
31-
self.gitlab_url_slug = gitlab_url_slug
31+
self.url_slug = url_slug
3232

3333
@property
3434
def commit_messages(self):

biz/event/event_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def on_merge_request_reviewed(mr_review_entity: MergeRequestReviewEntity):
3333
"""
3434
notifier.send_notification(content=im_msg, msg_type='markdown', title='Merge Request Review',
3535
project_name=mr_review_entity.project_name,
36-
gitlab_url_slug=mr_review_entity.gitlab_url_slug)
36+
url_slug=mr_review_entity.url_slug)
3737

3838
# 记录到数据库
3939
ReviewService().insert_mr_review_log(mr_review_entity)
@@ -60,7 +60,7 @@ def on_push_reviewed(entity: PushReviewEntity):
6060
im_msg += f"#### AI Review 结果: \n {entity.review_result}\n\n"
6161
notifier.send_notification(content=im_msg, msg_type='markdown',
6262
title=f"{entity.project_name} Push Event", project_name=entity.project_name,
63-
gitlab_url_slug=entity.gitlab_url_slug)
63+
url_slug=entity.url_slug)
6464

6565
# 记录到数据库
6666
ReviewService().insert_push_review_log(entity)

biz/github/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

biz/github/test_webhook_handler.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# @Time : 2025/3/18 17:58
4+
# @Author : Arrow
5+
import os
6+
from unittest import TestCase, main
7+
8+
from biz.github.webhook_handler import PushHandler
9+
10+
11+
# @Describe:
12+
class TestPushHandler(TestCase):
13+
def setUp(self):
14+
"""设置测试环境"""
15+
self.sample_webhook_data = {
16+
'repository': {
17+
'full_name': 'owner/repo'
18+
},
19+
'ref': 'refs/heads/main',
20+
'commits': [
21+
{
22+
'id': 'sample_commit_id',
23+
'message': 'Sample commit message',
24+
'author': {
25+
'name': 'Test Author'
26+
},
27+
'timestamp': '2023-01-01T12:00:00Z',
28+
'url': 'https://github.com/owner/repo/commit/sample_commit_id'
29+
}
30+
]
31+
}
32+
self.github_token = ''
33+
self.github_url = 'https://github.com'
34+
35+
# 创建PushHandler实例
36+
self.handler = PushHandler(self.sample_webhook_data, self.github_token, self.github_url)
37+
38+
def test_get_parent_commit_id(self):
39+
"""测试获取父提交ID"""
40+
commit_id = 'sample_commit_id'
41+
# 调用测试方法
42+
parent_id = self.handler.get_parent_commit_id(commit_id)
43+
44+
# 由于我们没有真正的GitHub令牌,此测试将失败,但确保方法存在
45+
self.assertIsInstance(parent_id, str)
46+
47+
48+
if __name__ == '__main__':
49+
main()

0 commit comments

Comments
 (0)