Bug 1497575: [staging-release] Add try subcommand to create a push for staging releases; r=aki,ahal
authorTom Prince <mozilla@hocat.ca>
Tue, 16 Oct 2018 23:04:03 +0000
changeset 490105 8b563ebe7cdad2b7f739cd53dfec0fc6ea9581f8
parent 490104 cc20c7974801cb69129d1bf78b40b9f92f364b98
child 490106 80f7c4e29bea3f238bbf047c3fdf8e853c8c0a5d
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersaki, ahal
bugs1497575
milestone64.0a1
Bug 1497575: [staging-release] Add try subcommand to create a push for staging releases; r=aki,ahal This adds `mach try release` which adds temporary changes to enable staging release to run. Differential Revision: https://phabricator.services.mozilla.com/D8625
taskcluster/taskgraph/decision.py
tools/moz.build
tools/tryselect/mach_commands.py
tools/tryselect/push.py
tools/tryselect/selectors/release.py
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -277,24 +277,30 @@ def get_decision_parameters(config, opti
         set_try_config(parameters, task_config_file)
 
     result = Parameters(**parameters)
     result.check()
     return result
 
 
 def set_try_config(parameters, task_config_file):
-    parameters['try_mode'] = None
     if os.path.isfile(task_config_file):
         logger.info("using try tasks from {}".format(task_config_file))
-        parameters['try_mode'] = 'try_task_config'
         with open(task_config_file, 'r') as fh:
-            parameters['try_task_config'] = json.load(fh)
-    else:
-        parameters['try_task_config'] = None
+            task_config = json.load(fh)
+        task_config_version = task_config.get('version', 1)
+        if task_config_version == 1:
+            parameters['try_mode'] = 'try_task_config'
+            parameters['try_task_config'] = task_config
+        elif task_config_version == 2:
+            parameters.update(task_config['parameters'])
+            return
+        else:
+            raise Exception(
+                "Unknown `try_task_config.json` version: {}".format(task_config_version))
 
     if 'try:' in parameters['message']:
         parameters['try_mode'] = 'try_option_syntax'
         args = parse_message(parameters['message'])
         parameters['try_options'] = args
     else:
         parameters['try_options'] = None
 
--- a/tools/moz.build
+++ b/tools/moz.build
@@ -36,16 +36,19 @@ with Files("rb/**"):
     BUG_COMPONENT = ("Core", "XPCOM")
 
 with Files("rewriting/**"):
     BUG_COMPONENT = ("Core", "Rewriting and Analysis")
 
 with Files("tryselect/**"):
     BUG_COMPONENT = ("Testing", "General")
 
+with Files("tryselect/selectors/release.py"):
+    BUG_COMPONENT = ("Release Engineering", "General")
+
 with Files("update-packaging/**"):
     BUG_COMPONENT = ("Release Engineering", "Other")
 
 SPHINX_TREES['lint'] = 'lint/docs'
 
 with Files('lint/docs/**'):
     SCHEDULES.exclusive = ['docs']
 
--- a/tools/tryselect/mach_commands.py
+++ b/tools/tryselect/mach_commands.py
@@ -229,8 +229,18 @@ class TrySelect(MachCommandBase):
 
         config_status = os.path.join(self.topobjdir, 'config.status')
         if (kwargs['paths'] or kwargs['tags']) and not config_status:
             print(CONFIG_ENVIRONMENT_NOT_FOUND)
             sys.exit(1)
 
         at = AutoTry(self.topsrcdir, self._mach_context)
         return at.run(**kwargs)
+
+    @SubCommand('try',
+                'release',
+                description='Push the current tree to try, configured for a staging release.',
+                parser=get_parser('release'))
+    def try_release(self, **kwargs):
+        """Push the current tree to try, configured for a staging release.
+        """
+        from tryselect.selectors.release import run_try_release
+        return run_try_release(**kwargs)
--- a/tools/tryselect/push.py
+++ b/tools/tryselect/push.py
@@ -47,17 +47,17 @@ vcs = get_repository_object(build.topsrc
 topsrcdir_hash = hashlib.sha256(os.path.abspath(build.topsrcdir)).hexdigest()
 history_path = os.path.join(get_state_dir()[0], 'history', topsrcdir_hash, 'try_task_configs.json')
 old_history_path = os.path.join(get_state_dir()[0], 'history', 'try_task_configs.json')
 
 
 def write_task_config(try_task_config):
     config_path = os.path.join(vcs.path, 'try_task_config.json')
     with open(config_path, 'w') as fh:
-        json.dump(try_task_config, fh, indent=2, separators=(',', ':'))
+        json.dump(try_task_config, fh, indent=4, separators=(',', ': '))
         fh.write('\n')
     return config_path
 
 
 def write_task_config_history(msg, try_task_config):
     if not os.path.isfile(history_path):
         if not os.path.isdir(os.path.dirname(history_path)):
             os.makedirs(os.path.dirname(history_path))
@@ -77,53 +77,65 @@ def check_working_directory(push=True):
         return
 
     if not vcs.working_directory_clean():
         print(UNCOMMITTED_CHANGES)
         sys.exit(1)
 
 
 def push_to_try(method, msg, labels=None, templates=None, try_task_config=None,
-                push=True, closed_tree=False):
+                push=True, closed_tree=False, files_to_change=None):
     check_working_directory(push)
 
     # Format the commit message
     closed_tree_string = " ON A CLOSED TREE" if closed_tree else ""
     commit_message = ('%s%s\n\nPushed via `mach try %s`' %
                       (msg, closed_tree_string, method))
 
     if labels or labels == []:
-        try_task_config = {'tasks': sorted(labels)}
+        try_task_config = {
+            'version': 1,
+            'tasks': sorted(labels),
+        }
         if templates:
             try_task_config['templates'] = templates
         if push:
             write_task_config_history(msg, try_task_config)
 
-    config = None
+    config_path = None
+    changed_files = []
     if try_task_config:
-        config = write_task_config(try_task_config)
+        config_path = write_task_config(try_task_config)
+        changed_files.append(config_path)
+
+    if files_to_change:
+        for path, content in files_to_change.items():
+            path = os.path.join(vcs.path, path)
+            with open(path, 'w') as fh:
+                fh.write(content)
+            changed_files.append(path)
 
     try:
         if not push:
             print("Commit message:")
             print(commit_message)
-            if config:
+            if config_path:
                 print("Calculated try_task_config.json:")
-                with open(config) as fh:
+                with open(config_path) as fh:
                     print(fh.read())
             return
 
-        if config:
-            vcs.add_remove_files(config)
+        for path in changed_files:
+            vcs.add_remove_files(path)
 
         try:
             vcs.push_to_try(commit_message)
         except MissingVCSExtension as e:
             if e.ext == 'push-to-try':
                 print(HG_PUSH_TO_TRY_NOT_FOUND)
             elif e.ext == 'cinnabar':
                 print(GIT_CINNABAR_NOT_FOUND)
             else:
                 raise
             sys.exit(1)
     finally:
-        if config and os.path.isfile(config):
-            os.remove(config)
+        if config_path and os.path.isfile(config_path):
+            os.remove(config_path)
new file mode 100644
--- /dev/null
+++ b/tools/tryselect/selectors/release.py
@@ -0,0 +1,61 @@
+# 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 attr
+from mozilla_version.gecko import FirefoxVersion
+
+from ..cli import BaseTryParser
+from ..push import push_to_try
+
+
+class ReleaseParser(BaseTryParser):
+    name = 'release'
+    arguments = [
+        [['-v', '--version'],
+         {'metavar': 'STR',
+          'required': True,
+          'action': 'store',
+          'type': FirefoxVersion.parse,
+          'help': "The version number to use for the staging release.",
+          }],
+    ]
+    common_groups = ['push']
+
+
+def run_try_release(version, push=True, message='{msg}', **kwargs):
+
+    if version.is_beta:
+        app_version = attr.evolve(version, beta_number=None)
+    else:
+        app_version = version
+
+    files_to_change = {
+        'browser/config/version.txt': '{}\n'.format(app_version),
+        'browser/config/version_display.txt': '{}\n'.format(version),
+    }
+
+    release_type = version.version_type.name.lower()
+    if release_type not in ('beta', 'release', 'esr'):
+        raise Exception(
+            "Can't do staging release for version: {} type: {}".format(
+                version, version.version_type))
+    task_config = {
+        'version': 2,
+        'parameters': {
+            'target_tasks_method': 'staging_release_builds',
+            'optimize_target_tasks': True,
+            'include_nightly': True,
+            'release_type': release_type,
+        },
+    }
+
+    msg = 'staging release: {}'.format(version)
+    return push_to_try(
+        'release', message.format(msg=msg),
+        push=push, closed_tree=kwargs["closed_tree"],
+        try_task_config=task_config,
+        files_to_change=files_to_change,
+    )