1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
The objective of optimization is to remove as many tasks from the graph as
possible, as efficiently as possible, thereby delivering useful results as
quickly as possible. For example, ideally if only a test script is modified in
a push, then the resulting graph contains only the corresponding test suite
task.
See ``taskcluster/docs/optimization.rst`` for more information.
"""
from taskgraph.optimize.base import Alias, All, Any, Not, register_strategy
from taskgraph.util.python_path import import_sibling_modules
# Trigger registration in sibling modules.
import_sibling_modules()
def split_bugbug_arg(arg, substrategies):
"""Split args for bugbug based strategies.
Many bugbug based optimizations require passing an empty dict by reference
to communicate to downstream strategies. This function passes the provided
arg to the first (non bugbug) strategies and a shared empty dict to the
bugbug strategy and all substrategies after it.
"""
from gecko_taskgraph.optimize.bugbug import BugBugPushSchedules
index = [
i
for i, strategy in enumerate(substrategies)
if isinstance(strategy, BugBugPushSchedules)
][0]
return [arg] * index + [{}] * (len(substrategies) - index)
# Register composite strategies.
register_strategy("build", args=("skip-unless-schedules",))(Alias)
register_strategy("test", args=("skip-unless-schedules",))(Alias)
register_strategy("test-inclusive", args=("skip-unless-schedules",))(Alias)
register_strategy("test-verify", args=("skip-unless-schedules",))(Alias)
register_strategy("upload-symbols", args=("never",))(Alias)
register_strategy("reprocess-symbols", args=("never",))(Alias)
# Strategy overrides used to tweak the default strategies. These are referenced
# by the `optimize_strategies` parameter.
class project:
"""Strategies that should be applied per-project."""
autoland = {
"test": Any(
# This `Any` strategy implements bi-modal behaviour. It allows different
# strategies on expanded pushes vs regular pushes.
# This first `All` handles "expanded" pushes.
All(
# There are three substrategies in this `All`, the first two act as barriers
# that help determine when to apply the third:
# 1. On backstop pushes, `skip-unless-backstop` returns False. Therefore
# the overall composite strategy is False and we don't optimize.
# 2. On regular pushes, `Not('skip-unless-expanded')` returns False. Therefore
# the overall composite strategy is False and we don't optimize.
# 3. On expanded pushes, the third strategy will determine whether or
# not to optimize each individual task.
# The barrier strategies.
"skip-unless-backstop",
Not("skip-unless-expanded"),
# The actual test strategy applied to "expanded" pushes.
Any(
"skip-unless-schedules",
"bugbug-reduced-manifests-fallback-last-10-pushes",
"platform-disperse",
split_args=split_bugbug_arg,
),
),
# This second `All` handles regular (aka not expanded or backstop)
# pushes.
All(
# There are two substrategies in this `All`, the first acts as a barrier
# that determines when to apply the second:
# 1. On expanded pushes (which includes backstops), `skip-unless-expanded`
# returns False. Therefore the overall composite strategy is False and we
# don't optimize.
# 2. On regular pushes, the second strategy will determine whether or
# not to optimize each individual task.
# The barrier strategy.
"skip-unless-expanded",
# The actual test strategy applied to regular pushes.
Any(
"skip-unless-schedules",
"bugbug-reduced-manifests-fallback-low",
"platform-disperse",
split_args=split_bugbug_arg,
),
),
),
"build": All(
"skip-unless-expanded",
Any(
"skip-unless-schedules",
"bugbug-reduced-fallback",
split_args=split_bugbug_arg,
),
),
}
"""Strategy overrides that apply to autoland."""
class experimental:
"""Experimental strategies either under development or used as benchmarks.
These run as "shadow-schedulers" on each autoland push (tier 3) and/or can be used
with `./mach try auto`. E.g:
./mach try auto --strategy relevant_tests
"""
bugbug_tasks_medium = {
"test": Any(
"skip-unless-schedules", "bugbug-tasks-medium", split_args=split_bugbug_arg
),
}
"""Doesn't limit platforms, medium confidence threshold."""
bugbug_tasks_high = {
"test": Any(
"skip-unless-schedules", "bugbug-tasks-high", split_args=split_bugbug_arg
),
}
"""Doesn't limit platforms, high confidence threshold."""
bugbug_debug_disperse = {
"test": Any(
"skip-unless-schedules",
"bugbug-low",
"platform-debug",
"platform-disperse",
split_args=split_bugbug_arg,
),
}
"""Restricts tests to debug platforms."""
bugbug_disperse_low = {
"test": Any(
"skip-unless-schedules",
"bugbug-low",
"platform-disperse",
split_args=split_bugbug_arg,
),
}
"""Disperse tests across platforms, low confidence threshold."""
bugbug_disperse_medium = {
"test": Any(
"skip-unless-schedules",
"bugbug-medium",
"platform-disperse",
split_args=split_bugbug_arg,
),
}
"""Disperse tests across platforms, medium confidence threshold."""
bugbug_disperse_reduced_medium = {
"test": Any(
"skip-unless-schedules",
"bugbug-reduced-manifests",
"platform-disperse",
split_args=split_bugbug_arg,
),
}
"""Disperse tests across platforms, medium confidence threshold with reduced tasks."""
bugbug_reduced_manifests_config_selection_low = {
"test": Any(
"skip-unless-schedules",
"bugbug-reduced-manifests-config-selection-low",
split_args=split_bugbug_arg,
),
}
"""Choose configs selected by bugbug, low confidence threshold with reduced tasks."""
bugbug_reduced_manifests_config_selection_medium = {
"test": Any(
"skip-unless-schedules",
"bugbug-reduced-manifests-config-selection",
split_args=split_bugbug_arg,
),
}
"""Choose configs selected by bugbug, medium confidence threshold with reduced tasks."""
bugbug_disperse_medium_no_unseen = {
"test": Any(
"skip-unless-schedules",
"bugbug-medium",
"platform-disperse-no-unseen",
split_args=split_bugbug_arg,
),
}
"""Disperse tests across platforms (no modified for unseen configurations), medium confidence
threshold."""
bugbug_disperse_medium_only_one = {
"test": Any(
"skip-unless-schedules",
"bugbug-medium",
"platform-disperse-only-one",
split_args=split_bugbug_arg,
),
}
"""Disperse tests across platforms (one platform per group), medium confidence threshold."""
bugbug_disperse_high = {
"test": Any(
"skip-unless-schedules",
"bugbug-high",
"platform-disperse",
split_args=split_bugbug_arg,
),
}
"""Disperse tests across platforms, high confidence threshold."""
bugbug_reduced = {
"test": Any(
"skip-unless-schedules", "bugbug-reduced", split_args=split_bugbug_arg
),
}
"""Use the reduced set of tasks (and no groups) chosen by bugbug."""
bugbug_reduced_high = {
"test": Any(
"skip-unless-schedules", "bugbug-reduced-high", split_args=split_bugbug_arg
),
}
"""Use the reduced set of tasks (and no groups) chosen by bugbug, high
confidence threshold."""
relevant_tests = {
"test": Any("skip-unless-schedules", "skip-unless-has-relevant-tests"),
}
"""Runs task containing tests in the same directories as modified files."""
class ExperimentalOverride:
"""Overrides dictionaries that are stored in a container with new values.
This can be used to modify all strategies in a collection the same way,
presumably with strategies affecting kinds of tasks tangential to the
current context.
Args:
base (object): A container class supporting attribute access.
overrides (dict): Values to update any accessed dictionaries with.
"""
def __init__(self, base, overrides):
self.base = base
self.overrides = overrides
def __getattr__(self, name):
val = getattr(self.base, name).copy()
for name, strategy in self.overrides.items():
if isinstance(strategy, str) and strategy.startswith("base:"):
strategy = val[strategy[len("base:") :]]
val[name] = strategy
return val
tryselect = ExperimentalOverride(
experimental,
{
"build": Any(
"skip-unless-schedules", "bugbug-reduced", split_args=split_bugbug_arg
),
"test-verify": "base:test",
"upload-symbols": Alias("always"),
"reprocess-symbols": Alias("always"),
},
)
|