Skip to content

Commit efa4908

Browse files
ci: Improve error handling in ci_changes_per_commit.py
- Add proper null checks for workflow and check suite data - Add timeout to GitHub API requests - Improve error messages and exception handling - Add type checking for pagination - Handle missing page info gracefully Co-Authored-By: bsatrom@blues.com <bsatrom@blues.com>
1 parent 28718f2 commit efa4908

File tree

1 file changed

+85
-54
lines changed

1 file changed

+85
-54
lines changed

tools/ci_changes_per_commit.py

Lines changed: 85 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,36 @@ def __init__(self, query, variables={}, headers={}):
111111
self.headers = headers
112112

113113
def paginate(self, page_info, name):
114-
has_page = page_info["hasNextPage" if name.startswith("after") else "hasPreviousPage"]
115-
if has_page:
116-
self.variables[name] = page_info[
117-
"endCursor" if name.startswith("after") else "startCursor"
118-
]
114+
if not isinstance(page_info, dict):
115+
return False
116+
117+
page_type = "hasNextPage" if name.startswith("after") else "hasPreviousPage"
118+
cursor_type = "endCursor" if name.startswith("after") else "startCursor"
119+
120+
has_page = page_info.get(page_type, False)
121+
if has_page and cursor_type in page_info:
122+
self.variables[name] = page_info[cursor_type]
123+
119124
return has_page
120125

121126
def fetch(self):
122-
request = requests.post(
123-
"https://api.github.com/graphql",
124-
json={"query": self.query, "variables": self.variables},
125-
headers=self.headers,
126-
)
127-
if request.status_code == 200:
127+
try:
128+
request = requests.post(
129+
"https://api.github.com/graphql",
130+
json={"query": self.query, "variables": self.variables},
131+
headers=self.headers,
132+
timeout=30
133+
)
134+
request.raise_for_status()
128135
return request.json()
129-
else:
130-
print(request.json())
131-
raise Exception("Query Failed: {}".format(request.status_code))
136+
except requests.RequestException as e:
137+
print(f"Request failed: {str(e)}")
138+
if hasattr(e.response, 'json'):
139+
try:
140+
print(e.response.json())
141+
except ValueError:
142+
pass
143+
raise Exception(f"Query failed: {str(e)}")
132144

133145

134146
def set_output(name, value):
@@ -146,22 +158,24 @@ def get_commit_depth_and_check_suite(query_commits):
146158
if commits["totalCount"] > 0:
147159
nodes = commits["nodes"]
148160
nodes.reverse()
149-
if nodes[0]["commit"]["oid"] == os.environ["EXCLUDE_COMMIT"]:
161+
if nodes[0]["commit"]["oid"] == os.environ.get("EXCLUDE_COMMIT"):
150162
nodes.pop(0)
151163
for commit in nodes:
152164
commit_depth += 1
153165
commit = commit["commit"]
154166
commit_sha = commit["oid"]
155-
check_suites = commit["checkSuites"]
156-
if check_suites["totalCount"] > 0:
157-
for check_suite in check_suites["nodes"]:
158-
workflow_run = check_suite.get("workflowRun")
159-
if workflow_run and workflow_run.get("workflow", {}).get("name") == "Build CI":
167+
check_suites = commit.get("checkSuites", {})
168+
if check_suites.get("totalCount", 0) > 0:
169+
for check_suite in check_suites.get("nodes", []):
170+
workflow_run = check_suite.get("workflowRun", {})
171+
workflow = workflow_run.get("workflow", {})
172+
workflow_name = workflow.get("name") if workflow else None
173+
if workflow_name == "Build CI":
160174
return [
161175
{"sha": commit_sha, "depth": commit_depth},
162176
(
163177
check_suite["id"]
164-
if check_suite["conclusion"] != "SUCCESS"
178+
if check_suite.get("conclusion") != "SUCCESS"
165179
else None
166180
),
167181
]
@@ -177,28 +191,32 @@ def get_bad_check_runs(query_check_runs):
177191
have_dependent_jobs = ["scheduler", "mpy-cross", "tests"]
178192

