OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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 """\ | 6 """\ |
7 Wrapper script around Rietveld's upload.py that simplifies working with groups | 7 Wrapper script around Rietveld's upload.py that simplifies working with groups |
8 of files. | 8 of files. |
9 """ | 9 """ |
10 | 10 |
11 import getpass | |
12 import optparse | 11 import optparse |
13 import os | 12 import os |
14 import random | 13 import random |
15 import re | 14 import re |
16 import string | 15 import string |
17 import subprocess | 16 import subprocess |
18 import sys | 17 import sys |
19 import tempfile | 18 import tempfile |
20 import time | 19 import time |
21 from third_party import upload | 20 from third_party import upload |
(...skipping 10 matching lines...) Expand all Loading... |
32 import simplejson as json # pylint: disable=F0401 | 31 import simplejson as json # pylint: disable=F0401 |
33 | 32 |
34 import breakpad # pylint: disable=W0611 | 33 import breakpad # pylint: disable=W0611 |
35 | 34 |
36 # gcl now depends on gclient. | 35 # gcl now depends on gclient. |
37 from scm import SVN | 36 from scm import SVN |
38 | 37 |
39 import fix_encoding | 38 import fix_encoding |
40 import gclient_utils | 39 import gclient_utils |
41 import presubmit_support | 40 import presubmit_support |
| 41 import rietveld |
42 import subprocess2 | 42 import subprocess2 |
43 | 43 |
44 __version__ = '1.2' | 44 __version__ = '1.2.1' |
45 | 45 |
46 | 46 |
47 CODEREVIEW_SETTINGS = { | 47 CODEREVIEW_SETTINGS = { |
48 # To make gcl send reviews to a server, check in a file named | 48 # To make gcl send reviews to a server, check in a file named |
49 # "codereview.settings" (see |CODEREVIEW_SETTINGS_FILE| below) to your | 49 # "codereview.settings" (see |CODEREVIEW_SETTINGS_FILE| below) to your |
50 # project's base directory and add the following line to codereview.settings: | 50 # project's base directory and add the following line to codereview.settings: |
51 # CODE_REVIEW_SERVER: codereview.yourserver.org | 51 # CODE_REVIEW_SERVER: codereview.yourserver.org |
52 } | 52 } |
53 | 53 |
54 # globals that store the root of the current repository and the directory where | 54 # globals that store the root of the current repository and the directory where |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
276 description: the description. | 276 description: the description. |
277 files: a list of 2 tuple containing (status, filename) of changed files, | 277 files: a list of 2 tuple containing (status, filename) of changed files, |
278 with paths being relative to the top repository directory. | 278 with paths being relative to the top repository directory. |
279 local_root: Local root directory | 279 local_root: Local root directory |
280 rietveld: rietveld server for this change | 280 rietveld: rietveld server for this change |
281 """ | 281 """ |
282 # Kept for unit test support. This is for the old format, it's deprecated. | 282 # Kept for unit test support. This is for the old format, it's deprecated. |
283 _SEPARATOR = "\n-----\n" | 283 _SEPARATOR = "\n-----\n" |
284 | 284 |
285 def __init__(self, name, issue, patchset, description, files, local_root, | 285 def __init__(self, name, issue, patchset, description, files, local_root, |
286 rietveld, needs_upload=False): | 286 rietveld_url, needs_upload=False): |
287 self.name = name | 287 self.name = name |
288 self.issue = int(issue) | 288 self.issue = int(issue) |
289 self.patchset = int(patchset) | 289 self.patchset = int(patchset) |
290 self._description = None | 290 self._description = None |
291 self._subject = None | 291 self._subject = None |
292 self._reviewers = None | 292 self._reviewers = None |
293 self._set_description(description) | 293 self._set_description(description) |
294 if files is None: | 294 if files is None: |
295 files = [] | 295 files = [] |
296 self._files = files | 296 self._files = files |
297 self.patch = None | 297 self.patch = None |
298 self._local_root = local_root | 298 self._local_root = local_root |
299 self.needs_upload = needs_upload | 299 self.needs_upload = needs_upload |
300 self.rietveld = rietveld | 300 self.rietveld = rietveld_url |
301 if not self.rietveld: | 301 if not self.rietveld: |
302 # Set the default value. | 302 # Set the default value. |
303 self.rietveld = GetCodeReviewSetting('CODE_REVIEW_SERVER') | 303 self.rietveld = GetCodeReviewSetting('CODE_REVIEW_SERVER') |
| 304 self._rpc_server = None |
304 | 305 |
305 def _get_description(self): | 306 def _get_description(self): |
306 return self._description | 307 return self._description |
307 | 308 |
308 def _set_description(self, description): | 309 def _set_description(self, description): |
309 # TODO(dpranke): Cloned from git_cl.py. These should be shared. | 310 # TODO(dpranke): Cloned from git_cl.py. These should be shared. |
310 if not description: | 311 if not description: |
311 self._description = description | 312 self._description = description |
312 return | 313 return |
313 | 314 |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 'files': self.GetFiles(), | 378 'files': self.GetFiles(), |
378 'description': self.description, | 379 'description': self.description, |
379 'rietveld': self.rietveld, | 380 'rietveld': self.rietveld, |
380 }, sort_keys=True, indent=2) | 381 }, sort_keys=True, indent=2) |
381 gclient_utils.FileWrite(GetChangelistInfoFile(self.name), data) | 382 gclient_utils.FileWrite(GetChangelistInfoFile(self.name), data) |
382 | 383 |
383 def Delete(self): | 384 def Delete(self): |
384 """Removes the changelist information from disk.""" | 385 """Removes the changelist information from disk.""" |
385 os.remove(GetChangelistInfoFile(self.name)) | 386 os.remove(GetChangelistInfoFile(self.name)) |
386 | 387 |
| 388 def RpcServer(self): |
| 389 if not self._rpc_server: |
| 390 if not self.rietveld: |
| 391 ErrorExit(CODEREVIEW_SETTINGS_FILE_NOT_FOUND) |
| 392 self._rpc_server = rietveld.Rietveld(self.rietveld, None, None) |
| 393 return self._rpc_server |
| 394 |
387 def CloseIssue(self): | 395 def CloseIssue(self): |
388 """Closes the Rietveld issue for this changelist.""" | 396 """Closes the Rietveld issue for this changelist.""" |
389 # Newer versions of Rietveld require us to pass an XSRF token to POST, so | 397 # Newer versions of Rietveld require us to pass an XSRF token to POST, so |
390 # we fetch it from the server. | 398 # we fetch it from the server. |
391 xsrf_token = self.SendToRietveld( | 399 xsrf_token = self.SendToRietveld( |
392 '/xsrf_token', | 400 '/xsrf_token', |
393 extra_headers={'X-Requesting-XSRF-Token': '1'}) | 401 extra_headers={'X-Requesting-XSRF-Token': '1'}) |
394 | 402 |
395 # You cannot close an issue with a GET. | 403 # You cannot close an issue with a GET. |
396 # We pass an empty string for the data so it is a POST rather than a GET. | 404 # We pass an empty string for the data so it is a POST rather than a GET. |
(...skipping 16 matching lines...) Expand all Loading... |
413 | 421 |
414 def PrimeLint(self): | 422 def PrimeLint(self): |
415 """Do background work on Rietveld to lint the file so that the results are | 423 """Do background work on Rietveld to lint the file so that the results are |
416 ready when the issue is viewed.""" | 424 ready when the issue is viewed.""" |
417 if self.issue and self.patchset: | 425 if self.issue and self.patchset: |
418 self.SendToRietveld('/lint/issue%s_%s' % (self.issue, self.patchset), | 426 self.SendToRietveld('/lint/issue%s_%s' % (self.issue, self.patchset), |
419 timeout=1) | 427 timeout=1) |
420 | 428 |
421 def SendToRietveld(self, request_path, timeout=None, **kwargs): | 429 def SendToRietveld(self, request_path, timeout=None, **kwargs): |
422 """Send a POST/GET to Rietveld. Returns the response body.""" | 430 """Send a POST/GET to Rietveld. Returns the response body.""" |
423 if not self.rietveld: | |
424 ErrorExit(CODEREVIEW_SETTINGS_FILE_NOT_FOUND) | |
425 def GetUserCredentials(): | |
426 """Prompts the user for a username and password.""" | |
427 email = upload.GetEmail('Email (login for uploading to %s)' % | |
428 self.rietveld) | |
429 password = getpass.getpass('Password for %s: ' % email) | |
430 return email, password | |
431 rpc_server = upload.HttpRpcServer(self.rietveld, | |
432 GetUserCredentials, | |
433 save_cookies=True) | |
434 try: | 431 try: |
435 return rpc_server.Send(request_path, timeout=timeout, **kwargs) | 432 return self.RpcServer().Send(request_path, timeout=timeout, **kwargs) |
436 except urllib2.URLError: | 433 except urllib2.URLError: |
437 if timeout is None: | 434 if timeout is None: |
438 ErrorExit('Error accessing url %s' % request_path) | 435 ErrorExit('Error accessing url %s' % request_path) |
439 else: | 436 else: |
440 return None | 437 return None |
441 | 438 |
442 def MissingTests(self): | 439 def MissingTests(self): |
443 """Returns True if the change looks like it needs unit tests but has none. | 440 """Returns True if the change looks like it needs unit tests but has none. |
444 | 441 |
445 A change needs unit tests if it contains any new source files or methods. | 442 A change needs unit tests if it contains any new source files or methods. |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
529 changelist doesn't exist. | 526 changelist doesn't exist. |
530 update_status: if True, the svn status will be updated for all the files | 527 update_status: if True, the svn status will be updated for all the files |
531 and unchanged files will be removed. | 528 and unchanged files will be removed. |
532 | 529 |
533 Returns: a ChangeInfo object. | 530 Returns: a ChangeInfo object. |
534 """ | 531 """ |
535 info_file = GetChangelistInfoFile(changename) | 532 info_file = GetChangelistInfoFile(changename) |
536 if not os.path.exists(info_file): | 533 if not os.path.exists(info_file): |
537 if fail_on_not_found: | 534 if fail_on_not_found: |
538 ErrorExit("Changelist " + changename + " not found.") | 535 ErrorExit("Changelist " + changename + " not found.") |
539 return ChangeInfo(changename, 0, 0, '', None, local_root, rietveld=None, | 536 return ChangeInfo( |
540 needs_upload=False) | 537 changename, 0, 0, '', None, local_root, rietveld_url=None, |
| 538 needs_upload=False) |
541 content = gclient_utils.FileRead(info_file, 'r') | 539 content = gclient_utils.FileRead(info_file, 'r') |
542 save = False | 540 save = False |
543 try: | 541 try: |
544 values = ChangeInfo._LoadNewFormat(content) | 542 values = ChangeInfo._LoadNewFormat(content) |
545 except ValueError: | 543 except ValueError: |
546 try: | 544 try: |
547 values = ChangeInfo._LoadOldFormat(content) | 545 values = ChangeInfo._LoadOldFormat(content) |
548 save = True | 546 save = True |
549 except ValueError: | 547 except ValueError: |
550 ErrorExit( | 548 ErrorExit( |
(...skipping 664 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1215 | 1213 |
1216 def DoPresubmitChecks(change_info, committing, may_prompt): | 1214 def DoPresubmitChecks(change_info, committing, may_prompt): |
1217 """Imports presubmit, then calls presubmit.DoPresubmitChecks.""" | 1215 """Imports presubmit, then calls presubmit.DoPresubmitChecks.""" |
1218 root_presubmit = GetCachedFile('PRESUBMIT.py', use_root=True) | 1216 root_presubmit = GetCachedFile('PRESUBMIT.py', use_root=True) |
1219 change = presubmit_support.SvnChange(change_info.name, | 1217 change = presubmit_support.SvnChange(change_info.name, |
1220 change_info.description, | 1218 change_info.description, |
1221 change_info.GetLocalRoot(), | 1219 change_info.GetLocalRoot(), |
1222 change_info.GetFiles(), | 1220 change_info.GetFiles(), |
1223 change_info.issue, | 1221 change_info.issue, |
1224 change_info.patchset) | 1222 change_info.patchset) |
1225 output = presubmit_support.DoPresubmitChecks(change=change, | 1223 output = presubmit_support.DoPresubmitChecks( |
1226 committing=committing, | 1224 change=change, |
1227 verbose=False, | 1225 committing=committing, |
1228 output_stream=sys.stdout, | 1226 verbose=False, |
1229 input_stream=sys.stdin, | 1227 output_stream=sys.stdout, |
1230 default_presubmit=root_presubmit, | 1228 input_stream=sys.stdin, |
1231 may_prompt=may_prompt, | 1229 default_presubmit=root_presubmit, |
1232 tbr=False, | 1230 may_prompt=may_prompt, |
1233 host_url=change_info.rietveld) | 1231 tbr=False, |
| 1232 rietveld=change_info.RpcServer()) |
1234 if not output.should_continue() and may_prompt: | 1233 if not output.should_continue() and may_prompt: |
1235 # TODO(dpranke): move into DoPresubmitChecks(), unify cmd line args. | 1234 # TODO(dpranke): move into DoPresubmitChecks(), unify cmd line args. |
1236 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" | 1235 print "\nPresubmit errors, can't continue (use --no_presubmit to bypass)" |
1237 | 1236 |
1238 return output | 1237 return output |
1239 | 1238 |
1240 | 1239 |
1241 @no_args | 1240 @no_args |
1242 def CMDchanges(): | 1241 def CMDchanges(): |
1243 """Lists all the changelists and their files.""" | 1242 """Lists all the changelists and their files.""" |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1456 raise | 1455 raise |
1457 print >> sys.stderr, ( | 1456 print >> sys.stderr, ( |
1458 'AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 1457 'AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' |
1459 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)) | 1458 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)) |
1460 return 1 | 1459 return 1 |
1461 | 1460 |
1462 | 1461 |
1463 if __name__ == "__main__": | 1462 if __name__ == "__main__": |
1464 fix_encoding.fix_encoding() | 1463 fix_encoding.fix_encoding() |
1465 sys.exit(main(sys.argv[1:])) | 1464 sys.exit(main(sys.argv[1:])) |
OLD | NEW |