1
1
from __future__ import annotations # This enables postponed evaluation of type annotations. Required for typing.TYPE_CHECKING. See https://peps.python.org/pep-0563/
2
- from typing import TYPE_CHECKING , List , Union , cast , Dict , Any
2
+ from typing import TYPE_CHECKING , List , Union , cast , Dict , Any , TypeVar , Callable , Sequence , Optional
3
3
import shutil
4
4
from tempfile import TemporaryDirectory
5
5
import subprocess
12
12
13
13
if TYPE_CHECKING :
14
14
from github import WorkflowRun , Repository
15
-
15
+
16
16
17
17
script_path = Path (__file__ ).resolve ()
18
18
root_path = script_path .parent .parent .parent
@@ -30,7 +30,7 @@ def get_check_runs(self: Repository.Repository, ref: str, **kwargs: str) -> Pagi
30
30
f"{ self .url } /commits/{ ref } /check-runs" ,
31
31
firstParams = None ,
32
32
list_item = "check_runs" )
33
-
33
+
34
34
Repository .Repository = MyRepository
35
35
36
36
from github import WorkflowRun , Artifact
@@ -51,7 +51,7 @@ def download_logs(self, path: Path) -> None:
51
51
if self ._requester ._Requester__auth is not None : # type: ignore
52
52
headers ["Authorization" ] = f"{ self ._requester ._Requester__auth .token_type } { self ._requester ._Requester__auth .token } " # type: ignore
53
53
headers ["User-Agent" ] = self ._requester ._Requester__userAgent # type: ignore
54
-
54
+
55
55
resp = requests .get (url , headers = headers , allow_redirects = True )
56
56
57
57
if resp .status_code != 200 :
@@ -70,7 +70,7 @@ def download_artifacts(self, path: Path) -> None:
70
70
if self ._requester ._Requester__auth is not None : # type: ignore
71
71
headers ["Authorization" ] = f"{ self ._requester ._Requester__auth .token_type } { self ._requester ._Requester__auth .token } " # type: ignore
72
72
headers ["User-Agent" ] = self ._requester ._Requester__userAgent # type: ignore
73
-
73
+
74
74
resp = requests .get (artifact .archive_download_url , headers = headers , allow_redirects = True )
75
75
76
76
if resp .status_code != 200 :
@@ -93,15 +93,15 @@ def download_artifact(self, name: str, path: Path) -> None:
93
93
if self ._requester ._Requester__auth is not None : # type: ignore
94
94
headers ["Authorization" ] = f"{ self ._requester ._Requester__auth .token_type } { self ._requester ._Requester__auth .token } " # type: ignore
95
95
headers ["User-Agent" ] = self ._requester ._Requester__userAgent # type: ignore
96
-
96
+
97
97
resp = requests .get (artifact .archive_download_url , headers = headers , allow_redirects = True )
98
98
99
99
if resp .status_code != 200 :
100
100
raise Exception (f"Unable to download artifact ${ artifact .name } . Received status code { resp .status_code } { resp .reason } " )
101
101
102
102
with (path / f"{ artifact .name } .zip" ).open ("wb" ) as f :
103
103
f .write (resp .content )
104
-
104
+
105
105
106
106
WorkflowRun .WorkflowRun = MyWorkflowRun
107
107
@@ -124,12 +124,16 @@ def make(self, directory: Path, workflow_runs: List[WorkflowRun.WorkflowRun]) ->
124
124
elif action_type == "workflow-artifact" :
125
125
actions .append (WorkflowArtifactAction (workflow_runs , ** cast (Dict [str , Any ], action_args )))
126
126
elif action_type == "shell" :
127
- actions .append (ShellAction (action_args ))
127
+ modifiers : List [Callable [[str ], str ]] = [
128
+ lambda cmd : re .sub (pattern = r"\${{\s*coding-standards\.root\s*}}" , repl = str (root_path ), string = cmd ),
129
+ lambda cmd : re .sub (pattern = r"\${{\s*layout\.root\s*}}" , repl = str (directory ), string = cmd )
130
+ ]
131
+ actions .append (ShellAction (action_args , modifiers = modifiers ))
128
132
elif action_type == "file" :
129
133
actions .append (FileAction (action_args ))
130
134
else :
131
135
raise Exception (f"Unknown action type { action_type } " )
132
-
136
+
133
137
artifacts .append (ReleaseArtifact (artifact , actions , self .skip_checks ))
134
138
135
139
for artifact in artifacts :
@@ -153,7 +157,7 @@ def run(self) -> List[Path]:
153
157
print (f"Downloading logs for { workflow_run .name } " )
154
158
workflow_run .download_logs (Path (self .temp_workdir .name )) # type: ignore
155
159
return list (map (Path , Path (self .temp_workdir .name ).glob ("**/*" )))
156
-
160
+
157
161
class WorkflowArtifactAction ():
158
162
159
163
def __init__ (self , workflow_runs : List [WorkflowRun .WorkflowRun ], ** kwargs : str ) -> None :
@@ -176,17 +180,29 @@ def run(self) -> List[Path]:
176
180
print (f"Downloading artifacts for { workflow_run .name } to { self .temp_workdir .name } " )
177
181
workflow_run .download_artifacts (Path (self .temp_workdir .name )) # type: ignore
178
182
return list (map (Path , Path (self .temp_workdir .name ).glob ("**/*" )))
179
-
183
+
180
184
class ShellAction ():
181
- def __init__ (self , command : str ) -> None :
185
+ def __init__ (self , command : str , ** kwargs : Any ) -> None :
182
186
self .command = command .strip ()
183
187
self .temp_workdir = TemporaryDirectory ()
188
+ self .options = kwargs
189
+
190
+ def _rewrite_command (self ) -> str :
191
+ E = TypeVar ("E" )
192
+ R = TypeVar ("R" )
193
+ def lfold (fn : Callable [[R , E ], R ], lst : Sequence [E ], init : R ) -> R :
194
+ return lfold (fn , lst [1 :], fn (init , lst [0 ])) if lst else init
195
+ if 'modifiers' in self .options :
196
+ return lfold (lambda acc , x : x (acc ), self .options ['modifiers' ], self .command )
197
+ else :
198
+ return self .command
184
199
185
200
def run (self ) -> List [Path ]:
186
- concrete_command = re .sub (pattern = r"\${{\s*coding-standards\.root\s*}}" , repl = str (root_path ), string = self .command )
201
+ #concrete_command = re.sub(pattern=r"\${{\s*coding-standards\.root\s*}}", repl=str(root_path), string=self.command)
202
+ concrete_command = self ._rewrite_command ()
187
203
subprocess .run (concrete_command , cwd = self .temp_workdir .name , check = True , shell = True )
188
204
return list (map (Path , Path (self .temp_workdir .name ).glob ("**/*" )))
189
-
205
+
190
206
class FileAction ():
191
207
def __init__ (self , path : Path ) -> None :
192
208
self .path = path
@@ -200,7 +216,7 @@ def __init__(self, name: str, actions: List[Union[WorkflowLogAction, WorkflowArt
200
216
self .actions = actions
201
217
self .allow_no_files = allow_no_files
202
218
203
- def make (self , directory : Path ) -> Path :
219
+ def make (self , directory : Path ) -> Optional [ Path ] :
204
220
files : list [Path ] = [file for action in self .actions for file in action .run ()]
205
221
if len (files ) == 0 :
206
222
if not self .allow_no_files :
@@ -212,8 +228,8 @@ def make(self, directory: Path) -> Path:
212
228
extension = "" .join (self .name .suffixes )[1 :]
213
229
if not extension in ["zip" , "tar" , "tar.gz" , "tar.bz2" , "tar.xz" ]:
214
230
raise Exception (f"Artifact { self .name } is not a support archive file, but has multiple files associated with it!" )
215
-
216
- ext_format_map = {
231
+
232
+ ext_format_map = {
217
233
"zip" : "zip" ,
218
234
"tar" : "tar" ,
219
235
"tar.gz" : "gztar" ,
@@ -225,7 +241,7 @@ def make(self, directory: Path) -> Path:
225
241
temp_dir_path = Path (temp_dir )
226
242
for file in files :
227
243
shutil .copy (file , temp_dir_path / file .name )
228
-
244
+
229
245
return Path (shutil .make_archive (str (directory / self .name .with_suffix ("" )), ext_format_map [extension ], root_dir = temp_dir_path ))
230
246
231
247
def main (args : 'argparse.Namespace' ) -> int :
@@ -248,13 +264,13 @@ def main(args: 'argparse.Namespace') -> int:
248
264
if len (pull_candidates ) != 1 :
249
265
print (f"Error: expected exactly one PR for SHA { args .head_sha } , but found { len (pull_candidates )} " , file = sys .stderr )
250
266
return 1
251
-
267
+
252
268
pull_request = pull_candidates [0 ]
253
269
254
270
if pull_request .state != "open" :
255
271
print (f"Error: PR { pull_request .url } is not open" , file = sys .stderr )
256
272
return 1
257
-
273
+
258
274
print (f"Found PR { pull_request .url } based on { pull_request .base .ref } " )
259
275
260
276
rc_branch_regex = r"^rc/(?P<version>.*)$"
@@ -286,7 +302,7 @@ def main(args: 'argparse.Namespace') -> int:
286
302
287
303
action_workflow_run_url_regex = r"^https://(?P<github_url>[^/]+)/(?P<owner>[^/]+)/(?P<repo>[^/]+)/actions/runs/(?P<run_id>\d+)$"
288
304
action_workflow_job_run_url_regex = r"^https://(?P<github_url>[^/]+)/(?P<owner>[^/]+)/(?P<repo>[^/]+)/actions/runs/(?P<run_id>\d+)/job/(?P<job_id>\d+)$"
289
-
305
+
290
306
workflow_runs : List [WorkflowRun .WorkflowRun ] = []
291
307
for check_run in check_runs : # type: ignore
292
308
check_run = cast (CheckRun .CheckRun , check_run )
@@ -306,7 +322,7 @@ def main(args: 'argparse.Namespace') -> int:
306
322
else :
307
323
print (f"Unable to handle checkrun { check_run .name } with id { check_run .id } with { check_run .details_url } " )
308
324
return 1
309
-
325
+
310
326
print ("Filtering workflow runs to only include the latest run for each workflow." )
311
327
workflow_runs_per_id : Dict [int , WorkflowRun .WorkflowRun ] = {}
312
328
for workflow_run in workflow_runs :
0 commit comments