Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: git_cl.py

Issue 1063103002: git cl try uses buildbucket via OAuth2 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: address more comments Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 # Copyright (C) 2008 Evan Martin <martine@danga.com> 6 # Copyright (C) 2008 Evan Martin <martine@danga.com>
7 7
8 """A git-command for integrating reviews on Rietveld.""" 8 """A git-command for integrating reviews on Rietveld."""
9 9
10 from distutils.version import LooseVersion 10 from distutils.version import LooseVersion
11 from multiprocessing.pool import ThreadPool 11 from multiprocessing.pool import ThreadPool
12 import base64 12 import base64
13 import glob 13 import glob
14 import httplib
14 import json 15 import json
15 import logging 16 import logging
16 import optparse 17 import optparse
17 import os 18 import os
18 import Queue 19 import Queue
19 import re 20 import re
20 import stat 21 import stat
21 import sys 22 import sys
22 import tempfile 23 import tempfile
23 import textwrap 24 import textwrap
25 import time
26 import traceback
24 import urllib2 27 import urllib2
25 import urlparse 28 import urlparse
26 import webbrowser 29 import webbrowser
27 import zlib 30 import zlib
28 31
29 try: 32 try:
30 import readline # pylint: disable=F0401,W0611 33 import readline # pylint: disable=F0401,W0611
31 except ImportError: 34 except ImportError:
32 pass 35 pass
33 36
34
35 from third_party import colorama 37 from third_party import colorama
38 from third_party import httplib2
36 from third_party import upload 39 from third_party import upload
37 import auth 40 import auth
38 import breakpad # pylint: disable=W0611 41 import breakpad # pylint: disable=W0611
39 import clang_format 42 import clang_format
40 import dart_format 43 import dart_format
41 import fix_encoding 44 import fix_encoding
42 import gclient_utils 45 import gclient_utils
43 import git_common 46 import git_common
44 import owners 47 import owners
45 import owners_finder 48 import owners_finder
46 import presubmit_support 49 import presubmit_support
47 import rietveld 50 import rietveld
48 import scm 51 import scm
49 import subcommand 52 import subcommand
50 import subprocess2 53 import subprocess2
51 import watchlists 54 import watchlists
52 55
53 __version__ = '1.0' 56 __version__ = '1.0'
54 57
55 DEFAULT_SERVER = 'https://codereview.appspot.com' 58 DEFAULT_SERVER = 'https://codereview.appspot.com'
56 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' 59 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s'
57 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' 60 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup'
58 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit' 61 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit'
59 CHANGE_ID = 'Change-Id:' 62 CHANGE_ID = 'Change-Id:'
60 REFS_THAT_ALIAS_TO_OTHER_REFS = { 63 REFS_THAT_ALIAS_TO_OTHER_REFS = {
61 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master', 64 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master',
62 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', 65 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master',
63 } 66 }
64 67
68 # Buildbucket-related constants
69 BUILDBUCKET_HOST = 'cr-buildbucket.appspot.com'
70
65 # Valid extensions for files we want to lint. 71 # Valid extensions for files we want to lint.
66 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" 72 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
67 DEFAULT_LINT_IGNORE_REGEX = r"$^" 73 DEFAULT_LINT_IGNORE_REGEX = r"$^"
68 74
69 # Shortcut since it quickly becomes redundant. 75 # Shortcut since it quickly becomes redundant.
70 Fore = colorama.Fore 76 Fore = colorama.Fore
71 77
72 # Initialized in main() 78 # Initialized in main()
73 settings = None 79 settings = None
74 80
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 else: 201 else:
196 git_set_branch_value('git-find-copies', int(options.find_copies)) 202 git_set_branch_value('git-find-copies', int(options.find_copies))
197 203
198 print('Using %d%% similarity for rename/copy detection. ' 204 print('Using %d%% similarity for rename/copy detection. '
199 'Override with --similarity.' % options.similarity) 205 'Override with --similarity.' % options.similarity)
200 206
201 return options, args 207 return options, args
202 parser.parse_args = Parse 208 parser.parse_args = Parse
203 209
204 210
211 def _prefix_master(master):
212 """Convert user-specified master name to full master name.
213
214 Buildbucket uses full master name(master.tryserver.chromium.linux) as bucket
215 name, while the developers always use shortened master name
216 (tryserver.chromium.linux) by stripping off the prefix 'master.'. This
217 function does the conversion for buildbucket migration.
218 """
219 prefix = 'master.'
220 if master.startswith(prefix):
221 return master
222 return '%s%s' % (prefix, master)
223
224
225 def trigger_try_jobs(
M-A Ruel 2015/05/12 17:48:58 This fits a single line, it's 75 cols.
sheyang 2015/05/12 18:20:51 Done.
226 auth_config, changelist, options, masters, category):
227 rietveld_url = settings.GetDefaultServerUrl()
228 rietveld_host = urlparse.urlparse(rietveld_url).hostname
229 authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config)
230 http = authenticator.authorize(httplib2.Http())
231 http.force_exception_to_status_code = True
232 issue_props = changelist.GetIssueProperties()
233 issue = changelist.GetIssue()
234 patchset = changelist.GetMostRecentPatchset()
235
236 buildbucket_put_url = (
237 'https://{hostname}/_ah/api/buildbucket/v1/builds/batch'.format(
238 hostname=BUILDBUCKET_HOST))
239 buildset = 'patch/rietveld/{hostname}/{issue}/{patch}'.format(
240 hostname=rietveld_host,
241 issue=issue,
242 patch=patchset)
243
244 batch_req_body = {'builds': []}
245 print_text = []
246 print_text.append('Tried jobs on:')
247 for master, builders_and_tests in sorted(masters.iteritems()):
248 print_text.append('Master: %s' % master)
249 bucket = _prefix_master(master)
250 for builder, tests in sorted(builders_and_tests.iteritems()):
251 print_text.append(' %s: %s' % (builder, tests))
252 parameters = {
253 'builder_name': builder,
254 'changes': [
255 {'author': {'email': issue_props['owner_email']}},
256 ],
257 'properties': {
258 'category': category,
259 'issue': issue,
260 'master': master,
261 'patch_project': issue_props['project'],
262 'patch_storage': 'rietveld',
263 'patchset': patchset,
264 'reason': options.name,
265 'revision': options.revision,
266 'rietveld': rietveld_url,
267 'testfilter': tests,
268 },
269 }
270 if options.clobber:
271 parameters['properties']['clobber'] = True
272 batch_req_body['builds'].append(
273 {
274 'bucket': bucket,
275 'parameters_json': json.dumps(parameters),
276 'tags': ['builder:%s' % builder,
277 'buildset:%s' % buildset,
278 'master:%s' % master,
279 'user_agent:git_cl_try']
280 }
281 )
282
283 for try_count in xrange(3):
284 response, content = http.request(
285 buildbucket_put_url,
286 'PUT',
287 body=json.dumps(batch_req_body),
288 headers={'Content-Type': 'application/json'},
289 )
290 content_json = None
291 try:
292 content_json = json.loads(content)
293 except ValueError:
294 pass
295
296 # Buildbucket could return an error even if status==200.
297 if content_json and content_json.get('error'):
298 msg = 'Error in response. Code: %d. Reason: %s. Message: %s.' % (
299 content_json['error'].get('code', ''),
300 content_json['error'].get('reason', ''),
301 content_json['error'].get('message', ''))
302 raise BuildbucketResponseException(msg)
303
304 if response.status == 200:
305 break
306 if response.status < 500 or try_count >= 2:
307 raise httplib2.HttpLib2Error(content)
M-A Ruel 2015/05/12 17:48:58 What about: - Endpoints returns HTTP 200 - Endpoin
sheyang 2015/05/12 18:20:51 In this case, it's a bug in Buildbucket. Adding ha
308
309 # status >= 500 means transient failures.
310 logging.debug('Transient errors when triggering tryjobs. Will retry.')
311 time.sleep(0.5 + 1.5*try_count)
312
313 print '\n'.join(print_text)
314
315
205 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): 316 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
206 """Return the corresponding git ref if |base_url| together with |glob_spec| 317 """Return the corresponding git ref if |base_url| together with |glob_spec|
207 matches the full |url|. 318 matches the full |url|.
208 319
209 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). 320 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below).
210 """ 321 """
211 fetch_suburl, as_ref = glob_spec.split(':') 322 fetch_suburl, as_ref = glob_spec.split(':')
212 if allow_wildcards: 323 if allow_wildcards:
213 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) 324 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl)
214 if glob_match: 325 if glob_match:
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 try: 373 try:
263 stdout = sys.stdout.fileno() 374 stdout = sys.stdout.fileno()
264 except AttributeError: 375 except AttributeError:
265 stdout = None 376 stdout = None
266 return subprocess2.call( 377 return subprocess2.call(
267 ['git', 378 ['git',
268 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, 379 'diff', '--no-ext-diff', '--stat'] + similarity_options + args,
269 stdout=stdout, env=env) 380 stdout=stdout, env=env)
270 381
271 382
383 class BuildbucketResponseException(Exception):
384 pass
385
386
272 class Settings(object): 387 class Settings(object):
273 def __init__(self): 388 def __init__(self):
274 self.default_server = None 389 self.default_server = None
275 self.cc = None 390 self.cc = None
276 self.root = None 391 self.root = None
277 self.is_git_svn = None 392 self.is_git_svn = None
278 self.svn_branch = None 393 self.svn_branch = None
279 self.tree_status_url = None 394 self.tree_status_url = None
280 self.viewvc_url = None 395 self.viewvc_url = None
281 self.updated = False 396 self.updated = False
(...skipping 2475 matching lines...) Expand 10 before | Expand all | Expand 10 after
2757 group.add_option( 2872 group.add_option(
2758 "-c", "--clobber", action="store_true", default=False, 2873 "-c", "--clobber", action="store_true", default=False,
2759 help="Force a clobber before building; e.g. don't do an " 2874 help="Force a clobber before building; e.g. don't do an "
2760 "incremental build") 2875 "incremental build")
2761 group.add_option( 2876 group.add_option(
2762 "--project", 2877 "--project",
2763 help="Override which project to use. Projects are defined " 2878 help="Override which project to use. Projects are defined "
2764 "server-side to define what default bot set to use") 2879 "server-side to define what default bot set to use")
2765 group.add_option( 2880 group.add_option(
2766 "-n", "--name", help="Try job name; default to current branch name") 2881 "-n", "--name", help="Try job name; default to current branch name")
2882 group.add_option(
2883 "--use-buildbucket", action="store_true", default=False,
M-A Ruel 2015/05/12 17:48:59 default=False is not needed, I don't know why peop
sheyang 2015/05/12 18:20:51 I think it makes the default value for a boolean f
2884 help="Use buildbucket to trigger try jobs.")
2767 parser.add_option_group(group) 2885 parser.add_option_group(group)
2768 auth.add_auth_options(parser) 2886 auth.add_auth_options(parser)
2769 options, args = parser.parse_args(args) 2887 options, args = parser.parse_args(args)
2770 auth_config = auth.extract_auth_config_from_options(options) 2888 auth_config = auth.extract_auth_config_from_options(options)
2771 2889
2772 if args: 2890 if args:
2773 parser.error('Unknown arguments: %s' % args) 2891 parser.error('Unknown arguments: %s' % args)
2774 2892
2775 cl = Changelist(auth_config=auth_config) 2893 cl = Changelist(auth_config=auth_config)
2776 if not cl.GetIssue(): 2894 if not cl.GetIssue():
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
2854 'Instead send your job to the parent.\n' 2972 'Instead send your job to the parent.\n'
2855 'Bot list: %s' % builders) 2973 'Bot list: %s' % builders)
2856 return 1 2974 return 1
2857 2975
2858 patchset = cl.GetMostRecentPatchset() 2976 patchset = cl.GetMostRecentPatchset()
2859 if patchset and patchset != cl.GetPatchset(): 2977 if patchset and patchset != cl.GetPatchset():
2860 print( 2978 print(
2861 '\nWARNING Mismatch between local config and server. Did a previous ' 2979 '\nWARNING Mismatch between local config and server. Did a previous '
2862 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' 2980 'upload fail?\ngit-cl try always uses latest patchset from rietveld. '
2863 'Continuing using\npatchset %s.\n' % patchset) 2981 'Continuing using\npatchset %s.\n' % patchset)
2864 try: 2982 if options.use_buildbucket:
2865 cl.RpcServer().trigger_distributed_try_jobs( 2983 try:
2866 cl.GetIssue(), patchset, options.name, options.clobber, 2984 trigger_try_jobs(
M-A Ruel 2015/05/12 17:48:58 This fits 80 cols, please do not wrap unless neede
sheyang 2015/05/12 18:20:51 Done.
2867 options.revision, masters) 2985 auth_config, cl, options, masters, 'git_cl_try')
2868 except urllib2.HTTPError, e: 2986 except BuildbucketResponseException as ex:
2869 if e.code == 404: 2987 print 'ERROR: %s' % ex
2870 print('404 from rietveld; '
2871 'did you mean to use "git try" instead of "git cl try"?')
2872 return 1 2988 return 1
2873 print('Tried jobs on:') 2989 except Exception as e:
2990 stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc())
2991 print 'ERROR: Exception when trying to trigger tryjobs: %s\n%s' % (
2992 e, stacktrace)
2993 return 1
2994 else:
2995 try:
2996 cl.RpcServer().trigger_distributed_try_jobs(
2997 cl.GetIssue(), patchset, options.name, options.clobber,
2998 options.revision, masters)
2999 except urllib2.HTTPError, e:
M-A Ruel 2015/05/12 17:48:58 as e
sheyang 2015/05/12 18:20:51 Done.
3000 if e.code == 404:
3001 print('404 from rietveld; '
3002 'did you mean to use "git try" instead of "git cl try"?')
3003 return 1
3004 print('Tried jobs on:')
2874 3005
2875 for (master, builders) in masters.iteritems(): 3006 for (master, builders) in sorted(masters.iteritems()):
2876 if master: 3007 if master:
2877 print 'Master: %s' % master 3008 print 'Master: %s' % master
2878 length = max(len(builder) for builder in builders) 3009 length = max(len(builder) for builder in builders)
2879 for builder in sorted(builders): 3010 for builder in sorted(builders):
2880 print ' %*s: %s' % (length, builder, ','.join(builders[builder])) 3011 print ' %*s: %s' % (length, builder, ','.join(builders[builder]))
2881 return 0 3012 return 0
2882 3013
2883 3014
2884 @subcommand.usage('[new upstream branch]') 3015 @subcommand.usage('[new upstream branch]')
2885 def CMDupstream(parser, args): 3016 def CMDupstream(parser, args):
2886 """Prints or sets the name of the upstream branch, if any.""" 3017 """Prints or sets the name of the upstream branch, if any."""
2887 _, args = parser.parse_args(args) 3018 _, args = parser.parse_args(args)
2888 if len(args) > 1: 3019 if len(args) > 1:
2889 parser.error('Unrecognized args: %s' % ' '.join(args)) 3020 parser.error('Unrecognized args: %s' % ' '.join(args))
2890 3021
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
3205 if __name__ == '__main__': 3336 if __name__ == '__main__':
3206 # These affect sys.stdout so do it outside of main() to simplify mocks in 3337 # These affect sys.stdout so do it outside of main() to simplify mocks in
3207 # unit testing. 3338 # unit testing.
3208 fix_encoding.fix_encoding() 3339 fix_encoding.fix_encoding()
3209 colorama.init() 3340 colorama.init()
3210 try: 3341 try:
3211 sys.exit(main(sys.argv[1:])) 3342 sys.exit(main(sys.argv[1:]))
3212 except KeyboardInterrupt: 3343 except KeyboardInterrupt:
3213 sys.stderr.write('interrupted\n') 3344 sys.stderr.write('interrupted\n')
3214 sys.exit(1) 3345 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698