Skip to content

Commit 46e3741

Browse files
committed
Add PR Event Logger workflow and authentication methods; remove old reviewer agent
1 parent a846fe6 commit 46e3741

File tree

7 files changed

+102
-165
lines changed

7 files changed

+102
-165
lines changed

.bot

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ID=Iv23liGyTZJYhySo4cEM
2+
SECRET=a0d16e1977f5dbd754649d9daa7d19d8ef32f38b

.github/workflows/pr-reviewer.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: PR Event Logger
2+
3+
on:
4+
pull_request:
5+
types: [opened, reopened, ready_for_review, review_requested]
6+
issue_comment:
7+
types: [created, edited]
8+
9+
jobs:
10+
log-event:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: '3.9'
20+
21+
- name: log existing secrets
22+
env:
23+
API_TOKEN: ${{ secrets.API_TOKEN }}
24+
WEBHOOK_SECRET: ${{ secrets.WEBHOOK_SECRET }}
25+
ORG_TOKEN: ${{ secrets.ORG_TOKEN }}
26+
run: echo $API_TOKEN $WEBHOOK_SECRET $ORG_TOKEN

.github/workflows/reviewer.yml

Lines changed: 0 additions & 43 deletions
This file was deleted.

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# PR Reviewer bot
2+
A bot that helps you to review the PRs in your repository.
3+
4+
## Get started
5+
6+
### Install the dependencies
7+
```bash
8+
pip3 install -r requirements.txt
9+
```
10+
11+
### To run the bot
12+
```bash
13+
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
14+
```
15+
16+
### Forward the port using ngrok
17+
```bash
18+
ngrok http 8000
19+
```

auth.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import base64
2+
import hashlib
3+
import hmac
4+
from Crypto.Cipher import AES
5+
from Crypto.Util.Padding import unpad
6+
7+
def decrypt_token(encrypted_token, iv, WEBHOOK_SECRET):
8+
"""Decrypt API token using WEBHOOK_SECRET as the key."""
9+
# Generate the key from WEBHOOK_SECRET in the same way as the GitHub Action
10+
key = hashlib.sha256(WEBHOOK_SECRET.encode()).hexdigest()
11+
key_bytes = bytes.fromhex(key)
12+
iv_bytes = bytes.fromhex(iv)
13+
14+
# Base64 decode the encrypted token
15+
encrypted_data = base64.b64decode(encrypted_token)
16+
17+
# Create cipher and decrypt
18+
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
19+
decrypted_bytes = cipher.decrypt(encrypted_data)
20+
21+
# Handle padding properly
22+
try:
23+
unpadded = unpad(decrypted_bytes, AES.block_size)
24+
except ValueError:
25+
# If unpadding fails, try to find the null termination
26+
if b'\x00' in decrypted_bytes:
27+
unpadded = decrypted_bytes[:decrypted_bytes.index(b'\x00')]
28+
else:
29+
unpadded = decrypted_bytes
30+
31+
return unpadded.decode('utf-8')
32+
33+
34+
def verify_signature(secret, body, signature):
35+
"""Verify GitHub webhook signature using HMAC-SHA256."""
36+
mac = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
37+
expected_signature = f"sha256={mac}"
38+
return hmac.compare_digest(expected_signature, signature)

github.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from fastapi import HTTPException, logger
2+
import requests
3+
4+
5+
def get_pr_commits(repo_full_name, pr_number, github_token):
6+
"""Fetch the list of commits for a PR from GitHub API."""
7+
url = f"https://api.github.com/repos/{repo_full_name}/pulls/{pr_number}/commits"
8+
print(url)
9+
headers = {"Authorization": f"{github_token}", "Accept": "application/vnd.github.v3+json"}
10+
11+
response = requests.get(url, headers=headers)
12+
13+
if response.status_code != 200:
14+
logger.error(f"Failed to fetch commits: {response.text}")
15+
raise HTTPException(status_code=500, detail="Error fetching PR commits")
16+
17+
return response.json()

listener.py

