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

Side by Side Diff: git_cl.py

Issue 963953003: OAuth2 support in depot_tools (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: address comments and add retry logic Created 5 years, 9 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 | git_cl_oauth2.py » ('j') | git_cl_oauth2.py » ('J')
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 import base64 11 import base64
12 import glob 12 import glob
13 import httplib
13 import json 14 import json
14 import logging 15 import logging
15 import optparse 16 import optparse
16 import os 17 import os
17 import Queue 18 import Queue
18 import re 19 import re
19 import stat 20 import stat
20 import sys 21 import sys
21 import tempfile 22 import tempfile
22 import textwrap 23 import textwrap
23 import threading 24 import threading
25 import time
24 import urllib2 26 import urllib2
25 import urlparse 27 import urlparse
26 import webbrowser 28 import webbrowser
27 import zlib 29 import zlib
28 30
29 try: 31 try:
30 import readline # pylint: disable=F0401,W0611 32 import readline # pylint: disable=F0401,W0611
31 except ImportError: 33 except ImportError:
32 pass 34 pass
33 35
34 36
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
40 from third_party.google_api_python_client import apiclient
37 import breakpad # pylint: disable=W0611 41 import breakpad # pylint: disable=W0611
38 import clang_format 42 import clang_format
39 import dart_format 43 import dart_format
40 import fix_encoding 44 import fix_encoding
41 import gclient_utils 45 import gclient_utils
46 import git_cl_oauth2
42 import git_common 47 import git_common
43 import owners 48 import owners
44 import owners_finder 49 import owners_finder
45 import presubmit_support 50 import presubmit_support
46 import rietveld 51 import rietveld
47 import scm 52 import scm
48 import subcommand 53 import subcommand
49 import subprocess2 54 import subprocess2
50 import watchlists 55 import watchlists
51 56
52 __version__ = '1.0' 57 __version__ = '1.0'
53 58
54 DEFAULT_SERVER = 'https://codereview.appspot.com' 59 DEFAULT_SERVER = 'https://codereview.appspot.com'
55 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' 60 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s'
56 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' 61 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup'
57 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit' 62 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit'
58 CHANGE_ID = 'Change-Id:' 63 CHANGE_ID = 'Change-Id:'
59 REFS_THAT_ALIAS_TO_OTHER_REFS = { 64 REFS_THAT_ALIAS_TO_OTHER_REFS = {
60 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master', 65 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master',
61 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', 66 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master',
62 } 67 }
63 68
69 # Buildbucket-related constants
70 DISCOVERY_URL = (
71 'https://cr-buildbucket.appspot.com/_ah/api/discovery/v1/apis/'
72 '{api}/{apiVersion}/rest')
73 DEFAULT_SCOPES = 'email'
74 BUILDSET_STR = 'patch/rietveld/{hostname}/{issue}/{patch}'
75
64 # Valid extensions for files we want to lint. 76 # Valid extensions for files we want to lint.
65 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" 77 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
66 DEFAULT_LINT_IGNORE_REGEX = r"$^" 78 DEFAULT_LINT_IGNORE_REGEX = r"$^"
67 79
68 # Shortcut since it quickly becomes redundant. 80 # Shortcut since it quickly becomes redundant.
69 Fore = colorama.Fore 81 Fore = colorama.Fore
70 82
71 # Initialized in main() 83 # Initialized in main()
72 settings = None 84 settings = None
73 85
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 if dirty: 220 if dirty:
209 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd 221 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd
210 print 'Uncommitted files: (git diff-index --name-status HEAD)' 222 print 'Uncommitted files: (git diff-index --name-status HEAD)'
211 print dirty[:4096] 223 print dirty[:4096]
212 if len(dirty) > 4096: 224 if len(dirty) > 4096:
213 print '... (run "git diff-index --name-status HEAD" to see full output).' 225 print '... (run "git diff-index --name-status HEAD" to see full output).'
214 return True 226 return True
215 return False 227 return False
216 228
217 229
230 def _prefix_master(master):
231 prefix = 'master.'
232 if master.startswith(prefix):
233 return master
234 else:
235 return '%s%s' % (prefix, master)
236
237
238 def _get_buildbucket(credentials):
239 http = httplib2.Http()
240 http = credentials.authorize(http)
241 return apiclient.discovery.build(
242 'buildbucket', 'v1',
243 http=http,
244 discoveryServiceUrl=DISCOVERY_URL,
245 )
246
247
248 def trigger_distributed_try_jobs(
249 credentials, changelist, options, masters, category):
250 buildbucket = _get_buildbucket(credentials)
251 creds_props = json.loads(credentials.to_json())
252 requester = creds_props['id_token']['email']
nodir 2015/03/10 22:39:40 I think you can get requester like this: credentia
sheyang 2015/03/13 20:58:27 No it doesn't work.
253 issue_props = changelist.GetIssueProperties()
254 rietveld_host = urlparse.urlparse(changelist.GetRietveldServer()).hostname
255 issue = changelist.GetIssue()
256 patchset = changelist.GetMostRecentPatchset()
257 buildset = BUILDSET_STR.format(
258 hostname=rietveld_host,
259 issue=str(issue),
260 patch=str(patchset))
261 print 'Tried jobs on:'
262 for (master, builders_and_tests) in masters.iteritems():
263 print 'Master: %s' % master
264 bucket = _prefix_master(master)
265 for builder, tests in builders_and_tests.iteritems():
266 req = buildbucket.put(body={
267 'bucket': bucket,
268 'parameters_json': json.dumps({
269 'builder_name': builder,
270 'changes':[
271 {'author': {'email': issue_props['owner_email']}},
272 ],
273 'properties': {
274 'category': category,
275 'clobber': options.clobber,
276 'issue': issue,
277 'master': master,
278 'patch_project': issue_props['project'],
279 'patch_storage': 'rietveld',
280 'patchset': patchset,
281 'reason': options.name,
282 'requester': requester,
283 'revision': options.revision,
284 'rietveld': changelist.GetRietveldServer(),
285 'testfilter': tests,
286 },
287 }),
288 'tags': ['buildset:%s' % buildset,
289 'master:%s' % master,
290 'builder:%s' % builder,
291 'requester:%s' % requester]
292 })
293 wait = 1
294 try_count = 3
295 while try_count > 0:
296 try:
297 response = req.execute()
nodir 2015/03/10 22:39:40 do try_count -=1 after this
sheyang 2015/03/13 20:58:27 You mean before this right? Otherwise we may never
nodir 2015/03/18 23:49:15 Right
298 if response.get('error'):
299 msg = 'Error in response. Reason: %s. Message: %s.' % (
300 response['error'].get('reason', ''),
301 response['error'].get('message', ''))
302 raise BuildbucketResponseException(msg)
303 try_count = 0
nodir 2015/03/10 22:39:40 If this intends to break the loop, please do `brea
sheyang 2015/03/13 20:58:27 Done.
304 except apiclient.errors.HttpError as ex:
305 status = ex.resp.status if ex.resp else None
306 if status >= 500:
307 logging.debug('Transient errors when triggering tryjobs. '
308 'Will retry in %d seconds.', wait)
309 time.sleep(wait)
310 wait *= 2
311 try_count -= 1
nodir 2015/03/10 22:39:40 not needed if try_count -=1 right after trying
sheyang 2015/03/13 20:58:27 Done.
312 if try_count <= 0:
313 raise
314 else:
315 raise
316 print ' %s: %s' % (builder, tests)
317
318
218 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): 319 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards):
219 """Return the corresponding git ref if |base_url| together with |glob_spec| 320 """Return the corresponding git ref if |base_url| together with |glob_spec|
220 matches the full |url|. 321 matches the full |url|.
221 322
222 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). 323 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below).
223 """ 324 """
224 fetch_suburl, as_ref = glob_spec.split(':') 325 fetch_suburl, as_ref = glob_spec.split(':')
225 if allow_wildcards: 326 if allow_wildcards:
226 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) 327 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl)
227 if glob_match: 328 if glob_match:
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 try: 376 try:
276 stdout = sys.stdout.fileno() 377 stdout = sys.stdout.fileno()
277 except AttributeError: 378 except AttributeError:
278 stdout = None 379 stdout = None
279 return subprocess2.call( 380 return subprocess2.call(
280 ['git', 381 ['git',
281 'diff', '--no-ext-diff', '--stat'] + similarity_options + args, 382 'diff', '--no-ext-diff', '--stat'] + similarity_options + args,
282 stdout=stdout, env=env) 383 stdout=stdout, env=env)
283 384
284 385
386 class BuildbucketResponseException(Exception):
387 pass
388
389
285 class Settings(object): 390 class Settings(object):
286 def __init__(self): 391 def __init__(self):
287 self.default_server = None 392 self.default_server = None
288 self.cc = None 393 self.cc = None
289 self.root = None 394 self.root = None
290 self.is_git_svn = None 395 self.is_git_svn = None
291 self.svn_branch = None 396 self.svn_branch = None
292 self.tree_status_url = None 397 self.tree_status_url = None
293 self.viewvc_url = None 398 self.viewvc_url = None
294 self.updated = False 399 self.updated = False
(...skipping 2401 matching lines...) Expand 10 before | Expand all | Expand 10 after
2696 "-c", "--clobber", action="store_true", default=False, 2801 "-c", "--clobber", action="store_true", default=False,
2697 help="Force a clobber before building; e.g. don't do an " 2802 help="Force a clobber before building; e.g. don't do an "
2698 "incremental build") 2803 "incremental build")
2699 group.add_option( 2804 group.add_option(
2700 "--project", 2805 "--project",
2701 help="Override which project to use. Projects are defined " 2806 help="Override which project to use. Projects are defined "
2702 "server-side to define what default bot set to use") 2807 "server-side to define what default bot set to use")
2703 group.add_option( 2808 group.add_option(
2704 "-n", "--name", help="Try job name; default to current branch name") 2809 "-n", "--name", help="Try job name; default to current branch name")
2705 parser.add_option_group(group) 2810 parser.add_option_group(group)
2811 git_cl_oauth2.add_oauth2_options(parser)
2706 options, args = parser.parse_args(args) 2812 options, args = parser.parse_args(args)
2707 2813
2708 if args: 2814 if args:
2709 parser.error('Unknown arguments: %s' % args) 2815 parser.error('Unknown arguments: %s' % args)
2710 2816
2711 cl = Changelist() 2817 cl = Changelist()
2712 if not cl.GetIssue(): 2818 if not cl.GetIssue():
2713 parser.error('Need to upload first') 2819 parser.error('Need to upload first')
2714 2820
2715 props = cl.GetIssueProperties() 2821 props = cl.GetIssueProperties()
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
2791 'Bot list: %s' % builders) 2897 'Bot list: %s' % builders)
2792 return 1 2898 return 1
2793 2899
2794 patchset = cl.GetMostRecentPatchset() 2900 patchset = cl.GetMostRecentPatchset()
2795 if patchset and patchset != cl.GetPatchset(): 2901 if patchset and patchset != cl.GetPatchset():
2796 print( 2902 print(
2797 '\nWARNING Mismatch between local config and server. Did a previous ' 2903 '\nWARNING Mismatch between local config and server. Did a previous '
2798 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' 2904 'upload fail?\ngit-cl try always uses latest patchset from rietveld. '
2799 'Continuing using\npatchset %s.\n' % patchset) 2905 'Continuing using\npatchset %s.\n' % patchset)
2800 try: 2906 try:
2801 cl.RpcServer().trigger_distributed_try_jobs( 2907 creds = git_cl_oauth2.get_oauth2_creds(
2802 cl.GetIssue(), patchset, options.name, options.clobber, 2908 options,
2803 options.revision, masters) 2909 urlparse.urlparse(cl.GetRietveldServer()).hostname)
2804 except urllib2.HTTPError, e: 2910 trigger_distributed_try_jobs(creds, cl, options, masters, 'git cl try')
2805 if e.code == 404: 2911 except apiclient.errors.HttpError as ex:
2806 print('404 from rietveld; ' 2912 status = ex.resp.status if ex.resp else None
2807 'did you mean to use "git try" instead of "git cl try"?') 2913 if status == httplib.FORBIDDEN:
2808 return 1 2914 print 'ERROR: Access denied. Please verify you have tryjob access.'
2809 print('Tried jobs on:') 2915 else:
2810 2916 print 'ERROR: Tryjob request failed: %s.' % ex
2811 for (master, builders) in masters.iteritems(): 2917 return 1
2812 if master: 2918 except BuildbucketResponseException as ex:
2813 print 'Master: %s' % master 2919 print ex
2814 length = max(len(builder) for builder in builders) 2920 return 1
2815 for builder in sorted(builders): 2921 except Exception as e:
2816 print ' %*s: %s' % (length, builder, ','.join(builders[builder])) 2922 print 'Unexcpected error when trying to trigger tryjobs: %s.' % e
2923 return 1
2817 return 0 2924 return 0
2818 2925
2819 2926
2820 @subcommand.usage('[new upstream branch]') 2927 @subcommand.usage('[new upstream branch]')
2821 def CMDupstream(parser, args): 2928 def CMDupstream(parser, args):
2822 """Prints or sets the name of the upstream branch, if any.""" 2929 """Prints or sets the name of the upstream branch, if any."""
2823 _, args = parser.parse_args(args) 2930 _, args = parser.parse_args(args)
2824 if len(args) > 1: 2931 if len(args) > 1:
2825 parser.error('Unrecognized args: %s' % ' '.join(args)) 2932 parser.error('Unrecognized args: %s' % ' '.join(args))
2826 2933
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after
3118 if __name__ == '__main__': 3225 if __name__ == '__main__':
3119 # These affect sys.stdout so do it outside of main() to simplify mocks in 3226 # These affect sys.stdout so do it outside of main() to simplify mocks in
3120 # unit testing. 3227 # unit testing.
3121 fix_encoding.fix_encoding() 3228 fix_encoding.fix_encoding()
3122 colorama.init() 3229 colorama.init()
3123 try: 3230 try:
3124 sys.exit(main(sys.argv[1:])) 3231 sys.exit(main(sys.argv[1:]))
3125 except KeyboardInterrupt: 3232 except KeyboardInterrupt:
3126 sys.stderr.write('interrupted\n') 3233 sys.stderr.write('interrupted\n')
3127 sys.exit(1) 3234 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | git_cl_oauth2.py » ('j') | git_cl_oauth2.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698