Chromium Code Reviews| 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 321 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 332 if response.status < 500 or try_count >= 2: | 332 if response.status < 500 or try_count >= 2: |
| 333 raise httplib2.HttpLib2Error(content) | 333 raise httplib2.HttpLib2Error(content) |
| 334 | 334 |
| 335 # status >= 500 means transient failures. | 335 # status >= 500 means transient failures. |
| 336 logging.debug('Transient errors when %s. Will retry.', operation_name) | 336 logging.debug('Transient errors when %s. Will retry.', operation_name) |
| 337 time_sleep(0.5 + 1.5*try_count) | 337 time_sleep(0.5 + 1.5*try_count) |
| 338 try_count += 1 | 338 try_count += 1 |
| 339 assert False, 'unreachable' | 339 assert False, 'unreachable' |
| 340 | 340 |
| 341 | 341 |
| 342 def _get_bucket_map(changelist, options, option_parser): | |
| 343 """Returns a dict mapping bucket names (or master names) to | |
| 344 builders and tests, for triggering try jobs. | |
| 345 """ | |
| 346 if not options.bot: | |
| 347 change = changelist.GetChange( | |
| 348 changelist.GetCommonAncestorWithUpstream(), None) | |
| 349 | |
| 350 # Get try masters from PRESUBMIT.py files. | |
| 351 masters = presubmit_support.DoGetTryMasters( | |
| 352 change=change, | |
| 353 changed_files=change.LocalPaths(), | |
| 354 repository_root=settings.GetRoot(), | |
| 355 default_presubmit=None, | |
| 356 project=None, | |
| 357 verbose=options.verbose, | |
| 358 output_stream=sys.stdout) | |
| 359 | |
| 360 if masters: | |
| 361 return masters | |
| 362 | |
| 363 # Fall back to deprecated method: get try slaves from PRESUBMIT.py | |
|
tandrii(chromium)
2016/10/23 16:55:55
we should kill this, it has lived for too long, bu
qyearsley
2016/10/24 19:09:52
Sounds good!
| |
| 364 # files. | |
| 365 options.bot = presubmit_support.DoGetTrySlaves( | |
| 366 change=change, | |
| 367 changed_files=change.LocalPaths(), | |
| 368 repository_root=settings.GetRoot(), | |
| 369 default_presubmit=None, | |
| 370 project=None, | |
| 371 verbose=options.verbose, | |
| 372 output_stream=sys.stdout) | |
| 373 | |
| 374 if not options.bot: | |
| 375 return {} | |
| 376 | |
| 377 if options.bucket: | |
| 378 return {options.bucket: {b: [] for b in options.bot}} | |
| 379 | |
| 380 builders_and_tests = {} | |
| 381 | |
| 382 # TODO(machenbach): The old style command-line options don't support | |
| 383 # multiple try masters yet. | |
| 384 old_style = filter(lambda x: isinstance(x, basestring), options.bot) | |
| 385 new_style = filter(lambda x: isinstance(x, tuple), options.bot) | |
| 386 | |
| 387 for bot in old_style: | |
| 388 if ':' in bot: | |
| 389 option_parser.error('Specifying testfilter is no longer supported') | |
| 390 elif ',' in bot: | |
| 391 option_parser.error('Specify one bot per --bot flag') | |
| 392 else: | |
| 393 builders_and_tests.setdefault(bot, []) | |
| 394 | |
| 395 for bot, tests in new_style: | |
| 396 builders_and_tests.setdefault(bot, []).extend(tests) | |
| 397 | |
| 398 if not options.master: | |
| 399 # TODO(crbug.com/640740): git cl try should be able to trigger | |
|
tandrii(chromium)
2016/10/23 16:55:55
lol :) I think styleguide mandates TODO(qyearsley)
qyearsley
2016/10/24 19:09:51
Ah, makes sense; done.
| |
| 400 # builders on multiple masters if no master is given. | |
| 401 options.master, error_message = _get_builder_master(options.bot) | |
| 402 if error_message: | |
| 403 option_parser.error( | |
| 404 'Tryserver master cannot be found because: %s\n' | |
| 405 'Please manually specify the tryserver master, e.g. ' | |
| 406 '"-m tryserver.chromium.linux".' % error_message) | |
|
qyearsley
2016/10/22 19:14:31
Note: This block is moved from lines 4810-4815 and
| |
| 407 | |
| 408 # Return a master map with one master to be backwards compatible. The | |
| 409 # master name defaults to an empty string, which will cause the master | |
| 410 # not to be set on rietveld (deprecated). | |
| 411 bucket = '' | |
| 412 if options.master: | |
| 413 # Add the "master." prefix to the master name to obtain the bucket name. | |
| 414 bucket = _prefix_master(options.master) | |
| 415 return {bucket: builders_and_tests} | |
| 416 | |
| 417 | |
| 418 def _get_builder_master(bot_list): | |
| 419 """Fetches a master for the given list of builders. | |
| 420 | |
| 421 Returns a pair (master, error_message), where either master or | |
| 422 error_message is None. | |
| 423 """ | |
| 424 map_url = 'https://builders-map.appspot.com/' | |
| 425 try: | |
| 426 master_map = json.load(urllib2.urlopen(map_url)) | |
| 427 except urllib2.URLError as e: | |
| 428 return None, ('Failed to fetch builder-to-master map from %s. Error: %s.' % | |
| 429 (map_url, e)) | |
| 430 except ValueError as e: | |
| 431 return None, ('Invalid json string from %s. Error: %s.' % (map_url, e)) | |
| 432 if not master_map: | |
| 433 return None, 'Failed to build master map.' | |
| 434 | |
| 435 result_master = '' | |
| 436 for bot in bot_list: | |
| 437 builder = bot.split(':', 1)[0] | |
| 438 master_list = master_map.get(builder, []) | |
| 439 if not master_list: | |
| 440 return None, ('No matching master for builder %s.' % builder) | |
| 441 elif len(master_list) > 1: | |
| 442 return None, ('The builder name %s exists in multiple masters %s.' % | |
| 443 (builder, master_list)) | |
| 444 else: | |
| 445 cur_master = master_list[0] | |
| 446 if not result_master: | |
| 447 result_master = cur_master | |
| 448 elif result_master != cur_master: | |
| 449 return None, 'The builders do not belong to the same master.' | |
| 450 return result_master, None | |
|
qyearsley
2016/10/22 19:14:31
This function is moved from below and renamed; in
tandrii(chromium)
2016/10/23 16:55:55
SGTM. +1 for "_" name.
| |
| 451 | |
| 452 | |
| 342 def _trigger_try_jobs(auth_config, changelist, buckets, options, | 453 def _trigger_try_jobs(auth_config, changelist, buckets, options, |
| 343 category='git_cl_try', patchset=None): | 454 category='git_cl_try', patchset=None): |
| 455 """Sends a request to Buildbucket to trigger try jobs for a changelist. | |
| 456 | |
| 457 Args: | |
| 458 auth_config: AuthConfig for Rietveld. | |
| 459 changelist: Changelist that the try jobs are associated with. | |
| 460 buckets: A nested dict mapping bucket names to builders to tests. | |
| 461 options: Command-line options. | |
| 462 """ | |
| 344 assert changelist.GetIssue(), 'CL must be uploaded first' | 463 assert changelist.GetIssue(), 'CL must be uploaded first' |
| 345 codereview_url = changelist.GetCodereviewServer() | 464 codereview_url = changelist.GetCodereviewServer() |
| 346 assert codereview_url, 'CL must be uploaded first' | 465 assert codereview_url, 'CL must be uploaded first' |
| 347 patchset = patchset or changelist.GetMostRecentPatchset() | 466 patchset = patchset or changelist.GetMostRecentPatchset() |
| 348 assert patchset, 'CL must be uploaded first' | 467 assert patchset, 'CL must be uploaded first' |
| 349 | 468 |
| 350 codereview_host = urlparse.urlparse(codereview_url).hostname | 469 codereview_host = urlparse.urlparse(codereview_url).hostname |
| 351 authenticator = auth.get_authenticator_for_host(codereview_host, auth_config) | 470 authenticator = auth.get_authenticator_for_host(codereview_host, auth_config) |
| 352 http = authenticator.authorize(httplib2.Http()) | 471 http = authenticator.authorize(httplib2.Http()) |
| 353 http.force_exception_to_status_code = True | 472 http.force_exception_to_status_code = True |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 427 buildbucket_put_url, | 546 buildbucket_put_url, |
| 428 'PUT', | 547 'PUT', |
| 429 body=json.dumps(batch_req_body), | 548 body=json.dumps(batch_req_body), |
| 430 headers={'Content-Type': 'application/json'} | 549 headers={'Content-Type': 'application/json'} |
| 431 ) | 550 ) |
| 432 print_text.append('To see results here, run: git cl try-results') | 551 print_text.append('To see results here, run: git cl try-results') |
| 433 print_text.append('To see results in browser, run: git cl web') | 552 print_text.append('To see results in browser, run: git cl web') |
| 434 print('\n'.join(print_text)) | 553 print('\n'.join(print_text)) |
| 435 | 554 |
| 436 | 555 |
| 556 def _trigger_dry_run(changelist): | |
| 557 """Triggers a dry run and prints a warning on failure.""" | |
| 558 try: | |
| 559 changelist.SetCQState(_CQState.DRY_RUN) | |
| 560 print('scheduled CQ Dry Run on %s' % changelist.GetIssueURL()) | |
| 561 return 0 | |
| 562 except KeyboardInterrupt: | |
| 563 raise | |
| 564 except: | |
| 565 print('WARNING: failed to trigger CQ Dry Run.\n' | |
| 566 'Either:\n' | |
| 567 ' * your project has no CQ\n' | |
| 568 ' * you don\'t have permission to trigger Dry Run\n' | |
| 569 ' * bug in this code (see stack trace below).\n' | |
| 570 'Consider specifying which bots to trigger manually ' | |
| 571 'or asking your project owners for permissions ' | |
| 572 'or contacting Chrome Infrastructure team at ' | |
| 573 'https://www.chromium.org/infra\n\n') | |
| 574 # Still raise exception so that stack trace is printed. | |
| 575 raise | |
|
qyearsley
2016/10/22 19:14:31
This block is extracted from below; potentially, i
tandrii(chromium)
2016/10/23 16:55:55
SGTM, how about making it method of ChangeList its
qyearsley
2016/10/24 19:09:51
Done, and added TODO note to make use of this func
| |
| 576 | |
| 577 | |
| 437 def fetch_try_jobs(auth_config, changelist, buildbucket_host, | 578 def fetch_try_jobs(auth_config, changelist, buildbucket_host, |
| 438 patchset=None): | 579 patchset=None): |
| 439 """Fetches try jobs from buildbucket. | 580 """Fetches try jobs from buildbucket. |
| 440 | 581 |
| 441 Returns a map from build id to build info as a dictionary. | 582 Returns a map from build id to build info as a dictionary. |
| 442 """ | 583 """ |
| 443 assert buildbucket_host | 584 assert buildbucket_host |
| 444 assert changelist.GetIssue(), 'CL must be uploaded first' | 585 assert changelist.GetIssue(), 'CL must be uploaded first' |
| 445 assert changelist.GetCodereviewServer(), 'CL must be uploaded first' | 586 assert changelist.GetCodereviewServer(), 'CL must be uploaded first' |
| 446 patchset = patchset or changelist.GetMostRecentPatchset() | 587 patchset = patchset or changelist.GetMostRecentPatchset() |
| (...skipping 4229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4676 """Fetches the tree status from a json url and returns the message | 4817 """Fetches the tree status from a json url and returns the message |
| 4677 with the reason for the tree to be opened or closed.""" | 4818 with the reason for the tree to be opened or closed.""" |
| 4678 url = settings.GetTreeStatusUrl() | 4819 url = settings.GetTreeStatusUrl() |
| 4679 json_url = urlparse.urljoin(url, '/current?format=json') | 4820 json_url = urlparse.urljoin(url, '/current?format=json') |
| 4680 connection = urllib2.urlopen(json_url) | 4821 connection = urllib2.urlopen(json_url) |
| 4681 status = json.loads(connection.read()) | 4822 status = json.loads(connection.read()) |
| 4682 connection.close() | 4823 connection.close() |
| 4683 return status['message'] | 4824 return status['message'] |
| 4684 | 4825 |
| 4685 | 4826 |
| 4686 def GetBuilderMaster(bot_list): | |
| 4687 """For a given builder, fetch the master from AE if available.""" | |
| 4688 map_url = 'https://builders-map.appspot.com/' | |
| 4689 try: | |
| 4690 master_map = json.load(urllib2.urlopen(map_url)) | |
| 4691 except urllib2.URLError as e: | |
| 4692 return None, ('Failed to fetch builder-to-master map from %s. Error: %s.' % | |
| 4693 (map_url, e)) | |
| 4694 except ValueError as e: | |
| 4695 return None, ('Invalid json string from %s. Error: %s.' % (map_url, e)) | |
| 4696 if not master_map: | |
| 4697 return None, 'Failed to build master map.' | |
| 4698 | |
| 4699 result_master = '' | |
| 4700 for bot in bot_list: | |
| 4701 builder = bot.split(':', 1)[0] | |
| 4702 master_list = master_map.get(builder, []) | |
| 4703 if not master_list: | |
| 4704 return None, ('No matching master for builder %s.' % builder) | |
| 4705 elif len(master_list) > 1: | |
| 4706 return None, ('The builder name %s exists in multiple masters %s.' % | |
| 4707 (builder, master_list)) | |
| 4708 else: | |
| 4709 cur_master = master_list[0] | |
| 4710 if not result_master: | |
| 4711 result_master = cur_master | |
| 4712 elif result_master != cur_master: | |
| 4713 return None, 'The builders do not belong to the same master.' | |
| 4714 return result_master, None | |
| 4715 | |
| 4716 | |
| 4717 def CMDtree(parser, args): | 4827 def CMDtree(parser, args): |
| 4718 """Shows the status of the tree.""" | 4828 """Shows the status of the tree.""" |
| 4719 _, args = parser.parse_args(args) | 4829 _, args = parser.parse_args(args) |
| 4720 status = GetTreeStatus() | 4830 status = GetTreeStatus() |
| 4721 if 'unset' == status: | 4831 if 'unset' == status: |
| 4722 print('You must configure your tree status URL by running "git cl config".') | 4832 print('You must configure your tree status URL by running "git cl config".') |
| 4723 return 2 | 4833 return 2 |
| 4724 | 4834 |
| 4725 print('The tree is %s' % status) | 4835 print('The tree is %s' % status) |
| 4726 print() | 4836 print() |
| 4727 print(GetTreeStatusReason()) | 4837 print(GetTreeStatusReason()) |
| 4728 if status != 'open': | 4838 if status != 'open': |
| 4729 return 1 | 4839 return 1 |
| 4730 return 0 | 4840 return 0 |
| 4731 | 4841 |
| 4732 | 4842 |
| 4733 def CMDtry(parser, args): | 4843 def CMDtry(parser, args): |
| 4734 """Triggers try jobs using CQ dry run or BuildBucket for individual builders. | 4844 """Triggers try jobs using either BuildBucket or CQ dry run.""" |
| 4735 """ | |
| 4736 group = optparse.OptionGroup(parser, 'Try job options') | 4845 group = optparse.OptionGroup(parser, 'Try job options') |
| 4737 group.add_option( | 4846 group.add_option( |
| 4738 '-b', '--bot', action='append', | 4847 '-b', '--bot', action='append', |
| 4739 help=('IMPORTANT: specify ONE builder per --bot flag. Use it multiple ' | 4848 help=('IMPORTANT: specify ONE builder per --bot flag. Use it multiple ' |
| 4740 'times to specify multiple builders. ex: ' | 4849 'times to specify multiple builders. ex: ' |
| 4741 '"-b win_rel -b win_layout". See ' | 4850 '"-b win_rel -b win_layout". See ' |
| 4742 'the try server waterfall for the builders name and the tests ' | 4851 'the try server waterfall for the builders name and the tests ' |
| 4743 'available.')) | 4852 'available.')) |
| 4744 group.add_option( | 4853 group.add_option( |
| 4745 '-B', '--bucket', default='', | 4854 '-B', '--bucket', default='', |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4800 error_message = cl.CannotTriggerTryJobReason() | 4909 error_message = cl.CannotTriggerTryJobReason() |
| 4801 if error_message: | 4910 if error_message: |
| 4802 parser.error('Can\'t trigger try jobs: %s') | 4911 parser.error('Can\'t trigger try jobs: %s') |
| 4803 | 4912 |
| 4804 if not options.name: | 4913 if not options.name: |
| 4805 options.name = cl.GetBranch() | 4914 options.name = cl.GetBranch() |
| 4806 | 4915 |
| 4807 if options.bucket and options.master: | 4916 if options.bucket and options.master: |
| 4808 parser.error('Only one of --bucket and --master may be used.') | 4917 parser.error('Only one of --bucket and --master may be used.') |
| 4809 | 4918 |
| 4810 if options.bot and not options.master and not options.bucket: | 4919 buckets = _get_bucket_map(cl, options, parser) |
| 4811 options.master, err_msg = GetBuilderMaster(options.bot) | |
| 4812 if err_msg: | |
| 4813 parser.error('Tryserver master cannot be found because: %s\n' | |
| 4814 'Please manually specify the tryserver master' | |
| 4815 ', e.g. "-m tryserver.chromium.linux".' % err_msg) | |
| 4816 | 4920 |
| 4817 def GetMasterMap(): | 4921 if not buckets: |
| 4818 # Process --bot. | 4922 # Default to triggering Dry Run (see http://crbug.com/625697). |
| 4819 if not options.bot: | 4923 if options.verbose: |
| 4820 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None) | 4924 print('git cl try with no bots now defaults to CQ Dry Run.') |
| 4821 | 4925 return _trigger_dry_run(cl) |
| 4822 # Get try masters from PRESUBMIT.py files. | |
| 4823 masters = presubmit_support.DoGetTryMasters( | |
| 4824 change, | |
| 4825 change.LocalPaths(), | |
| 4826 settings.GetRoot(), | |
| 4827 None, | |
| 4828 None, | |
| 4829 options.verbose, | |
| 4830 sys.stdout) | |
| 4831 if masters: | |
| 4832 return masters | |
| 4833 | |
| 4834 # Fall back to deprecated method: get try slaves from PRESUBMIT.py files. | |
| 4835 options.bot = presubmit_support.DoGetTrySlaves( | |
| 4836 change, | |
| 4837 change.LocalPaths(), | |
| 4838 settings.GetRoot(), | |
| 4839 None, | |
| 4840 None, | |
| 4841 options.verbose, | |
| 4842 sys.stdout) | |
| 4843 | |
| 4844 if not options.bot: | |
| 4845 return {} | |
| 4846 | |
| 4847 builders_and_tests = {} | |
| 4848 # TODO(machenbach): The old style command-line options don't support | |
| 4849 # multiple try masters yet. | |
| 4850 old_style = filter(lambda x: isinstance(x, basestring), options.bot) | |
| 4851 new_style = filter(lambda x: isinstance(x, tuple), options.bot) | |
| 4852 | |
| 4853 for bot in old_style: | |
| 4854 if ':' in bot: | |
| 4855 parser.error('Specifying testfilter is no longer supported') | |
| 4856 elif ',' in bot: | |
| 4857 parser.error('Specify one bot per --bot flag') | |
| 4858 else: | |
| 4859 builders_and_tests.setdefault(bot, []) | |
| 4860 | |
| 4861 for bot, tests in new_style: | |
| 4862 builders_and_tests.setdefault(bot, []).extend(tests) | |
| 4863 | |
| 4864 # Return a master map with one master to be backwards compatible. The | |
| 4865 # master name defaults to an empty string, which will cause the master | |
| 4866 # not to be set on rietveld (deprecated). | |
| 4867 bucket = '' | |
| 4868 if options.master: | |
| 4869 # Add the "master." prefix to the master name to obtain the bucket name. | |
| 4870 bucket = _prefix_master(options.master) | |
| 4871 return {bucket: builders_and_tests} | |
| 4872 | |
| 4873 if options.bucket: | |
| 4874 buckets = {options.bucket: {b: [] for b in options.bot}} | |
| 4875 else: | |
| 4876 buckets = GetMasterMap() | |
| 4877 if not buckets: | |
| 4878 # Default to triggering Dry Run (see http://crbug.com/625697). | |
| 4879 if options.verbose: | |
| 4880 print('git cl try with no bots now defaults to CQ Dry Run.') | |
| 4881 try: | |
| 4882 cl.SetCQState(_CQState.DRY_RUN) | |
| 4883 print('scheduled CQ Dry Run on %s' % cl.GetIssueURL()) | |
| 4884 return 0 | |
| 4885 except KeyboardInterrupt: | |
| 4886 raise | |
| 4887 except: | |
| 4888 print('WARNING: failed to trigger CQ Dry Run.\n' | |
| 4889 'Either:\n' | |
| 4890 ' * your project has no CQ\n' | |
| 4891 ' * you don\'t have permission to trigger Dry Run\n' | |
| 4892 ' * bug in this code (see stack trace below).\n' | |
| 4893 'Consider specifying which bots to trigger manually ' | |
| 4894 'or asking your project owners for permissions ' | |
| 4895 'or contacting Chrome Infrastructure team at ' | |
| 4896 'https://www.chromium.org/infra\n\n') | |
| 4897 # Still raise exception so that stack trace is printed. | |
| 4898 raise | |
| 4899 | 4926 |
| 4900 for builders in buckets.itervalues(): | 4927 for builders in buckets.itervalues(): |
| 4901 if any('triggered' in b for b in builders): | 4928 if any('triggered' in b for b in builders): |
| 4902 print('ERROR You are trying to send a job to a triggered bot. This type ' | 4929 print('ERROR You are trying to send a job to a triggered bot. This type ' |
| 4903 'of bot requires an initial job from a parent (usually a builder). ' | 4930 'of bot requires an initial job from a parent (usually a builder). ' |
| 4904 'Instead send your job to the parent.\n' | 4931 'Instead send your job to the parent.\n' |
| 4905 'Bot list: %s' % builders, file=sys.stderr) | 4932 'Bot list: %s' % builders, file=sys.stderr) |
| 4906 return 1 | 4933 return 1 |
| 4907 | 4934 |
| 4908 patchset = cl.GetMostRecentPatchset() | 4935 patchset = cl.GetMostRecentPatchset() |
| 4909 if patchset != cl.GetPatchset(): | 4936 if patchset != cl.GetPatchset(): |
| 4910 print('Warning: Codereview server has newer patchsets (%s) than most ' | 4937 print('Warning: Codereview server has newer patchsets (%s) than most ' |
| 4911 'recent upload from local checkout (%s). Did a previous upload ' | 4938 'recent upload from local checkout (%s). Did a previous upload ' |
| 4912 'fail?\n' | 4939 'fail?\n' |
| 4913 'By default, git cl try uses the latest patchset from ' | 4940 'By default, git cl try uses the latest patchset from ' |
| 4914 'codereview, continuing to use patchset %s.\n' % | 4941 'codereview, continuing to use patchset %s.\n' % |
| 4915 (patchset, cl.GetPatchset(), patchset)) | 4942 (patchset, cl.GetPatchset(), patchset)) |
| 4943 | |
| 4916 try: | 4944 try: |
| 4917 _trigger_try_jobs(auth_config, cl, buckets, options, 'git_cl_try', | 4945 _trigger_try_jobs(auth_config, cl, buckets, options, 'git_cl_try', |
| 4918 patchset) | 4946 patchset) |
| 4919 except BuildbucketResponseException as ex: | 4947 except BuildbucketResponseException as ex: |
| 4920 print('ERROR: %s' % ex) | 4948 print('ERROR: %s' % ex) |
| 4921 return 1 | 4949 return 1 |
| 4922 return 0 | 4950 return 0 |
| 4923 | 4951 |
| 4924 | 4952 |
| 4925 def CMDtry_results(parser, args): | 4953 def CMDtry_results(parser, args): |
| (...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5414 if __name__ == '__main__': | 5442 if __name__ == '__main__': |
| 5415 # These affect sys.stdout so do it outside of main() to simplify mocks in | 5443 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 5416 # unit testing. | 5444 # unit testing. |
| 5417 fix_encoding.fix_encoding() | 5445 fix_encoding.fix_encoding() |
| 5418 setup_color.init() | 5446 setup_color.init() |
| 5419 try: | 5447 try: |
| 5420 sys.exit(main(sys.argv[1:])) | 5448 sys.exit(main(sys.argv[1:])) |
| 5421 except KeyboardInterrupt: | 5449 except KeyboardInterrupt: |
| 5422 sys.stderr.write('interrupted\n') | 5450 sys.stderr.write('interrupted\n') |
| 5423 sys.exit(1) | 5451 sys.exit(1) |
| OLD | NEW |