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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 tbr, host_url, remove | 223 # TODO(dpranke): Update callers to pass in tbr, host_url, remove |
231 # default arguments. | 224 # default arguments. |
232 def __init__(self, change, presubmit_path, is_committing, tbr=False, | 225 def __init__(self, change, presubmit_path, is_committing, tbr, host_url=None): |
233 host_url='http://codereview.chromium.org'): | |
234 """Builds an InputApi object. | 226 """Builds an InputApi object. |
235 | 227 |
236 Args: | 228 Args: |
237 change: A presubmit.Change object. | 229 change: A presubmit.Change object. |
238 presubmit_path: The path to the presubmit script being processed. | 230 presubmit_path: The path to the presubmit script being processed. |
239 is_committing: True if the change is about to be committed. | 231 is_committing: True if the change is about to be committed. |
| 232 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
| 233 host_url: scheme, host, and path of rietveld instance |
240 """ | 234 """ |
241 # Version number of the presubmit_support script. | 235 # Version number of the presubmit_support script. |
242 self.version = [int(x) for x in __version__.split('.')] | 236 self.version = [int(x) for x in __version__.split('.')] |
243 self.change = change | 237 self.change = change |
244 self.host_url = host_url | 238 self.host_url = host_url |
245 self.is_committing = is_committing | 239 self.is_committing = is_committing |
246 self.tbr = tbr | 240 self.tbr = tbr |
| 241 self.host_url = host_url or 'http://codereview.chromium.org' |
247 | 242 |
248 # We expose various modules and functions as attributes of the input_api | 243 # We expose various modules and functions as attributes of the input_api |
249 # so that presubmit scripts don't have to import them. | 244 # so that presubmit scripts don't have to import them. |
250 self.basename = os.path.basename | 245 self.basename = os.path.basename |
251 self.cPickle = cPickle | 246 self.cPickle = cPickle |
252 self.cStringIO = cStringIO | 247 self.cStringIO = cStringIO |
253 self.json = json | 248 self.json = json |
254 self.os_path = os.path | 249 self.os_path = os.path |
255 self.pickle = pickle | 250 self.pickle = pickle |
256 self.marshal = marshal | 251 self.marshal = marshal |
(...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
928 results += executer.ExecPresubmitScript(presubmit_script) | 923 results += executer.ExecPresubmitScript(presubmit_script) |
929 | 924 |
930 slaves = list(set(results)) | 925 slaves = list(set(results)) |
931 if slaves and verbose: | 926 if slaves and verbose: |
932 output_stream.write(', '.join(slaves)) | 927 output_stream.write(', '.join(slaves)) |
933 output_stream.write('\n') | 928 output_stream.write('\n') |
934 return slaves | 929 return slaves |
935 | 930 |
936 | 931 |
937 class PresubmitExecuter(object): | 932 class PresubmitExecuter(object): |
938 def __init__(self, change, committing): | 933 def __init__(self, change, committing, tbr, host_url): |
939 """ | 934 """ |
940 Args: | 935 Args: |
941 change: The Change object. | 936 change: The Change object. |
942 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 937 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
| 938 tbr: True if '--tbr' was passed to skip any reviewer/owner checks |
| 939 host_url: scheme, host, and path of rietveld instance |
| 940 (or None for default) |
943 """ | 941 """ |
944 self.change = change | 942 self.change = change |
945 self.committing = committing | 943 self.committing = committing |
| 944 self.tbr = tbr |
| 945 self.host_url = host_url |
946 | 946 |
947 def ExecPresubmitScript(self, script_text, presubmit_path): | 947 def ExecPresubmitScript(self, script_text, presubmit_path): |
948 """Executes a single presubmit script. | 948 """Executes a single presubmit script. |
949 | 949 |
950 Args: | 950 Args: |
951 script_text: The text of the presubmit script. | 951 script_text: The text of the presubmit script. |
952 presubmit_path: The path to the presubmit file (this will be reported via | 952 presubmit_path: The path to the presubmit file (this will be reported via |
953 input_api.PresubmitLocalPath()). | 953 input_api.PresubmitLocalPath()). |
954 | 954 |
955 Return: | 955 Return: |
956 A list of result objects, empty if no problems. | 956 A list of result objects, empty if no problems. |
957 """ | 957 """ |
958 | 958 |
959 # Change to the presubmit file's directory to support local imports. | 959 # Change to the presubmit file's directory to support local imports. |
960 main_path = os.getcwd() | 960 main_path = os.getcwd() |
961 os.chdir(os.path.dirname(presubmit_path)) | 961 os.chdir(os.path.dirname(presubmit_path)) |
962 | 962 |
963 # Load the presubmit script into context. | 963 # Load the presubmit script into context. |
964 input_api = InputApi(self.change, presubmit_path, self.committing) | 964 input_api = InputApi(self.change, presubmit_path, self.committing, |
| 965 self.tbr, self.host_url) |
965 context = {} | 966 context = {} |
966 exec script_text in context | 967 exec script_text in context |
967 | 968 |
968 # These function names must change if we make substantial changes to | 969 # These function names must change if we make substantial changes to |
969 # the presubmit API that are not backwards compatible. | 970 # the presubmit API that are not backwards compatible. |
970 if self.committing: | 971 if self.committing: |
971 function_name = 'CheckChangeOnCommit' | 972 function_name = 'CheckChangeOnCommit' |
972 else: | 973 else: |
973 function_name = 'CheckChangeOnUpload' | 974 function_name = 'CheckChangeOnUpload' |
974 if function_name in context: | 975 if function_name in context: |
(...skipping 10 matching lines...) Expand all Loading... |
985 raise exceptions.RuntimeError( | 986 raise exceptions.RuntimeError( |
986 'All presubmit results must be of types derived from ' | 987 'All presubmit results must be of types derived from ' |
987 'output_api.PresubmitResult') | 988 'output_api.PresubmitResult') |
988 else: | 989 else: |
989 result = () # no error since the script doesn't care about current event. | 990 result = () # no error since the script doesn't care about current event. |
990 | 991 |
991 # Return the process to the original working directory. | 992 # Return the process to the original working directory. |
992 os.chdir(main_path) | 993 os.chdir(main_path) |
993 return result | 994 return result |
994 | 995 |
995 | 996 # TODO(dpranke): make all callers pass in tbr, host_url? |
996 def DoPresubmitChecks(change, | 997 def DoPresubmitChecks(change, |
997 committing, | 998 committing, |
998 verbose, | 999 verbose, |
999 output_stream, | 1000 output_stream, |
1000 input_stream, | 1001 input_stream, |
1001 default_presubmit, | 1002 default_presubmit, |
1002 may_prompt): | 1003 may_prompt, |
| 1004 tbr=False, |
| 1005 host_url=None): |
1003 """Runs all presubmit checks that apply to the files in the change. | 1006 """Runs all presubmit checks that apply to the files in the change. |
1004 | 1007 |
1005 This finds all PRESUBMIT.py files in directories enclosing the files in the | 1008 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 | 1009 change (up to the repository root) and calls the relevant entrypoint function |
1007 depending on whether the change is being committed or uploaded. | 1010 depending on whether the change is being committed or uploaded. |
1008 | 1011 |
1009 Prints errors, warnings and notifications. Prompts the user for warnings | 1012 Prints errors, warnings and notifications. Prompts the user for warnings |
1010 when needed. | 1013 when needed. |
1011 | 1014 |
1012 Args: | 1015 Args: |
1013 change: The Change object. | 1016 change: The Change object. |
1014 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 1017 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
1015 verbose: Prints debug info. | 1018 verbose: Prints debug info. |
1016 output_stream: A stream to write output from presubmit tests to. | 1019 output_stream: A stream to write output from presubmit tests to. |
1017 input_stream: A stream to read input from the user. | 1020 input_stream: A stream to read input from the user. |
1018 default_presubmit: A default presubmit script to execute in any case. | 1021 default_presubmit: A default presubmit script to execute in any case. |
1019 may_prompt: Enable (y/n) questions on warning or error. | 1022 may_prompt: Enable (y/n) questions on warning or error. |
| 1023 tbr: was --tbr specified to skip any reviewer/owner checks? |
| 1024 host_url: scheme, host, and port of host to use for rietveld-related |
| 1025 checks |
1020 | 1026 |
1021 Warning: | 1027 Warning: |
1022 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream | 1028 If may_prompt is true, output_stream SHOULD be sys.stdout and input_stream |
1023 SHOULD be sys.stdin. | 1029 SHOULD be sys.stdin. |
1024 | 1030 |
1025 Return: | 1031 Return: |
1026 True if execution can continue, False if not. | 1032 True if execution can continue, False if not. |
1027 """ | 1033 """ |
1028 print "Running presubmit hooks..." | 1034 print "Running presubmit hooks..." |
1029 start_time = time.time() | 1035 start_time = time.time() |
1030 presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), | 1036 presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True), |
1031 change.RepositoryRoot()) | 1037 change.RepositoryRoot()) |
1032 if not presubmit_files and verbose: | 1038 if not presubmit_files and verbose: |
1033 output_stream.write("Warning, no presubmit.py found.\n") | 1039 output_stream.write("Warning, no presubmit.py found.\n") |
1034 results = [] | 1040 results = [] |
1035 executer = PresubmitExecuter(change, committing) | 1041 executer = PresubmitExecuter(change, committing, tbr, host_url) |
1036 if default_presubmit: | 1042 if default_presubmit: |
1037 if verbose: | 1043 if verbose: |
1038 output_stream.write("Running default presubmit script.\n") | 1044 output_stream.write("Running default presubmit script.\n") |
1039 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') | 1045 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') |
1040 results += executer.ExecPresubmitScript(default_presubmit, fake_path) | 1046 results += executer.ExecPresubmitScript(default_presubmit, fake_path) |
1041 for filename in presubmit_files: | 1047 for filename in presubmit_files: |
1042 filename = os.path.abspath(filename) | 1048 filename = os.path.abspath(filename) |
1043 if verbose: | 1049 if verbose: |
1044 output_stream.write("Running %s\n" % filename) | 1050 output_stream.write("Running %s\n" % filename) |
1045 # Accept CRLF presubmit script. | 1051 # Accept CRLF presubmit script. |
(...skipping 11 matching lines...) Expand all Loading... |
1057 else: | 1063 else: |
1058 errors.append(result) | 1064 errors.append(result) |
1059 | 1065 |
1060 error_count = 0 | 1066 error_count = 0 |
1061 for name, items in (('Messages', notifications), | 1067 for name, items in (('Messages', notifications), |
1062 ('Warnings', warnings), | 1068 ('Warnings', warnings), |
1063 ('ERRORS', errors)): | 1069 ('ERRORS', errors)): |
1064 if items: | 1070 if items: |
1065 output_stream.write('** Presubmit %s **\n' % name) | 1071 output_stream.write('** Presubmit %s **\n' % name) |
1066 for item in items: | 1072 for item in items: |
1067 if not item.IsMessage(): | |
1068 continue | |
1069 | |
1070 # Access to a protected member XXX of a client class | 1073 # Access to a protected member XXX of a client class |
1071 # pylint: disable=W0212 | 1074 # pylint: disable=W0212 |
1072 if not item._Handle(output_stream, input_stream, | 1075 if not item._Handle(output_stream, input_stream, |
1073 may_prompt=False): | 1076 may_prompt=False): |
1074 error_count += 1 | 1077 error_count += 1 |
1075 output_stream.write('\n') | 1078 output_stream.write('\n') |
1076 | 1079 |
1077 total_time = time.time() - start_time | 1080 total_time = time.time() - start_time |
1078 if total_time > 1.0: | 1081 if total_time > 1.0: |
1079 print "Presubmit checks took %.1fs to calculate." % total_time | 1082 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, | 1188 options.commit, |
1186 options.verbose, | 1189 options.verbose, |
1187 sys.stdout, | 1190 sys.stdout, |
1188 sys.stdin, | 1191 sys.stdin, |
1189 options.default_presubmit, | 1192 options.default_presubmit, |
1190 options.may_prompt) | 1193 options.may_prompt) |
1191 | 1194 |
1192 | 1195 |
1193 if __name__ == '__main__': | 1196 if __name__ == '__main__': |
1194 sys.exit(Main(sys.argv)) | 1197 sys.exit(Main(sys.argv)) |
OLD | NEW |