Allow setting arbitrary 'git cl try' properties with buildbucket.

This allows calling 'git cl try' with properties. Each
property is set with the option -p key=value, where value
will be treated as json if possible or as string.

Examples for -p and pitfalls:

key=string -> 'string'
key=1 -> 1
key=[1,2,3] -> [1,2,3]
key=["one","two","three"] -> '[one,two,three]'
'key=["one","two","three"]' -> ['one','two','three']

BUG=488235

Review URL: https://codereview.chromium.org/1339073002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@296683 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git_cl.py b/git_cl.py
index caa9b60..9fcc3b7 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -214,6 +214,16 @@
   parser.parse_args = Parse
 
 
+def _get_properties_from_options(options):
+  properties = dict(x.split('=', 1) for x in options.properties)
+  for key, val in properties.iteritems():
+    try:
+      properties[key] = json.loads(val)
+    except ValueError:
+      pass  # If a value couldn't be evaluated, treat it as a string.
+  return properties
+
+
 def _prefix_master(master):
   """Convert user-specified master name to full master name.
 
@@ -228,8 +238,7 @@
   return '%s%s' % (prefix, master)
 
 
-def trigger_try_jobs(auth_config, changelist, options, masters, category,
-                     override_properties=None):
+def trigger_try_jobs(auth_config, changelist, options, masters, category):
   rietveld_url = settings.GetDefaultServerUrl()
   rietveld_host = urlparse.urlparse(rietveld_url).hostname
   authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config)
@@ -238,6 +247,7 @@
   issue_props = changelist.GetIssueProperties()
   issue = changelist.GetIssue()
   patchset = changelist.GetMostRecentPatchset()
+  properties = _get_properties_from_options(options)
 
   buildbucket_put_url = (
       'https://{hostname}/_ah/api/buildbucket/v1/builds/batch'.format(
@@ -273,8 +283,8 @@
               'testfilter': tests,
           },
       }
-      if override_properties:
-        parameters['properties'].update(override_properties)
+      if properties:
+        parameters['properties'].update(properties)
       if options.clobber:
         parameters['properties']['clobber'] = True
       batch_req_body['builds'].append(
@@ -3073,6 +3083,11 @@
       help="Override which project to use. Projects are defined "
            "server-side to define what default bot set to use")
   group.add_option(
+      "-p", "--property", dest="properties", action="append", default=[],
+      help="Specify generic properties in the form -p key1=value1 -p "
+           "key2=value2 etc (buildbucket only). The value will be treated as "
+           "json if decodable, or as string otherwise.")
+  group.add_option(
       "-n", "--name", help="Try job name; default to current branch name")
   group.add_option(
       "--use-rietveld", action="store_true", default=False,
@@ -3085,6 +3100,14 @@
   options, args = parser.parse_args(args)
   auth_config = auth.extract_auth_config_from_options(options)
 
+  if options.use_rietveld and options.properties:
+    parser.error('Properties can only be specified with buildbucket')
+
+  # Make sure that all properties are prop=value pairs.
+  bad_params = [x for x in options.properties if '=' not in x]
+  if bad_params:
+    parser.error('Got properties with missing "=": %s' % bad_params)
+
   if args:
     parser.error('Unknown arguments: %s' % args)