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 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
7 | 7 |
8 """A git-command for integrating reviews on Rietveld and Gerrit.""" | 8 """A git-command for integrating reviews on Rietveld and Gerrit.""" |
9 | 9 |
10 from __future__ import print_function | 10 from __future__ import print_function |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
334 raise httplib2.HttpLib2Error(content) | 334 raise httplib2.HttpLib2Error(content) |
335 | 335 |
336 # status >= 500 means transient failures. | 336 # status >= 500 means transient failures. |
337 logging.debug('Transient errors when %s. Will retry.', operation_name) | 337 logging.debug('Transient errors when %s. Will retry.', operation_name) |
338 time_sleep(0.5 + 1.5*try_count) | 338 time_sleep(0.5 + 1.5*try_count) |
339 try_count += 1 | 339 try_count += 1 |
340 assert False, 'unreachable' | 340 assert False, 'unreachable' |
341 | 341 |
342 | 342 |
343 def _get_bucket_map(changelist, options, option_parser): | 343 def _get_bucket_map(changelist, options, option_parser): |
344 """Returns a dict mapping bucket names (or master names) to | 344 """Returns a dict mapping bucket names to builders and tests, |
345 builders and tests, for triggering try jobs. | 345 for triggering try jobs. |
346 """ | 346 """ |
| 347 # If no bots are listed, we try to get a set of builders and tests based |
| 348 # on GetPreferredTryMasters functions in PRESUBMIT.py files. |
347 if not options.bot: | 349 if not options.bot: |
348 change = changelist.GetChange( | 350 change = changelist.GetChange( |
349 changelist.GetCommonAncestorWithUpstream(), None) | 351 changelist.GetCommonAncestorWithUpstream(), None) |
350 | 352 |
351 # Get try masters from PRESUBMIT.py files. | |
352 masters = presubmit_support.DoGetTryMasters( | 353 masters = presubmit_support.DoGetTryMasters( |
353 change=change, | 354 change=change, |
354 changed_files=change.LocalPaths(), | 355 changed_files=change.LocalPaths(), |
355 repository_root=settings.GetRoot(), | 356 repository_root=settings.GetRoot(), |
356 default_presubmit=None, | 357 default_presubmit=None, |
357 project=None, | 358 project=None, |
358 verbose=options.verbose, | 359 verbose=options.verbose, |
359 output_stream=sys.stdout) | 360 output_stream=sys.stdout) |
360 | 361 |
361 if masters: | 362 if masters: |
362 return masters | 363 return masters |
363 | 364 |
364 # Fall back to deprecated method: get try slaves from PRESUBMIT.py | 365 # Fall back to deprecated method: get try slaves from PRESUBMIT.py |
365 # files. | 366 # files. |
366 # TODO(qyearsley): Remove this. | 367 # TODO(qyearsley): Remove this. |
367 options.bot = presubmit_support.DoGetTrySlaves( | 368 options.bot = presubmit_support.DoGetTrySlaves( |
368 change=change, | 369 change=change, |
369 changed_files=change.LocalPaths(), | 370 changed_files=change.LocalPaths(), |
370 repository_root=settings.GetRoot(), | 371 repository_root=settings.GetRoot(), |
371 default_presubmit=None, | 372 default_presubmit=None, |
372 project=None, | 373 project=None, |
373 verbose=options.verbose, | 374 verbose=options.verbose, |
374 output_stream=sys.stdout) | 375 output_stream=sys.stdout) |
375 | 376 |
376 if not options.bot: | 377 if not options.bot: |
377 return {} | 378 return {} |
378 | 379 |
| 380 # If a bucket or master is passed, then we assume all bots are under |
| 381 # that one master. |
379 if options.bucket: | 382 if options.bucket: |
380 return {options.bucket: {b: [] for b in options.bot}} | 383 return {options.bucket: {b: [] for b in options.bot}} |
| 384 if options.master: |
| 385 return {_prefix_master(options.master): {b: [] for b in options.bot}} |
381 | 386 |
382 if not options.master: | 387 # If bots are listed but no master or bucket, then we need to find out |
383 bucket_map, error_message = _get_bucket_map_for_builders(options.bot) | 388 # the corresponding master for each bot. |
384 if error_message: | 389 bucket_map, error_message = _get_bucket_map_for_builders(options.bot) |
385 option_parser.error( | 390 if error_message: |
386 'Tryserver master cannot be found because: %s\n' | 391 option_parser.error( |
387 'Please manually specify the tryserver master, e.g. ' | 392 'Tryserver master cannot be found because: %s\n' |
388 '"-m tryserver.chromium.linux".' % error_message) | 393 'Please manually specify the tryserver master, e.g. ' |
389 return bucket_map | 394 '"-m tryserver.chromium.linux".' % error_message) |
390 | 395 return bucket_map |
391 builders_and_tests = {} | |
392 | |
393 # TODO(machenbach): The old style command-line options don't support | |
394 # multiple try masters yet. | |
395 # TODO(qyearsley): If options.bot is always a list of strings, then | |
396 # "new_style" never applies, and so we should remove support for Specifying | |
397 # test filters completely. | |
398 old_style = filter(lambda x: isinstance(x, basestring), options.bot) | |
399 new_style = filter(lambda x: isinstance(x, tuple), options.bot) | |
400 | |
401 for bot in old_style: | |
402 if ':' in bot: | |
403 option_parser.error('Specifying testfilter is no longer supported') | |
404 elif ',' in bot: | |
405 option_parser.error('Specify one bot per --bot flag') | |
406 else: | |
407 builders_and_tests.setdefault(bot, []) | |
408 | |
409 for bot, tests in new_style: | |
410 builders_and_tests.setdefault(bot, []).extend(tests) | |
411 | |
412 # Add the "master." prefix to the master name to obtain the bucket name. | |
413 bucket = _prefix_master(options.master) | |
414 return {bucket: builders_and_tests} | |
415 | 396 |
416 | 397 |
417 def _get_bucket_map_for_builders(builders): | 398 def _get_bucket_map_for_builders(builders): |
418 """Returns a map of buckets to builders for the given builders.""" | 399 """Returns a map of buckets to builders for the given builders.""" |
419 map_url = 'https://builders-map.appspot.com/' | 400 map_url = 'https://builders-map.appspot.com/' |
420 try: | 401 try: |
421 builders_map = json.load(urllib2.urlopen(map_url)) | 402 builders_map = json.load(urllib2.urlopen(map_url)) |
422 except urllib2.URLError as e: | 403 except urllib2.URLError as e: |
423 return None, ('Failed to fetch builder-to-master map from %s. Error: %s.' % | 404 return None, ('Failed to fetch builder-to-master map from %s. Error: %s.' % |
424 (map_url, e)) | 405 (map_url, e)) |
425 except ValueError as e: | 406 except ValueError as e: |
426 return None, ('Invalid json string from %s. Error: %s.' % (map_url, e)) | 407 return None, ('Invalid json string from %s. Error: %s.' % (map_url, e)) |
427 if not builders_map: | 408 if not builders_map: |
428 return None, 'Failed to build master map.' | 409 return None, 'Failed to build master map.' |
429 | 410 |
430 bucket_map = {} | 411 bucket_map = {} |
431 for builder in builders: | 412 for builder in builders: |
432 builder = builder.split(':', 1)[0] | |
433 masters = builders_map.get(builder, []) | 413 masters = builders_map.get(builder, []) |
434 if not masters: | 414 if not masters: |
435 return None, ('No matching master for builder %s.' % builder) | 415 return None, ('No matching master for builder %s.' % builder) |
436 if len(masters) > 1: | 416 if len(masters) > 1: |
437 return None, ('The builder name %s exists in multiple masters %s.' % | 417 return None, ('The builder name %s exists in multiple masters %s.' % |
438 (builder, masters)) | 418 (builder, masters)) |
439 bucket = _prefix_master(masters[0]) | 419 bucket = _prefix_master(masters[0]) |
440 bucket_map.setdefault(bucket, {})[builder] = [] | 420 bucket_map.setdefault(bucket, {})[builder] = [] |
441 | 421 |
442 return bucket_map, None | 422 return bucket_map, None |
(...skipping 4420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4863 | 4843 |
4864 error_message = cl.CannotTriggerTryJobReason() | 4844 error_message = cl.CannotTriggerTryJobReason() |
4865 if error_message: | 4845 if error_message: |
4866 parser.error('Can\'t trigger try jobs: %s' % error_message) | 4846 parser.error('Can\'t trigger try jobs: %s' % error_message) |
4867 | 4847 |
4868 if options.bucket and options.master: | 4848 if options.bucket and options.master: |
4869 parser.error('Only one of --bucket and --master may be used.') | 4849 parser.error('Only one of --bucket and --master may be used.') |
4870 | 4850 |
4871 buckets = _get_bucket_map(cl, options, parser) | 4851 buckets = _get_bucket_map(cl, options, parser) |
4872 | 4852 |
| 4853 # If no bots are listed and we couldn't get a list based on PRESUBMIT files, |
| 4854 # then we default to triggering a CQ dry run (see http://crbug.com/625697). |
4873 if not buckets: | 4855 if not buckets: |
4874 # Default to triggering Dry Run (see http://crbug.com/625697). | |
4875 if options.verbose: | 4856 if options.verbose: |
4876 print('git cl try with no bots now defaults to CQ Dry Run.') | 4857 print('git cl try with no bots now defaults to CQ Dry Run.') |
4877 return cl.TriggerDryRun() | 4858 return cl.TriggerDryRun() |
4878 | 4859 |
4879 for builders in buckets.itervalues(): | 4860 for builders in buckets.itervalues(): |
4880 if any('triggered' in b for b in builders): | 4861 if any('triggered' in b for b in builders): |
4881 print('ERROR You are trying to send a job to a triggered bot. This type ' | 4862 print('ERROR You are trying to send a job to a triggered bot. This type ' |
4882 'of bot requires an initial job from a parent (usually a builder). ' | 4863 'of bot requires an initial job from a parent (usually a builder). ' |
4883 'Instead send your job to the parent.\n' | 4864 'Instead send your job to the parent.\n' |
4884 'Bot list: %s' % builders, file=sys.stderr) | 4865 'Bot list: %s' % builders, file=sys.stderr) |
(...skipping 510 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5395 if __name__ == '__main__': | 5376 if __name__ == '__main__': |
5396 # These affect sys.stdout so do it outside of main() to simplify mocks in | 5377 # These affect sys.stdout so do it outside of main() to simplify mocks in |
5397 # unit testing. | 5378 # unit testing. |
5398 fix_encoding.fix_encoding() | 5379 fix_encoding.fix_encoding() |
5399 setup_color.init() | 5380 setup_color.init() |
5400 try: | 5381 try: |
5401 sys.exit(main(sys.argv[1:])) | 5382 sys.exit(main(sys.argv[1:])) |
5402 except KeyboardInterrupt: | 5383 except KeyboardInterrupt: |
5403 sys.stderr.write('interrupted\n') | 5384 sys.stderr.write('interrupted\n') |
5404 sys.exit(1) | 5385 sys.exit(1) |
OLD | NEW |