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
|
# 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/.
import datetime
import logging
import mozpack.path as mozpath
from mozbuild.base import MozbuildObject
from mozbuild.util import memoize
from taskgraph.optimize.base import OptimizationStrategy, register_strategy
from taskgraph.optimize.strategies import IndexSearch
from taskgraph.util.parameterization import resolve_timestamps
from gecko_taskgraph.optimize.mozlint import SkipUnlessMozlint
logger = logging.getLogger(__name__)
@register_strategy("skip-unless-schedules")
class SkipUnlessSchedules(OptimizationStrategy):
@memoize
def scheduled_by_push(self, files_changed):
mbo = MozbuildObject.from_environment()
# the decision task has a sparse checkout, so, mozbuild_reader will use
# a MercurialRevisionFinder with revision '.', which should be the same
# as `revision`; in other circumstances, it will use a default reader
rdr = mbo.mozbuild_reader(config_mode="empty")
components = set()
for p, m in rdr.files_info(files_changed).items():
components |= set(m["SCHEDULES"].components)
return components
def should_remove_task(self, task, params, conditions):
if params.get("pushlog_id") == -1:
return False
scheduled = self.scheduled_by_push(frozenset(params["files_changed"]))
conditions = set(conditions)
# if *any* of the condition components are scheduled, do not optimize
if conditions & scheduled:
return False
return True
@register_strategy("skip-unless-has-relevant-tests")
class SkipUnlessHasRelevantTests(OptimizationStrategy):
"""Optimizes tasks that don't run any tests that were
in child directories of a modified file.
"""
@memoize
def get_changed_dirs(self, files_changed):
changed = map(mozpath.dirname, files_changed)
# Filter out empty directories (from files modified in the root).
# Otherwise all tasks would be scheduled.
return {d for d in changed if d}
def should_remove_task(self, task, params, _):
if not task.attributes.get("test_manifests"):
return True
for d in self.get_changed_dirs(frozenset(params["files_changed"])):
for t in task.attributes["test_manifests"]:
if t.startswith(d):
logger.debug(
f"{task.label} runs a test path ({t}) contained by a modified file ({d})"
)
return False
return True
register_strategy("skip-unless-mozlint", args=("tools/lint",))(SkipUnlessMozlint)
@register_strategy("skip-unless-missing")
class SkipUnlessMissing(OptimizationStrategy):
"""Skips a task unless it is missing from a specified index.
This simply defers to Taskgraph's `index-search` optimization. The reason
we need this shim is because replacement and removal optimizations can't be
joined together in a composite strategy as removal and replacement happen
at different times.
"""
index_search = IndexSearch()
def _convert_datetime_str(self, dt):
if dt.endswith("Z"):
dt = dt[:-1]
return datetime.datetime.fromisoformat(dt).strftime(self.index_search.fmt)
def should_remove_task(self, task, params, index):
now = datetime.datetime.now(datetime.timezone.utc)
deadline = self._convert_datetime_str(
resolve_timestamps(now, task.task["deadline"])
)
return bool(
self.index_search.should_replace_task(task, params, deadline, [index])
)
@register_strategy("skip-constrained")
class SkipConstrained(OptimizationStrategy):
"""Skips tasks on constrained worker pools.
Always skips tasks that are on pools with capacity issues, typically
hardware.
"""
def should_remove_task(self, task, params, _):
if task.task["provisionerId"] == "releng-hardware":
return True
return False
|