1616
1717from typing import Optional
1818import argparse
19+ import time
1920from github .Repository import Repository
2021from github .Workflow import Workflow
22+ from github .WorkflowRun import WorkflowRun
2123from github .GithubException import GithubException
2224from library_functions import StrPath
2325from iterate_libraries import (
@@ -56,6 +58,46 @@ def run_gh_rest_check(
5658 return workflow_runs [0 ].conclusion
5759
5860
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+
59101def check_build_status (
60102 lib_repo : Repository ,
61103 user : Optional [str ] = None ,
@@ -105,13 +147,63 @@ def check_build_status(
105147 return None
106148
107149
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+
108199def check_build_statuses (
109200 gh_token : str ,
110201 user : Optional [str ] = None ,
111202 branch : Optional [str ] = "main" ,
112203 workflow_filename : Optional [str ] = "build.yml" ,
113204 * ,
114205 debug : bool = False ,
206+ local_folder : str = "" ,
115207) -> list [RemoteLibFunc_IterResult [bool ]]:
116208 """Checks all the libraries in the Adafruit CircuitPython Bundle to get the
117209 latest build status with the requested information
@@ -125,6 +217,7 @@ def check_build_statuses(
125217 provided, any workflow name is acceptable; the defail is `"build.yml"`
126218 :param bool debug: Whether debug statements should be printed to
127219 the standard output
220+ :param str local_folder: A path to a local folder containing extra repositories
128221 :return: A list of tuples containing paired Repoistory objects and build
129222 statuses
130223 :rtype: list
@@ -133,6 +226,49 @@ def check_build_statuses(
133226 return iter_remote_bundle_with_func (
134227 gh_token ,
135228 [(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 ,
136272 )
137273
138274
@@ -193,12 +329,52 @@ def save_build_statuses(
193329 parser .add_argument (
194330 "--debug" , action = "store_true" , help = "Print debug text during execution"
195331 )
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+ )
196348
197349 args = parser .parse_args ()
198350
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..." )
199369 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 ,
201376 )
377+
202378 fail_list = [
203379 repo_name .name for repo_name , repo_results in results if not repo_results [0 ]
204380 ]
0 commit comments