| 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 1066 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1077 if os.path.isfile(p) and re.match( | 1077 if os.path.isfile(p) and re.match( |
| 1078 r'PRESUBMIT.*\.py$', f) and not f.startswith('PRESUBMIT_test'): | 1078 r'PRESUBMIT.*\.py$', f) and not f.startswith('PRESUBMIT_test'): |
| 1079 results.append(p) | 1079 results.append(p) |
| 1080 except OSError: | 1080 except OSError: |
| 1081 pass | 1081 pass |
| 1082 | 1082 |
| 1083 logging.debug('Presubmit files: %s', ','.join(results)) | 1083 logging.debug('Presubmit files: %s', ','.join(results)) |
| 1084 return results | 1084 return results |
| 1085 | 1085 |
| 1086 | 1086 |
| 1087 class GetTrySlavesExecuter(object): | |
| 1088 @staticmethod | |
| 1089 def ExecPresubmitScript(script_text, presubmit_path, project, change): | |
| 1090 """Executes GetPreferredTrySlaves() from a single presubmit script. | |
| 1091 | |
| 1092 This will soon be deprecated and replaced by GetPreferredTryMasters(). | |
| 1093 | |
| 1094 Args: | |
| 1095 script_text: The text of the presubmit script. | |
| 1096 presubmit_path: Project script to run. | |
| 1097 project: Project name to pass to presubmit script for bot selection. | |
| 1098 | |
| 1099 Return: | |
| 1100 A list of try slaves. | |
| 1101 """ | |
| 1102 context = {} | |
| 1103 main_path = os.getcwd() | |
| 1104 try: | |
| 1105 os.chdir(os.path.dirname(presubmit_path)) | |
| 1106 exec script_text in context | |
| 1107 except Exception, e: | |
| 1108 raise PresubmitFailure('"%s" had an exception.\n%s' % (presubmit_path, e)) | |
| 1109 finally: | |
| 1110 os.chdir(main_path) | |
| 1111 | |
| 1112 function_name = 'GetPreferredTrySlaves' | |
| 1113 if function_name in context: | |
| 1114 get_preferred_try_slaves = context[function_name] | |
| 1115 function_info = inspect.getargspec(get_preferred_try_slaves) | |
| 1116 if len(function_info[0]) == 1: | |
| 1117 result = get_preferred_try_slaves(project) | |
| 1118 elif len(function_info[0]) == 2: | |
| 1119 result = get_preferred_try_slaves(project, change) | |
| 1120 else: | |
| 1121 result = get_preferred_try_slaves() | |
| 1122 if not isinstance(result, types.ListType): | |
| 1123 raise PresubmitFailure( | |
| 1124 'Presubmit functions must return a list, got a %s instead: %s' % | |
| 1125 (type(result), str(result))) | |
| 1126 for item in result: | |
| 1127 if isinstance(item, basestring): | |
| 1128 # Old-style ['bot'] format. | |
| 1129 botname = item | |
| 1130 elif isinstance(item, tuple): | |
| 1131 # New-style [('bot', set(['tests']))] format. | |
| 1132 botname = item[0] | |
| 1133 else: | |
| 1134 raise PresubmitFailure('PRESUBMIT.py returned invalid tryslave/test' | |
| 1135 ' format.') | |
| 1136 | |
| 1137 if botname != botname.strip(): | |
| 1138 raise PresubmitFailure( | |
| 1139 'Try slave names cannot start/end with whitespace') | |
| 1140 if ',' in botname: | |
| 1141 raise PresubmitFailure( | |
| 1142 'Do not use \',\' separated builder or test names: %s' % botname) | |
| 1143 else: | |
| 1144 result = [] | |
| 1145 | |
| 1146 def valid_oldstyle(result): | |
| 1147 return all(isinstance(i, basestring) for i in result) | |
| 1148 | |
| 1149 def valid_newstyle(result): | |
| 1150 return (all(isinstance(i, tuple) for i in result) and | |
| 1151 all(len(i) == 2 for i in result) and | |
| 1152 all(isinstance(i[0], basestring) for i in result) and | |
| 1153 all(isinstance(i[1], set) for i in result) | |
| 1154 ) | |
| 1155 | |
| 1156 # Ensure it's either all old-style or all new-style. | |
| 1157 if not valid_oldstyle(result) and not valid_newstyle(result): | |
| 1158 raise PresubmitFailure( | |
| 1159 'PRESUBMIT.py returned invalid trybot specification!') | |
| 1160 | |
| 1161 return result | |
| 1162 | |
| 1163 | |
| 1164 class GetTryMastersExecuter(object): | 1087 class GetTryMastersExecuter(object): |
| 1165 @staticmethod | 1088 @staticmethod |
| 1166 def ExecPresubmitScript(script_text, presubmit_path, project, change): | 1089 def ExecPresubmitScript(script_text, presubmit_path, project, change): |
| 1167 """Executes GetPreferredTryMasters() from a single presubmit script. | 1090 """Executes GetPreferredTryMasters() from a single presubmit script. |
| 1168 | 1091 |
| 1169 Args: | 1092 Args: |
| 1170 script_text: The text of the presubmit script. | 1093 script_text: The text of the presubmit script. |
| 1171 presubmit_path: Project script to run. | 1094 presubmit_path: Project script to run. |
| 1172 project: Project name to pass to presubmit script for bot selection. | 1095 project: Project name to pass to presubmit script for bot selection. |
| 1173 | 1096 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1215 function_name = 'PostUploadHook' | 1138 function_name = 'PostUploadHook' |
| 1216 if function_name not in context: | 1139 if function_name not in context: |
| 1217 return {} | 1140 return {} |
| 1218 post_upload_hook = context[function_name] | 1141 post_upload_hook = context[function_name] |
| 1219 if not len(inspect.getargspec(post_upload_hook)[0]) == 3: | 1142 if not len(inspect.getargspec(post_upload_hook)[0]) == 3: |
| 1220 raise PresubmitFailure( | 1143 raise PresubmitFailure( |
| 1221 'Expected function "PostUploadHook" to take three arguments.') | 1144 'Expected function "PostUploadHook" to take three arguments.') |
| 1222 return post_upload_hook(cl, change, OutputApi(False)) | 1145 return post_upload_hook(cl, change, OutputApi(False)) |
| 1223 | 1146 |
| 1224 | 1147 |
| 1225 def DoGetTrySlaves(change, | |
| 1226 changed_files, | |
| 1227 repository_root, | |
| 1228 default_presubmit, | |
| 1229 project, | |
| 1230 verbose, | |
| 1231 output_stream): | |
| 1232 """Get the list of try servers from the presubmit scripts (deprecated). | |
| 1233 | |
| 1234 Args: | |
| 1235 changed_files: List of modified files. | |
| 1236 repository_root: The repository root. | |
| 1237 default_presubmit: A default presubmit script to execute in any case. | |
| 1238 project: Optional name of a project used in selecting trybots. | |
| 1239 verbose: Prints debug info. | |
| 1240 output_stream: A stream to write debug output to. | |
| 1241 | |
| 1242 Return: | |
| 1243 List of try slaves | |
| 1244 """ | |
| 1245 presubmit_files = ListRelevantPresubmitFiles(changed_files, repository_root) | |
| 1246 if not presubmit_files and verbose: | |
| 1247 output_stream.write("Warning, no PRESUBMIT.py found.\n") | |
| 1248 results = [] | |
| 1249 executer = GetTrySlavesExecuter() | |
| 1250 | |
| 1251 if default_presubmit: | |
| 1252 if verbose: | |
| 1253 output_stream.write("Running default presubmit script.\n") | |
| 1254 fake_path = os.path.join(repository_root, 'PRESUBMIT.py') | |
| 1255 results.extend(executer.ExecPresubmitScript( | |
| 1256 default_presubmit, fake_path, project, change)) | |
| 1257 for filename in presubmit_files: | |
| 1258 filename = os.path.abspath(filename) | |
| 1259 if verbose: | |
| 1260 output_stream.write("Running %s\n" % filename) | |
| 1261 # Accept CRLF presubmit script. | |
| 1262 presubmit_script = gclient_utils.FileRead(filename, 'rU') | |
| 1263 results.extend(executer.ExecPresubmitScript( | |
| 1264 presubmit_script, filename, project, change)) | |
| 1265 | |
| 1266 | |
| 1267 slave_dict = {} | |
| 1268 old_style = filter(lambda x: isinstance(x, basestring), results) | |
| 1269 new_style = filter(lambda x: isinstance(x, tuple), results) | |
| 1270 | |
| 1271 for result in new_style: | |
| 1272 slave_dict.setdefault(result[0], set()).update(result[1]) | |
| 1273 slaves = list(slave_dict.items()) | |
| 1274 | |
| 1275 slaves.extend(set(old_style)) | |
| 1276 | |
| 1277 if slaves and verbose: | |
| 1278 output_stream.write(', '.join((str(x) for x in slaves))) | |
| 1279 output_stream.write('\n') | |
| 1280 return slaves | |
| 1281 | |
| 1282 | |
| 1283 def _MergeMasters(masters1, masters2): | 1148 def _MergeMasters(masters1, masters2): |
| 1284 """Merges two master maps. Merges also the tests of each builder.""" | 1149 """Merges two master maps. Merges also the tests of each builder.""" |
| 1285 result = {} | 1150 result = {} |
| 1286 for (master, builders) in itertools.chain(masters1.iteritems(), | 1151 for (master, builders) in itertools.chain(masters1.iteritems(), |
| 1287 masters2.iteritems()): | 1152 masters2.iteritems()): |
| 1288 new_builders = result.setdefault(master, {}) | 1153 new_builders = result.setdefault(master, {}) |
| 1289 for (builder, tests) in builders.iteritems(): | 1154 for (builder, tests) in builders.iteritems(): |
| 1290 new_builders.setdefault(builder, set([])).update(tests) | 1155 new_builders.setdefault(builder, set([])).update(tests) |
| 1291 return result | 1156 return result |
| 1292 | 1157 |
| (...skipping 512 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1805 return 2 | 1670 return 2 |
| 1806 | 1671 |
| 1807 | 1672 |
| 1808 if __name__ == '__main__': | 1673 if __name__ == '__main__': |
| 1809 fix_encoding.fix_encoding() | 1674 fix_encoding.fix_encoding() |
| 1810 try: | 1675 try: |
| 1811 sys.exit(main()) | 1676 sys.exit(main()) |
| 1812 except KeyboardInterrupt: | 1677 except KeyboardInterrupt: |
| 1813 sys.stderr.write('interrupted\n') | 1678 sys.stderr.write('interrupted\n') |
| 1814 sys.exit(2) | 1679 sys.exit(2) |
| OLD | NEW |