| 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 |