179193
while more_pages:
180-
check_runs = query_check_runs.fetch()["data"]["node"]
194+
response = query_check_runs.fetch()
195+
check_runs = response.get("data", {}).get("node", {})
181196
more_pages = False
182197

183198
for run_type in run_types:
184199
run_type_camel = run_type.capitalize() + "Run"
185200
run_type = run_type + "Runs"
186201

187-
for check_run in check_runs[run_type]["nodes"]:
188-
name = check_run["name"]
202+
run_data = check_runs.get(run_type, {})
203+
for check_run in run_data.get("nodes", []):
204+
name = check_run.get("name", "")
189205

190206
if any([name.startswith(job) for job in have_dependent_jobs]):
191207
return {}
192208

193209
if name.startswith("ports"):
194-
matrix_job = name.rsplit(" (", 1)[1][:-1]
195-
bad_runs.setdefault("ports", []).append(matrix_job)
210+
try:
211+
matrix_job = name.rsplit(" (", 1)[1][:-1]
212+
bad_runs.setdefault("ports", []).append(matrix_job)
213+
except IndexError:
214+
continue
196215
else:
197216
bad_runs[name] = True
198217

199-
if query_check_runs.paginate(
200-
check_runs[run_type]["pageInfo"], "after" + run_type_camel
201-
):
218+
page_info = run_data.get("pageInfo", {})
219+
if query_check_runs.paginate(page_info, "after" + run_type_camel):
202220
query_check_runs.variables["include" + run_type_camel] = True
203221
more_pages = True
204222

@@ -211,31 +229,44 @@ def set_commit(commit):
211229

212230

213231
def main():
214-
query_commits = Query(QUERY_COMMITS, query_variables_commits, headers)
215-
query_commits.variables["owner"], query_commits.variables["name"] = os.environ["REPO"].split(
216-
"/"
217-
)
218-
219-
commit, check_suite = get_commit_depth_and_check_suite(query_commits)
220-
221-
if not check_suite:
222-
if commit:
223-
set_commit(commit)
224-
else:
225-
print("Abort: No check suite found")
226-
quit()
227-
228-
query_check_runs = Query(QUERY_CHECK_RUNS, query_variables_check_runs, headers)
229-
query_check_runs.variables["checkSuiteID"] = check_suite
230-
231-
check_runs = get_bad_check_runs(query_check_runs)
232-
233-
if not check_runs:
234-
print("Abort: No check runs found")
235-
quit()
236-
237-
set_commit(commit)
238-
set_output("check_runs", json.dumps(check_runs))
232+
try:
233+
if "REPO" not in os.environ:
234+
print("Error: REPO environment variable not set")
235+
return
236+
237+
query_commits = Query(QUERY_COMMITS, query_variables_commits, headers)
238+
try:
239+
owner, name = os.environ["REPO"].split("/")
240+
except ValueError:
241+
print("Error: REPO must be in format 'owner/name'")
242+
return
243+
244+
query_commits.variables["owner"] = owner
245+
query_commits.variables["name"] = name
246+
247+
commit, check_suite = get_commit_depth_and_check_suite(query_commits)
248+
249+
if not check_suite:
250+
if commit:
251+
set_commit(commit)
252+
else:
253+
print("Abort: No check suite found")
254+
return
255+
256+
query_check_runs = Query(QUERY_CHECK_RUNS, query_variables_check_runs, headers)
257+
query_check_runs.variables["checkSuiteID"] = check_suite
258+
259+
check_runs = get_bad_check_runs(query_check_runs)
260+
261+
if not check_runs:
262+
print("Abort: No check runs found")
263+
return
264+
265+
set_commit(commit)
266+
set_output("check_runs", json.dumps(check_runs))
267+
except Exception as e:
268+
print(f"Error: {str(e)}")
269+
return
239270

240271

241272
if __name__ == "__main__":

0 commit comments

Comments
 (0)