88and filters them based on the event that happened on CI.
99"""
1010import dataclasses
11- import enum
1211import json
1312import logging
1413import os
14+ import re
15+ import typing
1516from pathlib import Path
1617from typing import List , Dict , Any , Optional
1718
@@ -44,22 +45,51 @@ def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]:
4445 return jobs
4546
4647
47- class WorkflowRunType (enum .Enum ):
48- PR = enum .auto ()
49- Try = enum .auto ()
50- Auto = enum .auto ()
48+ @dataclasses .dataclass
49+ class PRRunType :
50+ pass
51+
52+
53+ @dataclasses .dataclass
54+ class TryRunType :
55+ custom_jobs : List [str ]
56+
57+
58+ @dataclasses .dataclass
59+ class AutoRunType :
60+ pass
61+
62+
63+ WorkflowRunType = typing .Union [PRRunType , TryRunType , AutoRunType ]
5164
5265
5366@dataclasses .dataclass
5467class GitHubCtx :
5568 event_name : str
5669 ref : str
5770 repository : str
71+ commit_message : Optional [str ]
72+
73+
74+ def get_custom_jobs (ctx : GitHubCtx ) -> List [str ]:
75+ """
76+ Tries to parse names of specific CI jobs that should be executed in the form of
77+ try-job: <job-name>
78+ from the commit message of the passed GitHub context.
79+ """
80+ if ctx .commit_message is None :
81+ return []
82+
83+ regex = re .compile (r"^try-job: (.*)" , re .MULTILINE )
84+ jobs = []
85+ for match in regex .finditer (ctx .commit_message ):
86+ jobs .append (match .group (1 ))
87+ return jobs
5888
5989
6090def find_run_type (ctx : GitHubCtx ) -> Optional [WorkflowRunType ]:
6191 if ctx .event_name == "pull_request" :
62- return WorkflowRunType . PR
92+ return PRRunType ()
6393 elif ctx .event_name == "push" :
6494 old_bors_try_build = (
6595 ctx .ref in ("refs/heads/try" , "refs/heads/try-perf" ) and
@@ -72,20 +102,41 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]:
72102 try_build = old_bors_try_build or new_bors_try_build
73103
74104 if try_build :
75- return WorkflowRunType .Try
105+ jobs = get_custom_jobs (ctx )
106+ return TryRunType (custom_jobs = jobs )
76107
77108 if ctx .ref == "refs/heads/auto" and ctx .repository == "rust-lang-ci/rust" :
78- return WorkflowRunType . Auto
109+ return AutoRunType ()
79110
80111 return None
81112
82113
83114def calculate_jobs (run_type : WorkflowRunType , job_data : Dict [str , Any ]) -> List [Job ]:
84- if run_type == WorkflowRunType . PR :
115+ if isinstance ( run_type , PRRunType ) :
85116 return add_base_env (name_jobs (job_data ["pr" ], "PR" ), job_data ["envs" ]["pr" ])
86- elif run_type == WorkflowRunType .Try :
87- return add_base_env (name_jobs (job_data ["try" ], "try" ), job_data ["envs" ]["try" ])
88- elif run_type == WorkflowRunType .Auto :
117+ elif isinstance (run_type , TryRunType ):
118+ jobs = job_data ["try" ]
119+ custom_jobs = run_type .custom_jobs
120+ if custom_jobs :
121+ if len (custom_jobs ) > 10 :
122+ raise Exception (
123+ f"It is only possible to schedule up to 10 custom jobs, "
124+ f"received { len (custom_jobs )} jobs"
125+ )
126+
127+ jobs = []
128+ unknown_jobs = []
129+ for custom_job in custom_jobs :
130+ job = [j for j in job_data ["auto" ] if j ["image" ] == custom_job ]
131+ if not job :
132+ unknown_jobs .append (custom_job )
133+ continue
134+ jobs .append (job [0 ])
135+ if unknown_jobs :
136+ raise Exception (f"Custom job(s) `{ unknown_jobs } ` not found in auto jobs" )
137+
138+ return add_base_env (name_jobs (jobs , "try" ), job_data ["envs" ]["try" ])
139+ elif isinstance (run_type , AutoRunType ):
89140 return add_base_env (name_jobs (job_data ["auto" ], "auto" ), job_data ["envs" ]["auto" ])
90141
91142 return []
@@ -99,19 +150,25 @@ def skip_jobs(jobs: List[Dict[str, Any]], channel: str) -> List[Job]:
99150
100151
101152def get_github_ctx () -> GitHubCtx :
153+ event_name = os .environ ["GITHUB_EVENT_NAME" ]
154+
155+ commit_message = None
156+ if event_name == "push" :
157+ commit_message = os .environ ["COMMIT_MESSAGE" ]
102158 return GitHubCtx (
103- event_name = os . environ [ "GITHUB_EVENT_NAME" ] ,
159+ event_name = event_name ,
104160 ref = os .environ ["GITHUB_REF" ],
105- repository = os .environ ["GITHUB_REPOSITORY" ]
161+ repository = os .environ ["GITHUB_REPOSITORY" ],
162+ commit_message = commit_message
106163 )
107164
108165
109166def format_run_type (run_type : WorkflowRunType ) -> str :
110- if run_type == WorkflowRunType . PR :
167+ if isinstance ( run_type , PRRunType ) :
111168 return "pr"
112- elif run_type == WorkflowRunType . Auto :
169+ elif isinstance ( run_type , AutoRunType ) :
113170 return "auto"
114- elif run_type == WorkflowRunType . Try :
171+ elif isinstance ( run_type , TryRunType ) :
115172 return "try"
116173 else :
117174 raise AssertionError ()
@@ -135,6 +192,10 @@ def format_run_type(run_type: WorkflowRunType) -> str:
135192 if run_type is not None :
136193 jobs = calculate_jobs (run_type , data )
137194 jobs = skip_jobs (jobs , channel )
195+
196+ if not jobs :
197+ raise Exception ("Scheduled job list is empty, this is an error" )
198+
138199 run_type = format_run_type (run_type )
139200
140201 logging .info (f"Output:\n { yaml .dump (dict (jobs = jobs , run_type = run_type ), indent = 4 )} " )
0 commit comments