| 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 """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.6.1' | 9 __version__ = '1.6.1' |
| 10 | 10 |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 r".*\bRelease[\\\/].*", | 210 r".*\bRelease[\\\/].*", |
| 211 r".*\bxcodebuild[\\\/].*", | 211 r".*\bxcodebuild[\\\/].*", |
| 212 r".*\bsconsbuild[\\\/].*", | 212 r".*\bsconsbuild[\\\/].*", |
| 213 # All caps files like README and LICENCE. | 213 # All caps files like README and LICENCE. |
| 214 r".*\b[A-Z0-9_]{2,}$", | 214 r".*\b[A-Z0-9_]{2,}$", |
| 215 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 215 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |
| 216 r"(|.*[\\\/])\.git[\\\/].*", | 216 r"(|.*[\\\/])\.git[\\\/].*", |
| 217 r"(|.*[\\\/])\.svn[\\\/].*", | 217 r"(|.*[\\\/])\.svn[\\\/].*", |
| 218 ) | 218 ) |
| 219 | 219 |
| 220 def __init__(self, change, presubmit_path, is_committing, tbr, | 220 def __init__(self, change, presubmit_path, is_committing, |
| 221 rietveld_obj, verbose): | 221 rietveld_obj, verbose): |
| 222 """Builds an InputApi object. | 222 """Builds an InputApi object. |
| 223 | 223 |
| 224 Args: | 224 Args: |
| 225 change: A presubmit.Change object. | 225 change: A presubmit.Change object. |
| 226 presubmit_path: The path to the presubmit script being processed. | 226 presubmit_path: The path to the presubmit script being processed. |
| 227 is_committing: True if the change is about to be committed. | 227 is_committing: True if the change is about to be committed. |
| 228 tbr: True if '--tbr' was passed to skip any reviewer/owner checks | |
| 229 rietveld_obj: rietveld.Rietveld client object | 228 rietveld_obj: rietveld.Rietveld client object |
| 230 """ | 229 """ |
| 231 # Version number of the presubmit_support script. | 230 # Version number of the presubmit_support script. |
| 232 self.version = [int(x) for x in __version__.split('.')] | 231 self.version = [int(x) for x in __version__.split('.')] |
| 233 self.change = change | 232 self.change = change |
| 234 self.is_committing = is_committing | 233 self.is_committing = is_committing |
| 235 self.tbr = tbr | |
| 236 self.rietveld = rietveld_obj | 234 self.rietveld = rietveld_obj |
| 237 # TBD | 235 # TBD |
| 238 self.host_url = 'http://codereview.chromium.org' | 236 self.host_url = 'http://codereview.chromium.org' |
| 239 if self.rietveld: | 237 if self.rietveld: |
| 240 self.host_url = self.rietveld.url | 238 self.host_url = self.rietveld.url |
| 241 | 239 |
| 242 # We expose various modules and functions as attributes of the input_api | 240 # We expose various modules and functions as attributes of the input_api |
| 243 # so that presubmit scripts don't have to import them. | 241 # so that presubmit scripts don't have to import them. |
| 244 self.basename = os.path.basename | 242 self.basename = os.path.basename |
| 245 self.cPickle = cPickle | 243 self.cPickle = cPickle |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 410 """Reads an arbitrary file. | 408 """Reads an arbitrary file. |
| 411 | 409 |
| 412 Deny reading anything outside the repository. | 410 Deny reading anything outside the repository. |
| 413 """ | 411 """ |
| 414 if isinstance(file_item, AffectedFile): | 412 if isinstance(file_item, AffectedFile): |
| 415 file_item = file_item.AbsoluteLocalPath() | 413 file_item = file_item.AbsoluteLocalPath() |
| 416 if not file_item.startswith(self.change.RepositoryRoot()): | 414 if not file_item.startswith(self.change.RepositoryRoot()): |
| 417 raise IOError('Access outside the repository root is denied.') | 415 raise IOError('Access outside the repository root is denied.') |
| 418 return gclient_utils.FileRead(file_item, mode) | 416 return gclient_utils.FileRead(file_item, mode) |
| 419 | 417 |
| 418 @property |
| 419 def tbr(self): |
| 420 """Returns if a change is TBR'ed.""" |
| 421 return 'TBR' in self.change.tags |
| 422 |
| 420 | 423 |
| 421 class AffectedFile(object): | 424 class AffectedFile(object): |
| 422 """Representation of a file in a change.""" | 425 """Representation of a file in a change.""" |
| 423 # Method could be a function | 426 # Method could be a function |
| 424 # pylint: disable=R0201 | 427 # pylint: disable=R0201 |
| 425 def __init__(self, path, action, repository_root=''): | 428 def __init__(self, path, action, repository_root=''): |
| 426 self._path = path | 429 self._path = path |
| 427 self._action = action | 430 self._action = action |
| 428 self._local_root = repository_root | 431 self._local_root = repository_root |
| 429 self._is_directory = None | 432 self._is_directory = None |
| (...skipping 520 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 950 presubmit_script, filename, project) | 953 presubmit_script, filename, project) |
| 951 | 954 |
| 952 slaves = list(set(results)) | 955 slaves = list(set(results)) |
| 953 if slaves and verbose: | 956 if slaves and verbose: |
| 954 output_stream.write(', '.join(slaves)) | 957 output_stream.write(', '.join(slaves)) |
| 955 output_stream.write('\n') | 958 output_stream.write('\n') |
| 956 return slaves | 959 return slaves |
| 957 | 960 |
| 958 | 961 |
| 959 class PresubmitExecuter(object): | 962 class PresubmitExecuter(object): |
| 960 def __init__(self, change, committing, tbr, rietveld_obj, verbose): | 963 def __init__(self, change, committing, rietveld_obj, verbose): |
| 961 """ | 964 """ |
| 962 Args: | 965 Args: |
| 963 change: The Change object. | 966 change: The Change object. |
| 964 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 967 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 965 tbr: True if '--tbr' was passed to skip any reviewer/owner checks | |
| 966 rietveld_obj: rietveld.Rietveld client object. | 968 rietveld_obj: rietveld.Rietveld client object. |
| 967 """ | 969 """ |
| 968 self.change = change | 970 self.change = change |
| 969 self.committing = committing | 971 self.committing = committing |
| 970 self.tbr = tbr | |
| 971 self.rietveld = rietveld_obj | 972 self.rietveld = rietveld_obj |
| 972 self.verbose = verbose | 973 self.verbose = verbose |
| 973 | 974 |
| 974 def ExecPresubmitScript(self, script_text, presubmit_path): | 975 def ExecPresubmitScript(self, script_text, presubmit_path): |
| 975 """Executes a single presubmit script. | 976 """Executes a single presubmit script. |
| 976 | 977 |
| 977 Args: | 978 Args: |
| 978 script_text: The text of the presubmit script. | 979 script_text: The text of the presubmit script. |
| 979 presubmit_path: The path to the presubmit file (this will be reported via | 980 presubmit_path: The path to the presubmit file (this will be reported via |
| 980 input_api.PresubmitLocalPath()). | 981 input_api.PresubmitLocalPath()). |
| 981 | 982 |
| 982 Return: | 983 Return: |
| 983 A list of result objects, empty if no problems. | 984 A list of result objects, empty if no problems. |
| 984 """ | 985 """ |
| 985 | 986 |
| 986 # Change to the presubmit file's directory to support local imports. | 987 # Change to the presubmit file's directory to support local imports. |
| 987 main_path = os.getcwd() | 988 main_path = os.getcwd() |
| 988 os.chdir(os.path.dirname(presubmit_path)) | 989 os.chdir(os.path.dirname(presubmit_path)) |
| 989 | 990 |
| 990 # Load the presubmit script into context. | 991 # Load the presubmit script into context. |
| 991 input_api = InputApi(self.change, presubmit_path, self.committing, | 992 input_api = InputApi(self.change, presubmit_path, self.committing, |
| 992 self.tbr, self.rietveld, self.verbose) | 993 self.rietveld, self.verbose) |
| 993 context = {} | 994 context = {} |
| 994 try: | 995 try: |
| 995 exec script_text in context | 996 exec script_text in context |
| 996 except Exception, e: | 997 except Exception, e: |
| 997 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) | 998 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) |
| 998 | 999 |
| 999 # These function names must change if we make substantial changes to | 1000 # These function names must change if we make substantial changes to |
| 1000 # the presubmit API that are not backwards compatible. | 1001 # the presubmit API that are not backwards compatible. |
| 1001 if self.committing: | 1002 if self.committing: |
| 1002 function_name = 'CheckChangeOnCommit' | 1003 function_name = 'CheckChangeOnCommit' |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1024 return result | 1025 return result |
| 1025 | 1026 |
| 1026 | 1027 |
| 1027 def DoPresubmitChecks(change, | 1028 def DoPresubmitChecks(change, |
| 1028 committing, | 1029 committing, |
| 1029 verbose, | 1030 verbose, |
| 1030 output_stream, | 1031 output_stream, |
| 1031 input_stream, | 1032 input_stream, |
| 1032 default_presubmit, | 1033 default_presubmit, |
| 1033 may_prompt, | 1034 may_prompt, |
| 1034 tbr, | |
| 1035 rietveld_obj): | 1035 rietveld_obj): |
| 1036 """Runs all presubmit checks that apply to the files in the change. | 1036 """Runs all presubmit checks that apply to the files in the change. |
| 1037 | 1037 |
| 1038 This finds all PRESUBMIT.py files in directories enclosing the files in the | 1038 This finds all PRESUBMIT.py files in directories enclosing the files in the |
| 1039 change (up to the repository root) and calls the relevant entrypoint function | 1039 change (up to the repository root) and calls the relevant entrypoint function |
| 1040 depending on whether the change is being committed or uploaded. | 1040 depending on whether the change is being committed or uploaded. |
| 1041 | 1041 |
| 1042 Prints errors, warnings and notifications. Prompts the user for warnings | 1042 Prints errors, warnings and notifications. Prompts the user for warnings |
| 1043 when needed. | 1043 when needed. |
| 1044 | 1044 |
| 1045 Args: | 1045 Args: |
| 1046 change: The Change object. | 1046 change: The Change object. |
| 1047 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1047 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 1048 verbose: Prints debug info. | 1048 verbose: Prints debug info. |
| 1049 output_stream: A stream to write output from presubmit tests to. | 1049 output_stream: A stream to write output from presubmit tests to. |
| 1050 input_stream: A stream to read input from the user. | 1050 input_stream: A stream to read input from the user. |
| 1051 default_presubmit: A default presubmit script to execute in any case. | 1051 default_presubmit: A default presubmit script to execute in any case. |
| 1052 may_prompt: Enable (y/n) questions on warning or error. | 1052 may_prompt: Enable (y/n) questions on warning or error. |
| 1053 tbr: was --tbr specified to skip any reviewer/owner checks? | |
| 1054 rietveld_obj: rietveld.Rietveld object. | 1053 rietveld_obj: rietveld.Rietveld object. |
| 1055 | 1054 |
| 1056 Warning: | 1055 Warning: |
| 1057 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 1056 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |
| 1058 SHOULD be sys.stdin. | 1057 SHOULD be sys.stdin. |
| 1059 | 1058 |
| 1060 Return: | 1059 Return: |
| 1061 A PresubmitOutput object. Use output.should_continue() to figure out | 1060 A PresubmitOutput object. Use output.should_continue() to figure out |
| 1062 if there were errors or warnings and the caller should abort. | 1061 if there were errors or warnings and the caller should abort. |
| 1063 """ | 1062 """ |
| 1064 old_environ = os.environ | 1063 old_environ = os.environ |
| 1065 try: | 1064 try: |
| 1066 # Make sure python subprocesses won't generate .pyc files. | 1065 # Make sure python subprocesses won't generate .pyc files. |
| 1067 os.environ = os.environ.copy() | 1066 os.environ = os.environ.copy() |
| 1068 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' | 1067 os.environ['PYTHONDONTWRITEBYTECODE'] = '1' |
| 1069 | 1068 |
| 1070 output = PresubmitOutput(input_stream, output_stream) | 1069 output = PresubmitOutput(input_stream, output_stream) |
| 1071 if committing: | 1070 if committing: |
| 1072 output.write("Running presubmit commit checks ...\n") | 1071 output.write("Running presubmit commit checks ...\n") |
| 1073 else: | 1072 else: |
| 1074 output.write("Running presubmit upload checks ...\n") | 1073 output.write("Running presubmit upload checks ...\n") |
| 1075 start_time = time.time() | 1074 start_time = time.time() |
| 1076 presubmit_files = ListRelevantPresubmitFiles( | 1075 presubmit_files = ListRelevantPresubmitFiles( |
| 1077 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) | 1076 change.AbsoluteLocalPaths(True), change.RepositoryRoot()) |
| 1078 if not presubmit_files and verbose: | 1077 if not presubmit_files and verbose: |
| 1079 output.write("Warning, no presubmit.py found.\n") | 1078 output.write("Warning, no presubmit.py found.\n") |
| 1080 results = [] | 1079 results = [] |
| 1081 executer = PresubmitExecuter(change, committing, tbr, rietveld_obj, verbose) | 1080 executer = PresubmitExecuter(change, committing, rietveld_obj, verbose) |
| 1082 if default_presubmit: | 1081 if default_presubmit: |
| 1083 if verbose: | 1082 if verbose: |
| 1084 output.write("Running default presubmit script.\n") | 1083 output.write("Running default presubmit script.\n") |
| 1085 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1084 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
| 1086 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1085 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
| 1087 for filename in presubmit_files: | 1086 for filename in presubmit_files: |
| 1088 filename = os.path.abspath(filename) | 1087 filename = os.path.abspath(filename) |
| 1089 if verbose: | 1088 if verbose: |
| 1090 output.write("Running %s\n" % filename) | 1089 output.write("Running %s\n" % filename) |
| 1091 # Accept CRLF presubmit script. | 1090 # Accept CRLF presubmit script. |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1235 files, | 1234 files, |
| 1236 options.issue, | 1235 options.issue, |
| 1237 options.patchset, | 1236 options.patchset, |
| 1238 options.author), | 1237 options.author), |
| 1239 options.commit, | 1238 options.commit, |
| 1240 options.verbose, | 1239 options.verbose, |
| 1241 sys.stdout, | 1240 sys.stdout, |
| 1242 sys.stdin, | 1241 sys.stdin, |
| 1243 options.default_presubmit, | 1242 options.default_presubmit, |
| 1244 options.may_prompt, | 1243 options.may_prompt, |
| 1245 False, | |
| 1246 rietveld_obj) | 1244 rietveld_obj) |
| 1247 return not results.should_continue() | 1245 return not results.should_continue() |
| 1248 except PresubmitFailure, e: | 1246 except PresubmitFailure, e: |
| 1249 print >> sys.stderr, e | 1247 print >> sys.stderr, e |
| 1250 print >> sys.stderr, 'Maybe your depot_tools is out of date?' | 1248 print >> sys.stderr, 'Maybe your depot_tools is out of date?' |
| 1251 print >> sys.stderr, 'If all fails, contact maruel@' | 1249 print >> sys.stderr, 'If all fails, contact maruel@' |
| 1252 return 2 | 1250 return 2 |
| 1253 | 1251 |
| 1254 | 1252 |
| 1255 if __name__ == '__main__': | 1253 if __name__ == '__main__': |
| 1256 fix_encoding.fix_encoding() | 1254 fix_encoding.fix_encoding() |
| 1257 sys.exit(Main(None)) | 1255 sys.exit(Main(None)) |
| OLD | NEW |