summaryrefslogtreecommitdiffstats
path: root/comm/taskcluster/comm_taskgraph/decision.py
blob: a075b47cf56d42351ffd773869a9be7d748a93c9 (plain)
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
#  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 logging
import os
import sys

from redo import retry
from taskgraph.decision import (
    _determine_more_accurate_base_ref,
    _determine_more_accurate_base_rev,
    _get_env_prefix,
)
from taskgraph.taskgraph import TaskGraph
from taskgraph.util.taskcluster import get_artifact
from taskgraph.util.vcs import get_repository

from gecko_taskgraph.util.backstop import is_backstop
from gecko_taskgraph.util.partials import populate_release_history
from gecko_taskgraph.util.taskgraph import (
    find_decision_task,
    find_existing_tasks_from_previous_kinds,
)

from . import COMM
from comm_taskgraph.parameters import get_defaults

logger = logging.getLogger(__name__)

BALROG_PRODUCT = "Thunderbird"

# Backstop defaults
BACKSTOP_TIME_INTERVAL = 60 * 22  # minutes
INTEGRATION_PROJECTS = {"comm-central"}

PER_PROJECT_PARAMETERS = {
    "ash": {
        "target_tasks_method": "ash_tasks",
    },
    "jamun": {
        "target_tasks_method": "nightly_desktop",
        "release_type": "nightly",
    },
    "try-comm-central": {
        "enable_always_target": True,
        "target_tasks_method": "try_cc_tasks",
    },
    "comm-central": {
        "target_tasks_method": "comm_central_tasks",
        "release_type": "nightly",
    },
    "comm-beta": {
        "target_tasks_method": "mozilla_beta_tasks",
        "release_type": "beta",
    },
    "comm-esr115": {
        "target_tasks_method": "mozilla_esr115_tasks",
        "release_type": "release",
    },
}

CRON_OPTIONS = {
    "nightly_desktop": {
        "existing_tasks": lambda parameters, graph_config: get_existing_tasks(
            parameters, graph_config
        ),
        "release_history": lambda parameters, graph_config: populate_release_history(
            BALROG_PRODUCT, parameters["project"]
        ),
    }
}


def get_decision_parameters(graph_config, parameters):
    logger.info("{}.get_decision_parameters called".format(__name__))

    # Apply default values for all Thunderbird CI projects
    parameters.update(get_defaults(graph_config.vcs_root))

    # If the target method is nightly, we should build partials. This means
    # knowing what has been released previously.
    # An empty release_history is fine, it just means no partials will be built
    project = parameters["project"]

    if project in PER_PROJECT_PARAMETERS:
        # Upstream will set target_tasks_method to "default" when nothing is set
        if parameters["target_tasks_method"] == "default":
            del parameters["target_tasks_method"]

        # If running from .cron.yml, do not overwrite existing parameters
        update_parameters = [
            (_k, _v)
            for _k, _v in PER_PROJECT_PARAMETERS[project].items()
            if _k not in parameters or not parameters[_k]
        ]
        parameters.update(update_parameters)
        logger.info("project parameters set for project {} from {}.".format(project, __file__))
    else:
        # Projects without a target_tasks_method should not exist for Thunderbird CI
        raise Exception("No target_tasks_method is defined for project {}.".format(project))

    del parameters["backstop"]
    parameters["backstop"] = is_backstop(
        parameters,
        trust_domain="comm",
        time_interval=BACKSTOP_TIME_INTERVAL,
        integration_projects=INTEGRATION_PROJECTS,
    )
    for n in (
        "COMM_BASE_REPOSITORY",
        "COMM_BASE_REV",
        "COMM_HEAD_REPOSITORY",
        "COMM_HEAD_REV",
        "COMM_HEAD_REF",
    ):
        val = os.environ.get(n, "")
        parameters[n.lower()] = val

    repo_path = COMM
    repo = get_repository(repo_path)
    logger.info("Determining comm_base_ref...")
    parameters["comm_base_ref"] = _determine_more_accurate_base_ref(
        repo,
        candidate_base_ref="",
        head_ref=parameters.get("comm_head_ref"),
        base_rev=parameters.get("comm_base_rev"),
    )
    logger.info("Determining comm_base_rev...")
    parameters["comm_base_rev"] = _determine_more_accurate_base_rev(
        repo,
        base_ref=parameters["comm_base_ref"],
        candidate_base_rev=parameters.get("comm_base_rev"),
        head_rev=parameters.get("comm_head_rev"),
        env_prefix=_get_env_prefix(graph_config),
    )

    parameters.setdefault("release_history", dict())
    if parameters.get("tasks_for", "") == "cron":
        for key, _callable in CRON_OPTIONS.get(parameters["target_tasks_method"], {}).items():
            result = _callable(parameters, graph_config)
            parameters[key] = result


def get_existing_tasks(parameters, graph_config):
    """
    Find the decision task corresponding to the on-push graph, and return
    a mapping of labels to task-ids from it.
    """
    try:
        decision_task = retry(
            find_decision_task,
            args=(parameters, graph_config),
            attempts=4,
            sleeptime=5 * 60,
        )
    except Exception:
        logger.exception("Didn't find existing push task.")
        sys.exit(1)

    _, task_graph = TaskGraph.from_json(get_artifact(decision_task, "public/full-task-graph.json"))
    return find_existing_tasks_from_previous_kinds(task_graph, [decision_task], [])