| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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.3.5' | 9 __version__ = '1.3.5' |
| 10 | 10 |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 def IsFatal(self): | 147 def IsFatal(self): |
| 148 """An error that is fatal stops g4 mail/submit immediately, i.e. before | 148 """An error that is fatal stops g4 mail/submit immediately, i.e. before |
| 149 other presubmit scripts are run. | 149 other presubmit scripts are run. |
| 150 """ | 150 """ |
| 151 return False | 151 return False |
| 152 | 152 |
| 153 def ShouldPrompt(self): | 153 def ShouldPrompt(self): |
| 154 """Whether this presubmit result should result in a prompt warning.""" | 154 """Whether this presubmit result should result in a prompt warning.""" |
| 155 return False | 155 return False |
| 156 | 156 |
| 157 def IsMessage(self): | |
| 158 """Whether this result contains anything needing to be displayed.""" | |
| 159 return True | |
| 160 | |
| 161 class PresubmitAddText(PresubmitResult): | 157 class PresubmitAddText(PresubmitResult): |
| 162 """Propagates a line of text back to the caller.""" | 158 """Propagates a line of text back to the caller.""" |
| 163 def __init__(self, message, items=None, long_text=''): | 159 def __init__(self, message, items=None, long_text=''): |
| 164 super(OutputApi.PresubmitAddText, self).__init__("ADD: " + message, | 160 super(OutputApi.PresubmitAddText, self).__init__("ADD: " + message, |
| 165 items, long_text) | 161 items, long_text) |
| 166 | 162 |
| 167 def IsMessage(self): | |
| 168 return False | |
| 169 | |
| 170 class PresubmitError(PresubmitResult): | 163 class PresubmitError(PresubmitResult): |
| 171 """A hard presubmit error.""" | 164 """A hard presubmit error.""" |
| 172 def IsFatal(self): | 165 def IsFatal(self): |
| 173 return True | 166 return True |
| 174 | 167 |
| 175 class PresubmitPromptWarning(PresubmitResult): | 168 class PresubmitPromptWarning(PresubmitResult): |
| 176 """An warning that prompts the user if they want to continue.""" | 169 """An warning that prompts the user if they want to continue.""" |
| 177 def ShouldPrompt(self): | 170 def ShouldPrompt(self): |
| 178 return True | 171 return True |
| 179 | 172 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 r".*\bRelease[\\\/].*", | 213 r".*\bRelease[\\\/].*", |
| 221 r".*\bxcodebuild[\\\/].*", | 214 r".*\bxcodebuild[\\\/].*", |
| 222 r".*\bsconsbuild[\\\/].*", | 215 r".*\bsconsbuild[\\\/].*", |
| 223 # All caps files like README and LICENCE. | 216 # All caps files like README and LICENCE. |
| 224 r".*\b[A-Z0-9_]{2,}$", | 217 r".*\b[A-Z0-9_]{2,}$", |
| 225 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) | 218 # SCM (can happen in dual SCM configuration). (Slightly over aggressive) |
| 226 r"(|.*[\\\/])\.git[\\\/].*", | 219 r"(|.*[\\\/])\.git[\\\/].*", |
| 227 r"(|.*[\\\/])\.svn[\\\/].*", | 220 r"(|.*[\\\/])\.svn[\\\/].*", |
| 228 ) | 221 ) |
| 229 | 222 |
| 230 # TODO(dpranke): Update callers to pass in is_tbr, host_url, remove | 223 def __init__(self, change, presubmit_path, is_committing, tbr, host_url=None): |
| 231 # default arguments. | |
| 232 def __init__(self, change, presubmit_path, is_committing, is_tbr=False, | |
| 233 host_url='http://codereview.chromium.org'): | |
| 234 """Builds an InputApi object. | 224 """Builds an InputApi object. |
| 235 | 225 |
| 236 Args: | 226 Args: |
| 237 change: A presubmit.Change object. | 227 change: A presubmit.Change object. |
| 238 presubmit_path: The path to the presubmit script being processed. | 228 presubmit_path: The path to the presubmit script being processed. |
| 239 is_committing: True if the change is about to be committed. | 229 is_committing: True if the change is about to be committed. |
| 230 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
| 231 host_url: scheme, host, and path of rietveld instance |
| 240 """ | 232 """ |
| 241 # Version number of the presubmit_support script. | 233 # Version number of the presubmit_support script. |
| 242 self.version = [int(x) for x in __version__.split('.')] | 234 self.version = [int(x) for x in __version__.split('.')] |
| 243 self.change = change | 235 self.change = change |
| 244 self.host_url = host_url | 236 self.host_url = host_url |
| 245 self.is_committing = is_committing | 237 self.is_committing = is_committing |
| 246 self.is_tbr = is_tbr | 238 self.tbr = tbr |
| 239 self.host_url = host_url or 'http://codereview.chromium.org' |
| 247 | 240 |
| 248 # We expose various modules and functions as attributes of the input_api | 241 # We expose various modules and functions as attributes of the input_api |
| 249 # so that presubmit scripts don't have to import them. | 242 # so that presubmit scripts don't have to import them. |
| 250 self.basename = os.path.basename | 243 self.basename = os.path.basename |
| 251 self.cPickle = cPickle | 244 self.cPickle = cPickle |
| 252 self.cStringIO = cStringIO | 245 self.cStringIO = cStringIO |
| 253 self.json = json | 246 self.json = json |
| 254 self.os_path = os.path | 247 self.os_path = os.path |
| 255 self.pickle = pickle | 248 self.pickle = pickle |
| 256 self.marshal = marshal | 249 self.marshal = marshal |
| (...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 928 results += executer.ExecPresubmitScript(presubmit_script) | 921 results += executer.ExecPresubmitScript(presubmit_script) |
| 929 | 922 |
| 930 slaves = list(set(results)) | 923 slaves = list(set(results)) |
| 931 if slaves and verbose: | 924 if slaves and verbose: |
| 932 output_stream.write(', '.join(slaves)) | 925 output_stream.write(', '.join(slaves)) |
| 933 output_stream.write('\n') | 926 output_stream.write('\n') |
| 934 return slaves | 927 return slaves |
| 935 | 928 |
| 936 | 929 |
| 937 class PresubmitExecuter(object): | 930 class PresubmitExecuter(object): |
| 938 def __init__(self, change, committing): | 931 def __init__(self, change, committing, tbr, host_url): |
| 939 """ | 932 """ |
| 940 Args: | 933 Args: |
| 941 change: The Change object. | 934 change: The Change object. |
| 942 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 935 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 936 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
| 937 host_url: scheme, host, and path of rietveld instance |
| 938 (or None for default) |
| 943 """ | 939 """ |
| 944 self.change = change | 940 self.change = change |
| 945 self.committing = committing | 941 self.committing = committing |
| 942 self.tbr = tbr |
| 943 self.host_url = host_url |
| 946 | 944 |
| 947 def ExecPresubmitScript(self, script_text, presubmit_path): | 945 def ExecPresubmitScript(self, script_text, presubmit_path): |
| 948 """Executes a single presubmit script. | 946 """Executes a single presubmit script. |
| 949 | 947 |
| 950 Args: | 948 Args: |
| 951 script_text: The text of the presubmit script. | 949 script_text: The text of the presubmit script. |
| 952 presubmit_path: The path to the presubmit file (this will be reported via | 950 presubmit_path: The path to the presubmit file (this will be reported via |
| 953 input_api.PresubmitLocalPath()). | 951 input_api.PresubmitLocalPath()). |
| 954 | 952 |
| 955 Return: | 953 Return: |
| 956 A list of result objects, empty if no problems. | 954 A list of result objects, empty if no problems. |
| 957 """ | 955 """ |
| 958 | 956 |
| 959 # Change to the presubmit file's directory to support local imports. | 957 # Change to the presubmit file's directory to support local imports. |
| 960 main_path = os.getcwd() | 958 main_path = os.getcwd() |
| 961 os.chdir(os.path.dirname(presubmit_path)) | 959 os.chdir(os.path.dirname(presubmit_path)) |
| 962 | 960 |
| 963 # Load the presubmit script into context. | 961 # Load the presubmit script into context. |
| 964 input_api = InputApi(self.change, presubmit_path, self.committing) | 962 input_api = InputApi(self.change, presubmit_path, self.committing, |
| 963 self.tbr, self.host_url) |
| 965 context = {} | 964 context = {} |
| 966 exec script_text in context | 965 exec script_text in context |
| 967 | 966 |
| 968 # These function names must change if we make substantial changes to | 967 # These function names must change if we make substantial changes to |
| 969 # the presubmit API that are not backwards compatible. | 968 # the presubmit API that are not backwards compatible. |
| 970 if self.committing: | 969 if self.committing: |
| 971 function_name = 'CheckChangeOnCommit' | 970 function_name = 'CheckChangeOnCommit' |
| 972 else: | 971 else: |
| 973 function_name = 'CheckChangeOnUpload' | 972 function_name = 'CheckChangeOnUpload' |
| 974 if function_name in context: | 973 if function_name in context: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 985 raise exceptions.RuntimeError( | 984 raise exceptions.RuntimeError( |
| 986 'All presubmit results must be of types derived from ' | 985 'All presubmit results must be of types derived from ' |
| 987 'output_api.PresubmitResult') | 986 'output_api.PresubmitResult') |
| 988 else: | 987 else: |
| 989 result = () # no error since the script doesn't care about current event. | 988 result = () # no error since the script doesn't care about current event. |
| 990 | 989 |
| 991 # Return the process to the original working directory. | 990 # Return the process to the original working directory. |
| 992 os.chdir(main_path) | 991 os.chdir(main_path) |
| 993 return result | 992 return result |
| 994 | 993 |
| 995 | 994 # TODO(dpranke): make all callers pass in tbr, host_url? |
| 996 def DoPresubmitChecks(change, | 995 def DoPresubmitChecks(change, |
| 997 committing, | 996 committing, |
| 998 verbose, | 997 verbose, |
| 999 output_stream, | 998 output_stream, |
| 1000 input_stream, | 999 input_stream, |
| 1001 default_presubmit, | 1000 default_presubmit, |
| 1002 may_prompt): | 1001 may_prompt, |
| 1002 tbr=False, |
| 1003 host_url=None): |
| 1003 """Runs all presubmit checks that apply to the files in the change. | 1004 """Runs all presubmit checks that apply to the files in the change. |
| 1004 | 1005 |
| 1005 This finds all PRESUBMIT.py files in directories enclosing the files in the | 1006 This finds all PRESUBMIT.py files in directories enclosing the files in the |
| 1006 change (up to the repository root) and calls the relevant entrypoint function | 1007 change (up to the repository root) and calls the relevant entrypoint function |
| 1007 depending on whether the change is being committed or uploaded. | 1008 depending on whether the change is being committed or uploaded. |
| 1008 | 1009 |
| 1009 Prints errors, warnings and notifications. Prompts the user for warnings | 1010 Prints errors, warnings and notifications. Prompts the user for warnings |
| 1010 when needed. | 1011 when needed. |
| 1011 | 1012 |
| 1012 Args: | 1013 Args: |
| 1013 change: The Change object. | 1014 change: The Change object. |
| 1014 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1015 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 1015 verbose: Prints debug info. | 1016 verbose: Prints debug info. |
| 1016 output_stream: A stream to write output from presubmit tests to. | 1017 output_stream: A stream to write output from presubmit tests to. |
| 1017 input_stream: A stream to read input from the user. | 1018 input_stream: A stream to read input from the user. |
| 1018 default_presubmit: A default presubmit script to execute in any case. | 1019 default_presubmit: A default presubmit script to execute in any case. |
| 1019 may_prompt: Enable (y/n) questions on warning or error. | 1020 may_prompt: Enable (y/n) questions on warning or error. |
| 1021 tbr: was --tbr specified to skip any reviewer/owner checks? |
| 1022 host_url: scheme, host, and port of host to use for rietveld-related |
| 1023 checks |
| 1020 | 1024 |
| 1021 Warning: | 1025 Warning: |
| 1022 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 1026 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |
| 1023 SHOULD be sys.stdin. | 1027 SHOULD be sys.stdin. |
| 1024 | 1028 |
| 1025 Return: | 1029 Return: |
| 1026 True if execution can continue, False if not. | 1030 True if execution can continue, False if not. |
| 1027 """ | 1031 """ |
| 1028 print "Running presubmit hooks..." | 1032 print "Running presubmit hooks..." |
| 1029 start_time = time.time() | 1033 start_time = time.time() |
| 1030 presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), | 1034 presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), |
| 1031 change.RepositoryRoot()) | 1035 change.RepositoryRoot()) |
| 1032 if not presubmit_files and verbose: | 1036 if not presubmit_files and verbose: |
| 1033 output_stream.write("Warning, no presubmit.py found.\n") | 1037 output_stream.write("Warning, no presubmit.py found.\n") |
| 1034 results = [] | 1038 results = [] |
| 1035 executer = PresubmitExecuter(change, committing) | 1039 executer = PresubmitExecuter(change, committing, tbr, host_url) |
| 1036 if default_presubmit: | 1040 if default_presubmit: |
| 1037 if verbose: | 1041 if verbose: |
| 1038 output_stream.write("Running default presubmit script.\n") | 1042 output_stream.write("Running default presubmit script.\n") |
| 1039 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1043 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
| 1040 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1044 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
| 1041 for filename in presubmit_files: | 1045 for filename in presubmit_files: |
| 1042 filename = os.path.abspath(filename) | 1046 filename = os.path.abspath(filename) |
| 1043 if verbose: | 1047 if verbose: |
| 1044 output_stream.write("Running %s\n" % filename) | 1048 output_stream.write("Running %s\n" % filename) |
| 1045 # Accept CRLF presubmit script. | 1049 # Accept CRLF presubmit script. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1057 else: | 1061 else: |
| 1058 errors.append(result) | 1062 errors.append(result) |
| 1059 | 1063 |
| 1060 error_count = 0 | 1064 error_count = 0 |
| 1061 for name, items in (('Messages', notifications), | 1065 for name, items in (('Messages', notifications), |
| 1062 ('Warnings', warnings), | 1066 ('Warnings', warnings), |
| 1063 ('ERRORS', errors)): | 1067 ('ERRORS', errors)): |
| 1064 if items: | 1068 if items: |
| 1065 output_stream.write('** Presubmit %s **\n' % name) | 1069 output_stream.write('** Presubmit %s **\n' % name) |
| 1066 for item in items: | 1070 for item in items: |
| 1067 if not item.IsMessage(): | |
| 1068 continue | |
| 1069 | |
| 1070 # Access to a protected member XXX of a client class | 1071 # Access to a protected member XXX of a client class |
| 1071 # pylint: disable=W0212 | 1072 # pylint: disable=W0212 |
| 1072 if not item._Handle(output_stream, input_stream, | 1073 if not item._Handle(output_stream, input_stream, |
| 1073 may_prompt=False): | 1074 may_prompt=False): |
| 1074 error_count += 1 | 1075 error_count += 1 |
| 1075 output_stream.write('\n') | 1076 output_stream.write('\n') |
| 1076 | 1077 |
| 1077 total_time = time.time() - start_time | 1078 total_time = time.time() - start_time |
| 1078 if total_time > 1.0: | 1079 if total_time > 1.0: |
| 1079 print "Presubmit checks took %.1fs to calculate." % total_time | 1080 print "Presubmit checks took %.1fs to calculate." % total_time |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1185 options.commit, | 1186 options.commit, |
| 1186 options.verbose, | 1187 options.verbose, |
| 1187 sys.stdout, | 1188 sys.stdout, |
| 1188 sys.stdin, | 1189 sys.stdin, |
| 1189 options.default_presubmit, | 1190 options.default_presubmit, |
| 1190 options.may_prompt) | 1191 options.may_prompt) |
| 1191 | 1192 |
| 1192 | 1193 |
| 1193 if __name__ == '__main__': | 1194 if __name__ == '__main__': |
| 1194 sys.exit(Main(sys.argv)) | 1195 sys.exit(Main(sys.argv)) |
| OLD | NEW |