File: strategies.py

package info (click to toggle)
firefox 149.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,767,760 kB
  • sloc: cpp: 7,416,064; javascript: 6,752,859; ansic: 3,774,850; python: 1,250,473; xml: 641,578; asm: 439,191; java: 186,617; sh: 56,634; makefile: 18,856; objc: 13,092; perl: 12,763; pascal: 5,960; yacc: 4,583; cs: 3,846; lex: 1,720; ruby: 1,002; php: 436; lisp: 258; awk: 105; sql: 66; sed: 53; csh: 10; exp: 6
file content (119 lines) | stat: -rw-r--r-- 4,170 bytes parent folder | download
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