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 |