-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
189 lines (158 loc) · 6.21 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import os
import subprocess
import requests
from jwt import encode
import time
from github import Github
import json
APP_ID = 1087519
def get_commit_diff(base_sha, head_sha):
"""
Get the git diff between two commit SHAs.
"""
try:
# Ensure the directory is considered safe
os.system("git config --global --add safe.directory /github/workspace")
# Run the git diff command
result = subprocess.run(
["git", "--no-pager", "diff", f"{base_sha}..{head_sha}", "--pretty=format:%s"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True
)
return result.stdout.decode().strip()
except subprocess.CalledProcessError as e:
print(f"Error getting commit diff: {e}")
return ""
def get_pr_details(repo_name, pr_number, github_token):
try:
pr_data = requests.get(
f"https://api.github.com/repos/{repo_name}/pulls/{pr_number}",
headers={
"Authorization": f"Bearer {github_token}",
"Accept": "application/vnd.github.v3+json"
}
).json()
title = pr_data.get("title", "")
body = pr_data.get("body", "")
except Exception as e:
print(f"Error fetching PR details: {e}")
exit(1)
return title, body
def comment_on_pr_via_api(bot_key, repo, pr_number, comment, is_script=False):
"""
Comment on a GitHub pull request using the GitHub API.
"""
try:
INSTALLATION_ID = get_installation_id(bot_key, repo)["installation_id"]
jwt_token = generate_jwt(bot_key)
token = get_installation_access_token(jwt_token, INSTALLATION_ID)
print(f"Commenting on PR #{pr_number}...")
print(f"Comment: {comment}")
try:
if not is_script:
summary = json.loads(comment)["summary"]
report = json.loads(comment)["report"]
else:
script = json.loads(comment)["script"]
output = json.loads(comment)["output"]
g = Github(token)
repo = g.get_repo(repo)
pr = repo.get_pull(pr_number)
if not is_script:
pr.create_issue_comment("# Summary \n" + summary)
pr.create_issue_comment("# Report \n" + report)
else:
pr.create_issue_comment("# Script \nI have also generated the following test script: \n```py\n" + script + "\n``` \nOutput: \n```\n" + output + "\n```\n")
if (comment.isprintable()):
print(f"Commented on PR #{pr_number}")
except:
g = Github(token)
repo = g.get_repo(repo)
pr = repo.get_pull(pr_number)
pr.create_issue_comment(comment)
exit(1) # Unintended error
print(f"Commented on PR #{pr_number}")
except requests.RequestException as e:
print(f"Error commenting on PR: {e}")
def generate_jwt(bot_key):
"""
Generate a JWT for the GitHub App authentication.
"""
now = int(time.time())
payload = {
"iat": now,
"exp": now + (10 * 60), # JWT valid for 10 minutes
"iss": APP_ID,
}
return encode(payload, bot_key, algorithm="RS256")
def get_installation_access_token(jwt_token, installation_id):
"""
Exchange the JWT for an installation access token.
"""
url = f"https://api.github.com/app/installations/{installation_id}/access_tokens"
headers = {
"Authorization": f"Bearer {jwt_token}",
"Accept": "application/vnd.github+json",
}
response = requests.post(url, headers=headers)
response.raise_for_status()
return response.json()["token"]
def get_installation_id(bot_key, repo_name):
"""
Find the installation ID and user for the given repository name.
Supports pagination for installations.
TODO: Add pagination support for repositories(done) and installations(tbc).
"""
# Generate the JWT for authentication
jwt_token = generate_jwt(bot_key)
base_url = "https://api.github.com/app/installations"
headers = {
"Authorization": f"Bearer {jwt_token}",
"Accept": "application/vnd.github+json",
}
installations = []
url = base_url
# Fetch all installations using pagination
while url:
response = requests.get(url, headers=headers)
response.raise_for_status()
installations.extend(response.json())
# Check for a 'next' link in the headers
if "Link" in response.headers:
links = response.headers["Link"].split(",")
next_link = None
for link in links:
if 'rel="next"' in link:
next_link = link.split(";")[0].strip("<> ")
url = next_link
else:
url = None # No more pages
# Loop through installations and check repositories
for installation in installations:
installation_id = installation["id"]
account_login = installation["account"]["login"]
# Get repositories for this installation
token = get_installation_access_token(jwt_token, installation_id)
repo_url = "https://api.github.com/installation/repositories"
repo_headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
}
repo_page = 1
while True:
repo_response = requests.get(f"{repo_url}?page={repo_page}", headers=repo_headers)
repo_response.raise_for_status()
repositories = repo_response.json()["repositories"]
if not repositories:
break # No more repositories
# Check if the repository matches
for repo in repositories:
if repo["full_name"] == repo_name:
return {
"installation_id": installation_id,
"added_by": account_login,
"repo_name": repo_name,
}
repo_page += 1 # Move to the next page
raise ValueError(f"Repository '{repo_name}' not found in any installation.")