Lines changed: 0 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +0,0 @@
1-
import hmac
2-
import hashlib
3-
import json
4-
import logging
5-
import os
6-
import base64
7-
import requests
8-
from fastapi import APIRouter, Request, Header, HTTPException
9-
from Crypto.Cipher import AES
10-
from Crypto.Util.Padding import unpad
11-
12-
from dotenv import load_dotenv
13-
14-
load_dotenv()
15-
16-
router = APIRouter()
17-
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")
18-
19-
logger = logging.getLogger(__name__)
20-
logging.basicConfig(level=logging.INFO)
21-
22-
23-
def verify_signature(secret, body, signature):
24-
"""Verify GitHub webhook signature using HMAC-SHA256."""
25-
mac = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
26-
expected_signature = f"sha256={mac}"
27-
return hmac.compare_digest(expected_signature, signature)
28-
29-
30-
def decrypt_token(encrypted_token, iv):
31-
"""Decrypt API token using WEBHOOK_SECRET as the key."""
32-
try:
33-
# Generate the key from WEBHOOK_SECRET in the same way as the GitHub Action
34-
key = hashlib.sha256(WEBHOOK_SECRET.encode()).hexdigest()
35-
key_bytes = bytes.fromhex(key)
36-
iv_bytes = bytes.fromhex(iv)
37-
38-
# Base64 decode the encrypted token
39-
encrypted_data = base64.b64decode(encrypted_token)
40-
41-
# Create cipher and decrypt
42-
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
43-
decrypted_bytes = cipher.decrypt(encrypted_data)
44-
45-
# Handle padding properly
46-
try:
47-
unpadded = unpad(decrypted_bytes, AES.block_size)
48-
except ValueError:
49-
# If unpadding fails, try to find the null termination
50-
if b'\x00' in decrypted_bytes:
51-
unpadded = decrypted_bytes[:decrypted_bytes.index(b'\x00')]
52-
else:
53-
unpadded = decrypted_bytes
54-
55-
return unpadded.decode('utf-8')
56-
except Exception as e:
57-
logger.error(f"Token decryption error: {str(e)}")
58-
raise HTTPException(status_code=500, detail="Failed to decrypt token")
59-
60-
61-
def get_pr_commits(repo_full_name, pr_number, github_token):
62-
"""Fetch the list of commits for a PR from GitHub API."""
63-
url = f"https://api.github.com/repos/{repo_full_name}/pulls/{pr_number}/commits"
64-
print(url)
65-
headers = {"Authorization": f"{github_token}", "Accept": "application/vnd.github.v3+json"}
66-
67-
response = requests.get(url, headers=headers)
68-
69-
if response.status_code != 200:
70-
logger.error(f"Failed to fetch commits: {response.text}")
71-
raise HTTPException(status_code=500, detail="Error fetching PR commits")
72-
73-
return response.json()
74-
75-
76-
@router.post("/github-webhook")
77-
async def github_webhook(
78-
request: Request,
79-
x_hub_signature_256: str = Header(None),
80-
x_encrypted_token: str = Header(None, alias="X-Encrypted-Token"),
81-
x_token_iv: str = Header(None, alias="X-Token-IV")
82-
):
83-
"""Receives GitHub webhook payload and fetches PR commits if applicable."""
84-
body = await request.body()
85-
86-
# Verify webhook signature
87-
if WEBHOOK_SECRET and x_hub_signature_256:
88-
if not verify_signature(WEBHOOK_SECRET, body, x_hub_signature_256):
89-
logger.error("Signature verification failed")
90-
raise HTTPException(status_code=403, detail="Invalid signature")
91-
92-
# Validate encrypted token headers
93-
if not x_encrypted_token or not x_token_iv:
94-
logger.error("Missing encryption headers")
95-
raise HTTPException(status_code=403, detail="Missing token encryption headers")
96-
97-
# Decrypt the token
98-
try:
99-
github_token = decrypt_token(x_encrypted_token, x_token_iv)
100-
except Exception as e:
101-
logger.error(f"Token decryption failed: {str(e)}")
102-
raise HTTPException(status_code=403, detail="Token decryption failed")
103-
104-
payload = await request.json()
105-
# save this locally
106-
with open("samples/payload.json", "w") as f:
107-
json.dump(payload, f)
108-
event_type = payload.get("action", "")
109-
110-
logger.info(f"Received GitHub event: {event_type}")
111-
112-
if event_type == "synchronize":
113-
action = payload.get("action", "")
114-
if action in ["opened", "synchronize", "reopened"]:
115-
repo_full_name = payload["repository"]["full_name"]
116-
pr_number = payload["pull_request"]["number"]
117-
commits = get_pr_commits(repo_full_name, pr_number, github_token)
118-
119-
logger.info(f"Fetched {len(commits)} commits for PR #{pr_number}")
120-
return {"message": "PR processed", "pr_number": pr_number, "commits_count": len(commits)}
121-
122-
return {"message": "Webhook received", "event": event_type}

0 commit comments

Comments
 (0)