Bug 1400469 - Add ability to specify commit message to |mach try|, r?armenzg draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Mon, 18 Sep 2017 12:43:03 -0400
changeset 667841 b9be4e586a5b0373242748ae460667ea26520ae6
parent 667637 0c49ebf4195ce51072b24aacf01eaaffdcff9be6
child 732534 be07660452478dd659a168ffbfcfacc99afc6b3a
push id80870
push userahalberstadt@mozilla.com
push dateWed, 20 Sep 2017 20:55:03 +0000
reviewersarmenzg
bugs1400469
milestone57.0a1
Bug 1400469 - Add ability to specify commit message to |mach try|, r?armenzg MozReview-Commit-ID: LWkAEDWn8NC
tools/tryselect/cli.py
tools/tryselect/selectors/fuzzy.py
tools/tryselect/selectors/syntax.py
tools/tryselect/test/cram.ini
tools/tryselect/test/setup.sh
tools/tryselect/test/test_fuzzy.t
tools/tryselect/test/test_message.t
tools/tryselect/test/test_preset.t
tools/tryselect/vcs.py
--- a/tools/tryselect/cli.py
+++ b/tools/tryselect/cli.py
@@ -1,22 +1,32 @@
 # 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 subprocess
+import tempfile
 from argparse import ArgumentParser
 
 from .templates import all_templates
 
 
 class BaseTryParser(ArgumentParser):
     name = 'try'
     common_arguments = [
+        [['-m', '--message'],
+         {'const': 'editor',
+          'default': '{msg}',
+          'nargs': '?',
+          'help': 'Use the specified commit message, or create it in your '
+                  '$EDITOR if blank. Defaults to computed message.',
+          }],
         [['--no-push'],
          {'dest': 'push',
           'action': 'store_false',
           'help': 'Do not push to try as a result of running this command (if '
                   'specified this command will only print calculated try '
                   'syntax and selection info).',
           }],
         [['--save'],
@@ -47,18 +57,31 @@ class BaseTryParser(ArgumentParser):
         for cli, kwargs in self.common_arguments:
             group.add_argument(*cli, **kwargs)
 
         group = self.add_argument_group("template arguments")
         self.templates = {t: all_templates[t]() for t in self.templates}
         for template in self.templates.values():
             template.add_arguments(group)
 
+    def validate(self, args):
+        if args.message == 'editor':
+            if 'EDITOR' not in os.environ:
+                self.error("must set the $EDITOR environment variable to use blank --message")
+
+            with tempfile.NamedTemporaryFile(mode='r') as fh:
+                subprocess.call([os.environ['EDITOR'], fh.name])
+                args.message = fh.read().strip()
+
+        if '{msg}' not in args.message:
+            args.message = '{}\n\n{}'.format(args.message, '{msg}')
+
     def parse_known_args(self, *args, **kwargs):
         args, remainder = ArgumentParser.parse_known_args(self, *args, **kwargs)
+        self.validate(args)
 
         if self.templates:
             args.templates = {}
             for name, cls in self.templates.iteritems():
                 context = cls.context(**vars(args))
                 if context is not None:
                     args.templates[name] = context
 
--- a/tools/tryselect/selectors/fuzzy.py
+++ b/tools/tryselect/selectors/fuzzy.py
@@ -193,17 +193,18 @@ def format_header():
     shortcuts = []
     for action, key in sorted(fzf_header_shortcuts.iteritems()):
         shortcuts.append('{t.white}{action}{t.normal}: {t.yellow}<{key}>{t.normal}'.format(
                          t=terminal, action=action, key=key))
     return FZF_HEADER.format(shortcuts=', '.join(shortcuts), t=terminal)
 
 
 def run_fuzzy_try(update=False, query=None, templates=None, full=False, parameters=None,
-                  save=False, preset=None, list_presets=False, push=True, **kwargs):
+                  save=False, preset=None, list_presets=False, push=True, message='{msg}',
+                  **kwargs):
     if list_presets:
         return pset.list_presets(section='fuzzy')
 
     fzf = fzf_bootstrap(update)
 
     if not fzf:
         print(FZF_NOT_FOUND)
         return
@@ -243,9 +244,9 @@ def run_fuzzy_try(update=False, query=No
         print("no tasks selected")
         return
 
     if save:
         pset.save('fuzzy', save, query)
 
     query = " with query: {}".format(query) if query else ""
     msg = "Fuzzy{}".format(query)
-    return vcs.push_to_try('fuzzy', msg, selected, templates, push=push)
+    return vcs.push_to_try('fuzzy', message.format(msg=msg), selected, templates, push=push)
--- a/tools/tryselect/selectors/syntax.py
+++ b/tools/tryselect/selectors/syntax.py
@@ -633,14 +633,14 @@ class AutoTry(object):
         if kwargs["verbose"] and paths_by_flavor:
             print('The following tests will be selected: ')
             for flavor, paths in paths_by_flavor.iteritems():
                 print("%s: %s" % (flavor, ",".join(paths)))
 
         if kwargs["verbose"]:
             print('The following try syntax was calculated:\n%s' % msg)
 
-        self.vcs.push_to_try('syntax', msg, push=kwargs['push'])
+        self.vcs.push_to_try('syntax', kwargs["message"].format(msg=msg), push=kwargs['push'])
 
         if kwargs["save"]:
             assert msg.startswith("try: ")
             msg = msg[len("try: "):]
             preset.save('try', kwargs["save"], msg)
--- a/tools/tryselect/test/cram.ini
+++ b/tools/tryselect/test/cram.ini
@@ -1,2 +1,3 @@
 [test_fuzzy.t]
+[test_message.t]
 [test_preset.t]
--- a/tools/tryselect/test/setup.sh
+++ b/tools/tryselect/test/setup.sh
@@ -1,11 +1,17 @@
 export topsrcdir=$TESTDIR/../../../
 export MOZBUILD_STATE_PATH=$TMP/mozbuild
 
+export MACHRC=$TMP/machrc
+cat > $MACHRC << EOF
+[try]
+default=syntax
+EOF
+
 cachedir=$MOZBUILD_STATE_PATH/cache/taskgraph
 mkdir -p $cachedir
 
 cat > $cachedir/target_task_set << EOF
 test/foo-opt
 test/foo-debug
 build-baz
 EOF
--- a/tools/tryselect/test/test_fuzzy.t
+++ b/tools/tryselect/test/test_fuzzy.t
@@ -1,73 +1,73 @@
   $ . $TESTDIR/setup.sh
   $ cd $topsrcdir
 
 Test fuzzy selector
 
   $ ./mach try fuzzy $testargs -q "'foo"
-  Calculated try selector:
+  Commit message:
+  Fuzzy with query: 'foo
+  
+  Pushed via `mach try fuzzy`
+  Calculated try_task_config.json:
   {
     "tasks":[
       "test/foo-debug",
       "test/foo-opt"
     ]
   }
   
-  Commit message:
-  Fuzzy with query: 'foo
-  
-  Pushed via `mach try fuzzy`
   $ ./mach try fuzzy $testargs -q "'bar"
   no tasks selected
   $ ./mach try fuzzy $testargs --full -q "'bar"
-  Calculated try selector:
+  Commit message:
+  Fuzzy with query: 'bar
+  
+  Pushed via `mach try fuzzy`
+  Calculated try_task_config.json:
   {
     "tasks":[
       "test/bar-debug",
       "test/bar-opt"
     ]
   }
   
-  Commit message:
-  Fuzzy with query: 'bar
-  
-  Pushed via `mach try fuzzy`
 
 Test templates
 
   $ ./mach try fuzzy --no-push --artifact -q "'foo"
-  Calculated try selector:
+  Commit message:
+  Fuzzy with query: 'foo
+  
+  Pushed via `mach try fuzzy`
+  Calculated try_task_config.json:
   {
     "templates":{
       "artifact":{
         "enabled":"1"
       }
     },
     "tasks":[
       "test/foo-debug",
       "test/foo-opt"
     ]
   }
   
+  $ ./mach try fuzzy $testargs --env FOO=1 --env BAR=baz -q "'foo"
   Commit message:
   Fuzzy with query: 'foo
   
   Pushed via `mach try fuzzy`
-  $ ./mach try fuzzy $testargs --env FOO=1 --env BAR=baz -q "'foo"
-  Calculated try selector:
+  Calculated try_task_config.json:
   {
     "templates":{
       "env":{
         "FOO":"1",
         "BAR":"baz"
       }
     },
     "tasks":[
       "test/foo-debug",
       "test/foo-opt"
     ]
   }
   
-  Commit message:
-  Fuzzy with query: 'foo
-  
-  Pushed via `mach try fuzzy`
new file mode 100644
--- /dev/null
+++ b/tools/tryselect/test/test_message.t
@@ -0,0 +1,55 @@
+  $ . $TESTDIR/setup.sh
+  $ cd $topsrcdir
+
+Test custom commit messages with fuzzy selector
+
+  $ ./mach try fuzzy $testargs -q foo --message "Foobar"
+  Commit message:
+  Foobar
+  
+  Fuzzy with query: foo
+  
+  Pushed via `mach try fuzzy`
+  Calculated try_task_config.json:
+  {
+    "tasks":[
+      "test/foo-debug",
+      "test/foo-opt"
+    ]
+  }
+  
+  $ ./mach try fuzzy $testargs -q foo -m "Foobar: {msg}"
+  Commit message:
+  Foobar: Fuzzy with query: foo
+  
+  Pushed via `mach try fuzzy`
+  Calculated try_task_config.json:
+  {
+    "tasks":[
+      "test/foo-debug",
+      "test/foo-opt"
+    ]
+  }
+  
+  $ unset EDITOR
+  $ ./mach try fuzzy $testargs -q foo -m > /dev/null 2>&1
+  [2]
+
+
+Test custom commit messages with syntax selector
+
+  $ ./mach try syntax $testargs -p linux -u mochitests --message "Foobar"
+  Commit message:
+  Foobar
+  
+  try: -b do -p linux -u mochitests
+  
+  Pushed via `mach try syntax`
+  $ ./mach try syntax $testargs -p linux -u mochitests -m "Foobar: {msg}"
+  Commit message:
+  Foobar: try: -b do -p linux -u mochitests
+  
+  Pushed via `mach try syntax`
+  $ unset EDITOR
+  $ ./mach try syntax $testargs -p linux -u mochitests -m > /dev/null 2>&1
+  [2]
--- a/tools/tryselect/test/test_preset.t
+++ b/tools/tryselect/test/test_preset.t
@@ -1,99 +1,87 @@
   $ . $TESTDIR/setup.sh
   $ cd $topsrcdir
 
 Test preset with no subcommand
 
   $ ./mach try $testargs --save foo -b do -p linux -u mochitests -t none --tag foo
-  Calculated try selector:
-  try: -b do -p linux -u mochitests -t none --tag foo
   Commit message:
   try: -b do -p linux -u mochitests -t none --tag foo
   
   Pushed via `mach try syntax`
   preset saved, run with: --preset=foo
   $ ./mach try $testargs --preset foo
-  Calculated try selector:
-  try: -b do -p linux -u mochitests -t none --tag foo
   Commit message:
   try: -b do -p linux -u mochitests -t none --tag foo
   
   Pushed via `mach try syntax`
   $ ./mach try syntax $testargs --preset foo
-  Calculated try selector:
-  try: -b do -p linux -u mochitests -t none --tag foo
   Commit message:
   try: -b do -p linux -u mochitests -t none --tag foo
   
   Pushed via `mach try syntax`
   $ ./mach try $testargs --list-presets
   foo: -b do -p linux -u mochitests -t none --tag foo
 
 Test preset with syntax subcommand
 
   $ ./mach try syntax $testargs --save bar -b do -p win32 -u none -t all --tag bar
-  Calculated try selector:
-  try: -b do -p win32 -u none -t all --tag bar
   Commit message:
   try: -b do -p win32 -u none -t all --tag bar
   
   Pushed via `mach try syntax`
   preset saved, run with: --preset=bar
   $ ./mach try syntax $testargs --preset bar
-  Calculated try selector:
-  try: -b do -p win32 -u none -t all --tag bar
   Commit message:
   try: -b do -p win32 -u none -t all --tag bar
   
   Pushed via `mach try syntax`
   $ ./mach try $testargs --preset bar
-  Calculated try selector:
-  try: -b do -p win32 -u none -t all --tag bar
   Commit message:
   try: -b do -p win32 -u none -t all --tag bar
   
   Pushed via `mach try syntax`
   $ ./mach try syntax $testargs --list-presets
   foo: -b do -p linux -u mochitests -t none --tag foo
   bar: -b do -p win32 -u none -t all --tag bar
 
 Test preset with fuzzy subcommand
 
   $ ./mach try fuzzy $testargs --save baz -q "'baz"
   preset saved, run with: --preset=baz
-  Calculated try selector:
-  {
-    "tasks":[
-      "build-baz"
-    ]
-  }
-  
   Commit message:
   Fuzzy with query: 'baz
   
   Pushed via `mach try fuzzy`
-  $ ./mach try fuzzy $testargs --preset baz
-  Calculated try selector:
+  Calculated try_task_config.json:
   {
     "tasks":[
       "build-baz"
     ]
   }
   
+  $ ./mach try fuzzy $testargs --preset baz
   Commit message:
   Fuzzy with query: 'baz
   
   Pushed via `mach try fuzzy`
-  $ ./mach try $testargs --preset baz
-  Calculated try selector:
+  Calculated try_task_config.json:
   {
     "tasks":[
       "build-baz"
     ]
   }
   
+  $ ./mach try $testargs --preset baz
   Commit message:
   Fuzzy with query: 'baz
   
   Pushed via `mach try fuzzy`
+  Calculated try_task_config.json:
+  {
+    "tasks":[
+      "build-baz"
+    ]
+  }
+  
   $ ./mach try fuzzy $testargs --list-presets
   baz: 'baz
--- a/tools/tryselect/vcs.py
+++ b/tools/tryselect/vcs.py
@@ -103,25 +103,22 @@ class VCSHelper(object):
         self.check_working_directory(push)
 
         config = None
         if labels:
             config = self.write_task_config(labels, templates)
 
         try:
             if not push:
-                print("Calculated try selector:")
+                print("Commit message:")
+                print(commit_message)
                 if config:
+                    print("Calculated try_task_config.json:")
                     with open(config) as fh:
                         print(fh.read())
-                else:
-                    print(msg)
-
-                print('Commit message:')
-                print(commit_message)
                 return
 
             self._push_to_try(commit_message, config)
         finally:
             if config and os.path.isfile(config):
                 os.remove(config)
 
     @abstractmethod