OLD | NEW |
---|---|
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 |
24 import urllib2 | 25 import urllib2 |
25 import urlparse | 26 import urlparse |
26 import webbrowser | 27 import webbrowser |
27 import zlib | 28 import zlib |
28 | 29 |
29 try: | 30 try: |
30 import readline # pylint: disable=F0401,W0611 | 31 import readline # pylint: disable=F0401,W0611 |
31 except ImportError: | 32 except ImportError: |
32 pass | 33 pass |
33 | 34 |
34 | 35 |
35 from third_party import colorama | 36 from third_party import colorama |
37 from third_party import httplib2 | |
36 from third_party import upload | 38 from third_party import upload |
39 from third_party.google_api_python_client import apiclient | |
37 import breakpad # pylint: disable=W0611 | 40 import breakpad # pylint: disable=W0611 |
38 import clang_format | 41 import clang_format |
39 import dart_format | 42 import dart_format |
40 import fix_encoding | 43 import fix_encoding |
41 import gclient_utils | 44 import gclient_utils |
45 import git_cl_oauth2 | |
42 import git_common | 46 import git_common |
43 import owners | 47 import owners |
44 import owners_finder | 48 import owners_finder |
45 import presubmit_support | 49 import presubmit_support |
46 import rietveld | 50 import rietveld |
47 import scm | 51 import scm |
48 import subcommand | 52 import subcommand |
49 import subprocess2 | 53 import subprocess2 |
50 import watchlists | 54 import watchlists |
51 | 55 |
52 __version__ = '1.0' | 56 __version__ = '1.0' |
53 | 57 |
54 DEFAULT_SERVER = 'https://codereview.appspot.com' | 58 DEFAULT_SERVER = 'https://codereview.appspot.com' |
55 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' | 59 POSTUPSTREAM_HOOK_PATTERN = '.git/hooks/post-cl-%s' |
56 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' | 60 DESCRIPTION_BACKUP_FILE = '~/.git_cl_description_backup' |
57 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit' | 61 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit' |
58 CHANGE_ID = 'Change-Id:' | 62 CHANGE_ID = 'Change-Id:' |
59 REFS_THAT_ALIAS_TO_OTHER_REFS = { | 63 REFS_THAT_ALIAS_TO_OTHER_REFS = { |
60 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master', | 64 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master', |
61 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', | 65 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', |
62 } | 66 } |
63 | 67 |
68 # Buildbucket-related constants | |
69 DISCOVERY_URL = ( | |
70 'https://cr-buildbucket.appspot.com/_ah/api/discovery/v1/apis/' | |
71 '{api}/{apiVersion}/rest') | |
72 DEFAULT_SCOPES = 'email' | |
73 BUILDSET_STR = 'patch/rietveld/{hostname}/{issue}/{patch}' | |
74 | |
64 # Valid extensions for files we want to lint. | 75 # Valid extensions for files we want to lint. |
65 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" | 76 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" |
66 DEFAULT_LINT_IGNORE_REGEX = r"$^" | 77 DEFAULT_LINT_IGNORE_REGEX = r"$^" |
67 | 78 |
68 # Shortcut since it quickly becomes redundant. | 79 # Shortcut since it quickly becomes redundant. |
69 Fore = colorama.Fore | 80 Fore = colorama.Fore |
70 | 81 |
71 # Initialized in main() | 82 # Initialized in main() |
72 settings = None | 83 settings = None |
73 | 84 |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 if dirty: | 219 if dirty: |
209 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd | 220 print 'Cannot %s with a dirty tree. You must commit locally first.' % cmd |
210 print 'Uncommitted files: (git diff-index --name-status HEAD)' | 221 print 'Uncommitted files: (git diff-index --name-status HEAD)' |
211 print dirty[:4096] | 222 print dirty[:4096] |
212 if len(dirty) > 4096: | 223 if len(dirty) > 4096: |
213 print '... (run "git diff-index --name-status HEAD" to see full output).' | 224 print '... (run "git diff-index --name-status HEAD" to see full output).' |
214 return True | 225 return True |
215 return False | 226 return False |
216 | 227 |
217 | 228 |
229 def _prefix_master(master): | |
230 prefix = 'master.' | |
231 if master.startswith(prefix): | |
232 return master | |
233 else: | |
234 return '%s%s' % (prefix, master) | |
235 | |
236 | |
237 def _get_buildbucket(credentials): | |
238 http = httplib2.Http() | |
239 http = credentials.authorize(http) | |
240 return apiclient.discovery.build( | |
241 'buildbucket', 'v1', | |
242 http=http, | |
243 discoveryServiceUrl=DISCOVERY_URL, | |
244 ) | |
245 | |
246 | |
247 def trigger_distributed_try_jobs( | |
nodir
2015/03/05 03:58:55
what distributed means in this context?
sheyang
2015/03/06 00:29:10
Just keep the same name.
I guess the original me
| |
248 credentials, changelist, options, masters, category): | |
249 buildbucket = _get_buildbucket(credentials) | |
250 cred_props = json.loads(credentials.to_json()) | |
251 requester = cred_props['id_token']['email'] | |
252 issue_props = changelist.GetIssueProperties() | |
253 rietveld_host = urlparse.urlparse(changelist.GetRietveldServer()).hostname | |
254 issue = changelist.GetIssue() | |
255 patchset = changelist.GetMostRecentPatchset() | |
256 buildset = BUILDSET_STR.format( | |
257 hostname=rietveld_host, | |
258 issue=str(issue), | |
259 patch=str(patchset)) | |
260 print 'Tried jobs on:' | |
261 for (master, builders_and_tests) in masters.iteritems(): | |
262 print 'Master: %s' % master | |
263 bb_master = _prefix_master(master) | |
nodir
2015/03/05 03:58:55
call it bucket
sheyang
2015/03/06 00:29:10
Done.
| |
264 for builder, tests in builders_and_tests.iteritems(): | |
265 req = buildbucket.put(body={ | |
266 'bucket': bb_master, | |
267 'parameters_json': json.dumps({ | |
268 'builder_name': builder, | |
269 'changes':[ | |
270 {'author': {'email': issue_props['owner_email']}}, | |
271 ], | |
272 'properties': { | |
273 'category': category, | |
274 'clobber': options.clobber, | |
275 'issue': issue, | |
276 'master': master, | |
277 'patch_project': issue_props['project'], | |
278 'patch_storage': 'rietveld', | |
279 'patchset': patchset, | |
280 'reason': options.name, | |
281 'requester': requester, | |
282 'revision': options.revision, | |
283 'rietveld': changelist.GetRietveldServer(), | |
284 'testfilter': tests, | |
285 }, | |
286 }), | |
287 'tags': ['buildset:%s' % buildset, | |
288 'master:%s' % master, | |
289 'builder:%s' % builder, | |
290 'requester:%s' % requester] | |
291 }) | |
292 req.execute() | |
nodir
2015/03/05 03:58:55
You ignore the result that may contain an error. I
| |
293 print ' %s: %s' % (builder, tests) | |
294 | |
295 | |
218 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): | 296 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): |
219 """Return the corresponding git ref if |base_url| together with |glob_spec| | 297 """Return the corresponding git ref if |base_url| together with |glob_spec| |
220 matches the full |url|. | 298 matches the full |url|. |
221 | 299 |
222 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). | 300 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). |
223 """ | 301 """ |
224 fetch_suburl, as_ref = glob_spec.split(':') | 302 fetch_suburl, as_ref = glob_spec.split(':') |
225 if allow_wildcards: | 303 if allow_wildcards: |
226 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) | 304 glob_match = re.match('(.+/)?(\*|{[^/]*})(/.+)?', fetch_suburl) |
227 if glob_match: | 305 if glob_match: |
(...skipping 2468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2696 "-c", "--clobber", action="store_true", default=False, | 2774 "-c", "--clobber", action="store_true", default=False, |
2697 help="Force a clobber before building; e.g. don't do an " | 2775 help="Force a clobber before building; e.g. don't do an " |
2698 "incremental build") | 2776 "incremental build") |
2699 group.add_option( | 2777 group.add_option( |
2700 "--project", | 2778 "--project", |
2701 help="Override which project to use. Projects are defined " | 2779 help="Override which project to use. Projects are defined " |
2702 "server-side to define what default bot set to use") | 2780 "server-side to define what default bot set to use") |
2703 group.add_option( | 2781 group.add_option( |
2704 "-n", "--name", help="Try job name; default to current branch name") | 2782 "-n", "--name", help="Try job name; default to current branch name") |
2705 parser.add_option_group(group) | 2783 parser.add_option_group(group) |
2784 git_cl_oauth2.add_oauth2_options(parser) | |
2706 options, args = parser.parse_args(args) | 2785 options, args = parser.parse_args(args) |
2707 | 2786 |
2708 if args: | 2787 if args: |
2709 parser.error('Unknown arguments: %s' % args) | 2788 parser.error('Unknown arguments: %s' % args) |
2710 | 2789 |
2711 cl = Changelist() | 2790 cl = Changelist() |
2712 if not cl.GetIssue(): | 2791 if not cl.GetIssue(): |
2713 parser.error('Need to upload first') | 2792 parser.error('Need to upload first') |
2714 | 2793 |
2715 props = cl.GetIssueProperties() | 2794 props = cl.GetIssueProperties() |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2791 'Bot list: %s' % builders) | 2870 'Bot list: %s' % builders) |
2792 return 1 | 2871 return 1 |
2793 | 2872 |
2794 patchset = cl.GetMostRecentPatchset() | 2873 patchset = cl.GetMostRecentPatchset() |
2795 if patchset and patchset != cl.GetPatchset(): | 2874 if patchset and patchset != cl.GetPatchset(): |
2796 print( | 2875 print( |
2797 '\nWARNING Mismatch between local config and server. Did a previous ' | 2876 '\nWARNING Mismatch between local config and server. Did a previous ' |
2798 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' | 2877 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' |
2799 'Continuing using\npatchset %s.\n' % patchset) | 2878 'Continuing using\npatchset %s.\n' % patchset) |
2800 try: | 2879 try: |
2801 cl.RpcServer().trigger_distributed_try_jobs( | 2880 creds = git_cl_oauth2.get_oauth2_cred( |
2802 cl.GetIssue(), patchset, options.name, options.clobber, | 2881 options, |
2803 options.revision, masters) | 2882 urlparse.urlparse(cl.GetRietveldServer()).hostname) |
2804 except urllib2.HTTPError, e: | 2883 trigger_distributed_try_jobs(creds, cl, options, masters, 'git cl try') |
nodir
2015/03/05 03:58:55
Please do not commit this CL until we move/replica
sheyang
2015/03/06 00:29:10
I won't - see COMMIT=false in the description.
nodir
2015/03/10 22:39:39
Acknowledged.
| |
2805 if e.code == 404: | 2884 except apiclient.errors.HttpError as ex: |
2806 print('404 from rietveld; ' | 2885 status = ex.resp.status if ex.resp else None |
2807 'did you mean to use "git try" instead of "git cl try"?') | 2886 if status == httplib.FORBIDDEN: |
2808 return 1 | 2887 print 'ERROR: Access denied. Please verify you have tryjob access.' |
2809 print('Tried jobs on:') | 2888 else: |
2810 | 2889 print 'Tryjob request failed: %s.' % e |
nodir
2015/03/05 03:58:55
typo: ex
sheyang
2015/03/06 00:29:10
Done.
| |
2811 for (master, builders) in masters.iteritems(): | 2890 return 1 |
2812 if master: | 2891 except Exception as e: |
2813 print 'Master: %s' % master | 2892 print 'Unexcpected error when trying to trigger tryjobs: %s.' % e |
2814 length = max(len(builder) for builder in builders) | 2893 return 1 |
2815 for builder in sorted(builders): | |
2816 print ' %*s: %s' % (length, builder, ','.join(builders[builder])) | |
2817 return 0 | 2894 return 0 |
2818 | 2895 |
2819 | 2896 |
2820 @subcommand.usage('[new upstream branch]') | 2897 @subcommand.usage('[new upstream branch]') |
2821 def CMDupstream(parser, args): | 2898 def CMDupstream(parser, args): |
2822 """Prints or sets the name of the upstream branch, if any.""" | 2899 """Prints or sets the name of the upstream branch, if any.""" |
2823 _, args = parser.parse_args(args) | 2900 _, args = parser.parse_args(args) |
2824 if len(args) > 1: | 2901 if len(args) > 1: |
2825 parser.error('Unrecognized args: %s' % ' '.join(args)) | 2902 parser.error('Unrecognized args: %s' % ' '.join(args)) |
2826 | 2903 |
(...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3118 if __name__ == '__main__': | 3195 if __name__ == '__main__': |
3119 # These affect sys.stdout so do it outside of main() to simplify mocks in | 3196 # These affect sys.stdout so do it outside of main() to simplify mocks in |
3120 # unit testing. | 3197 # unit testing. |
3121 fix_encoding.fix_encoding() | 3198 fix_encoding.fix_encoding() |
3122 colorama.init() | 3199 colorama.init() |
3123 try: | 3200 try: |
3124 sys.exit(main(sys.argv[1:])) | 3201 sys.exit(main(sys.argv[1:])) |
3125 except KeyboardInterrupt: | 3202 except KeyboardInterrupt: |
3126 sys.stderr.write('interrupted\n') | 3203 sys.stderr.write('interrupted\n') |
3127 sys.exit(1) | 3204 sys.exit(1) |
OLD | NEW |