forked from TinyTapeout/tt-support-tools
-
Notifications
You must be signed in to change notification settings - Fork 1
/
git_utils.py
123 lines (97 loc) · 3.79 KB
/
git_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
import base64
import errno
import logging
import os
import sys
import typing
from urllib.parse import urlparse
import requests
from git.repo import Repo
def fetch_file(url: str, filename: str):
logging.info("trying to download {}".format(url))
r = requests.get(url)
if r.status_code != 200:
logging.warning("couldn't download {}".format(url))
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), filename)
with open(filename, "wb") as fh:
logging.info("written to {}".format(filename))
fh.write(r.content)
def check_status(r: requests.Response):
if r.status_code == 401:
logging.error(
"unauthorised, check INFO.md for information about GitHub API keys"
)
exit(1)
def headers_try_to_add_authorization_from_environment(
headers: typing.Dict[str, str]
) -> bool:
gh_token = os.getenv("GH_TOKEN", "") # override like gh CLI
if not gh_token:
gh_token = os.getenv("GITHUB_TOKEN", "") # GHA inherited
if len(gh_token) > 0:
# As per https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api
headers["authorization"] = "Bearer " + gh_token
return True
# Use a token instead which is designed to limit exposure of passwords
# I can't find any GH docs explaining use cases for Basic auth and confirming a token
# can be used instead of PASSWORD in the password field of authorization header.
gh_username = os.getenv("GH_USERNAME", "") # override like gh CLI
if not gh_username:
gh_username = os.getenv("GITHUB_ACTOR", "") # GHA inherited
gh_password = os.getenv("GH_PASSWORD", "")
if len(gh_username) > 0 and len(gh_password) > 0:
auth_string = gh_username + ":" + gh_password
encoded = base64.b64encode(auth_string.encode("ascii"))
headers["authorization"] = "Basic " + encoded.decode("ascii")
return True
print(
"WARNING: No github token found from environment, trying public API requests without, see docs/INFO.md#instructions-to-build-gds",
file=sys.stderr,
)
return False
def get_most_recent_action_page(
commits: typing.List[typing.Dict[str, str]],
runs: typing.List[typing.Dict[str, str]],
) -> typing.Optional[str]:
release_sha_to_page_url = {
run["head_sha"]: run["html_url"] for run in runs if run["name"] == "gds"
}
for commit in commits:
if commit["sha"] in release_sha_to_page_url:
return release_sha_to_page_url[commit["sha"]]
return None
def split_git_url(url: str):
res = urlparse(url)
try:
_, user_name, repo = res.path.split("/")
except ValueError:
logging.error(f"couldn't split repo from {url}")
exit(1)
repo = repo.replace(".git", "")
return user_name, repo
def get_latest_action_url(url: str):
logging.debug(url)
user_name, repo = split_git_url(url)
headers = {
"Accept": "application/vnd.github+json",
}
# authenticate for rate limiting
headers_try_to_add_authorization_from_environment(headers)
# first fetch the git commit history
api_url = f"https://api.github.com/repos/{user_name}/{repo}/commits"
r = requests.get(api_url, headers=headers)
check_status(r)
requests_remaining = int(r.headers["X-RateLimit-Remaining"])
if requests_remaining == 0:
logging.error("no API requests remaining")
exit(1)
commits = r.json()
# get runs
api_url = f"https://api.github.com/repos/{user_name}/{repo}/actions/runs"
r = requests.get(api_url, headers=headers, params={"per_page": 100})
check_status(r)
runs = r.json()
page_url = get_most_recent_action_page(commits, runs["workflow_runs"])
return page_url
def get_first_remote(repo: Repo) -> str:
return list(repo.remotes[0].urls)[0]