1616_tox_contrib_env_regex = re_compile (
1717 r"py39-test-(?P<name>[-\w]+\w)-?(?P<contrib_requirements>\d+)?"
1818)
19+ _OS_ALIASES = {
20+ "ubuntu-latest" : "Ubuntu" ,
21+ "windows-latest" : "Windows" ,
22+ "loongsuite-python-arc" : "ARC" ,
23+ }
24+
25+
26+ def _slug (value : str ) -> str :
27+ return (
28+ str (value ).lower ().replace ("." , "" ).replace ("-" , "_" ).replace (" " , "_" )
29+ )
1930
2031
2132def _get_job_package_name (name : str , package_names = None ) -> str :
@@ -59,8 +70,6 @@ def get_test_job_datas(
5970 operating_systems : list ,
6071 package_names = None ,
6172) -> list :
62- os_alias = {"ubuntu-latest" : "Ubuntu" , "windows-latest" : "Windows" }
63-
6473 python_version_alias = {
6574 "pypy3" : "pypy-3.9" ,
6675 "pypy310" : "pypy-3.10" ,
@@ -103,7 +112,7 @@ def get_test_job_datas(
103112 f"{ groups ['name' ]} "
104113 f"{ test_requirements } "
105114 f"{ aliased_python_version } "
106- f"{ os_alias [operating_system ]} "
115+ f"{ _OS_ALIASES [operating_system ]} "
107116 ),
108117 "package" : _get_job_package_name (
109118 groups ["name" ],
@@ -193,9 +202,80 @@ def get_misc_job_datas(tox_envs: list) -> list:
193202 return misc_job_datas
194203
195204
205+ def _batch_job_datas (
206+ job_datas : list ,
207+ batch_size : int ,
208+ parallelism : int ,
209+ group_keys = (),
210+ ) -> list :
211+ if batch_size < 1 :
212+ raise ValueError ("batch_size must be >= 1" )
213+
214+ if parallelism < 1 :
215+ raise ValueError ("parallelism must be >= 1" )
216+
217+ grouped_job_datas = {}
218+ for job_data in job_datas :
219+ key = tuple (job_data .get (group_key ) for group_key in group_keys )
220+ grouped_job_datas .setdefault (key , []).append (job_data )
221+
222+ batched_job_datas = []
223+ batch_index = 0
224+
225+ for group in grouped_job_datas .values ():
226+ for chunk_start in range (0 , len (group ), batch_size ):
227+ chunk = group [chunk_start : chunk_start + batch_size ]
228+ first_job_data = chunk [0 ]
229+ tox_envs = [job_data ["tox_env" ] for job_data in chunk ]
230+
231+ if len (chunk ) == 1 :
232+ batched_job_data = dict (first_job_data )
233+ batched_job_data ["tox_envs" ] = tox_envs
234+ batched_job_data ["parallelism" ] = 1
235+ batched_job_data ["env_count" ] = 1
236+ batched_job_datas .append (batched_job_data )
237+ continue
238+
239+ batch_index += 1
240+ name_parts = [
241+ _slug (first_job_data [group_key ])
242+ for group_key in group_keys
243+ if group_key in first_job_data
244+ ]
245+ name_suffix = "_" .join (name_parts ) if name_parts else "tox"
246+
247+ if "python_version" in first_job_data and "os" in first_job_data :
248+ ui_prefix = (
249+ f"{ first_job_data ['python_version' ]} "
250+ f"{ _OS_ALIASES .get (first_job_data ['os' ], first_job_data ['os' ])} "
251+ )
252+ else :
253+ ui_prefix = "tox"
254+
255+ batched_job_data = dict (first_job_data )
256+ batched_job_data ["name" ] = f"batch_{ batch_index :03d} _{ name_suffix } "
257+ batched_job_data ["ui_name" ] = (
258+ f"{ ui_prefix } batch { batch_index :03d} ({ len (chunk )} envs)"
259+ )
260+ batched_job_data ["tox_env" ] = tox_envs [0 ]
261+ batched_job_data ["tox_envs" ] = tox_envs
262+ batched_job_data ["parallelism" ] = min (parallelism , len (chunk ))
263+ batched_job_data ["env_count" ] = len (chunk )
264+ batched_job_datas .append (batched_job_data )
265+
266+ return batched_job_datas
267+
268+
196269def _generate_workflow (
197- job_datas : list , name : str , workflow_directory_path : Path , max_jobs = 250
270+ job_datas : list ,
271+ name : str ,
272+ workflow_directory_path : Path ,
273+ max_jobs = 250 ,
274+ runner = "ubuntu-latest" ,
198275):
276+ for stale_workflow_path in workflow_directory_path .glob (f"{ name } _*.yml" ):
277+ stale_workflow_path .unlink ()
278+
199279 # Github seems to limit the amount of jobs in a workflow file, that is why
200280 # they are split in groups of 250 per workflow file.
201281 for file_number , job_datas in enumerate (
@@ -210,16 +290,32 @@ def _generate_workflow(
210290 test_yml_file .write (
211291 Environment (loader = FileSystemLoader (Path (__file__ ).parent ))
212292 .get_template (f"{ name } .yml.j2" )
213- .render (job_datas = job_datas , file_number = file_number )
293+ .render (
294+ job_datas = job_datas ,
295+ file_number = file_number ,
296+ runner = runner ,
297+ )
214298 )
215299 test_yml_file .write ("\n " )
216300
217301
218302def generate_test_workflow (
219- tox_ini_path : Path , workflow_directory_path : Path , * operating_systems
303+ tox_ini_path : Path ,
304+ workflow_directory_path : Path ,
305+ * operating_systems ,
306+ batch_size = 1 ,
307+ parallelism = 1 ,
220308) -> None :
309+ job_datas = get_test_job_datas (
310+ get_tox_envs (tox_ini_path ), operating_systems
311+ )
221312 _generate_workflow (
222- get_test_job_datas (get_tox_envs (tox_ini_path ), operating_systems ),
313+ _batch_job_datas (
314+ job_datas ,
315+ batch_size = batch_size ,
316+ parallelism = parallelism ,
317+ group_keys = ("os" , "python_version" ),
318+ ),
223319 "test" ,
224320 workflow_directory_path ,
225321 )
@@ -228,11 +324,20 @@ def generate_test_workflow(
228324def generate_lint_workflow (
229325 tox_ini_path : Path ,
230326 workflow_directory_path : Path ,
327+ runner = "ubuntu-latest" ,
328+ batch_size = 1 ,
329+ parallelism = 1 ,
231330) -> None :
331+ job_datas = get_lint_job_datas (get_tox_envs (tox_ini_path ))
232332 _generate_workflow (
233- get_lint_job_datas (get_tox_envs (tox_ini_path )),
333+ _batch_job_datas (
334+ job_datas ,
335+ batch_size = batch_size ,
336+ parallelism = parallelism ,
337+ ),
234338 "lint" ,
235339 workflow_directory_path ,
340+ runner = runner ,
236341 )
237342
238343
@@ -317,6 +422,7 @@ def generate_extension_lint_workflow(
317422 tox_ini_path : Path ,
318423 workflow_directory_path : Path ,
319424 additional_config_path : Path ,
425+ runner = "ubuntu-latest" ,
320426) -> None :
321427 loongsuite_envs = get_loongsuite_tox_envs (additional_config_path )
322428 if not loongsuite_envs :
@@ -327,6 +433,7 @@ def generate_extension_lint_workflow(
327433 "loongsuite_lint" ,
328434 "loongsuite_lint" ,
329435 workflow_directory_path ,
436+ runner = runner ,
330437 )
331438
332439
@@ -353,6 +460,7 @@ def _generate_workflow_with_template(
353460 template_name : str ,
354461 workflow_directory_path : Path ,
355462 max_jobs = 250 ,
463+ runner = "ubuntu-latest" ,
356464):
357465 if (
358466 name in {"loongsuite_lint" , "loongsuite_test" }
@@ -364,6 +472,9 @@ def _generate_workflow_with_template(
364472 "cross-workflow aggregate before allowing generated chunks."
365473 )
366474
475+ for stale_workflow_path in workflow_directory_path .glob (f"{ name } _*.yml" ):
476+ stale_workflow_path .unlink ()
477+
367478 # Github seems to limit the amount of jobs in a workflow file, that is why
368479 # they are split in groups of 250 per workflow file.
369480 for file_number , job_datas in enumerate (
@@ -378,6 +489,10 @@ def _generate_workflow_with_template(
378489 test_yml_file .write (
379490 Environment (loader = FileSystemLoader (Path (__file__ ).parent ))
380491 .get_template (f"{ template_name } .yml.j2" )
381- .render (job_datas = job_datas , file_number = file_number )
492+ .render (
493+ job_datas = job_datas ,
494+ file_number = file_number ,
495+ runner = runner ,
496+ )
382497 )
383498 test_yml_file .write ("\n " )
0 commit comments