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

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 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 prefix = 'master.'
213 if master.startswith(prefix):
214 return master
215 else:
M-A Ruel 2015/05/12 13:05:27 No need for else, just return
sheyang 2015/05/12 17:40:39 Done.
216 return '%s%s' % (prefix, master)
217
218
219 def trigger_try_jobs(
220 auth_config, changelist, options, masters, category):
221 rietveld_url = settings.GetDefaultServerUrl()
222 rietveld_host = urlparse.urlparse(rietveld_url).hostname
223 authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config)
224 http = authenticator.authorize(httplib2.Http())
225 http.force_exception_to_status_code = True
226 issue_props = changelist.GetIssueProperties()
227 issue = changelist.GetIssue()
228 patchset = changelist.GetMostRecentPatchset()
229
230 buildbucket_url_template = (
231 'https://{hostname}/_ah/api/buildbucket/v1/builds/batch')
232 buildset_template = 'patch/rietveld/{hostname}/{issue}/{patch}'
M-A Ruel 2015/05/12 13:05:28 I don't see the value to use named variable for th
sheyang 2015/05/12 17:40:39 Done.
233 buildbucket_put_url = buildbucket_url_template.format(
234 hostname=BUILDBUCKET_HOST)
235 buildset = buildset_template.format(
236 hostname=rietveld_host,
237 issue=issue,
238 patch=patchset)
239
240 batch_req_body = {'builds': []}
241 print_text = []
242 print_text.append('Tried jobs on:')
243 for master, builders_and_tests in sorted(masters.iteritems()):
244 print_text.append('Master: %s' % master)
245 bucket = _prefix_master(master)
246 for builder, tests in sorted(builders_and_tests.iteritems()):
247 print_text.append(' %s: %s' % (builder, tests))
248 parameters = {
249 'builder_name': builder,
M-A Ruel 2015/05/12 13:05:27 FYI, I always use +2 indentation for structures in
sheyang 2015/05/12 17:40:39 I thought +4 is for line break?
250 'changes':[
M-A Ruel 2015/05/12 13:05:27 'changes': [
sheyang 2015/05/12 17:40:38 Eagle eyes...Done.
251 {'author': {'email': issue_props['owner_email']}},
252 ],
253 'properties': {
254 'category': category,
255 'issue': issue,
256 'master': master,
257 'patch_project': issue_props['project'],
258 'patch_storage': 'rietveld',
259 'patchset': patchset,
260 'reason': options.name,
261 'revision': options.revision,
262 'rietveld': rietveld_url,
263 'testfilter': tests,
264 },
265 }
266 if options.clobber:
267 parameters['properties']['clobber'] = True
268 batch_req_body['builds'].append(
269 {
270 'bucket': bucket,
271 'parameters_json': json.dumps(parameters),
272 'tags': ['builder:%s' % builder,
273 'buildset:%s' % buildset,
274 'master:%s' % master,
275 'user_agent:git_cl_try']
276 }
277 )
278
279 wait = 1
M-A Ruel 2015/05/12 13:05:27 In general we do: for try_count in xrange(3): t
sheyang 2015/05/12 17:40:38 Done.
280 try_count = 3
281 while try_count > 0:
282 try_count -= 1
283 response, content = http.request(
284 buildbucket_put_url,
285 'PUT',
286 body=json.dumps(batch_req_body),
287 headers={'Content-Type': 'application/json'},
288 )
289 content_json = None
290 try:
291 content_json = json.loads(content)
292 except ValueError:
293 pass
294
295 # Buildbucket could return an error even if status==200.
296 if content_json and content_json.get('error'):
297 msg = 'Error in response. Code: %d. Reason: %s. Message: %s.' % (
298 content_json['error'].get('code', ''),
299 content_json['error'].get('reason', ''),
300 content_json['error'].get('message', ''))
301 raise BuildbucketResponseException(msg)
302
303 if response.status == 200:
304 break
305
306 if response.status < 500 or try_count <= 0:
307 raise httplib2.HttpLib2Error(content)
308
309 # status >= 500 means transient failures.
310 logging.debug('Transient errors when triggering tryjobs. '
311 'Will retry in %d seconds.', wait)
312 time.sleep(wait)
313 wait *= 2
314
315 print '\n'.join(print_text)
316
317
205 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): 318 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
206 """Return the corresponding git ref if |base_url| together with |glob_spec| 319 """Return the corresponding git ref if |base_url| together with |glob_spec|
207 matches the full |url|. 320 matches the full |url|.
208 321
209 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). 322 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below).
210 """ 323 """
211 fetch_suburl, as_ref = glob_spec.split(':') 324 fetch_suburl, as_ref = glob_spec.split(':')
212 if allow_wildcards: 325 if allow_wildcards:
213 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) 326 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl)
214 if glob_match: 327 if glob_match:
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 try: 375 try:
263 stdout = sys.stdout.fileno() 376 stdout = sys.stdout.fileno()
264 except AttributeError: 377 except AttributeError:
265 stdout = None 378 stdout = None
266 return subprocess2.call( 379 return subprocess2.call(
267 ['git', 380 ['git',
268 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, 381 'diff', '--no-ext-diff', '--stat'] + similarity_options + args,
269 stdout=stdout, env=env) 382 stdout=stdout, env=env)
270 383
271 384
385 class BuildbucketResponseException(Exception):
386 pass
387
388
272 class Settings(object): 389 class Settings(object):
273 def __init__(self): 390 def __init__(self):
274 self.default_server = None 391 self.default_server = None
275 self.cc = None 392 self.cc = None
276 self.root = None 393 self.root = None
277 self.is_git_svn = None 394 self.is_git_svn = None
278 self.svn_branch = None 395 self.svn_branch = None
279 self.tree_status_url = None 396 self.tree_status_url = None
280 self.viewvc_url = None 397 self.viewvc_url = None
281 self.updated = False 398 self.updated = False
(...skipping 2475 matching lines...) Expand 10 before | Expand all | Expand 10 after
2757 group.add_option( 2874 group.add_option(
2758 "-c", "--clobber", action="store_true", default=False, 2875 "-c", "--clobber", action="store_true", default=False,
2759 help="Force a clobber before building; e.g. don't do an " 2876 help="Force a clobber before building; e.g. don't do an "
2760 "incremental build") 2877 "incremental build")
2761 group.add_option( 2878 group.add_option(
2762 "--project", 2879 "--project",
2763 help="Override which project to use. Projects are defined " 2880 help="Override which project to use. Projects are defined "
2764 "server-side to define what default bot set to use") 2881 "server-side to define what default bot set to use")
2765 group.add_option( 2882 group.add_option(
2766 "-n", "--name", help="Try job name; default to current branch name") 2883 "-n", "--name", help="Try job name; default to current branch name")
2884 group.add_option(
2885 "--use-buildbucket", action="store_true", default=False,
2886 help="Use buildbucket to trigger try jobs.")
2767 parser.add_option_group(group) 2887 parser.add_option_group(group)
2768 auth.add_auth_options(parser) 2888 auth.add_auth_options(parser)
2769 options, args = parser.parse_args(args) 2889 options, args = parser.parse_args(args)
2770 auth_config = auth.extract_auth_config_from_options(options) 2890 auth_config = auth.extract_auth_config_from_options(options)
2771 2891
2772 if args: 2892 if args:
2773 parser.error('Unknown arguments: %s' % args) 2893 parser.error('Unknown arguments: %s' % args)
2774 2894
2775 cl = Changelist(auth_config=auth_config) 2895 cl = Changelist(auth_config=auth_config)
2776 if not cl.GetIssue(): 2896 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' 2974 'Instead send your job to the parent.\n'
2855 'Bot list: %s' % builders) 2975 'Bot list: %s' % builders)
2856 return 1 2976 return 1
2857 2977
2858 patchset = cl.GetMostRecentPatchset() 2978 patchset = cl.GetMostRecentPatchset()
2859 if patchset and patchset != cl.GetPatchset(): 2979 if patchset and patchset != cl.GetPatchset():
2860 print( 2980 print(
2861 '\nWARNING Mismatch between local config and server. Did a previous ' 2981 '\nWARNING Mismatch between local config and server. Did a previous '
2862 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' 2982 'upload fail?\ngit-cl try always uses latest patchset from rietveld. '
2863 'Continuing using\npatchset %s.\n' % patchset) 2983 'Continuing using\npatchset %s.\n' % patchset)
2864 try: 2984 if options.use_buildbucket:
2865 cl.RpcServer().trigger_distributed_try_jobs( 2985 try:
2866 cl.GetIssue(), patchset, options.name, options.clobber, 2986 trigger_try_jobs(
2867 options.revision, masters) 2987 auth_config, cl, options, masters, 'git_cl_try')
2868 except urllib2.HTTPError, e: 2988 except BuildbucketResponseException as ex:
2869 if e.code == 404: 2989 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 2990 return 1
2873 print('Tried jobs on:') 2991 except Exception as e:
2992 stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc())
2993 print 'ERROR: Exception when trying to trigger tryjobs: %s\n%s' % (
2994 e, stacktrace)
2995 return 1
2996 else:
2997 try:
2998 cl.RpcServer().trigger_distributed_try_jobs(
2999 cl.GetIssue(), patchset, options.name, options.clobber,
3000 options.revision, masters)
3001 except urllib2.HTTPError, e:
3002 if e.code == 404:
3003 print('404 from rietveld; '
3004 'did you mean to use "git try" instead of "git cl try"?')
3005 return 1
3006 print('Tried jobs on:')
2874 3007
2875 for (master, builders) in masters.iteritems(): 3008 for (master, builders) in masters.iteritems():
M-A Ruel 2015/05/12 13:05:28 sort here while at it, it always bothered me that
sheyang 2015/05/12 17:40:38 Done.
2876 if master: 3009 if master:
2877 print 'Master: %s' % master 3010 print 'Master: %s' % master
2878 length = max(len(builder) for builder in builders) 3011 length = max(len(builder) for builder in builders)
2879 for builder in sorted(builders): 3012 for builder in sorted(builders):
2880 print ' %*s: %s' % (length, builder, ','.join(builders[builder])) 3013 print ' %*s: %s' % (length, builder, ','.join(builders[builder]))
2881 return 0 3014 return 0
2882 3015
2883 3016
2884 @subcommand.usage('[new upstream branch]') 3017 @subcommand.usage('[new upstream branch]')
2885 def CMDupstream(parser, args): 3018 def CMDupstream(parser, args):
2886 """Prints or sets the name of the upstream branch, if any.""" 3019 """Prints or sets the name of the upstream branch, if any."""
2887 _, args = parser.parse_args(args) 3020 _, args = parser.parse_args(args)
2888 if len(args) > 1: 3021 if len(args) > 1:
2889 parser.error('Unrecognized args: %s' % ' '.join(args)) 3022 parser.error('Unrecognized args: %s' % ' '.join(args))
2890 3023
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
3205 if __name__ == '__main__': 3338 if __name__ == '__main__':
3206 # These affect sys.stdout so do it outside of main() to simplify mocks in 3339 # These affect sys.stdout so do it outside of main() to simplify mocks in
3207 # unit testing. 3340 # unit testing.
3208 fix_encoding.fix_encoding() 3341 fix_encoding.fix_encoding()
3209 colorama.init() 3342 colorama.init()
3210 try: 3343 try:
3211 sys.exit(main(sys.argv[1:])) 3344 sys.exit(main(sys.argv[1:]))
3212 except KeyboardInterrupt: 3345 except KeyboardInterrupt:
3213 sys.stderr.write('interrupted\n') 3346 sys.stderr.write('interrupted\n')
3214 sys.exit(1) 3347 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