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

Side by Side Diff: apply_issue.py

Issue 1373363006: apply_issue: cleanup Rietveld patch download failure. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: nit Created 5 years, 2 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 """Applies an issue from Rietveld. 6 """Applies an issue from Rietveld.
7 """ 7 """
8
9 import getpass 8 import getpass
10 import json 9 import json
11 import logging 10 import logging
12 import optparse 11 import optparse
13 import os 12 import os
14 import subprocess 13 import subprocess
15 import sys 14 import sys
16 import urllib2 15 import urllib2
17 16
18 import breakpad # pylint: disable=W0611 17 import breakpad # pylint: disable=W0611
19 18
20 import annotated_gclient 19 import annotated_gclient
21 import auth 20 import auth
22 import checkout 21 import checkout
23 import fix_encoding 22 import fix_encoding
24 import gclient_utils 23 import gclient_utils
25 import rietveld 24 import rietveld
26 import scm 25 import scm
27 26
28 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 27 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
29 28
30 29
30 RETURN_CODE_ARG_PARSER = 2 # default in python.
31 RETURN_CODE_INFRA_FAILURE = 3 # considered as infra failure.
32 RETURN_CODE_OTHERWISE = 1 # any other failure, likely patch apply one.
pgervais 2015/10/05 16:13:51 Nit: RETURN_CODE_FAILURE_OTHER ? 'RETURN_CODE_OTH
tandrii(chromium) 2015/10/05 17:55:43 Done.
33
34
31 class Unbuffered(object): 35 class Unbuffered(object):
32 """Disable buffering on a file object.""" 36 """Disable buffering on a file object."""
33 def __init__(self, stream): 37 def __init__(self, stream):
34 self.stream = stream 38 self.stream = stream
35 39
36 def write(self, data): 40 def write(self, data):
37 self.stream.write(data) 41 self.stream.write(data)
38 self.stream.flush() 42 self.stream.flush()
39 43
40 def __getattr__(self, attr): 44 def __getattr__(self, attr):
41 return getattr(self.stream, attr) 45 return getattr(self.stream, attr)
42 46
43 47
44 def main(): 48 def _get_arg_parser():
45 # TODO(pgervais): This function is way too long. Split.
46 sys.stdout = Unbuffered(sys.stdout)
47 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) 49 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
48 parser.add_option( 50 parser.add_option(
49 '-v', '--verbose', action='count', default=0, 51 '-v', '--verbose', action='count', default=0,
50 help='Prints debugging infos') 52 help='Prints debugging infos')
51 parser.add_option( 53 parser.add_option(
52 '-e', '--email', 54 '-e', '--email',
53 help='Email address to access rietveld. If not specified, anonymous ' 55 help='Email address to access rietveld. If not specified, anonymous '
54 'access will be used.') 56 'access will be used.')
55 parser.add_option( 57 parser.add_option(
56 '-E', '--email-file', 58 '-E', '--email-file',
(...skipping 28 matching lines...) Expand all
85 'is detected.') 87 'is detected.')
86 parser.add_option('-b', '--base_ref', help='DEPRECATED do not use.') 88 parser.add_option('-b', '--base_ref', help='DEPRECATED do not use.')
87 parser.add_option('--whitelist', action='append', default=[], 89 parser.add_option('--whitelist', action='append', default=[],
88 help='Patch only specified file(s).') 90 help='Patch only specified file(s).')
89 parser.add_option('--blacklist', action='append', default=[], 91 parser.add_option('--blacklist', action='append', default=[],
90 help='Don\'t patch specified file(s).') 92 help='Don\'t patch specified file(s).')
91 parser.add_option('-d', '--ignore_deps', action='store_true', 93 parser.add_option('-d', '--ignore_deps', action='store_true',
92 help='Don\'t run gclient sync on DEPS changes.') 94 help='Don\'t run gclient sync on DEPS changes.')
93 95
94 auth.add_auth_options(parser) 96 auth.add_auth_options(parser)
97 return parser
98
99
100 def main():
101 # TODO(pgervais,tandrii): split this func, it's still too long.
102 sys.stdout = Unbuffered(sys.stdout)
103 parser = _get_arg_parser()
95 options, args = parser.parse_args() 104 options, args = parser.parse_args()
96 auth_config = auth.extract_auth_config_from_options(options) 105 auth_config = auth.extract_auth_config_from_options(options)
97 106
98 if options.whitelist and options.blacklist: 107 if options.whitelist and options.blacklist:
99 parser.error('Cannot specify both --whitelist and --blacklist') 108 parser.error('Cannot specify both --whitelist and --blacklist')
100 109
101 if options.email and options.email_file: 110 if options.email and options.email_file:
102 parser.error('-e and -E options are incompatible') 111 parser.error('-e and -E options are incompatible')
103 112
104 if (os.path.isfile(os.path.join(os.getcwd(), 'update.flag')) 113 if (os.path.isfile(os.path.join(os.getcwd(), 'update.flag'))
105 and not options.force): 114 and not options.force):
106 print 'update.flag file found: bot_update has run and checkout is already ' 115 print 'update.flag file found: bot_update has run and checkout is already '
107 print 'in a consistent state. No actions will be performed in this step.' 116 print 'in a consistent state. No actions will be performed in this step.'
108 return 0 117 return 0
118
109 logging.basicConfig( 119 logging.basicConfig(
110 format='%(levelname)5s %(module)11s(%(lineno)4d): %(message)s', 120 format='%(levelname)5s %(module)11s(%(lineno)4d): %(message)s',
111 level=[logging.WARNING, logging.INFO, logging.DEBUG][ 121 level=[logging.WARNING, logging.INFO, logging.DEBUG][
112 min(2, options.verbose)]) 122 min(2, options.verbose)])
113 if args: 123 if args:
114 parser.error('Extra argument(s) "%s" not understood' % ' '.join(args)) 124 parser.error('Extra argument(s) "%s" not understood' % ' '.join(args))
115 if not options.issue: 125 if not options.issue:
116 parser.error('Require --issue') 126 parser.error('Require --issue')
117 options.server = options.server.rstrip('/') 127 options.server = options.server.rstrip('/')
118 if not options.server: 128 if not options.server:
119 parser.error('Require a valid server') 129 parser.error('Require a valid server')
120 130
121 options.revision_mapping = json.loads(options.revision_mapping) 131 options.revision_mapping = json.loads(options.revision_mapping)
122 132
123 # read email if needed 133 # read email if needed
124 if options.email_file: 134 if options.email_file:
125 if not os.path.exists(options.email_file): 135 if not os.path.exists(options.email_file):
126 parser.error('file does not exist: %s' % options.email_file) 136 parser.error('file does not exist: %s' % options.email_file)
127 with open(options.email_file, 'rb') as f: 137 with open(options.email_file, 'rb') as f:
128 options.email = f.read().strip() 138 options.email = f.read().strip()
129 139
130 print('Connecting to %s' % options.server) 140 print('Connecting to %s' % options.server)
131 # Always try un-authenticated first, except for OAuth2 141 # Always try un-authenticated first, except for OAuth2
132 if options.private_key_file: 142 if options.private_key_file:
133 # OAuth2 authentication 143 # OAuth2 authentication
134 obj = rietveld.JwtOAuth2Rietveld(options.server, 144 rietveld_obj = rietveld.JwtOAuth2Rietveld(options.server,
135 options.email, 145 options.email,
136 options.private_key_file) 146 options.private_key_file)
137 properties = obj.get_issue_properties(options.issue, False) 147 try:
148 properties = rietveld_obj.get_issue_properties(options.issue, False)
149 except urllib2.URLError:
150 logging.exception('failed to fetch issue properties')
151 sys.exit(RETURN_CODE_INFRA_FAILURE)
138 else: 152 else:
139 # Passing None as auth_config disables authentication. 153 # Passing None as auth_config disables authentication.
140 obj = rietveld.Rietveld(options.server, None) 154 rietveld_obj = rietveld.Rietveld(options.server, None)
141 properties = None 155 properties = None
142 # Bad except clauses order (HTTPError is an ancestor class of 156 # Bad except clauses order (HTTPError is an ancestor class of
143 # ClientLoginError) 157 # ClientLoginError)
144 # pylint: disable=E0701 158 # pylint: disable=E0701
145 try: 159 try:
146 properties = obj.get_issue_properties(options.issue, False) 160 properties = rietveld_obj.get_issue_properties(options.issue, False)
147 except urllib2.HTTPError as e: 161 except urllib2.HTTPError as e:
148 if e.getcode() != 302: 162 if e.getcode() != 302:
149 raise 163 raise
150 if options.no_auth: 164 if options.no_auth:
151 exit('FAIL: Login detected -- is issue private?') 165 exit('FAIL: Login detected -- is issue private?')
152 # TODO(maruel): A few 'Invalid username or password.' are printed first, 166 # TODO(maruel): A few 'Invalid username or password.' are printed first,
153 # we should get rid of those. 167 # we should get rid of those.
168 except urllib2.URLError:
169 logging.exception('failed to fetch issue properties')
170 return RETURN_CODE_INFRA_FAILURE
154 except rietveld.upload.ClientLoginError as e: 171 except rietveld.upload.ClientLoginError as e:
155 # Fine, we'll do proper authentication. 172 # Fine, we'll do proper authentication.
156 pass 173 pass
157 if properties is None: 174 if properties is None:
158 obj = rietveld.Rietveld(options.server, auth_config, options.email) 175 rietveld_obj = rietveld.Rietveld(options.server, auth_config,
176 options.email)
159 try: 177 try:
160 properties = obj.get_issue_properties(options.issue, False) 178 properties = rietveld_obj.get_issue_properties(options.issue, False)
161 except rietveld.upload.ClientLoginError as e: 179 except rietveld.upload.ClientLoginError as e:
162 print('Accessing the issue requires proper credentials.') 180 print('Accessing the issue requires proper credentials.')
163 return 1 181 return RETURN_CODE_OTHERWISE
182 except urllib2.URLError:
183 logging.exception('failed to fetch issue properties')
184 return RETURN_CODE_INFRA_FAILURE
164 185
165 if not options.patchset: 186 if not options.patchset:
166 options.patchset = properties['patchsets'][-1] 187 options.patchset = properties['patchsets'][-1]
167 print('No patchset specified. Using patchset %d' % options.patchset) 188 print('No patchset specified. Using patchset %d' % options.patchset)
168 189
169 issues_patchsets_to_apply = [(options.issue, options.patchset)] 190 issues_patchsets_to_apply = [(options.issue, options.patchset)]
170 depends_on_info = obj.get_depends_on_patchset(options.issue, options.patchset) 191 try:
192 depends_on_info = rietveld_obj.get_depends_on_patchset(
193 options.issue, options.patchset)
194 except urllib2.URLError:
195 logging.exception('failed to fetch depends_on_patchset')
196 return RETURN_CODE_INFRA_FAILURE
197
171 while depends_on_info: 198 while depends_on_info:
172 depends_on_issue = int(depends_on_info['issue']) 199 depends_on_issue = int(depends_on_info['issue'])
173 depends_on_patchset = int(depends_on_info['patchset']) 200 depends_on_patchset = int(depends_on_info['patchset'])
174 try: 201 try:
175 depends_on_info = obj.get_depends_on_patchset(depends_on_issue, 202 depends_on_info = rietveld_obj.get_depends_on_patchset(depends_on_issue,
176 depends_on_patchset) 203 depends_on_patchset)
177 issues_patchsets_to_apply.insert(0, (depends_on_issue, 204 issues_patchsets_to_apply.insert(0, (depends_on_issue,
178 depends_on_patchset)) 205 depends_on_patchset))
179 except urllib2.HTTPError: 206 except urllib2.HTTPError:
180 print ('The patchset that was marked as a dependency no longer ' 207 print ('The patchset that was marked as a dependency no longer '
181 'exists: %s/%d/#ps%d' % ( 208 'exists: %s/%d/#ps%d' % (
182 options.server, depends_on_issue, depends_on_patchset)) 209 options.server, depends_on_issue, depends_on_patchset))
183 print 'Therefore it is likely that this patch will not apply cleanly.' 210 print 'Therefore it is likely that this patch will not apply cleanly.'
184 print 211 print
185 depends_on_info = None 212 depends_on_info = None
213 except urllib2.URLError:
214 logging.exception('failed to fetch dependency issue')
215 return RETURN_CODE_INFRA_FAILURE
186 216
187 num_issues_patchsets_to_apply = len(issues_patchsets_to_apply) 217 num_issues_patchsets_to_apply = len(issues_patchsets_to_apply)
188 if num_issues_patchsets_to_apply > 1: 218 if num_issues_patchsets_to_apply > 1:
189 print 219 print
190 print 'apply_issue.py found %d dependent CLs.' % ( 220 print 'apply_issue.py found %d dependent CLs.' % (
191 num_issues_patchsets_to_apply - 1) 221 num_issues_patchsets_to_apply - 1)
192 print 'They will be applied in the following order:' 222 print 'They will be applied in the following order:'
193 num = 1 223 num = 1
194 for issue_to_apply, patchset_to_apply in issues_patchsets_to_apply: 224 for issue_to_apply, patchset_to_apply in issues_patchsets_to_apply:
195 print ' #%d %s/%d/#ps%d' % ( 225 print ' #%d %s/%d/#ps%d' % (
196 num, options.server, issue_to_apply, patchset_to_apply) 226 num, options.server, issue_to_apply, patchset_to_apply)
197 num += 1 227 num += 1
198 print 228 print
199 229
200 for issue_to_apply, patchset_to_apply in issues_patchsets_to_apply: 230 for issue_to_apply, patchset_to_apply in issues_patchsets_to_apply:
201 issue_url = '%s/%d/#ps%d' % (options.server, issue_to_apply, 231 issue_url = '%s/%d/#ps%d' % (options.server, issue_to_apply,
202 patchset_to_apply) 232 patchset_to_apply)
203 print('Downloading patch from %s' % issue_url) 233 print('Downloading patch from %s' % issue_url)
204 try: 234 try:
205 patchset = obj.get_patch(issue_to_apply, patchset_to_apply) 235 patchset = rietveld_obj.get_patch(issue_to_apply, patchset_to_apply)
206 except urllib2.HTTPError: 236 except urllib2.HTTPError:
207 print( 237 print(
208 'Failed to fetch the patch for issue %d, patchset %d.\n' 238 'Failed to fetch the patch for issue %d, patchset %d.\n'
209 'Try visiting %s/%d') % ( 239 'Try visiting %s/%d') % (
210 issue_to_apply, patchset_to_apply, 240 issue_to_apply, patchset_to_apply,
211 options.server, issue_to_apply) 241 options.server, issue_to_apply)
212 # Special code for bot_update to indicate that cause is network or 242 # If we got this far, then this is likely missing patchset.
213 # Rietveld. Not 2, because 2 is returned on arg parsing failure. 243 # Thus, it's not infra failure.
214 return 3 244 return RETURN_CODE_OTHERWISE
245 except urllib2.URLError:
246 logging.exception(
247 'Failed to fetch the patch for issue %d, patchset %d',
248 issue_to_apply, patchset_to_apply)
249 return RETURN_CODE_INFRA_FAILURE
215 if options.whitelist: 250 if options.whitelist:
216 patchset.patches = [patch for patch in patchset.patches 251 patchset.patches = [patch for patch in patchset.patches
217 if patch.filename in options.whitelist] 252 if patch.filename in options.whitelist]
218 if options.blacklist: 253 if options.blacklist:
219 patchset.patches = [patch for patch in patchset.patches 254 patchset.patches = [patch for patch in patchset.patches
220 if patch.filename not in options.blacklist] 255 if patch.filename not in options.blacklist]
221 for patch in patchset.patches: 256 for patch in patchset.patches:
222 print(patch) 257 print(patch)
223 full_dir = os.path.abspath(options.root_dir) 258 full_dir = os.path.abspath(options.root_dir)
224 scm_type = scm.determine_scm(full_dir) 259 scm_type = scm.determine_scm(full_dir)
(...skipping 15 matching lines...) Expand all
240 # chromium_commands.py?view=markup 275 # chromium_commands.py?view=markup
241 open('.buildbot-patched', 'w').close() 276 open('.buildbot-patched', 'w').close()
242 277
243 print('\nApplying the patch from %s' % issue_url) 278 print('\nApplying the patch from %s' % issue_url)
244 try: 279 try:
245 scm_obj.apply_patch(patchset, verbose=True) 280 scm_obj.apply_patch(patchset, verbose=True)
246 except checkout.PatchApplicationFailed as e: 281 except checkout.PatchApplicationFailed as e:
247 print(str(e)) 282 print(str(e))
248 print('CWD=%s' % os.getcwd()) 283 print('CWD=%s' % os.getcwd())
249 print('Checkout path=%s' % scm_obj.project_path) 284 print('Checkout path=%s' % scm_obj.project_path)
250 return 1 285 return RETURN_CODE_OTHERWISE
251 286
252 if ('DEPS' in map(os.path.basename, patchset.filenames) 287 if ('DEPS' in map(os.path.basename, patchset.filenames)
253 and not options.ignore_deps): 288 and not options.ignore_deps):
254 gclient_root = gclient_utils.FindGclientRoot(full_dir) 289 gclient_root = gclient_utils.FindGclientRoot(full_dir)
255 if gclient_root and scm_type: 290 if gclient_root and scm_type:
256 print( 291 print(
257 'A DEPS file was updated inside a gclient checkout, running gclient ' 292 'A DEPS file was updated inside a gclient checkout, running gclient '
258 'sync.') 293 'sync.')
259 gclient_path = os.path.join(BASE_DIR, 'gclient') 294 gclient_path = os.path.join(BASE_DIR, 'gclient')
260 if sys.platform == 'win32': 295 if sys.platform == 'win32':
(...skipping 17 matching lines...) Expand all
278 annotated_gclient.emit_buildprops(revisions) 313 annotated_gclient.emit_buildprops(revisions)
279 314
280 return retcode 315 return retcode
281 return 0 316 return 0
282 317
283 318
284 if __name__ == "__main__": 319 if __name__ == "__main__":
285 fix_encoding.fix_encoding() 320 fix_encoding.fix_encoding()
286 try: 321 try:
287 sys.exit(main()) 322 sys.exit(main())
288 except urllib2.URLError:
289 # Weird flakiness of GAE, see http://crbug.com/537417
290 logging.exception('failed to fetch something from Rietveld')
291 sys.exit(3)
292 except KeyboardInterrupt: 323 except KeyboardInterrupt:
293 sys.stderr.write('interrupted\n') 324 sys.stderr.write('interrupted\n')
294 sys.exit(1) 325 sys.exit(RETURN_CODE_OTHERWISE)
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