8
8
and filters them based on the event that happened on CI.
9
9
"""
10
10
import dataclasses
11
- import enum
12
11
import json
13
12
import logging
14
13
import os
14
+ import re
15
+ import typing
15
16
from pathlib import Path
16
17
from typing import List, Dict, Any, Optional
17
18
@@ -44,22 +45,51 @@ def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]:
44
45
return jobs
45
46
46
47
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]
51
64
52
65
53
66
@dataclasses.dataclass
54
67
class GitHubCtx:
55
68
event_name: str
56
69
ref: str
57
70
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
58
88
59
89
60
90
def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]:
61
91
if ctx.event_name == "pull_request":
62
- return WorkflowRunType . PR
92
+ return PRRunType()
63
93
elif ctx.event_name == "push":
64
94
old_bors_try_build = (
65
95
ctx.ref in ("refs/heads/try", "refs/heads/try-perf") and
@@ -72,20 +102,41 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]:
72
102
try_build = old_bors_try_build or new_bors_try_build
73
103
74
104
if try_build:
75
- return WorkflowRunType .Try
105
+ jobs = get_custom_jobs(ctx)
106
+ return TryRunType(custom_jobs=jobs)
76
107
77
108
if ctx.ref == "refs/heads/auto" and ctx.repository == "rust-lang-ci/rust":
78
- return WorkflowRunType . Auto
109
+ return AutoRunType()
79
110
80
111
return None
81
112
82
113
83
114
def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]:
84
- if run_type == WorkflowRunType . PR :
115
+ if isinstance( run_type, PRRunType) :
85
116
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):
89
140
return add_base_env(name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"])
90
141
91
142
return []
@@ -99,19 +150,25 @@ def skip_jobs(jobs: List[Dict[str, Any]], channel: str) -> List[Job]:
99
150
100
151
101
152
def 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"]
102
158
return GitHubCtx(
103
- event_name = os . environ [ "GITHUB_EVENT_NAME" ] ,
159
+ event_name=event_name ,
104
160
ref=os.environ["GITHUB_REF"],
105
- repository = os .environ ["GITHUB_REPOSITORY" ]
161
+ repository=os.environ["GITHUB_REPOSITORY"],
162
+ commit_message=commit_message
106
163
)
107
164
108
165
109
166
def format_run_type(run_type: WorkflowRunType) -> str:
110
- if run_type == WorkflowRunType . PR :
167
+ if isinstance( run_type, PRRunType) :
111
168
return "pr"
112
- elif run_type == WorkflowRunType . Auto :
169
+ elif isinstance( run_type, AutoRunType) :
113
170
return "auto"
114
- elif run_type == WorkflowRunType . Try :
171
+ elif isinstance( run_type, TryRunType) :
115
172
return "try"
116
173
else:
117
174
raise AssertionError()
@@ -135,6 +192,10 @@ def format_run_type(run_type: WorkflowRunType) -> str:
135
192
if run_type is not None:
136
193
jobs = calculate_jobs(run_type, data)
137
194
jobs = skip_jobs(jobs, channel)
195
+
196
+ if not jobs:
197
+ raise Exception("Scheduled job list is empty, this is an error")
198
+
138
199
run_type = format_run_type(run_type)
139
200
140
201
logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}")
0 commit comments