Skip to content

Commit 63e02bf

Browse files
gibsondanclaude
andcommitted
remove PyGithub from PEX, use stdlib urllib for GitHub API calls
The PEX only used PyGithub for two small scripts (fetch_github_avatar.py and create_or_update_comment.py). Rewriting them against urllib drops the dependency entirely and shrinks the PEX a bit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 658d4b3 commit 63e02bf

3 files changed

Lines changed: 104 additions & 35 deletions

File tree

scripts/release.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ def build_dagster_cloud_pex(
130130
dagster_dg_core_pkg,
131131
dagster_pipes_pkg,
132132
dagster_shared_pkg,
133-
"PyGithub",
134133
"pex>=2.1.132,<3",
135134
"pip",
136135
"-o=dagster-cloud.pex",

src/create_or_update_comment.py

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import datetime
2-
import github
3-
from github import Github
2+
import json
43
import os
4+
import sys
5+
import urllib.error
6+
import urllib.parse
7+
import urllib.request
58

69
"""
710
Creates or updates a build status comment on a Pull Request, for branch deployments.
811
"""
912

13+
GITHUB_API = "https://api.github.com"
14+
1015
SUCCESS_IMAGE_URL = (
1116
"https://raw.githubusercontent.com/dagster-io/dagster-cloud-action/main/assets/success.png"
1217
)
@@ -17,9 +22,9 @@
1722
"https://raw.githubusercontent.com/dagster-io/dagster-cloud-action/main/assets/failed.png"
1823
)
1924

25+
2026
def main():
21-
# Fetch various pieces of info from the environment
22-
g = Github(os.getenv("GITHUB_TOKEN"))
27+
token = os.getenv("GITHUB_TOKEN")
2328
pr_id = int(os.getenv("INPUT_PR"))
2429
repo_id = os.getenv("GITHUB_REPOSITORY")
2530
action = os.getenv("INPUT_ACTION")
@@ -30,23 +35,7 @@ def main():
3035

3136
location_name = os.getenv("INPUT_LOCATION_NAME")
3237

33-
repo = g.get_repo(repo_id)
34-
pr = repo.get_pull(pr_id)
35-
36-
comments = pr.get_issue_comments()
37-
comment_to_update = None
38-
39-
# Check if a comment exists on the PR from the github actions user
40-
# which is specific to this location name
41-
# otherwise we create a new comment
42-
for comment in comments:
43-
if (
44-
comment.user.login == "github-actions[bot]"
45-
and "Dagster Cloud" in comment.body
46-
and f"`{location_name}`" in comment.body
47-
):
48-
comment_to_update = comment
49-
break
38+
comment_to_update_id = _find_existing_comment(token, repo_id, pr_id, location_name)
5039

5140
deployment_url = f"{org_url}/{deployment_name}/home"
5241

@@ -64,18 +53,78 @@ def main():
6453

6554
time_str = datetime.datetime.now(datetime.timezone.utc).strftime("%b %d, %Y at %I:%M %p (%Z)")
6655

67-
message = f"""
56+
body = f"""
6857
Your pull request is automatically being deployed to Dagster Cloud.
6958
7059
| Location | Status | Link | Updated |
71-
| ----------------- | --------------- | ------- | --------------- |
60+
| ----------------- | --------------- | ------- | --------------- |
7261
| `{location_name}` | {status_image} | {message} | {time_str} |
7362
"""
7463

75-
if comment_to_update:
76-
comment_to_update.edit(message)
64+
if comment_to_update_id is not None:
65+
_request(
66+
"PATCH",
67+
f"{GITHUB_API}/repos/{repo_id}/issues/comments/{comment_to_update_id}",
68+
token,
69+
{"body": body},
70+
)
7771
else:
78-
pr.create_issue_comment(message)
72+
_request(
73+
"POST",
74+
f"{GITHUB_API}/repos/{repo_id}/issues/{pr_id}/comments",
75+
token,
76+
{"body": body},
77+
)
78+
79+
80+
def _find_existing_comment(token, repo_id, pr_id, location_name):
81+
# Check if a comment exists on the PR from the github actions user
82+
# which is specific to this location name
83+
page = 1
84+
while True:
85+
url = (
86+
f"{GITHUB_API}/repos/{repo_id}/issues/{pr_id}/comments"
87+
f"?per_page=100&page={page}"
88+
)
89+
comments = _request("GET", url, token)
90+
if not comments:
91+
return None
92+
for comment in comments:
93+
user_login = (comment.get("user") or {}).get("login")
94+
body = comment.get("body") or ""
95+
if (
96+
user_login == "github-actions[bot]"
97+
and "Dagster Cloud" in body
98+
and f"`{location_name}`" in body
99+
):
100+
return comment["id"]
101+
if len(comments) < 100:
102+
return None
103+
page += 1
104+
105+
106+
def _request(method, url, token, payload=None):
107+
data = None
108+
headers = {
109+
"Accept": "application/vnd.github+json",
110+
"X-GitHub-Api-Version": "2022-11-28",
111+
"User-Agent": "dagster-cloud-action",
112+
}
113+
if token:
114+
headers["Authorization"] = f"Bearer {token}"
115+
if payload is not None:
116+
data = json.dumps(payload).encode("utf-8")
117+
headers["Content-Type"] = "application/json"
118+
req = urllib.request.Request(url, data=data, headers=headers, method=method)
119+
try:
120+
with urllib.request.urlopen(req) as resp:
121+
body = resp.read()
122+
if not body:
123+
return None
124+
return json.loads(body)
125+
except urllib.error.HTTPError as err:
126+
print(f"GitHub API {method} {url} failed: {err}", file=sys.stderr)
127+
raise
79128

80129

81130
if __name__ == "__main__":

src/fetch_github_avatar.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,43 @@
1-
from github import Github
1+
import json
22
import os
3+
import sys
4+
import urllib.error
5+
import urllib.request
36

47
"""
58
Fetches a user's avatar from the Github API based on email or username
69
"""
710

11+
GITHUB_API = "https://api.github.com"
812

9-
def main():
10-
# Fetch various pieces of info from the environment
11-
g = Github(os.getenv("GITHUB_TOKEN"))
1213

14+
def main():
15+
token = os.getenv("GITHUB_TOKEN")
1316
repo_id = os.getenv("GITHUB_REPOSITORY")
1417
commit_sha = os.getenv("GITHUB_SHA")
1518

16-
repo = g.get_repo(repo_id)
17-
commit = repo.get_commit(commit_sha)
18-
19-
print(commit.author.avatar_url)
19+
url = f"{GITHUB_API}/repos/{repo_id}/commits/{commit_sha}"
20+
req = urllib.request.Request(url, headers=_headers(token))
21+
try:
22+
with urllib.request.urlopen(req) as resp:
23+
commit = json.load(resp)
24+
except urllib.error.HTTPError as err:
25+
print(f"Failed to fetch commit {commit_sha}: {err}", file=sys.stderr)
26+
sys.exit(1)
27+
28+
author = commit.get("author") or {}
29+
print(author.get("avatar_url", ""))
30+
31+
32+
def _headers(token):
33+
headers = {
34+
"Accept": "application/vnd.github+json",
35+
"X-GitHub-Api-Version": "2022-11-28",
36+
"User-Agent": "dagster-cloud-action",
37+
}
38+
if token:
39+
headers["Authorization"] = f"Bearer {token}"
40+
return headers
2041

2142

2243
if __name__ == "__main__":

0 commit comments

Comments
 (0)