| 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 |