gerrit: make --no-emails setting a common one

Many actions that update server settings have an option to disable
sending out notifications, so hoist that to the common parser.

Also plumb it through for a few more actions that were missing it.

This also fixes the existing --dry-run flag which was working for
subparsers, but not the main command.

BUG=None
TEST=CQ passes

Change-Id: I909f5ff68e6b588c2f663ea0046ad9484269ff0b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2705104
Tested-by: Mike Frysinger <vapier@chromium.org>
Auto-Submit: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Alex Klein <saklein@chromium.org>
Commit-Queue: Alex Klein <saklein@chromium.org>
diff --git a/scripts/gerrit.py b/scripts/gerrit.py
index 7cf8928..a32f0c2 100644
--- a/scripts/gerrit.py
+++ b/scripts/gerrit.py
@@ -473,9 +473,6 @@
   @classmethod
   def init_subparser(cls, parser):
     """Add arguments to this action's subparser."""
-    parser.add_argument('--ne', '--no-emails', dest='notify',
-                        default='ALL', action='store_const', const='NONE',
-                        help='Do not send e-mail notifications')
     parser.add_argument('-m', '--msg', '--message', metavar='MESSAGE',
                         help='Optional message to include')
     parser.add_argument('cls', nargs='+', metavar='CL',
@@ -551,7 +548,7 @@
   @staticmethod
   def _process_one(helper, cl, opts):
     """Use |helper| to process the single |cl|."""
-    helper.SubmitChange(cl, dryrun=opts.dryrun)
+    helper.SubmitChange(cl, dryrun=opts.dryrun, notify=opts.notify)
 
 
 class ActionAbandon(_ActionSimpleParallelCLs):
@@ -562,7 +559,7 @@
   @staticmethod
   def _process_one(helper, cl, opts):
     """Use |helper| to process the single |cl|."""
-    helper.AbandonChange(cl, dryrun=opts.dryrun)
+    helper.AbandonChange(cl, dryrun=opts.dryrun, notify=opts.notify)
 
 
 class ActionRestore(_ActionSimpleParallelCLs):
@@ -606,9 +603,6 @@
   @staticmethod
   def init_subparser(parser):
     """Add arguments to this action's subparser."""
-    parser.add_argument('--ne', '--no-emails', dest='notify',
-                        default='ALL', action='store_const', const='NONE',
-                        help='Do not send e-mail notifications')
     parser.add_argument('cl', metavar='CL',
                         help='The CL to update')
     parser.add_argument('reviewers', nargs='+',
@@ -824,7 +818,7 @@
       for arg in opts.cls:
         helper, cl = GetGerrit(opts, arg)
         ret = helper.CherryPick(cl, branch, rev=opts.rev, msg=opts.msg,
-                                dryrun=opts.dryrun)
+                                dryrun=opts.dryrun, notify=opts.notify)
         logging.debug('Response: %s', ret)
         if opts.raw:
           print(ret['_number'])
@@ -971,21 +965,38 @@
   site_params = config_lib.GetSiteParams()
   parser = commandline.ArgumentParser(
       description=description, default_log_level='notice')
-  parser.add_argument('-i', '--internal', dest='gob', action='store_const',
-                      default=site_params.EXTERNAL_GOB_INSTANCE,
-                      const=site_params.INTERNAL_GOB_INSTANCE,
-                      help='Query internal Chromium Gerrit instance')
-  parser.add_argument('-g', '--gob',
-                      default=site_params.EXTERNAL_GOB_INSTANCE,
-                      help=('Gerrit (on borg) instance to query (default: %s)' %
-                            (site_params.EXTERNAL_GOB_INSTANCE)))
+
+  group = parser.add_argument_group('Server options')
+  group.add_argument('-i', '--internal', dest='gob', action='store_const',
+                     default=site_params.EXTERNAL_GOB_INSTANCE,
+                     const=site_params.INTERNAL_GOB_INSTANCE,
+                     help='Query internal Chrome Gerrit instance')
+  group.add_argument('-g', '--gob',
+                     default=site_params.EXTERNAL_GOB_INSTANCE,
+                     help='Gerrit (on borg) instance to query (default: %s)' %
+                          (site_params.EXTERNAL_GOB_INSTANCE))
+
+  def _AddCommonOptions(p):
+    """Add options that should work before & after the subcommand.
+
+    Make it easy to do `gerrit --dry-run foo` and `gerrit foo --dry-run`.
+    """
+    parser.add_common_argument_to_group(
+        p, '--ne', '--no-emails', dest='notify',
+        default='ALL', action='store_const', const='NONE',
+        help='Do not send e-mail notifications')
+    parser.add_common_argument_to_group(
+        p, '-n', '--dry-run', dest='dryrun',
+        default=False, action='store_true',
+        help='Show what would be done, but do not make changes')
+
+  group = parser.add_argument_group('CL options')
+  _AddCommonOptions(group)
+
   parser.add_argument('--raw', default=False, action='store_true',
                       help='Return raw results (suitable for scripting)')
   parser.add_argument('--json', default=False, action='store_true',
                       help='Return results in JSON (suitable for scripting)')
-  parser.add_argument('-n', '--dry-run', default=False, action='store_true',
-                      dest='dryrun',
-                      help='Show what would be done, but do not make changes')
 
   # Subparsers are required by default under Python 2.  Python 3 changed to
   # not required, but didn't include a required option until 3.7.  Setting
@@ -996,9 +1007,7 @@
     # Format the full docstring by removing the file level indentation.
     description = re.sub(r'^  ', '', cls.__doc__, flags=re.M)
     subparser = subparsers.add_parser(cmd, description=description)
-    subparser.add_argument('-n', '--dry-run', dest='dryrun',
-                           default=False, action='store_true',
-                           help='Show what would be done only')
+    _AddCommonOptions(subparser)
     cls.init_subparser(subparser)
 
   return parser