-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathgithub_api.py
More file actions
205 lines (184 loc) · 6.78 KB
/
github_api.py
File metadata and controls
205 lines (184 loc) · 6.78 KB
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import os
import re
import httpx
import base64
import difflib
from tenacity import retry, stop_after_attempt, wait_fixed
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
github_token = os.environ["GITHUB_TOKEN"]
async def api_request(
url, method="GET", data=None, headers=None, files=None, is_binary=False
):
try:
default_headers = {
"Authorization": f"Bearer {github_token}",
"Accept": "application/vnd.github.v3+json",
}
if headers:
default_headers.update(headers)
async with httpx.AsyncClient() as client:
logger.debug(f"Requesting URL: {url} with method: {method}")
if is_binary:
response = await client.request(
method,
url,
content=data,
headers=default_headers,
follow_redirects=True,
)
else:
response = await client.request(
method,
url,
json=data,
headers=default_headers,
files=files,
follow_redirects=True,
)
if response.is_redirect:
new_url = response.headers.get("Location")
if new_url:
logger.debug(f"Following redirect to URL: {new_url}")
return await api_request(new_url, method, data, headers, files)
else:
raise Exception("Redirected, but no Location header found.")
response.raise_for_status()
try:
return response.json()
except ValueError:
return response.text
except httpx.HTTPStatusError as e:
if e.response.status_code == 451:
logger.warning(f"Repository unavailable due to legal reasons: {url}")
return None
logger.error(f"HTTP error occurred: {e}")
raise
except Exception as e:
logger.error(f"An error occurred: {e}")
raise
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
async def get_repos(org_name):
url = f"https://api.github.com/orgs/{org_name}/repos?per_page=100&page="
try:
logger.info("Fetching repositories from GitHub.")
valid_repos = []
for page in range(1, 5):
repos = await api_request(f"{url}{page}")
if not repos:
return valid_repos
for repo in repos:
if repo and not repo.get("private") and not repo.get("archived"):
repo["languages"] = await get_languages(repo["languages_url"])
if not repo["languages"]:
continue
valid_repos.append(repo)
except Exception as e:
logger.error(f"Error fetching repositories: {e}")
return []
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
async def get_languages(languages_url):
try:
logger.debug(f"Fetching languages from {languages_url}")
return await api_request(languages_url)
except Exception as e:
logger.error(f"Error fetching languages: {e}")
return {}
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
async def get_readme_content(org_name, repo_name):
url = f"https://api.github.com/repos/{org_name}/{repo_name}/contents/README.md"
try:
logger.info(f"Fetching README content for {repo_name}")
readme_data = await api_request(url)
return (
base64.b64decode(readme_data["content"]).decode("utf-8"),
readme_data["sha"],
)
except Exception as e:
logger.error(f"Error fetching README content: {e}")
return None, None
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
async def update_readme(org_name, repo_name, content, sha, new_content):
url = f"https://api.github.com/repos/{org_name}/{repo_name}/contents/README.md"
try:
logger.info(f"Updating README for {repo_name}")
new_date = datetime.now().strftime("%m/%d/%y")
updated_readme = (
content.split("| --- | :---: | :---: | :---: |")[0]
+ "| --- | :---: | :---: | :---: |\n"
+ new_content
)
updated_readme = re.sub(
r"Current Addon List \([^)]+\)",
f"Current Addon List ({new_date})",
updated_readme,
)
commit_data = {
"message": f"Update README with new addon list on {new_date}",
"content": base64.b64encode(updated_readme.encode("utf-8")).decode("utf-8"),
"sha": sha,
}
await api_request(url, method="PUT", data=commit_data)
except Exception as e:
logger.error(f"Error updating README: {e}")
async def get_latest_addon_list(org_name, repo_name):
releases = await api_request(
f"https://api.github.com/repos/{org_name}/{repo_name}/releases"
)
if releases:
latest_release = releases[0]
for asset in latest_release["assets"]:
if asset["name"] == "addon_list.md":
return await api_request(asset["browser_download_url"])
return ""
async def create_release(org_name, repo_name, body):
url = f"https://api.github.com/repos/{org_name}/{repo_name}/releases"
releases = await api_request(url)
old_addon_list = await get_latest_addon_list(org_name, repo_name)
changes = "\n".join(
list(
difflib.unified_diff(
old_addon_list.splitlines(),
body.splitlines(),
fromfile="old_list",
tofile="new_list",
lineterm="",
n=0,
)
)[
3:
] # ugly but I dont want the first 3 lines.
)
if not changes and releases:
logger.info("No changes detected, skipping release creation.")
return
latest_version = len(releases) + 1
release_note = (
f"Automatically generated addon list\n\n### Changes:\n{changes}"
if releases
else "Creating first release"
)
release_data = {
"tag_name": f"v{latest_version}",
"name": f"Addon List v{latest_version}",
"body": release_note,
"draft": False,
"prerelease": False,
}
release_response = await api_request(url, method="POST", data=release_data)
upload_url = release_response["upload_url"].replace(
"{?name,label}", "?name=addon_list.md"
)
with open("addon_list.md", "rb") as f:
file_content = f.read()
await api_request(
upload_url,
method="POST",
data=file_content,
headers={
"Content-Type": "application/octet-stream",
"X-GitHub-Api-Version": "2022-11-28",
},
is_binary=True,
)