Bug 1490082: Add support to hg-push to get the .taskcluster.yml from an alternative repo; r=aki
authorTom Prince <mozilla@hocat.ca>
Wed, 22 Apr 2020 21:15:39 +0000
changeset 217 58d4ac539f776a4c214f177e9c303074208cff93
parent 216 095cfacca85d7e246085e52fef9e3b8fd4df9126
child 218 6b68d2423c65a2e26faafd97d4b3cafad1580c8e
push id156
push usermozilla@hocat.ca
push dateWed, 22 Apr 2020 21:23:55 +0000
treeherderci-admin@6b68d2423c65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaki
bugs1490082
Bug 1490082: Add support to hg-push to get the .taskcluster.yml from an alternative repo; r=aki ci-admin and ci-config share a taskgraph configuration. Since ci-config *only* contains the taskcluster configuration, there is not a need for it to have it's own `.taskcluster.yml`. This adds support to ci-admin to specify that a project uses a `.taskcluster.yml` from another repository. Differential Revision: https://phabricator.services.mozilla.com/D71692
src/ciadmin/generate/ciconfig/projects.py
src/ciadmin/generate/hg_pushes.py
tests/ciadmin/test_generate_ciconfig_projects.py
--- a/src/ciadmin/generate/ciconfig/projects.py
+++ b/src/ciadmin/generate/ciconfig/projects.py
@@ -46,16 +46,17 @@ class Project:
         ],
     )
     trust_domain = attr.ib(type=str, default=None)
     trust_project = attr.ib(type=str, default=None)
     parent_repo = attr.ib(type=str, default=None)
     is_try = attr.ib(type=bool, default=False)
     features = attr.ib(type=dict, factory=lambda: {})
     cron = attr.ib(type=dict, factory=lambda: {})
+    taskcluster_yml_project = attr.ib(type=str, default=None)
 
     def __attrs_post_init__(self):
         """
         Once the object is initialised, perform more sanity checks to ensure
         the values received are sane together
         """
         self.cron["targets"] = _convert_cron_targets(self.cron.get("targets", []))
 
@@ -90,16 +91,26 @@ class Project:
                 )
 
     @staticmethod
     async def fetch_all():
         """Load project metadata from projects.yml in ci-configuration"""
         projects = await get_ciconfig_file("projects.yml")
         return [Project(alias, **info) for alias, info in projects.items()]
 
+    @staticmethod
+    async def get(alias):
+        projects = await Project.fetch_all()
+
+        for project in projects:
+            if project.alias == alias:
+                return project
+        else:
+            raise KeyError("Project {} is not defined".format(alias))
+
     # The `features` property is designed for ease of use in yaml, with true and false
     # values for each feature; the `feature()` and `enabled_features` attributes provide
     # easier access for Python uses.
 
     def feature(self, feature):
         "Return True if this feature is enabled"
         return feature in self.features and self.features[feature]
 
--- a/src/ciadmin/generate/hg_pushes.py
+++ b/src/ciadmin/generate/hg_pushes.py
@@ -13,29 +13,47 @@ from tcadmin.resources import Binding, H
 from .ciconfig.get import get_ciconfig_file
 from .ciconfig.projects import Project
 
 
 async def make_hook(project):
     hookGroupId = "hg-push"
     hookId = project.alias
 
+    if project.taskcluster_yml_project:
+        taskcluster_yml_project = await Project.get(project.taskcluster_yml_project)
+        if project.level > taskcluster_yml_project.level:
+            raise ValueError(
+                "Cannot use `.taskcluster.yml` from {} which has level {}, "
+                "for {} which has level {}.".format(
+                    project.taskcluster_yml_project,
+                    taskcluster_yml_project.level,
+                    project.alias,
+                    project.level,
+                )
+            )
+        taskcluster_yml_repo = taskcluster_yml_project.repo
+    else:
+        taskcluster_yml_repo = None
+
     # use the hg-push-template.yml from the ci-configuration repository, rendering it
     # with the context values described there
     task_template = await get_ciconfig_file("hg-push-template.yml")
+
     task = jsone.render(
         task_template,
         {
             "level": project.level,
             "trust_domain": project.trust_domain,
             "hookGroupId": hookGroupId,
             "hookId": hookId,
             "project_repo": project.repo,
             "project_role_prefix": project.role_prefix,
             "alias": project.alias,
+            "taskcluster_yml_repo": taskcluster_yml_repo,
         },
     )
 
     return Hook(
         hookGroupId=hookGroupId,
         hookId=hookId,
         name="{}/{}".format(hookGroupId, hookId),
         description=textwrap.dedent(
--- a/tests/ciadmin/test_generate_ciconfig_projects.py
+++ b/tests/ciadmin/test_generate_ciconfig_projects.py
@@ -36,16 +36,17 @@ async def test_fetch_empty(mock_ciconfig
                 "trust_domain": "gecko",
                 "trust_project": None,
                 # defaults
                 "_level": None,
                 "is_try": False,
                 "parent_repo": None,
                 "features": {},
                 "cron": {"targets": []},
+                "taskcluster_yml_project": None,
             },
         ),
         (
             "fenix",
             {
                 "repo": "https://github.com/mozilla-mobile/fenix/",
                 "repo_type": "git",
                 "level": 3,
@@ -58,16 +59,17 @@ async def test_fetch_empty(mock_ciconfig
                 "_level": 3,
                 "access": None,
                 "is_try": False,
                 "parent_repo": None,
                 "trust_domain": None,
                 "trust_project": None,
                 "features": {},
                 "cron": {"targets": []},
+                "taskcluster_yml_project": None,
             },
         ),
     ),
 )
 @pytest.mark.asyncio
 async def test_fetch_defaults(
     mock_ciconfig_file, project_name, project_data, expected_data
 ):
@@ -114,16 +116,17 @@ async def test_fetch_defaults(
                 "cron": {
                     "email_when_trigger_failure": True,
                     "notify_emails": [],
                     "targets": [
                         {"target": "a", "bindings": []},
                         {"target": "b", "bindings": []},
                     ],
                 },
+                "taskcluster_yml_project": None,
             },
         ),
         (
             "beetmoverscript",  # git project but not mobile
             {
                 "repo": "https://github.com/mozilla-releng/beetmoverscript/",
                 "repo_type": "git",
                 "level": 3,
@@ -154,16 +157,17 @@ async def test_fetch_defaults(
                 "cron": {
                     "email_when_trigger_failure": True,
                     "notify_emails": [],
                     "targets": [
                         {"target": "a", "bindings": []},
                         {"target": "b", "bindings": []},
                     ],
                 },
+                "taskcluster_yml_project": None,
             },
         ),
     ),
 )
 @pytest.mark.asyncio
 async def test_fetch_nodefaults(
     mock_ciconfig_file, project_name, project_data, expected_data
 ):