16
16
17
17
from typing import Optional
18
18
import argparse
19
+ import time
19
20
from github.Repository import Repository
20
21
from github.Workflow import Workflow
22
+ from github.WorkflowRun import WorkflowRun
21
23
from github.GithubException import GithubException
22
24
from library_functions import StrPath
23
25
from iterate_libraries import (
@@ -56,6 +58,46 @@ def run_gh_rest_check(
56
58
return workflow_runs[0].conclusion
57
59
58
60
61
+ def run_gh_rest_rerun(
62
+ lib_repo: Repository,
63
+ user: Optional[str] = None,
64
+ branch: Optional[str] = None,
65
+ workflow_filename: Optional[str] = "build.yml",
66
+ rerun_level: int = 0,
67
+ ) -> bool:
68
+ """Uses ``PyGithub`` to rerun the CI status of a repository
69
+
70
+ :param Repository lib_repo: The repo as a github.Repository.Repository object
71
+ :param str|None user: The user that triggered the run; if `None` is
72
+ provided, any user is acceptable
73
+ :param str|None branch: The branch name to specifically check; if `None` is
74
+ provided, all branches are allowed; this is the default
75
+ :param str|None workflow_filename: The filename of the workflow; if `None` is
76
+ provided, any workflow name is acceptable; the default is ``"build.yml"``
77
+ :param int rerun_level: The level at which rerun should occur (0 = none,
78
+ 1 = failed, 2 = all)
79
+ :return: The requested runs conclusion
80
+ :rtype: bool
81
+ """
82
+ if not rerun_level:
83
+ return False
84
+ if rerun_level == 1:
85
+ result = (
86
+ run_gh_rest_check(lib_repo, user, branch, workflow_filename) == "success"
87
+ )
88
+ if rerun_level == 2 or not result:
89
+ arg_dict = {}
90
+ if user is not None:
91
+ arg_dict["actor"] = user
92
+ if branch is not None:
93
+ arg_dict["branch"] = branch
94
+ workflow: Workflow = lib_repo.get_workflow(workflow_filename)
95
+ latest_run: WorkflowRun = workflow.get_runs(**arg_dict)[0]
96
+ latest_run.rerun()
97
+ return True
98
+ return False
99
+
100
+
59
101
def check_build_status(
60
102
lib_repo: Repository,
61
103
user: Optional[str] = None,
@@ -105,13 +147,63 @@ def check_build_status(
105
147
return None
106
148
107
149
150
+ # pylint: disable=too-many-arguments
151
+ def rerun_workflow(
152
+ lib_repo: Repository,
153
+ user: Optional[str] = None,
154
+ branch: Optional[str] = None,
155
+ workflow_filename: Optional[str] = "build.yml",
156
+ rerun_level: int = 0,
157
+ debug: bool = False,
158
+ ):
159
+ """Uses ``PyGithub`` to rerun the CI of the Adafruit
160
+ CircuitPython Bundle repositories
161
+
162
+ :param Repository lib_repo: The repo as a github.Repository.Repository object
163
+ :param str|None user: The user that triggered the run; if `None` is
164
+ provided, any user is acceptable
165
+ :param str|None branch: The branch name to specifically check; if `None` is
166
+ provided, all branches are allowed; this is the default
167
+ :param str|None workflow_filename: The filename of the workflow; if `None`
168
+ is provided, any workflow name is acceptable; the defail is `"build.yml"`
169
+ :param int rerun_level: The level at which rerun should occur (0 = none,
170
+ 1 = failed, 2 = all)
171
+ :param bool debug: Whether debug statements should be printed to the standard
172
+ output
173
+ :return: The result of the workflow run, or ``None`` if it could not be
174
+ determined
175
+ :rtype: bool|None
176
+ """
177
+ if lib_repo.archived:
178
+ return False
179
+
180
+ try:
181
+ result = run_gh_rest_rerun(
182
+ lib_repo, user, branch, workflow_filename, rerun_level
183
+ )
184
+ if debug and result:
185
+ print("***", "Library", lib_repo.name, "workflow was rerun!", "***")
186
+ return result
187
+ except GithubException:
188
+ if debug:
189
+ print(
190
+ "???",
191
+ "Library",
192
+ lib_repo.name,
193
+ "had an issue occur",
194
+ "???",
195
+ )
196
+ return None
197
+
198
+
108
199
def check_build_statuses(
109
200
gh_token: str,
110
201
user: Optional[str] = None,
111
202
branch: Optional[str] = "main",
112
203
workflow_filename: Optional[str] = "build.yml",
113
204
*,
114
205
debug: bool = False,
206
+ local_folder: str = "",
115
207
) -> list[RemoteLibFunc_IterResult[bool]]:
116
208
"""Checks all the libraries in the Adafruit CircuitPython Bundle to get the
117
209
latest build status with the requested information
@@ -125,6 +217,7 @@ def check_build_statuses(
125
217
provided, any workflow name is acceptable; the defail is `"build.yml"`
126
218
:param bool debug: Whether debug statements should be printed to
127
219
the standard output
220
+ :param str local_folder: A path to a local folder containing extra repositories
128
221
:return: A list of tuples containing paired Repoistory objects and build
129
222
statuses
130
223
:rtype: list
@@ -133,6 +226,49 @@ def check_build_statuses(
133
226
return iter_remote_bundle_with_func(
134
227
gh_token,
135
228
[(check_build_status, (user, branch, workflow_filename), {"debug": debug})],
229
+ local_folder=local_folder,
230
+ )
231
+
232
+
233
+ def rerun_workflows(
234
+ gh_token: str,
235
+ user: Optional[str] = None,
236
+ branch: Optional[str] = "main",
237
+ workflow_filename: Optional[str] = "build.yml",
238
+ rerun_level: int = 0,
239
+ *,
240
+ debug: bool = False,
241
+ local_folder: str = "",
242
+ ) -> list[RemoteLibFunc_IterResult[bool]]:
243
+ """Reruns the CI of all the libraries in the Adafruit CircuitPython Bundle.
244
+
245
+ :param str gh_token: The Github token to be used for with the Github API
246
+ :param str|None user: The user that triggered the run; if `None` is
247
+ provided, any user is acceptable
248
+ :param str|None branch: The branch name to specifically check; if `None` is
249
+ provided, all branches are allowed; this is the default
250
+ :param str|None workflow_filename: The filename of the workflow; if `None` is
251
+ provided, any workflow name is acceptable; the defail is `"build.yml"`
252
+ :param int rerun_level: The level at which reruns should occur (0 = none,
253
+ 1 = failed, 2 = all)
254
+ :param bool debug: Whether debug statements should be printed to
255
+ the standard output
256
+ :param str local_folder: A path to a local folder containing extra repositories
257
+ :return: A list of tuples containing paired Repoistory objects and build
258
+ statuses
259
+ :rtype: list
260
+ """
261
+
262
+ return iter_remote_bundle_with_func(
263
+ gh_token,
264
+ [
265
+ (
266
+ rerun_workflow,
267
+ (user, branch, workflow_filename, rerun_level),
268
+ {"debug": debug},
269
+ )
270
+ ],
271
+ local_folder=local_folder,
136
272
)
137
273
138
274
@@ -193,12 +329,52 @@ def save_build_statuses(
193
329
parser.add_argument(
194
330
"--debug", action="store_true", help="Print debug text during execution"
195
331
)
332
+ parser.add_argument(
333
+ "--rerun-level",
334
+ metavar="R",
335
+ type=int,
336
+ dest="rerun_level",
337
+ default=0,
338
+ help="Level to rerun CI workflows (0 = none, 1 = failed, 2 = all)",
339
+ )
340
+ parser.add_argument(
341
+ "--local-folder",
342
+ metavar="L",
343
+ type=str,
344
+ dest="local_folder",
345
+ default="",
346
+ help="An additional folder to check and run",
347
+ )
196
348
197
349
args = parser.parse_args()
198
350
351
+ if args.rerun_level:
352
+ if args.debug:
353
+ print("Rerunning workflows...")
354
+ rerun_workflows(
355
+ args.gh_token,
356
+ args.user,
357
+ args.branch,
358
+ args.workflow,
359
+ args.rerun_level,
360
+ debug=args.debug,
361
+ local_folder=args.local_folder,
362
+ )
363
+ if args.debug:
364
+ print("Waiting 10 minutes to allow workflows to finish running...")
365
+ time.sleep(600)
366
+
367
+ if args.debug:
368
+ print("Checking workflows statuses...")
199
369
results = check_build_statuses(
200
- args.gh_token, args.user, args.branch, args.workflow, debug=args.debug
370
+ args.gh_token,
371
+ args.user,
372
+ args.branch,
373
+ args.workflow,
374
+ debug=args.debug,
375
+ local_folder=args.local_folder,
201
376
)
377
+
202
378
fail_list = [
203
379
repo_name.name for repo_name, repo_results in results if not repo_results[0]
204
380
]
0 commit comments