2016-05-17 01:53:22 +03:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# 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/.
|
|
|
|
|
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
|
2018-09-29 23:53:10 +03:00
|
|
|
import os.path
|
2016-05-17 01:53:22 +03:00
|
|
|
import json
|
2017-09-29 18:35:30 +03:00
|
|
|
import time
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from mozbuild.util import ReadOnlyDict, memoize
|
|
|
|
from mozversioncontrol import get_repository_object
|
|
|
|
|
2018-09-29 23:53:10 +03:00
|
|
|
from . import GECKO
|
2018-12-27 12:30:52 +03:00
|
|
|
from .util.attributes import release_level
|
2016-05-17 01:53:22 +03:00
|
|
|
|
2017-09-28 22:25:34 +03:00
|
|
|
|
|
|
|
class ParameterMismatch(Exception):
|
|
|
|
"""Raised when a parameters.yml has extra or missing parameters."""
|
|
|
|
|
|
|
|
|
2017-09-29 18:35:30 +03:00
|
|
|
@memoize
|
|
|
|
def get_head_ref():
|
|
|
|
return get_repository_object(GECKO).head_ref
|
|
|
|
|
|
|
|
|
2018-01-27 00:09:35 +03:00
|
|
|
def get_contents(path):
|
|
|
|
with open(path, "r") as fh:
|
|
|
|
contents = fh.readline().rstrip()
|
|
|
|
return contents
|
|
|
|
|
|
|
|
|
2018-09-29 23:53:10 +03:00
|
|
|
def get_version(product_dir='browser'):
|
|
|
|
version_path = os.path.join(GECKO, product_dir, 'config',
|
|
|
|
'version_display.txt')
|
|
|
|
return get_contents(version_path)
|
|
|
|
|
|
|
|
|
|
|
|
def get_app_version(product_dir='browser'):
|
|
|
|
app_version_path = os.path.join(GECKO, product_dir, 'config',
|
|
|
|
'version.txt')
|
|
|
|
return get_contents(app_version_path)
|
2018-01-27 00:09:35 +03:00
|
|
|
|
|
|
|
|
2016-11-07 22:13:40 +03:00
|
|
|
# Please keep this list sorted and in sync with taskcluster/docs/parameters.rst
|
2019-03-14 22:32:05 +03:00
|
|
|
# Parameters are of the form: {name: default} or {name: lambda: default}
|
2017-09-29 18:35:30 +03:00
|
|
|
PARAMETERS = {
|
2018-01-27 00:09:35 +03:00
|
|
|
'app_version': get_app_version(),
|
2017-09-29 18:35:30 +03:00
|
|
|
'base_repository': 'https://hg.mozilla.org/mozilla-unified',
|
|
|
|
'build_date': lambda: int(time.time()),
|
2017-11-09 02:52:48 +03:00
|
|
|
'build_number': 1,
|
2017-10-25 01:28:19 +03:00
|
|
|
'do_not_optimize': [],
|
|
|
|
'existing_tasks': {},
|
2018-10-25 21:32:09 +03:00
|
|
|
'filters': ['target_tasks_method'],
|
2017-09-29 18:35:30 +03:00
|
|
|
'head_ref': get_head_ref,
|
|
|
|
'head_repository': 'https://hg.mozilla.org/mozilla-central',
|
|
|
|
'head_rev': get_head_ref,
|
2018-10-29 14:11:46 +03:00
|
|
|
'hg_branch': 'default',
|
2017-09-29 18:35:30 +03:00
|
|
|
'level': '3',
|
|
|
|
'message': '',
|
|
|
|
'moz_build_date': lambda: datetime.now().strftime("%Y%m%d%H%M%S"),
|
2017-11-09 02:52:48 +03:00
|
|
|
'next_version': None,
|
2017-09-29 18:35:30 +03:00
|
|
|
'optimize_target_tasks': True,
|
|
|
|
'owner': 'nobody@mozilla.com',
|
|
|
|
'project': 'mozilla-central',
|
|
|
|
'pushdate': lambda: int(time.time()),
|
|
|
|
'pushlog_id': '0',
|
2019-03-04 22:40:11 +03:00
|
|
|
'phabricator_diff': None,
|
2018-04-17 05:48:40 +03:00
|
|
|
'release_enable_emefree': False,
|
|
|
|
'release_enable_partners': False,
|
2018-01-18 17:10:53 +03:00
|
|
|
'release_eta': '',
|
2017-09-29 18:35:30 +03:00
|
|
|
'release_history': {},
|
2018-04-17 05:48:40 +03:00
|
|
|
'release_partners': None,
|
|
|
|
'release_partner_config': None,
|
|
|
|
'release_partner_build_number': 1,
|
2018-09-17 21:09:16 +03:00
|
|
|
'release_type': 'nightly',
|
2018-05-03 03:33:52 +03:00
|
|
|
'release_product': None,
|
2018-11-21 00:26:08 +03:00
|
|
|
'required_signoffs': [],
|
|
|
|
'signoff_urls': {},
|
2017-09-29 18:35:30 +03:00
|
|
|
'target_tasks_method': 'default',
|
2019-03-14 23:01:32 +03:00
|
|
|
'tasks_for': 'hg-push',
|
2017-09-29 18:35:30 +03:00
|
|
|
'try_mode': None,
|
|
|
|
'try_options': None,
|
|
|
|
'try_task_config': None,
|
2018-01-27 00:09:35 +03:00
|
|
|
'version': get_version(),
|
2017-09-29 18:35:30 +03:00
|
|
|
}
|
2017-08-15 18:36:29 +03:00
|
|
|
|
2017-07-27 21:26:48 +03:00
|
|
|
COMM_PARAMETERS = {
|
|
|
|
'comm_base_repository': 'https://hg.mozilla.org/comm-central',
|
|
|
|
'comm_head_ref': None,
|
|
|
|
'comm_head_repository': 'https://hg.mozilla.org/comm-central',
|
|
|
|
'comm_head_rev': None,
|
|
|
|
}
|
|
|
|
|
2016-06-21 04:06:55 +03:00
|
|
|
|
2016-05-17 01:53:22 +03:00
|
|
|
class Parameters(ReadOnlyDict):
|
|
|
|
"""An immutable dictionary with nicer KeyError messages on failure"""
|
2017-09-29 18:35:30 +03:00
|
|
|
|
|
|
|
def __init__(self, strict=True, **kwargs):
|
|
|
|
self.strict = strict
|
|
|
|
|
|
|
|
if not self.strict:
|
|
|
|
# apply defaults to missing parameters
|
|
|
|
for name, default in PARAMETERS.items():
|
|
|
|
if name not in kwargs:
|
|
|
|
if callable(default):
|
|
|
|
default = default()
|
|
|
|
kwargs[name] = default
|
|
|
|
|
2017-07-27 21:26:48 +03:00
|
|
|
if set(kwargs) & set(COMM_PARAMETERS.keys()):
|
|
|
|
for name, default in COMM_PARAMETERS.items():
|
|
|
|
if name not in kwargs:
|
|
|
|
if callable(default):
|
|
|
|
default = default()
|
|
|
|
kwargs[name] = default
|
|
|
|
|
2017-09-29 18:35:30 +03:00
|
|
|
ReadOnlyDict.__init__(self, **kwargs)
|
|
|
|
|
2016-11-07 22:13:40 +03:00
|
|
|
def check(self):
|
|
|
|
names = set(self)
|
2017-09-29 18:35:30 +03:00
|
|
|
valid = set(PARAMETERS.keys())
|
2017-07-27 21:26:48 +03:00
|
|
|
valid_comm = set(COMM_PARAMETERS.keys())
|
2016-11-07 22:13:40 +03:00
|
|
|
msg = []
|
|
|
|
|
2017-09-29 18:35:30 +03:00
|
|
|
missing = valid - names
|
2016-11-07 22:13:40 +03:00
|
|
|
if missing:
|
|
|
|
msg.append("missing parameters: " + ", ".join(missing))
|
|
|
|
|
2017-09-29 18:35:30 +03:00
|
|
|
extra = names - valid
|
2017-07-27 21:26:48 +03:00
|
|
|
|
|
|
|
if extra & set(valid_comm):
|
|
|
|
# If any comm_* parameters are specified, ensure all of them are specified.
|
|
|
|
missing = valid_comm - extra
|
|
|
|
if missing:
|
|
|
|
msg.append("missing parameters: " + ", ".join(missing))
|
|
|
|
extra = extra - valid_comm
|
|
|
|
|
2017-09-29 18:35:30 +03:00
|
|
|
if extra and self.strict:
|
2016-11-07 22:13:40 +03:00
|
|
|
msg.append("extra parameters: " + ", ".join(extra))
|
|
|
|
|
|
|
|
if msg:
|
2017-09-28 22:25:34 +03:00
|
|
|
raise ParameterMismatch("; ".join(msg))
|
2016-11-07 22:13:40 +03:00
|
|
|
|
2016-05-17 01:53:22 +03:00
|
|
|
def __getitem__(self, k):
|
2017-07-27 21:26:48 +03:00
|
|
|
if not (k in PARAMETERS.keys() or k in COMM_PARAMETERS.keys()):
|
2016-11-07 22:13:40 +03:00
|
|
|
raise KeyError("no such parameter {!r}".format(k))
|
2016-05-17 01:53:22 +03:00
|
|
|
try:
|
|
|
|
return super(Parameters, self).__getitem__(k)
|
|
|
|
except KeyError:
|
|
|
|
raise KeyError("taskgraph parameter {!r} not found".format(k))
|
|
|
|
|
2018-01-16 10:15:59 +03:00
|
|
|
def is_try(self):
|
|
|
|
"""
|
2018-08-03 19:25:42 +03:00
|
|
|
Determine whether this graph is being built on a try project or for
|
|
|
|
`mach try fuzzy`.
|
2018-01-16 10:15:59 +03:00
|
|
|
"""
|
2018-08-03 19:25:42 +03:00
|
|
|
return 'try' in self['project'] or self['try_mode'] == 'try_select'
|
2018-01-16 10:15:59 +03:00
|
|
|
|
2018-01-16 10:29:30 +03:00
|
|
|
def file_url(self, path):
|
|
|
|
"""
|
|
|
|
Determine the VCS URL for viewing a file in the tree, suitable for
|
|
|
|
viewing by a human.
|
|
|
|
|
|
|
|
:param basestring path: The path, relative to the root of the repository.
|
|
|
|
|
|
|
|
:return basestring: The URL displaying the given path.
|
|
|
|
"""
|
|
|
|
if path.startswith('comm/'):
|
|
|
|
path = path[len('comm/'):]
|
|
|
|
repo = self['comm_head_repository']
|
|
|
|
rev = self['comm_head_rev']
|
|
|
|
else:
|
|
|
|
repo = self['head_repository']
|
|
|
|
rev = self['head_rev']
|
|
|
|
|
|
|
|
return '{}/file/{}/{}'.format(repo, rev, path)
|
|
|
|
|
2018-09-12 00:09:28 +03:00
|
|
|
def release_level(self):
|
|
|
|
"""
|
|
|
|
Whether this is a staging release or not.
|
|
|
|
|
2019-02-04 20:08:51 +03:00
|
|
|
:return six.text_type: One of "production" or "staging".
|
2018-09-12 00:09:28 +03:00
|
|
|
"""
|
2018-12-27 12:30:52 +03:00
|
|
|
return release_level(self['project'])
|
2018-09-12 00:09:28 +03:00
|
|
|
|
2016-05-17 01:53:22 +03:00
|
|
|
|
2019-02-15 00:34:49 +03:00
|
|
|
def load_parameters_file(filename, strict=True, overrides=None, trust_domain=None):
|
2016-05-17 01:53:22 +03:00
|
|
|
"""
|
2017-07-04 23:10:05 +03:00
|
|
|
Load parameters from a path, url, decision task-id or project.
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
task-id=fdtgsD5DQUmAQZEaGMvQ4Q
|
|
|
|
project=mozilla-central
|
2016-05-17 01:53:22 +03:00
|
|
|
"""
|
2016-11-23 22:45:33 +03:00
|
|
|
import urllib
|
2017-07-04 23:10:05 +03:00
|
|
|
from taskgraph.util.taskcluster import get_artifact_url, find_task_id
|
2019-02-05 01:18:57 +03:00
|
|
|
from taskgraph.util import yaml
|
2016-11-23 22:45:33 +03:00
|
|
|
|
2018-08-03 19:25:42 +03:00
|
|
|
if overrides is None:
|
|
|
|
overrides = {}
|
|
|
|
|
2016-05-17 01:53:22 +03:00
|
|
|
if not filename:
|
2018-08-03 19:25:42 +03:00
|
|
|
return Parameters(strict=strict, **overrides)
|
2016-11-23 22:45:33 +03:00
|
|
|
|
|
|
|
try:
|
|
|
|
# reading parameters from a local parameters.yml file
|
|
|
|
f = open(filename)
|
|
|
|
except IOError:
|
2017-07-04 23:10:05 +03:00
|
|
|
# fetching parameters.yml using task task-id, project or supplied url
|
|
|
|
task_id = None
|
2016-11-23 22:45:33 +03:00
|
|
|
if filename.startswith("task-id="):
|
|
|
|
task_id = filename.split("=")[1]
|
2017-07-04 23:10:05 +03:00
|
|
|
elif filename.startswith("project="):
|
2019-02-15 00:34:49 +03:00
|
|
|
if trust_domain is None:
|
|
|
|
raise ValueError(
|
|
|
|
"Can't specify parameters by project "
|
|
|
|
"if trust domain isn't supplied.",
|
|
|
|
)
|
|
|
|
index = "{trust_domain}.v2.{project}.latest.taskgraph.decision".format(
|
|
|
|
trust_domain=trust_domain,
|
2018-04-20 00:32:02 +03:00
|
|
|
project=filename.split("=")[1],
|
|
|
|
)
|
2017-07-04 23:10:05 +03:00
|
|
|
task_id = find_task_id(index)
|
|
|
|
|
|
|
|
if task_id:
|
2017-02-17 06:04:48 +03:00
|
|
|
filename = get_artifact_url(task_id, 'public/parameters.yml')
|
2016-11-23 22:45:33 +03:00
|
|
|
f = urllib.urlopen(filename)
|
|
|
|
|
|
|
|
if filename.endswith('.yml'):
|
2019-02-05 01:18:57 +03:00
|
|
|
kwargs = yaml.load_stream(f)
|
2016-11-23 22:45:33 +03:00
|
|
|
elif filename.endswith('.json'):
|
2018-08-03 19:25:42 +03:00
|
|
|
kwargs = json.load(f)
|
2016-11-23 22:45:33 +03:00
|
|
|
else:
|
|
|
|
raise TypeError("Parameters file `{}` is not JSON or YAML".format(filename))
|
2018-08-03 19:25:42 +03:00
|
|
|
|
|
|
|
kwargs.update(overrides)
|
|
|
|
|
|
|
|
return Parameters(strict=strict, **kwargs)
|
2019-02-15 00:34:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
def parameters_loader(filename, strict=True, overrides=None):
|
|
|
|
def get_parameters(graph_config):
|
|
|
|
parameters = load_parameters_file(
|
|
|
|
filename,
|
|
|
|
strict=strict,
|
|
|
|
overrides=overrides,
|
|
|
|
trust_domain=graph_config["trust-domain"],
|
|
|
|
)
|
|
|
|
parameters.check()
|
|
|
|
return parameters
|
|
|
|
return get_parameters
|