| 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 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 r".*\b[A-Z0-9_]{2,}$", | 258 r".*\b[A-Z0-9_]{2,}$", |
| 259 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 259 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |
| 260 r"(|.*[\\\/])\.git[\\\/].*", | 260 r"(|.*[\\\/])\.git[\\\/].*", |
| 261 r"(|.*[\\\/])\.svn[\\\/].*", | 261 r"(|.*[\\\/])\.svn[\\\/].*", |
| 262 # There is no point in processing a patch file. | 262 # There is no point in processing a patch file. |
| 263 r".+\.diff$", | 263 r".+\.diff$", |
| 264 r".+\.patch$", | 264 r".+\.patch$", |
| 265 ) | 265 ) |
| 266 | 266 |
| 267 def __init__(self, change, presubmit_path, is_committing, | 267 def __init__(self, change, presubmit_path, is_committing, |
| 268 rietveld_obj, verbose): | 268 rietveld_obj, verbose, dry_run=None): |
| 269 """Builds an InputApi object. | 269 """Builds an InputApi object. |
| 270 | 270 |
| 271 Args: | 271 Args: |
| 272 change: A presubmit.Change object. | 272 change: A presubmit.Change object. |
| 273 presubmit_path: The path to the presubmit script being processed. | 273 presubmit_path: The path to the presubmit script being processed. |
| 274 is_committing: True if the change is about to be committed. | 274 is_committing: True if the change is about to be committed. |
| 275 rietveld_obj: rietveld.Rietveld client object | 275 rietveld_obj: rietveld.Rietveld client object |
| 276 """ | 276 """ |
| 277 # Version number of the presubmit_support script. | 277 # Version number of the presubmit_support script. |
| 278 self.version = [int(x) for x in __version__.split('.')] | 278 self.version = [int(x) for x in __version__.split('.')] |
| 279 self.change = change | 279 self.change = change |
| 280 self.is_committing = is_committing | 280 self.is_committing = is_committing |
| 281 self.rietveld = rietveld_obj | 281 self.rietveld = rietveld_obj |
| 282 self.dry_run = dry_run |
| 282 # TBD | 283 # TBD |
| 283 self.host_url = 'http://codereview.chromium.org' | 284 self.host_url = 'http://codereview.chromium.org' |
| 284 if self.rietveld: | 285 if self.rietveld: |
| 285 self.host_url = self.rietveld.url | 286 self.host_url = self.rietveld.url |
| 286 | 287 |
| 287 # 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 |
| 288 # so that presubmit scripts don't have to import them. | 289 # so that presubmit scripts don't have to import them. |
| 289 self.basename = os.path.basename | 290 self.basename = os.path.basename |
| 290 self.cPickle = cPickle | 291 self.cPickle = cPickle |
| 291 self.cpplint = cpplint | 292 self.cpplint = cpplint |
| (...skipping 1048 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1340 if results: | 1341 if results: |
| 1341 output_stream.write('** Post Upload Hook Messages **\n') | 1342 output_stream.write('** Post Upload Hook Messages **\n') |
| 1342 for result in results: | 1343 for result in results: |
| 1343 result.handle(output_stream) | 1344 result.handle(output_stream) |
| 1344 output_stream.write('\n') | 1345 output_stream.write('\n') |
| 1345 | 1346 |
| 1346 return results | 1347 return results |
| 1347 | 1348 |
| 1348 | 1349 |
| 1349 class PresubmitExecuter(object): | 1350 class PresubmitExecuter(object): |
| 1350 def __init__(self, change, committing, rietveld_obj, verbose): | 1351 def __init__(self, change, committing, rietveld_obj, verbose, |
| 1352 dry_run=None): |
| 1351 """ | 1353 """ |
| 1352 Args: | 1354 Args: |
| 1353 change: The Change object. | 1355 change: The Change object. |
| 1354 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. |
| 1355 rietveld_obj: rietveld.Rietveld client object. | 1357 rietveld_obj: rietveld.Rietveld client object. |
| 1356 """ | 1358 """ |
| 1357 self.change = change | 1359 self.change = change |
| 1358 self.committing = committing | 1360 self.committing = committing |
| 1359 self.rietveld = rietveld_obj | 1361 self.rietveld = rietveld_obj |
| 1360 self.verbose = verbose | 1362 self.verbose = verbose |
| 1363 self.dry_run = dry_run |
| 1361 | 1364 |
| 1362 def ExecPresubmitScript(self, script_text, presubmit_path): | 1365 def ExecPresubmitScript(self, script_text, presubmit_path): |
| 1363 """Executes a single presubmit script. | 1366 """Executes a single presubmit script. |
| 1364 | 1367 |
| 1365 Args: | 1368 Args: |
| 1366 script_text: The text of the presubmit script. | 1369 script_text: The text of the presubmit script. |
| 1367 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 |
| 1368 input_api.PresubmitLocalPath()). | 1371 input_api.PresubmitLocalPath()). |
| 1369 | 1372 |
| 1370 Return: | 1373 Return: |
| 1371 A list of result objects, empty if no problems. | 1374 A list of result objects, empty if no problems. |
| 1372 """ | 1375 """ |
| 1373 | 1376 |
| 1374 # Change to the presubmit file's directory to support local imports. | 1377 # Change to the presubmit file's directory to support local imports. |
| 1375 main_path = os.getcwd() | 1378 main_path = os.getcwd() |
| 1376 os.chdir(os.path.dirname(presubmit_path)) | 1379 os.chdir(os.path.dirname(presubmit_path)) |
| 1377 | 1380 |
| 1378 # Load the presubmit script into context. | 1381 # Load the presubmit script into context. |
| 1379 input_api = InputApi(self.change, presubmit_path, self.committing, | 1382 input_api = InputApi(self.change, presubmit_path, self.committing, |
| 1380 self.rietveld, self.verbose) | 1383 self.rietveld, self.verbose, |
| 1384 dry_run=self.dry_run) |
| 1381 context = {} | 1385 context = {} |
| 1382 try: | 1386 try: |
| 1383 exec script_text in context | 1387 exec script_text in context |
| 1384 except Exception, e: | 1388 except Exception, e: |
| 1385 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) | 1389 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) |
| 1386 | 1390 |
| 1387 # These function names must change if we make substantial changes to | 1391 # These function names must change if we make substantial changes to |
| 1388 # the presubmit API that are not backwards compatible. | 1392 # the presubmit API that are not backwards compatible. |
| 1389 if self.committing: | 1393 if self.committing: |
| 1390 function_name = 'CheckChangeOnCommit' | 1394 function_name = 'CheckChangeOnCommit' |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1412 return result | 1416 return result |
| 1413 | 1417 |
| 1414 | 1418 |
| 1415 def DoPresubmitChecks(change, | 1419 def DoPresubmitChecks(change, |
| 1416 committing, | 1420 committing, |
| 1417 verbose, | 1421 verbose, |
| 1418 output_stream, | 1422 output_stream, |
| 1419 input_stream, | 1423 input_stream, |
| 1420 default_presubmit, | 1424 default_presubmit, |
| 1421 may_prompt, | 1425 may_prompt, |
| 1422 rietveld_obj): | 1426 rietveld_obj, |
| 1427 dry_run=None): |
| 1423 """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. |
| 1424 | 1429 |
| 1425 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 |
| 1426 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 |
| 1427 depending on whether the change is being committed or uploaded. | 1432 depending on whether the change is being committed or uploaded. |
| 1428 | 1433 |
| 1429 Prints errors, warnings and notifications. Prompts the user for warnings | 1434 Prints errors, warnings and notifications. Prompts the user for warnings |
| 1430 when needed. | 1435 when needed. |
| 1431 | 1436 |
| 1432 Args: | 1437 Args: |
| 1433 change: The Change object. | 1438 change: The Change object. |
| 1434 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. |
| 1435 verbose: Prints debug info. | 1440 verbose: Prints debug info. |
| 1436 output_stream: A stream to write output from presubmit tests to. | 1441 output_stream: A stream to write output from presubmit tests to. |
| 1437 input_stream: A stream to read input from the user. | 1442 input_stream: A stream to read input from the user. |
| 1438 default_presubmit: A default presubmit script to execute in any case. | 1443 default_presubmit: A default presubmit script to execute in any case. |
| 1439 may_prompt: Enable (y/n) questions on warning or error. | 1444 may_prompt: Enable (y/n) questions on warning or error. |
| 1440 rietveld_obj: rietveld.Rietveld object. | 1445 rietveld_obj: rietveld.Rietveld object. |
| 1446 dry_run: if true, some Checks will be skipped. |
| 1441 | 1447 |
| 1442 Warning: | 1448 Warning: |
| 1443 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 |
| 1444 SHOULD be sys.stdin. | 1450 SHOULD be sys.stdin. |
| 1445 | 1451 |
| 1446 Return: | 1452 Return: |
| 1447 A PresubmitOutput object. Use output.should_continue() to figure out | 1453 A PresubmitOutput object. Use output.should_continue() to figure out |
| 1448 if there were errors or warnings and the caller should abort. | 1454 if there were errors or warnings and the caller should abort. |
| 1449 """ | 1455 """ |
| 1450 old_environ = os.environ | 1456 old_environ = os.environ |
| 1451 try: | 1457 try: |
| 1452 # Make sure python subprocesses won't generate .pyc files. | 1458 # Make sure python subprocesses won't generate .pyc files. |
| 1453 os.environ = os.environ.copy() | 1459 os.environ = os.environ.copy() |
| 1454 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' | 1460 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' |
| 1455 | 1461 |
| 1456 output = PresubmitOutput(input_stream, output_stream) | 1462 output = PresubmitOutput(input_stream, output_stream) |
| 1457 if committing: | 1463 if committing: |
| 1458 output.write("Running presubmit commit checks ...\n") | 1464 output.write("Running presubmit commit checks ...\n") |
| 1459 else: | 1465 else: |
| 1460 output.write("Running presubmit upload checks ...\n") | 1466 output.write("Running presubmit upload checks ...\n") |
| 1461 start_time = time.time() | 1467 start_time = time.time() |
| 1462 presubmit_files = ListRelevantPresubmitFiles( | 1468 presubmit_files = ListRelevantPresubmitFiles( |
| 1463 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) | 1469 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) |
| 1464 if not presubmit_files and verbose: | 1470 if not presubmit_files and verbose: |
| 1465 output.write("Warning, no PRESUBMIT.py found.\n") | 1471 output.write("Warning, no PRESUBMIT.py found.\n") |
| 1466 results = [] | 1472 results = [] |
| 1467 executer = PresubmitExecuter(change, committing, rietveld_obj, verbose) | 1473 executer = PresubmitExecuter(change, committing, rietveld_obj, verbose, |
| 1474 dry_run=dry_run) |
| 1468 if default_presubmit: | 1475 if default_presubmit: |
| 1469 if verbose: | 1476 if verbose: |
| 1470 output.write("Running default presubmit script.\n") | 1477 output.write("Running default presubmit script.\n") |
| 1471 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1478 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
| 1472 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1479 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
| 1473 for filename in presubmit_files: | 1480 for filename in presubmit_files: |
| 1474 filename = os.path.abspath(filename) | 1481 filename = os.path.abspath(filename) |
| 1475 if verbose: | 1482 if verbose: |
| 1476 output.write("Running %s\n" % filename) | 1483 output.write("Running %s\n" % filename) |
| 1477 # Accept CRLF presubmit script. | 1484 # Accept CRLF presubmit script. |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1637 "system directories will also be searched.") | 1644 "system directories will also be searched.") |
| 1638 parser.add_option("--upstream", | 1645 parser.add_option("--upstream", |
| 1639 help="Git only: the base ref or upstream branch against " | 1646 help="Git only: the base ref or upstream branch against " |
| 1640 "which the diff should be computed.") | 1647 "which the diff should be computed.") |
| 1641 parser.add_option("--default_presubmit") | 1648 parser.add_option("--default_presubmit") |
| 1642 parser.add_option("--may_prompt", action='store_true', default=False) | 1649 parser.add_option("--may_prompt", action='store_true', default=False) |
| 1643 parser.add_option("--skip_canned", action='append', default=[], | 1650 parser.add_option("--skip_canned", action='append', default=[], |
| 1644 help="A list of checks to skip which appear in " | 1651 help="A list of checks to skip which appear in " |
| 1645 "presubmit_canned_checks. Can be provided multiple times " | 1652 "presubmit_canned_checks. Can be provided multiple times " |
| 1646 "to skip multiple canned checks.") | 1653 "to skip multiple canned checks.") |
| 1654 parser.add_option("--dry_run", action='store_true', |
| 1655 help=optparse.SUPPRESS_HELP) |
| 1647 parser.add_option("--gerrit_url", help=optparse.SUPPRESS_HELP) | 1656 parser.add_option("--gerrit_url", help=optparse.SUPPRESS_HELP) |
| 1648 parser.add_option("--gerrit_fetch", help=optparse.SUPPRESS_HELP) | 1657 parser.add_option("--gerrit_fetch", action='store_true', |
| 1658 help=optparse.SUPPRESS_HELP) |
| 1649 parser.add_option("--rietveld_url", help=optparse.SUPPRESS_HELP) | 1659 parser.add_option("--rietveld_url", help=optparse.SUPPRESS_HELP) |
| 1650 parser.add_option("--rietveld_email", help=optparse.SUPPRESS_HELP) | 1660 parser.add_option("--rietveld_email", help=optparse.SUPPRESS_HELP) |
| 1651 parser.add_option("--rietveld_fetch", action='store_true', default=False, | 1661 parser.add_option("--rietveld_fetch", action='store_true', default=False, |
| 1652 help=optparse.SUPPRESS_HELP) | 1662 help=optparse.SUPPRESS_HELP) |
| 1653 # These are for OAuth2 authentication for bots. See also apply_issue.py | 1663 # These are for OAuth2 authentication for bots. See also apply_issue.py |
| 1654 parser.add_option("--rietveld_email_file", help=optparse.SUPPRESS_HELP) | 1664 parser.add_option("--rietveld_email_file", help=optparse.SUPPRESS_HELP) |
| 1655 parser.add_option("--rietveld_private_key_file", help=optparse.SUPPRESS_HELP) | 1665 parser.add_option("--rietveld_private_key_file", help=optparse.SUPPRESS_HELP) |
| 1656 | 1666 |
| 1657 # TODO(phajdan.jr): Update callers and remove obsolete --trybot-json . | 1667 # TODO(phajdan.jr): Update callers and remove obsolete --trybot-json . |
| 1658 parser.add_option("--trybot-json", | 1668 parser.add_option("--trybot-json", |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1723 print >> sys.stderr, ('Patchset %d was not found in Gerrit issue %d' % | 1733 print >> sys.stderr, ('Patchset %d was not found in Gerrit issue %d' % |
| 1724 options.patchset, options.issue) | 1734 options.patchset, options.issue) |
| 1725 return 2 | 1735 return 2 |
| 1726 logging.info('Got author: "%s"', options.author) | 1736 logging.info('Got author: "%s"', options.author) |
| 1727 logging.info('Got description: """\n%s\n"""', options.description) | 1737 logging.info('Got description: """\n%s\n"""', options.description) |
| 1728 | 1738 |
| 1729 try: | 1739 try: |
| 1730 with canned_check_filter(options.skip_canned): | 1740 with canned_check_filter(options.skip_canned): |
| 1731 results = DoPresubmitChecks( | 1741 results = DoPresubmitChecks( |
| 1732 change_class(options.name, | 1742 change_class(options.name, |
| 1733 options.description, | 1743 options.description, |
| 1734 options.root, | 1744 options.root, |
| 1735 files, | 1745 files, |
| 1736 options.issue, | 1746 options.issue, |
| 1737 options.patchset, | 1747 options.patchset, |
| 1738 options.author, | 1748 options.author, |
| 1739 upstream=options.upstream), | 1749 upstream=options.upstream), |
| 1740 options.commit, | 1750 options.commit, |
| 1741 options.verbose, | 1751 options.verbose, |
| 1742 sys.stdout, | 1752 sys.stdout, |
| 1743 sys.stdin, | 1753 sys.stdin, |
| 1744 options.default_presubmit, | 1754 options.default_presubmit, |
| 1745 options.may_prompt, | 1755 options.may_prompt, |
| 1746 rietveld_obj) | 1756 rietveld_obj, |
| 1757 options.dry_run) |
| 1747 return not results.should_continue() | 1758 return not results.should_continue() |
| 1748 except NonexistantCannedCheckFilter, e: | 1759 except NonexistantCannedCheckFilter, e: |
| 1749 print >> sys.stderr, ( | 1760 print >> sys.stderr, ( |
| 1750 'Attempted to skip nonexistent canned presubmit check: %s' % e.message) | 1761 'Attempted to skip nonexistent canned presubmit check: %s' % e.message) |
| 1751 return 2 | 1762 return 2 |
| 1752 except PresubmitFailure, e: | 1763 except PresubmitFailure, e: |
| 1753 print >> sys.stderr, e | 1764 print >> sys.stderr, e |
| 1754 print >> sys.stderr, 'Maybe your depot_tools is out of date?' | 1765 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
| 1755 print >> sys.stderr, 'If all fails, contact maruel@' | 1766 print >> sys.stderr, 'If all fails, contact maruel@' |
| 1756 return 2 | 1767 return 2 |
| 1757 | 1768 |
| 1758 | 1769 |
| 1759 if __name__ == '__main__': | 1770 if __name__ == '__main__': |
| 1760 fix_encoding.fix_encoding() | 1771 fix_encoding.fix_encoding() |
| 1761 try: | 1772 try: |
| 1762 sys.exit(main()) | 1773 sys.exit(main()) |
| 1763 except KeyboardInterrupt: | 1774 except KeyboardInterrupt: |
| 1764 sys.stderr.write('interrupted\n') | 1775 sys.stderr.write('interrupted\n') |
| 1765 sys.exit(1) | 1776 sys.exit(1) |
| OLD | NEW |