1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, print_function, unicode_literals
6
7import re
8
9import six
10
11
12INTEGRATION_PROJECTS = {
13    "autoland",
14}
15
16TRUNK_PROJECTS = INTEGRATION_PROJECTS | {"mozilla-central", "comm-central"}
17
18RELEASE_PROJECTS = {
19    "mozilla-central",
20    "mozilla-beta",
21    "mozilla-release",
22    "mozilla-esr78",
23    "mozilla-esr91",
24    "comm-central",
25    "comm-beta",
26    "comm-esr78",
27    "comm-esr91",
28    "oak",
29}
30
31RELEASE_PROMOTION_PROJECTS = {
32    "jamun",
33    "maple",
34    "try",
35    "try-comm-central",
36} | RELEASE_PROJECTS
37
38TEMPORARY_PROJECTS = set(
39    {
40        # When using a "Disposeabel Project Branch" you can specify your branch here. e.g.:
41        # 'oak',
42    }
43)
44
45TRY_PROJECTS = {
46    "try",
47    "try-comm-central",
48}
49
50ALL_PROJECTS = RELEASE_PROMOTION_PROJECTS | TRUNK_PROJECTS | TEMPORARY_PROJECTS
51
52RUN_ON_PROJECT_ALIASES = {
53    # key is alias, value is lambda to test it against
54    "all": lambda project: True,
55    "integration": lambda project: project in INTEGRATION_PROJECTS,
56    "release": lambda project: project in RELEASE_PROJECTS,
57    "trunk": lambda project: project in TRUNK_PROJECTS,
58}
59
60_COPYABLE_ATTRIBUTES = (
61    "accepted-mar-channel-ids",
62    "artifact_map",
63    "artifact_prefix",
64    "build_platform",
65    "build_type",
66    "l10n_chunk",
67    "locale",
68    "mar-channel-id",
69    "nightly",
70    "required_signoffs",
71    "shippable",
72    "shipping_phase",
73    "shipping_product",
74    "signed",
75    "stub-installer",
76    "update-channel",
77)
78
79
80def attrmatch(attributes, **kwargs):
81    """Determine whether the given set of task attributes matches.  The
82    conditions are given as keyword arguments, where each keyword names an
83    attribute.  The keyword value can be a literal, a set, or a callable.  A
84    literal must match the attribute exactly.  Given a set, the attribute value
85    must be in the set.  A callable is called with the attribute value.  If an
86    attribute is specified as a keyword argument but not present in the
87    attributes, the result is False."""
88    for kwkey, kwval in six.iteritems(kwargs):
89        if kwkey not in attributes:
90            return False
91        attval = attributes[kwkey]
92        if isinstance(kwval, set):
93            if attval not in kwval:
94                return False
95        elif callable(kwval):
96            if not kwval(attval):
97                return False
98        elif kwval != attributes[kwkey]:
99            return False
100    return True
101
102
103def keymatch(attributes, target):
104    """Determine if any keys in attributes are a match to target, then return
105    a list of matching values. First exact matches will be checked. Failing
106    that, regex matches and finally a default key.
107    """
108    # exact match
109    if target in attributes:
110        return [attributes[target]]
111
112    # regular expression match
113    matches = [v for k, v in six.iteritems(attributes) if re.match(k + "$", target)]
114    if matches:
115        return matches
116
117    # default
118    if "default" in attributes:
119        return [attributes["default"]]
120
121    return []
122
123
124def match_run_on_projects(project, run_on_projects):
125    """Determine whether the given project is included in the `run-on-projects`
126    parameter, applying expansions for things like "integration" mentioned in
127    the attribute documentation."""
128    aliases = RUN_ON_PROJECT_ALIASES.keys()
129    run_aliases = set(aliases) & set(run_on_projects)
130    if run_aliases:
131        if any(RUN_ON_PROJECT_ALIASES[alias](project) for alias in run_aliases):
132            return True
133
134    return project in run_on_projects
135
136
137def match_run_on_hg_branches(hg_branch, run_on_hg_branches):
138    """Determine whether the given project is included in the `run-on-hg-branches`
139    parameter. Allows 'all'."""
140    if "all" in run_on_hg_branches:
141        return True
142
143    for expected_hg_branch_pattern in run_on_hg_branches:
144        if re.match(expected_hg_branch_pattern, hg_branch):
145            return True
146
147    return False
148
149
150def copy_attributes_from_dependent_job(dep_job, denylist=()):
151    return {
152        attr: dep_job.attributes[attr]
153        for attr in _COPYABLE_ATTRIBUTES
154        if attr in dep_job.attributes and attr not in denylist
155    }
156
157
158def sorted_unique_list(*args):
159    """Join one or more lists, and return a sorted list of unique members"""
160    combined = set().union(*args)
161    return sorted(combined)
162
163
164def release_level(project):
165    """
166    Whether this is a staging release or not.
167
168    :return six.text_type: One of "production" or "staging".
169    """
170    return "production" if project in RELEASE_PROJECTS else "staging"
171