Index: git_cl.py |
diff --git a/git_cl.py b/git_cl.py |
index d4486e5b10abd24e0667f0b6509695950029660d..8bbb303e9033a1a857f6682c4052a5d8f104cfca 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 time |
+import traceback |
import urllib2 |
import urlparse |
import webbrowser |
@@ -31,9 +34,15 @@ try: |
except ImportError: |
pass |
- |
from third_party import colorama |
+from third_party import httplib2 |
from third_party import upload |
+ |
+ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) |
+sys.path.insert( |
+ 0, os.path.join(ROOT_DIR, 'third_party', 'google_api_python_client')) |
+ |
+import apiclient |
import auth |
import breakpad # pylint: disable=W0611 |
import clang_format |
@@ -62,6 +71,12 @@ 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') |
nodir
2015/04/14 16:12:48
Rename to BUILDBUCKET_DISCOVERY_URL
sheyang
2015/04/14 17:29:57
Done.
|
+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"$^" |
@@ -202,6 +217,92 @@ def add_git_similarity(parser): |
parser.parse_args = Parse |
+def _prefix_master(master): |
+ prefix = 'master.' |
+ if master.startswith(prefix): |
+ return master |
+ else: |
+ return '%s%s' % (prefix, master) |
+ |
+ |
+def _get_buildbucket(rietveld_host, auth_config): |
+ authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config) |
+ http = httplib2.Http() |
+ return apiclient.discovery.build( |
+ 'buildbucket', 'v1', |
+ http=authenticator.authorize(http), |
+ discoveryServiceUrl=DISCOVERY_URL, |
+ ) |
+ |
+ |
+def trigger_distributed_try_jobs( |
+ auth_config, changelist, options, masters, category): |
+ rietveld_host = urlparse.urlparse(changelist.GetRietveldServer()).hostname |
nodir
2015/04/14 16:12:48
We will need to make sure that all branches on all
sheyang
2015/04/14 17:29:57
As a fallback, rietveld_server will be fetched fro
|
+ buildbucket = _get_buildbucket(rietveld_host, auth_config) |
+ issue_props = changelist.GetIssueProperties() |
+ issue = changelist.GetIssue() |
+ patchset = changelist.GetMostRecentPatchset() |
+ buildset = BUILDSET_STR.format( |
+ hostname=rietveld_host, |
+ issue=str(issue), |
+ patch=str(patchset)) |
nodir
2015/04/14 16:12:48
You don't need str calls here
sheyang
2015/04/14 17:29:57
Done.
|
+ print 'Tried jobs on:' |
nodir
2015/04/14 16:12:48
"Trying", as they were not tried yet
sheyang
2015/04/14 17:29:57
This is from the old code... Updated.
|
+ for (master, builders_and_tests) in masters.iteritems(): |
nodir
2015/04/14 16:12:48
Parens are unnecessary
sheyang
2015/04/14 17:29:57
Done.
|
+ print 'Master: %s' % master |
+ bucket = _prefix_master(master) |
+ for builder, tests in builders_and_tests.iteritems(): |
+ req = buildbucket.put(body={ |
nodir
2015/04/14 16:12:48
In https://codereview.chromium.org/1058893004/ esp
sheyang
2015/04/14 17:29:57
Acknowledged.
|
+ '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, |
+ 'revision': options.revision, |
+ 'rietveld': changelist.GetRietveldServer(), |
nodir
2015/04/14 16:12:48
why not rietveld_host ?
sheyang
2015/04/14 17:29:57
I think it needs 'https' prefix... adding rietveld
|
+ 'testfilter': tests, |
+ }, |
+ }), |
+ 'tags': ['buildset:%s' % buildset, |
+ 'master:%s' % master, |
+ 'builder:%s' % builder, |
+ 'user_agent:git-cl-try'] |
+ }) |
+ 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: |
nodir
2015/04/14 16:12:48
Inverse this condition so you can unindent the ">=
sheyang
2015/04/14 17:29:57
Done.
sheyang
2015/04/14 17:29:57
Done.
|
+ logging.debug('Transient errors when triggering tryjobs. ' |
+ 'Will retry in %d seconds.', wait) |
+ time.sleep(wait) |
+ wait *= 2 |
+ if try_count <= 0: |
+ raise |
nodir
2015/04/14 16:12:48
Move these two lines before logging about transien
sheyang
2015/04/14 17:29:57
Done.
sheyang
2015/04/14 17:29:57
Done.
|
+ 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|. |
@@ -269,6 +370,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 |
@@ -2860,22 +2965,23 @@ 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"?') |
- 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( |
+ auth_config, 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: |
+ stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc()) |
+ print 'ERROR: unexpected error when trying to trigger tryjobs: %s\n%s' % ( |
+ e, stacktrace) |
nodir
2015/04/14 16:12:48
doesn't logging.exception do the same with current
sheyang
2015/04/14 17:29:57
logging.exception only has traceback.format_exc().
|
+ return 1 |
return 0 |