summaryrefslogtreecommitdiffstats
path: root/taskcluster/taskgraph/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'taskcluster/taskgraph/config.py')
-rw-r--r--taskcluster/taskgraph/config.py196
1 files changed, 196 insertions, 0 deletions
diff --git a/taskcluster/taskgraph/config.py b/taskcluster/taskgraph/config.py
new file mode 100644
index 0000000000..dace95a0a8
--- /dev/null
+++ b/taskcluster/taskgraph/config.py
@@ -0,0 +1,196 @@
+# 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
+
+import os
+import logging
+import sys
+import attr
+from six import text_type
+from mozpack import path
+
+from .util.python_path import find_object
+from .util.schema import validate_schema, Schema, optionally_keyed_by
+from voluptuous import Required, Optional, Any
+from .util.yaml import load_yaml
+
+logger = logging.getLogger(__name__)
+
+graph_config_schema = Schema(
+ {
+ # The trust-domain for this graph.
+ # (See https://firefox-source-docs.mozilla.org/taskcluster/taskcluster/taskgraph.html#taskgraph-trust-domain) # noqa
+ Required("trust-domain"): text_type,
+ # This specifes the prefix for repo parameters that refer to the project being built.
+ # This selects between `head_rev` and `comm_head_rev` and related paramters.
+ # (See http://firefox-source-docs.mozilla.org/taskcluster/taskcluster/parameters.html#push-information # noqa
+ # and http://firefox-source-docs.mozilla.org/taskcluster/taskcluster/parameters.html#comm-push-information) # noqa
+ Required("project-repo-param-prefix"): text_type,
+ # This specifies the top level directory of the application being built.
+ # ie. "browser/" for Firefox, "comm/mail/" for Thunderbird.
+ Required("product-dir"): text_type,
+ Required("treeherder"): {
+ # Mapping of treeherder group symbols to descriptive names
+ Required("group-names"): {text_type: text_type}
+ },
+ Required("index"): {Required("products"): [text_type]},
+ Required("try"): {
+ # We have a few platforms for which we want to do some "extra" builds, or at
+ # least build-ish things. Sort of. Anyway, these other things are implemented
+ # as different "platforms". These do *not* automatically ride along with "-p
+ # all"
+ Required("ridealong-builds"): {text_type: [text_type]},
+ },
+ Required("release-promotion"): {
+ Required("products"): [text_type],
+ Required("flavors"): {
+ text_type: {
+ Required("product"): text_type,
+ Required("target-tasks-method"): text_type,
+ Optional("is-rc"): bool,
+ Optional("rebuild-kinds"): [text_type],
+ Optional("version-bump"): bool,
+ Optional("partial-updates"): bool,
+ }
+ },
+ },
+ Required("merge-automation"): {
+ Required("behaviors"): {
+ text_type: {
+ Optional("from-branch"): text_type,
+ Required("to-branch"): text_type,
+ Optional("from-repo"): text_type,
+ Required("to-repo"): text_type,
+ Required("version-files"): [
+ {
+ Required("filename"): text_type,
+ Optional("new-suffix"): text_type,
+ Optional("version-bump"): Any("major", "minor"),
+ }
+ ],
+ Required("replacements"): [[text_type]],
+ Required("merge-old-head"): bool,
+ Optional("base-tag"): text_type,
+ Optional("end-tag"): text_type,
+ Optional("fetch-version-from"): text_type,
+ }
+ },
+ },
+ Required("scriptworker"): {
+ # Prefix to add to scopes controlling scriptworkers
+ Required("scope-prefix"): text_type,
+ },
+ Required("task-priority"): optionally_keyed_by(
+ "project",
+ Any(
+ "highest",
+ "very-high",
+ "high",
+ "medium",
+ "low",
+ "very-low",
+ "lowest",
+ ),
+ ),
+ Required("partner-urls"): {
+ Required("release-partner-repack"): optionally_keyed_by(
+ "release-product", "release-level", "release-type", Any(text_type, None)
+ ),
+ Optional("release-partner-attribution"): optionally_keyed_by(
+ "release-product", "release-level", "release-type", Any(text_type, None)
+ ),
+ Required("release-eme-free-repack"): optionally_keyed_by(
+ "release-product", "release-level", "release-type", Any(text_type, None)
+ ),
+ },
+ Required("workers"): {
+ Required("aliases"): {
+ text_type: {
+ Required("provisioner"): optionally_keyed_by("level", text_type),
+ Required("implementation"): text_type,
+ Required("os"): text_type,
+ Required("worker-type"): optionally_keyed_by(
+ "level", "release-level", text_type
+ ),
+ }
+ },
+ },
+ Required("mac-notarization"): {
+ Required("mac-behavior"): optionally_keyed_by(
+ "project",
+ "shippable",
+ Any("mac_notarize", "mac_geckodriver", "mac_sign", "mac_sign_and_pkg"),
+ ),
+ Required("mac-entitlements"): optionally_keyed_by(
+ "platform", "release-level", text_type
+ ),
+ },
+ Required("taskgraph"): {
+ Optional(
+ "register",
+ description="Python function to call to register extensions.",
+ ): text_type,
+ Optional("decision-parameters"): text_type,
+ },
+ }
+)
+
+
+@attr.s(frozen=True, cmp=False)
+class GraphConfig(object):
+ _config = attr.ib()
+ root_dir = attr.ib()
+
+ _PATH_MODIFIED = False
+
+ def __getitem__(self, name):
+ return self._config[name]
+
+ def register(self):
+ """
+ Add the project's taskgraph directory to the python path, and register
+ any extensions present.
+ """
+ modify_path = os.path.dirname(self.root_dir)
+ if GraphConfig._PATH_MODIFIED:
+ if GraphConfig._PATH_MODIFIED == modify_path:
+ # Already modified path with the same root_dir.
+ # We currently need to do this to enable actions to call
+ # taskgraph_decision, e.g. relpro.
+ return
+ raise Exception("Can't register multiple directories on python path.")
+ GraphConfig._PATH_MODIFIED = modify_path
+ sys.path.insert(0, modify_path)
+ register_path = self["taskgraph"].get("register")
+ if register_path:
+ find_object(register_path)(self)
+
+ @property
+ def taskcluster_yml(self):
+ if path.split(self.root_dir)[-2:] != ["taskcluster", "ci"]:
+ raise Exception(
+ "Not guessing path to `.taskcluster.yml`. "
+ "Graph config in non-standard location."
+ )
+ return os.path.join(
+ os.path.dirname(os.path.dirname(self.root_dir)),
+ ".taskcluster.yml",
+ )
+
+
+def validate_graph_config(config):
+ validate_schema(graph_config_schema, config, "Invalid graph configuration:")
+
+
+def load_graph_config(root_dir):
+ config_yml = os.path.join(root_dir, "config.yml")
+ if not os.path.exists(config_yml):
+ raise Exception("Couldn't find taskgraph configuration: {}".format(config_yml))
+
+ logger.debug("loading config from `{}`".format(config_yml))
+ config = load_yaml(config_yml)
+ logger.debug("validating the graph config.")
+ validate_graph_config(config)
+ return GraphConfig(config=config, root_dir=root_dir)