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 |