| Index: git_cl.py
|
| diff --git a/git_cl.py b/git_cl.py
|
| index 039b35df17cdfb4c402730c3d233c057df27e4b0..441f5443281f9b829c6979eb5134e55f13d47a23 100755
|
| --- a/git_cl.py
|
| +++ b/git_cl.py
|
| @@ -11,6 +11,7 @@ from distutils.version import LooseVersion
|
| from multiprocessing.pool import ThreadPool
|
| import base64
|
| import glob
|
| +import httplib
|
| import json
|
| import logging
|
| import optparse
|
| @@ -21,6 +22,8 @@ import stat
|
| import sys
|
| import tempfile
|
| import textwrap
|
| +import threading
|
| +import time
|
| import urllib2
|
| import urlparse
|
| import webbrowser
|
| @@ -33,12 +36,15 @@ except ImportError:
|
|
|
|
|
| from third_party import colorama
|
| +from third_party import httplib2
|
| from third_party import upload
|
| +from third_party.google_api_python_client import apiclient
|
| import breakpad # pylint: disable=W0611
|
| import clang_format
|
| import dart_format
|
| import fix_encoding
|
| import gclient_utils
|
| +import git_cl_oauth2
|
| import git_common
|
| import owners
|
| import owners_finder
|
| @@ -61,6 +67,13 @@ REFS_THAT_ALIAS_TO_OTHER_REFS = {
|
| 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master',
|
| }
|
|
|
| +# Buildbucket-related constants
|
| +DISCOVERY_URL = (
|
| + 'https://cr-buildbucket.appspot.com/_ah/api/discovery/v1/apis/'
|
| + '{api}/{apiVersion}/rest')
|
| +DEFAULT_SCOPES = 'email'
|
| +BUILDSET_STR = 'patch/rietveld/{hostname}/{issue}/{patch}'
|
| +
|
| # Valid extensions for files we want to lint.
|
| DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
|
| DEFAULT_LINT_IGNORE_REGEX = r"$^"
|
| @@ -215,6 +228,95 @@ def is_dirty_git_tree(cmd):
|
| return False
|
|
|
|
|
| +def _prefix_master(master):
|
| + prefix = 'master.'
|
| + if master.startswith(prefix):
|
| + return master
|
| + else:
|
| + return '%s%s' % (prefix, master)
|
| +
|
| +
|
| +def _get_buildbucket(credentials):
|
| + http = httplib2.Http()
|
| + http = credentials.authorize(http)
|
| + return apiclient.discovery.build(
|
| + 'buildbucket', 'v1',
|
| + http=http,
|
| + discoveryServiceUrl=DISCOVERY_URL,
|
| + )
|
| +
|
| +
|
| +def trigger_distributed_try_jobs(
|
| + credentials, changelist, options, masters, category):
|
| + buildbucket = _get_buildbucket(credentials)
|
| + creds_props = json.loads(credentials.to_json())
|
| + requester = creds_props['id_token']['email']
|
| + issue_props = changelist.GetIssueProperties()
|
| + rietveld_host = urlparse.urlparse(changelist.GetRietveldServer()).hostname
|
| + issue = changelist.GetIssue()
|
| + patchset = changelist.GetMostRecentPatchset()
|
| + buildset = BUILDSET_STR.format(
|
| + hostname=rietveld_host,
|
| + issue=str(issue),
|
| + patch=str(patchset))
|
| + print 'Tried jobs on:'
|
| + for (master, builders_and_tests) in masters.iteritems():
|
| + print 'Master: %s' % master
|
| + bucket = _prefix_master(master)
|
| + for builder, tests in builders_and_tests.iteritems():
|
| + req = buildbucket.put(body={
|
| + 'bucket': bucket,
|
| + 'parameters_json': json.dumps({
|
| + 'builder_name': builder,
|
| + 'changes':[
|
| + {'author': {'email': issue_props['owner_email']}},
|
| + ],
|
| + 'properties': {
|
| + 'category': category,
|
| + 'clobber': options.clobber,
|
| + 'issue': issue,
|
| + 'master': master,
|
| + 'patch_project': issue_props['project'],
|
| + 'patch_storage': 'rietveld',
|
| + 'patchset': patchset,
|
| + 'reason': options.name,
|
| + 'requester': requester,
|
| + 'revision': options.revision,
|
| + 'rietveld': changelist.GetRietveldServer(),
|
| + 'testfilter': tests,
|
| + },
|
| + }),
|
| + 'tags': ['buildset:%s' % buildset,
|
| + 'master:%s' % master,
|
| + 'builder:%s' % builder,
|
| + 'requester:%s' % requester]
|
| + })
|
| + wait = 1
|
| + try_count = 3
|
| + while try_count > 0:
|
| + try:
|
| + try_count -= 1
|
| + response = req.execute()
|
| + if response.get('error'):
|
| + msg = 'Error in response. Reason: %s. Message: %s.' % (
|
| + response['error'].get('reason', ''),
|
| + response['error'].get('message', ''))
|
| + raise BuildbucketResponseException(msg)
|
| + break
|
| + except apiclient.errors.HttpError as ex:
|
| + status = ex.resp.status if ex.resp else None
|
| + if status >= 500:
|
| + logging.debug('Transient errors when triggering tryjobs. '
|
| + 'Will retry in %d seconds.', wait)
|
| + time.sleep(wait)
|
| + wait *= 2
|
| + if try_count <= 0:
|
| + raise
|
| + else:
|
| + raise
|
| + print ' %s: %s' % (builder, tests)
|
| +
|
| +
|
| def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
|
| """Return the corresponding git ref if |base_url| together with |glob_spec|
|
| matches the full |url|.
|
| @@ -282,6 +384,10 @@ def print_stats(similarity, find_copies, args):
|
| stdout=stdout, env=env)
|
|
|
|
|
| +class BuildbucketResponseException(Exception):
|
| + pass
|
| +
|
| +
|
| class Settings(object):
|
| def __init__(self):
|
| self.default_server = None
|
| @@ -2719,6 +2825,7 @@ def CMDtry(parser, args):
|
| group.add_option(
|
| "-n", "--name", help="Try job name; default to current branch name")
|
| parser.add_option_group(group)
|
| + git_cl_oauth2.add_oauth2_options(parser)
|
| options, args = parser.parse_args(args)
|
|
|
| if args:
|
| @@ -2814,22 +2921,26 @@ def CMDtry(parser, args):
|
| 'upload fail?\ngit-cl try always uses latest patchset from rietveld. '
|
| 'Continuing using\npatchset %s.\n' % patchset)
|
| try:
|
| - cl.RpcServer().trigger_distributed_try_jobs(
|
| - cl.GetIssue(), patchset, options.name, options.clobber,
|
| - options.revision, masters)
|
| - except urllib2.HTTPError, e:
|
| - if e.code == 404:
|
| - print('404 from rietveld; '
|
| - 'did you mean to use "git try" instead of "git cl try"?')
|
| + creds = git_cl_oauth2.get_oauth2_creds(
|
| + options,
|
| + urlparse.urlparse(cl.GetRietveldServer()).hostname)
|
| + if not creds:
|
| + print 'Failed to fetch credentials. Aborting...'
|
| return 1
|
| - print('Tried jobs on:')
|
| -
|
| - for (master, builders) in masters.iteritems():
|
| - if master:
|
| - print 'Master: %s' % master
|
| - length = max(len(builder) for builder in builders)
|
| - for builder in sorted(builders):
|
| - print ' %*s: %s' % (length, builder, ','.join(builders[builder]))
|
| + trigger_distributed_try_jobs(creds, cl, options, masters, 'git cl try')
|
| + except apiclient.errors.HttpError as ex:
|
| + status = ex.resp.status if ex.resp else None
|
| + if status == httplib.FORBIDDEN:
|
| + print 'ERROR: Access denied. Please verify you have tryjob access.'
|
| + else:
|
| + print 'ERROR: Tryjob request failed: %s.' % ex
|
| + return 1
|
| + except BuildbucketResponseException as ex:
|
| + print ex
|
| + return 1
|
| + except Exception as e:
|
| + print 'Unexpected error when trying to trigger tryjobs: %s.' % e
|
| + return 1
|
| return 0
|
|
|
|
|
|
|