| 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 """Enables directory-specific presubmit checks to run at upload and/or commit. | 6 """Enables directory-specific presubmit checks to run at upload and/or commit. |
| 7 """ | 7 """ |
| 8 | 8 |
| 9 __version__ = '1.8.0' | 9 __version__ = '1.8.0' |
| 10 | 10 |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 | 190 |
| 191 | 191 |
| 192 # Top level object so multiprocessing can pickle | 192 # Top level object so multiprocessing can pickle |
| 193 # Public access through OutputApi object. | 193 # Public access through OutputApi object. |
| 194 class _MailTextResult(_PresubmitResult): | 194 class _MailTextResult(_PresubmitResult): |
| 195 """A warning that should be included in the review request email.""" | 195 """A warning that should be included in the review request email.""" |
| 196 def __init__(self, *args, **kwargs): | 196 def __init__(self, *args, **kwargs): |
| 197 super(_MailTextResult, self).__init__() | 197 super(_MailTextResult, self).__init__() |
| 198 raise NotImplementedError() | 198 raise NotImplementedError() |
| 199 | 199 |
| 200 class GerritAccessor(object): | |
| 201 """Limited Gerrit functionality for canned presubmit checks to work. | |
| 202 | |
| 203 To avoid excessive Gerrit calls, caches the results. | |
| 204 """ | |
| 205 | |
| 206 def __init__(self, host): | |
| 207 self.host = host | |
| 208 self.cache = {} | |
| 209 | |
| 210 def _FetchChangeDetail(self, issue): | |
| 211 # Separate function to be easily mocked in tests. | |
| 212 return gerrit_util.GetChangeDetail( | |
| 213 self.host, str(issue), | |
| 214 ['ALL_REVISIONS', 'DETAILED_LABELS']) | |
| 215 | |
| 216 def GetChangeInfo(self, issue): | |
| 217 """Returns labels and all revisions (patchsets) for this issue. | |
| 218 | |
| 219 The result is a dictionary according to Gerrit REST Api. | |
| 220 https://gerrit-review.googlesource.com/Documentation/rest-api.html | |
| 221 | |
| 222 However, API isn't very clear what's inside, so see tests for example. | |
| 223 """ | |
| 224 assert issue | |
| 225 cache_key = int(issue) | |
| 226 if cache_key not in self.cache: | |
| 227 self.cache[cache_key] = self._FetchChangeDetail(issue) | |
| 228 return self.cache[cache_key] | |
| 229 | |
| 230 def GetChangeDescription(self, issue, patchset=None): | |
| 231 """If patchset is none, fetches current patchset.""" | |
| 232 info = self.GetChangeInfo(issue) | |
| 233 # info is a reference to cache. We'll modify it here adding description to | |
| 234 # it to the right patchset, if it is not yet there. | |
| 235 | |
| 236 # Find revision info for the patchset we want. | |
| 237 if patchset is not None: | |
| 238 for rev, rev_info in info['revisions'].iteritems(): | |
| 239 if str(rev_info['_number']) == str(patchset): | |
| 240 break | |
| 241 else: | |
| 242 raise Exception('patchset %s doesn\'t exist in issue %s' % ( | |
| 243 patchset, issue)) | |
| 244 else: | |
| 245 rev = info['current_revision'] | |
| 246 rev_info = info['revisions'][rev] | |
| 247 | |
| 248 # Updates revision info, which is part of cached issue info. | |
| 249 if 'real_description' not in rev_info: | |
| 250 rev_info['real_description'] = ( | |
| 251 gerrit_util.GetChangeDescriptionFromGitiles( | |
| 252 rev_info['fetch']['http']['url'], rev)) | |
| 253 return rev_info['real_description'] | |
| 254 | |
| 255 def GetChangeOwner(self, issue): | |
| 256 return self.GetChangeInfo(issue)['owner']['email'] | |
| 257 | |
| 258 def GetChangeReviewers(self, issue, approving_only=True): | |
| 259 # Gerrit has 'approved' sub-section, but it only lists 1 approver. | |
| 260 # So, if we look only for approvers, we have to look at all anyway. | |
| 261 # Also, assume LGTM means Code-Review label == 2. Other configurations | |
| 262 # aren't supported. | |
| 263 return [r['email'] | |
| 264 for r in self.GetChangeInfo(issue)['labels']['Code-Review']['all'] | |
| 265 if not approving_only or '2' == str(r.get('value', 0))] | |
| 266 | |
| 267 | 200 |
| 268 class OutputApi(object): | 201 class OutputApi(object): |
| 269 """An instance of OutputApi gets passed to presubmit scripts so that they | 202 """An instance of OutputApi gets passed to presubmit scripts so that they |
| 270 can output various types of results. | 203 can output various types of results. |
| 271 """ | 204 """ |
| 272 PresubmitResult = _PresubmitResult | 205 PresubmitResult = _PresubmitResult |
| 273 PresubmitAddReviewers = _PresubmitAddReviewers | 206 PresubmitAddReviewers = _PresubmitAddReviewers |
| 274 PresubmitError = _PresubmitError | 207 PresubmitError = _PresubmitError |
| 275 PresubmitPromptWarning = _PresubmitPromptWarning | 208 PresubmitPromptWarning = _PresubmitPromptWarning |
| 276 PresubmitNotifyResult = _PresubmitNotifyResult | 209 PresubmitNotifyResult = _PresubmitNotifyResult |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 r".*\b[A-Z0-9_]{2,}$", | 258 r".*\b[A-Z0-9_]{2,}$", |
| 326 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 259 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |
| 327 r"(|.*[\\\/])\.git[\\\/].*", | 260 r"(|.*[\\\/])\.git[\\\/].*", |
| 328 r"(|.*[\\\/])\.svn[\\\/].*", | 261 r"(|.*[\\\/])\.svn[\\\/].*", |
| 329 # There is no point in processing a patch file. | 262 # There is no point in processing a patch file. |
| 330 r".+\.diff$", | 263 r".+\.diff$", |
| 331 r".+\.patch$", | 264 r".+\.patch$", |
| 332 ) | 265 ) |
| 333 | 266 |
| 334 def __init__(self, change, presubmit_path, is_committing, | 267 def __init__(self, change, presubmit_path, is_committing, |
| 335 rietveld_obj, verbose, gerrit_obj=None, dry_run=None): | 268 rietveld_obj, verbose, dry_run=None): |
| 336 """Builds an InputApi object. | 269 """Builds an InputApi object. |
| 337 | 270 |
| 338 Args: | 271 Args: |
| 339 change: A presubmit.Change object. | 272 change: A presubmit.Change object. |
| 340 presubmit_path: The path to the presubmit script being processed. | 273 presubmit_path: The path to the presubmit script being processed. |
| 341 is_committing: True if the change is about to be committed. | 274 is_committing: True if the change is about to be committed. |
| 342 rietveld_obj: rietveld.Rietveld client object | 275 rietveld_obj: rietveld.Rietveld client object |
| 343 gerrit_obj: provides basic Gerrit codereview functionality. | |
| 344 dry_run: if true, some Checks will be skipped. | |
| 345 """ | 276 """ |
| 346 # Version number of the presubmit_support script. | 277 # Version number of the presubmit_support script. |
| 347 self.version = [int(x) for x in __version__.split('.')] | 278 self.version = [int(x) for x in __version__.split('.')] |
| 348 self.change = change | 279 self.change = change |
| 349 self.is_committing = is_committing | 280 self.is_committing = is_committing |
| 350 self.rietveld = rietveld_obj | 281 self.rietveld = rietveld_obj |
| 351 self.gerrit = gerrit_obj | |
| 352 self.dry_run = dry_run | 282 self.dry_run = dry_run |
| 353 # TBD | 283 # TBD |
| 354 self.host_url = 'http://codereview.chromium.org' | 284 self.host_url = 'http://codereview.chromium.org' |
| 355 if self.rietveld: | 285 if self.rietveld: |
| 356 self.host_url = self.rietveld.url | 286 self.host_url = self.rietveld.url |
| 357 | 287 |
| 358 # We expose various modules and functions as attributes of the input_api | 288 # We expose various modules and functions as attributes of the input_api |
| 359 # so that presubmit scripts don't have to import them. | 289 # so that presubmit scripts don't have to import them. |
| 360 self.basename = os.path.basename | 290 self.basename = os.path.basename |
| 361 self.cPickle = cPickle | 291 self.cPickle = cPickle |
| (...skipping 1050 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1412 output_stream.write('** Post Upload Hook Messages **\n') | 1342 output_stream.write('** Post Upload Hook Messages **\n') |
| 1413 for result in results: | 1343 for result in results: |
| 1414 result.handle(output_stream) | 1344 result.handle(output_stream) |
| 1415 output_stream.write('\n') | 1345 output_stream.write('\n') |
| 1416 | 1346 |
| 1417 return results | 1347 return results |
| 1418 | 1348 |
| 1419 | 1349 |
| 1420 class PresubmitExecuter(object): | 1350 class PresubmitExecuter(object): |
| 1421 def __init__(self, change, committing, rietveld_obj, verbose, | 1351 def __init__(self, change, committing, rietveld_obj, verbose, |
| 1422 gerrit_obj=None, dry_run=None): | 1352 dry_run=None): |
| 1423 """ | 1353 """ |
| 1424 Args: | 1354 Args: |
| 1425 change: The Change object. | 1355 change: The Change object. |
| 1426 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1356 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 1427 rietveld_obj: rietveld.Rietveld client object. | 1357 rietveld_obj: rietveld.Rietveld client object. |
| 1428 gerrit_obj: provides basic Gerrit codereview functionality. | |
| 1429 dry_run: if true, some Checks will be skipped. | |
| 1430 """ | 1358 """ |
| 1431 self.change = change | 1359 self.change = change |
| 1432 self.committing = committing | 1360 self.committing = committing |
| 1433 self.rietveld = rietveld_obj | 1361 self.rietveld = rietveld_obj |
| 1434 self.gerrit = gerrit_obj | |
| 1435 self.verbose = verbose | 1362 self.verbose = verbose |
| 1436 self.dry_run = dry_run | 1363 self.dry_run = dry_run |
| 1437 | 1364 |
| 1438 def ExecPresubmitScript(self, script_text, presubmit_path): | 1365 def ExecPresubmitScript(self, script_text, presubmit_path): |
| 1439 """Executes a single presubmit script. | 1366 """Executes a single presubmit script. |
| 1440 | 1367 |
| 1441 Args: | 1368 Args: |
| 1442 script_text: The text of the presubmit script. | 1369 script_text: The text of the presubmit script. |
| 1443 presubmit_path: The path to the presubmit file (this will be reported via | 1370 presubmit_path: The path to the presubmit file (this will be reported via |
| 1444 input_api.PresubmitLocalPath()). | 1371 input_api.PresubmitLocalPath()). |
| 1445 | 1372 |
| 1446 Return: | 1373 Return: |
| 1447 A list of result objects, empty if no problems. | 1374 A list of result objects, empty if no problems. |
| 1448 """ | 1375 """ |
| 1449 | 1376 |
| 1450 # Change to the presubmit file's directory to support local imports. | 1377 # Change to the presubmit file's directory to support local imports. |
| 1451 main_path = os.getcwd() | 1378 main_path = os.getcwd() |
| 1452 os.chdir(os.path.dirname(presubmit_path)) | 1379 os.chdir(os.path.dirname(presubmit_path)) |
| 1453 | 1380 |
| 1454 # Load the presubmit script into context. | 1381 # Load the presubmit script into context. |
| 1455 input_api = InputApi(self.change, presubmit_path, self.committing, | 1382 input_api = InputApi(self.change, presubmit_path, self.committing, |
| 1456 self.rietveld, self.verbose, | 1383 self.rietveld, self.verbose, |
| 1457 gerrit_obj=self.gerrit, dry_run=self.dry_run) | 1384 dry_run=self.dry_run) |
| 1458 context = {} | 1385 context = {} |
| 1459 try: | 1386 try: |
| 1460 exec script_text in context | 1387 exec script_text in context |
| 1461 except Exception, e: | 1388 except Exception, e: |
| 1462 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) | 1389 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) |
| 1463 | 1390 |
| 1464 # These function names must change if we make substantial changes to | 1391 # These function names must change if we make substantial changes to |
| 1465 # the presubmit API that are not backwards compatible. | 1392 # the presubmit API that are not backwards compatible. |
| 1466 if self.committing: | 1393 if self.committing: |
| 1467 function_name = 'CheckChangeOnCommit' | 1394 function_name = 'CheckChangeOnCommit' |
| (...skipping 22 matching lines...) Expand all Loading... |
| 1490 | 1417 |
| 1491 | 1418 |
| 1492 def DoPresubmitChecks(change, | 1419 def DoPresubmitChecks(change, |
| 1493 committing, | 1420 committing, |
| 1494 verbose, | 1421 verbose, |
| 1495 output_stream, | 1422 output_stream, |
| 1496 input_stream, | 1423 input_stream, |
| 1497 default_presubmit, | 1424 default_presubmit, |
| 1498 may_prompt, | 1425 may_prompt, |
| 1499 rietveld_obj, | 1426 rietveld_obj, |
| 1500 gerrit_obj=None, | |
| 1501 dry_run=None): | 1427 dry_run=None): |
| 1502 """Runs all presubmit checks that apply to the files in the change. | 1428 """Runs all presubmit checks that apply to the files in the change. |
| 1503 | 1429 |
| 1504 This finds all PRESUBMIT.py files in directories enclosing the files in the | 1430 This finds all PRESUBMIT.py files in directories enclosing the files in the |
| 1505 change (up to the repository root) and calls the relevant entrypoint function | 1431 change (up to the repository root) and calls the relevant entrypoint function |
| 1506 depending on whether the change is being committed or uploaded. | 1432 depending on whether the change is being committed or uploaded. |
| 1507 | 1433 |
| 1508 Prints errors, warnings and notifications. Prompts the user for warnings | 1434 Prints errors, warnings and notifications. Prompts the user for warnings |
| 1509 when needed. | 1435 when needed. |
| 1510 | 1436 |
| 1511 Args: | 1437 Args: |
| 1512 change: The Change object. | 1438 change: The Change object. |
| 1513 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1439 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 1514 verbose: Prints debug info. | 1440 verbose: Prints debug info. |
| 1515 output_stream: A stream to write output from presubmit tests to. | 1441 output_stream: A stream to write output from presubmit tests to. |
| 1516 input_stream: A stream to read input from the user. | 1442 input_stream: A stream to read input from the user. |
| 1517 default_presubmit: A default presubmit script to execute in any case. | 1443 default_presubmit: A default presubmit script to execute in any case. |
| 1518 may_prompt: Enable (y/n) questions on warning or error. | 1444 may_prompt: Enable (y/n) questions on warning or error. |
| 1519 rietveld_obj: rietveld.Rietveld object. | 1445 rietveld_obj: rietveld.Rietveld object. |
| 1520 gerrit_obj: provides basic Gerrit codereview functionality. | |
| 1521 dry_run: if true, some Checks will be skipped. | 1446 dry_run: if true, some Checks will be skipped. |
| 1522 | 1447 |
| 1523 Warning: | 1448 Warning: |
| 1524 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 1449 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |
| 1525 SHOULD be sys.stdin. | 1450 SHOULD be sys.stdin. |
| 1526 | 1451 |
| 1527 Return: | 1452 Return: |
| 1528 A PresubmitOutput object. Use output.should_continue() to figure out | 1453 A PresubmitOutput object. Use output.should_continue() to figure out |
| 1529 if there were errors or warnings and the caller should abort. | 1454 if there were errors or warnings and the caller should abort. |
| 1530 """ | 1455 """ |
| 1531 old_environ = os.environ | 1456 old_environ = os.environ |
| 1532 try: | 1457 try: |
| 1533 # Make sure python subprocesses won't generate .pyc files. | 1458 # Make sure python subprocesses won't generate .pyc files. |
| 1534 os.environ = os.environ.copy() | 1459 os.environ = os.environ.copy() |
| 1535 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' | 1460 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' |
| 1536 | 1461 |
| 1537 output = PresubmitOutput(input_stream, output_stream) | 1462 output = PresubmitOutput(input_stream, output_stream) |
| 1538 if committing: | 1463 if committing: |
| 1539 output.write("Running presubmit commit checks ...\n") | 1464 output.write("Running presubmit commit checks ...\n") |
| 1540 else: | 1465 else: |
| 1541 output.write("Running presubmit upload checks ...\n") | 1466 output.write("Running presubmit upload checks ...\n") |
| 1542 start_time = time.time() | 1467 start_time = time.time() |
| 1543 presubmit_files = ListRelevantPresubmitFiles( | 1468 presubmit_files = ListRelevantPresubmitFiles( |
| 1544 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) | 1469 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) |
| 1545 if not presubmit_files and verbose: | 1470 if not presubmit_files and verbose: |
| 1546 output.write("Warning, no PRESUBMIT.py found.\n") | 1471 output.write("Warning, no PRESUBMIT.py found.\n") |
| 1547 results = [] | 1472 results = [] |
| 1548 executer = PresubmitExecuter(change, committing, rietveld_obj, verbose, | 1473 executer = PresubmitExecuter(change, committing, rietveld_obj, verbose, |
| 1549 gerrit_obj, dry_run) | 1474 dry_run=dry_run) |
| 1550 if default_presubmit: | 1475 if default_presubmit: |
| 1551 if verbose: | 1476 if verbose: |
| 1552 output.write("Running default presubmit script.\n") | 1477 output.write("Running default presubmit script.\n") |
| 1553 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1478 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
| 1554 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1479 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
| 1555 for filename in presubmit_files: | 1480 for filename in presubmit_files: |
| 1556 filename = os.path.abspath(filename) | 1481 filename = os.path.abspath(filename) |
| 1557 if verbose: | 1482 if verbose: |
| 1558 output.write("Running %s\n" % filename) | 1483 output.write("Running %s\n" % filename) |
| 1559 # Accept CRLF presubmit script. | 1484 # Accept CRLF presubmit script. |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1764 "can be passed to this program.") | 1689 "can be passed to this program.") |
| 1765 if options.rietveld_email_file: | 1690 if options.rietveld_email_file: |
| 1766 with open(options.rietveld_email_file, "rb") as f: | 1691 with open(options.rietveld_email_file, "rb") as f: |
| 1767 options.rietveld_email = f.read().strip() | 1692 options.rietveld_email = f.read().strip() |
| 1768 | 1693 |
| 1769 change_class, files = load_files(options, args) | 1694 change_class, files = load_files(options, args) |
| 1770 if not change_class: | 1695 if not change_class: |
| 1771 parser.error('For unversioned directory, <files> is not optional.') | 1696 parser.error('For unversioned directory, <files> is not optional.') |
| 1772 logging.info('Found %d file(s).' % len(files)) | 1697 logging.info('Found %d file(s).' % len(files)) |
| 1773 | 1698 |
| 1774 rietveld_obj, gerrit_obj = None, None | 1699 rietveld_obj = None |
| 1775 | |
| 1776 if options.rietveld_url: | 1700 if options.rietveld_url: |
| 1777 # The empty password is permitted: '' is not None. | 1701 # The empty password is permitted: '' is not None. |
| 1778 if options.rietveld_private_key_file: | 1702 if options.rietveld_private_key_file: |
| 1779 rietveld_obj = rietveld.JwtOAuth2Rietveld( | 1703 rietveld_obj = rietveld.JwtOAuth2Rietveld( |
| 1780 options.rietveld_url, | 1704 options.rietveld_url, |
| 1781 options.rietveld_email, | 1705 options.rietveld_email, |
| 1782 options.rietveld_private_key_file) | 1706 options.rietveld_private_key_file) |
| 1783 else: | 1707 else: |
| 1784 rietveld_obj = rietveld.CachingRietveld( | 1708 rietveld_obj = rietveld.CachingRietveld( |
| 1785 options.rietveld_url, | 1709 options.rietveld_url, |
| 1786 auth_config, | 1710 auth_config, |
| 1787 options.rietveld_email) | 1711 options.rietveld_email) |
| 1788 if options.rietveld_fetch: | 1712 if options.rietveld_fetch: |
| 1789 assert options.issue | 1713 assert options.issue |
| 1790 props = rietveld_obj.get_issue_properties(options.issue, False) | 1714 props = rietveld_obj.get_issue_properties(options.issue, False) |
| 1791 options.author = props['owner_email'] | 1715 options.author = props['owner_email'] |
| 1792 options.description = props['description'] | 1716 options.description = props['description'] |
| 1793 logging.info('Got author: "%s"', options.author) | 1717 logging.info('Got author: "%s"', options.author) |
| 1794 logging.info('Got description: """\n%s\n"""', options.description) | 1718 logging.info('Got description: """\n%s\n"""', options.description) |
| 1795 | 1719 |
| 1796 if options.gerrit_url and options.gerrit_fetch: | 1720 if options.gerrit_url and options.gerrit_fetch: |
| 1721 rietveld_obj = None |
| 1797 assert options.issue and options.patchset | 1722 assert options.issue and options.patchset |
| 1798 rietveld_obj = None | 1723 props = gerrit_util.GetChangeDetail( |
| 1799 gerrit_obj = GerritAccessor(urlparse.urlparse(options.gerrit_url).netloc) | 1724 urlparse.urlparse(options.gerrit_url).netloc, str(options.issue), |
| 1800 options.author = gerrit_obj.GetChangeOwner(options.issue) | 1725 ['ALL_REVISIONS']) |
| 1801 options.description = gerrit_obj.GetChangeDescription(options.patchset) | 1726 options.author = props['owner']['email'] |
| 1727 for rev, rev_info in props['revisions'].iteritems(): |
| 1728 if str(rev_info['_number']) == str(options.patchset): |
| 1729 options.description = gerrit_util.GetChangeDescriptionFromGitiles( |
| 1730 rev_info['fetch']['http']['url'], rev) |
| 1731 break |
| 1732 else: |
| 1733 print >> sys.stderr, ('Patchset %d was not found in Gerrit issue %d' % |
| 1734 options.patchset, options.issue) |
| 1735 return 2 |
| 1802 logging.info('Got author: "%s"', options.author) | 1736 logging.info('Got author: "%s"', options.author) |
| 1803 logging.info('Got description: """\n%s\n"""', options.description) | 1737 logging.info('Got description: """\n%s\n"""', options.description) |
| 1804 | 1738 |
| 1805 try: | 1739 try: |
| 1806 with canned_check_filter(options.skip_canned): | 1740 with canned_check_filter(options.skip_canned): |
| 1807 results = DoPresubmitChecks( | 1741 results = DoPresubmitChecks( |
| 1808 change_class(options.name, | 1742 change_class(options.name, |
| 1809 options.description, | 1743 options.description, |
| 1810 options.root, | 1744 options.root, |
| 1811 files, | 1745 files, |
| 1812 options.issue, | 1746 options.issue, |
| 1813 options.patchset, | 1747 options.patchset, |
| 1814 options.author, | 1748 options.author, |
| 1815 upstream=options.upstream), | 1749 upstream=options.upstream), |
| 1816 options.commit, | 1750 options.commit, |
| 1817 options.verbose, | 1751 options.verbose, |
| 1818 sys.stdout, | 1752 sys.stdout, |
| 1819 sys.stdin, | 1753 sys.stdin, |
| 1820 options.default_presubmit, | 1754 options.default_presubmit, |
| 1821 options.may_prompt, | 1755 options.may_prompt, |
| 1822 rietveld_obj, | 1756 rietveld_obj, |
| 1823 gerrit_obj, | |
| 1824 options.dry_run) | 1757 options.dry_run) |
| 1825 return not results.should_continue() | 1758 return not results.should_continue() |
| 1826 except NonexistantCannedCheckFilter, e: | 1759 except NonexistantCannedCheckFilter, e: |
| 1827 print >> sys.stderr, ( | 1760 print >> sys.stderr, ( |
| 1828 'Attempted to skip nonexistent canned presubmit check: %s' % e.message) | 1761 'Attempted to skip nonexistent canned presubmit check: %s' % e.message) |
| 1829 return 2 | 1762 return 2 |
| 1830 except PresubmitFailure, e: | 1763 except PresubmitFailure, e: |
| 1831 print >> sys.stderr, e | 1764 print >> sys.stderr, e |
| 1832 print >> sys.stderr, 'Maybe your depot_tools is out of date?' | 1765 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
| 1833 print >> sys.stderr, 'If all fails, contact maruel@' | 1766 print >> sys.stderr, 'If all fails, contact maruel@' |
| 1834 return 2 | 1767 return 2 |
| 1835 | 1768 |
| 1836 | 1769 |
| 1837 if __name__ == '__main__': | 1770 if __name__ == '__main__': |
| 1838 fix_encoding.fix_encoding() | 1771 fix_encoding.fix_encoding() |
| 1839 try: | 1772 try: |
| 1840 sys.exit(main()) | 1773 sys.exit(main()) |
| 1841 except KeyboardInterrupt: | 1774 except KeyboardInterrupt: |
| 1842 sys.stderr.write('interrupted\n') | 1775 sys.stderr.write('interrupted\n') |
| 1843 sys.exit(1) | 1776 sys.exit(1) |
| OLD | NEW |