Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: git_cl.py

Issue 2419113002: Add -B/--bucket flag to git-cl try (Closed)
Patch Set: Address comments Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
69 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit' 69 GIT_INSTRUCTIONS_URL = 'http://code.google.com/p/chromium/wiki/UsingGit'
70 REFS_THAT_ALIAS_TO_OTHER_REFS = { 70 REFS_THAT_ALIAS_TO_OTHER_REFS = {
71 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master', 71 'refs/remotes/origin/lkgr': 'refs/remotes/origin/master',
72 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master', 72 'refs/remotes/origin/lkcr': 'refs/remotes/origin/master',
73 } 73 }
74 74
75 # Valid extensions for files we want to lint. 75 # Valid extensions for files we want to lint.
76 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)" 76 DEFAULT_LINT_REGEX = r"(.*\.cpp|.*\.cc|.*\.h)"
77 DEFAULT_LINT_IGNORE_REGEX = r"$^" 77 DEFAULT_LINT_IGNORE_REGEX = r"$^"
78 78
79 # Buildbucket master name prefix.
80 MASTER_PREFIX = 'master.'
81
79 # Shortcut since it quickly becomes redundant. 82 # Shortcut since it quickly becomes redundant.
80 Fore = colorama.Fore 83 Fore = colorama.Fore
81 84
82 # Initialized in main() 85 # Initialized in main()
83 settings = None 86 settings = None
84 87
85 88
86 def DieWithError(message): 89 def DieWithError(message):
87 print(message, file=sys.stderr) 90 print(message, file=sys.stderr)
88 sys.exit(1) 91 sys.exit(1)
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 271
269 272
270 def _prefix_master(master): 273 def _prefix_master(master):
271 """Convert user-specified master name to full master name. 274 """Convert user-specified master name to full master name.
272 275
273 Buildbucket uses full master name(master.tryserver.chromium.linux) as bucket 276 Buildbucket uses full master name(master.tryserver.chromium.linux) as bucket
274 name, while the developers always use shortened master name 277 name, while the developers always use shortened master name
275 (tryserver.chromium.linux) by stripping off the prefix 'master.'. This 278 (tryserver.chromium.linux) by stripping off the prefix 'master.'. This
276 function does the conversion for buildbucket migration. 279 function does the conversion for buildbucket migration.
277 """ 280 """
278 prefix = 'master.' 281 if master.startswith(MASTER_PREFIX):
279 if master.startswith(prefix):
280 return master 282 return master
281 return '%s%s' % (prefix, master) 283 return '%s%s' % (MASTER_PREFIX, master)
284
285
286 def _unprefix_master(bucket):
287 """Convert bucket name to shortened master name.
288
289 Buildbucket uses full master name(master.tryserver.chromium.linux) as bucket
290 name, while the developers always use shortened master name
291 (tryserver.chromium.linux) by stripping off the prefix 'master.'. This
292 function does the conversion for buildbucket migration.
293 """
294 if bucket.startswith(MASTER_PREFIX):
295 return bucket[len(MASTER_PREFIX):]
296 return bucket
282 297
283 298
284 def _buildbucket_retry(operation_name, http, *args, **kwargs): 299 def _buildbucket_retry(operation_name, http, *args, **kwargs):
285 """Retries requests to buildbucket service and returns parsed json content.""" 300 """Retries requests to buildbucket service and returns parsed json content."""
286 try_count = 0 301 try_count = 0
287 while True: 302 while True:
288 response, content = http.request(*args, **kwargs) 303 response, content = http.request(*args, **kwargs)
289 try: 304 try:
290 content_json = json.loads(content) 305 content_json = json.loads(content)
291 except ValueError: 306 except ValueError:
(...skipping 19 matching lines...) Expand all
311 if response.status < 500 or try_count >= 2: 326 if response.status < 500 or try_count >= 2:
312 raise httplib2.HttpLib2Error(content) 327 raise httplib2.HttpLib2Error(content)
313 328
314 # status >= 500 means transient failures. 329 # status >= 500 means transient failures.
315 logging.debug('Transient errors when %s. Will retry.', operation_name) 330 logging.debug('Transient errors when %s. Will retry.', operation_name)
316 time.sleep(0.5 + 1.5*try_count) 331 time.sleep(0.5 + 1.5*try_count)
317 try_count += 1 332 try_count += 1
318 assert False, 'unreachable' 333 assert False, 'unreachable'
319 334
320 335
321 def _trigger_try_jobs(auth_config, changelist, masters, options, 336 def _trigger_try_jobs(auth_config, changelist, buckets, options,
322 category='git_cl_try', patchset=None): 337 category='git_cl_try', patchset=None):
323 assert changelist.GetIssue(), 'CL must be uploaded first' 338 assert changelist.GetIssue(), 'CL must be uploaded first'
324 codereview_url = changelist.GetCodereviewServer() 339 codereview_url = changelist.GetCodereviewServer()
325 assert codereview_url, 'CL must be uploaded first' 340 assert codereview_url, 'CL must be uploaded first'
326 patchset = patchset or changelist.GetMostRecentPatchset() 341 patchset = patchset or changelist.GetMostRecentPatchset()
327 assert patchset, 'CL must be uploaded first' 342 assert patchset, 'CL must be uploaded first'
328 343
329 codereview_host = urlparse.urlparse(codereview_url).hostname 344 codereview_host = urlparse.urlparse(codereview_url).hostname
330 authenticator = auth.get_authenticator_for_host(codereview_host, auth_config) 345 authenticator = auth.get_authenticator_for_host(codereview_host, auth_config)
331 http = authenticator.authorize(httplib2.Http()) 346 http = authenticator.authorize(httplib2.Http())
(...skipping 11 matching lines...) Expand all
343 buildset = 'patch/{codereview}/{hostname}/{issue}/{patch}'.format( 358 buildset = 'patch/{codereview}/{hostname}/{issue}/{patch}'.format(
344 codereview='gerrit' if changelist.IsGerrit() else 'rietveld', 359 codereview='gerrit' if changelist.IsGerrit() else 'rietveld',
345 hostname=codereview_host, 360 hostname=codereview_host,
346 issue=changelist.GetIssue(), 361 issue=changelist.GetIssue(),
347 patch=patchset) 362 patch=patchset)
348 extra_properties = _get_properties_from_options(options) 363 extra_properties = _get_properties_from_options(options)
349 364
350 batch_req_body = {'builds': []} 365 batch_req_body = {'builds': []}
351 print_text = [] 366 print_text = []
352 print_text.append('Tried jobs on:') 367 print_text.append('Tried jobs on:')
353 for master, builders_and_tests in sorted(masters.iteritems()): 368 for bucket, builders_and_tests in sorted(buckets.iteritems()):
354 print_text.append('Master: %s' % master) 369 print_text.append('Bucket: %s' % bucket)
355 bucket = _prefix_master(master) 370 master = None
371 if bucket.startswith(MASTER_PREFIX):
372 master = _unprefix_master(bucket)
356 for builder, tests in sorted(builders_and_tests.iteritems()): 373 for builder, tests in sorted(builders_and_tests.iteritems()):
357 print_text.append(' %s: %s' % (builder, tests)) 374 print_text.append(' %s: %s' % (builder, tests))
358 parameters = { 375 parameters = {
359 'builder_name': builder, 376 'builder_name': builder,
360 'changes': [{ 377 'changes': [{
361 'author': {'email': owner_email}, 378 'author': {'email': owner_email},
362 'revision': options.revision, 379 'revision': options.revision,
363 }], 380 }],
364 'properties': { 381 'properties': {
365 'category': category, 382 'category': category,
366 'issue': changelist.GetIssue(), 383 'issue': changelist.GetIssue(),
367 'master': master,
368 'patch_project': project, 384 'patch_project': project,
369 'patch_storage': 'rietveld', 385 'patch_storage': 'rietveld',
370 'patchset': patchset, 386 'patchset': patchset,
371 'reason': options.name, 387 'reason': options.name,
372 'rietveld': codereview_url, 388 'rietveld': codereview_url,
373 }, 389 },
374 } 390 }
375 if 'presubmit' in builder.lower(): 391 if 'presubmit' in builder.lower():
376 parameters['properties']['dry_run'] = 'true' 392 parameters['properties']['dry_run'] = 'true'
377 if tests: 393 if tests:
378 parameters['properties']['testfilter'] = tests 394 parameters['properties']['testfilter'] = tests
379 if extra_properties: 395 if extra_properties:
380 parameters['properties'].update(extra_properties) 396 parameters['properties'].update(extra_properties)
381 if options.clobber: 397 if options.clobber:
382 parameters['properties']['clobber'] = True 398 parameters['properties']['clobber'] = True
399
400 tags = [
401 'builder:%s' % builder,
402 'buildset:%s' % buildset,
403 'user_agent:git_cl_try',
404 ]
405 if master:
406 parameters['properties']['master'] = master
407 tags.append('master:%s' % master)
408
383 batch_req_body['builds'].append( 409 batch_req_body['builds'].append(
384 { 410 {
385 'bucket': bucket, 411 'bucket': bucket,
386 'parameters_json': json.dumps(parameters), 412 'parameters_json': json.dumps(parameters),
387 'client_operation_id': str(uuid.uuid4()), 413 'client_operation_id': str(uuid.uuid4()),
388 'tags': ['builder:%s' % builder, 414 'tags': tags,
389 'buildset:%s' % buildset,
390 'master:%s' % master,
391 'user_agent:git_cl_try']
392 } 415 }
393 ) 416 )
394 417
395 _buildbucket_retry( 418 _buildbucket_retry(
396 'triggering try jobs', 419 'triggering try jobs',
397 http, 420 http,
398 buildbucket_put_url, 421 buildbucket_put_url,
399 'PUT', 422 'PUT',
400 body=json.dumps(batch_req_body), 423 body=json.dumps(batch_req_body),
401 headers={'Content-Type': 'application/json'} 424 headers={'Content-Type': 'application/json'}
(...skipping 4296 matching lines...) Expand 10 before | Expand all | Expand 10 after
4698 """ 4721 """
4699 group = optparse.OptionGroup(parser, 'Try job options') 4722 group = optparse.OptionGroup(parser, 'Try job options')
4700 group.add_option( 4723 group.add_option(
4701 '-b', '--bot', action='append', 4724 '-b', '--bot', action='append',
4702 help=('IMPORTANT: specify ONE builder per --bot flag. Use it multiple ' 4725 help=('IMPORTANT: specify ONE builder per --bot flag. Use it multiple '
4703 'times to specify multiple builders. ex: ' 4726 'times to specify multiple builders. ex: '
4704 '"-b win_rel -b win_layout". See ' 4727 '"-b win_rel -b win_layout". See '
4705 'the try server waterfall for the builders name and the tests ' 4728 'the try server waterfall for the builders name and the tests '
4706 'available.')) 4729 'available.'))
4707 group.add_option( 4730 group.add_option(
4731 '-B', '--bucket', default='',
4732 help=('Buildbucket bucket to send the try requests.'))
4733 group.add_option(
4708 '-m', '--master', default='', 4734 '-m', '--master', default='',
4709 help=('Specify a try master where to run the tries.')) 4735 help=('Specify a try master where to run the tries.'))
4710 # TODO(tandrii,nodir): add -B --bucket flag.
4711 group.add_option( 4736 group.add_option(
4712 '-r', '--revision', 4737 '-r', '--revision',
4713 help='Revision to use for the try job; default: the revision will ' 4738 help='Revision to use for the try job; default: the revision will '
4714 'be determined by the try recipe that builder runs, which usually ' 4739 'be determined by the try recipe that builder runs, which usually '
4715 'defaults to HEAD of origin/master') 4740 'defaults to HEAD of origin/master')
4716 group.add_option( 4741 group.add_option(
4717 '-c', '--clobber', action='store_true', default=False, 4742 '-c', '--clobber', action='store_true', default=False,
4718 help='Force a clobber before building; that is don\'t do an ' 4743 help='Force a clobber before building; that is don\'t do an '
4719 'incremental build') 4744 'incremental build')
4720 group.add_option( 4745 group.add_option(
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
4758 'If your project has Commit Queue, dry run is a workaround:\n' 4783 'If your project has Commit Queue, dry run is a workaround:\n'
4759 ' git cl set-commit --dry-run') 4784 ' git cl set-commit --dry-run')
4760 4785
4761 error_message = cl.CannotTriggerTryJobReason() 4786 error_message = cl.CannotTriggerTryJobReason()
4762 if error_message: 4787 if error_message:
4763 parser.error('Can\'t trigger try jobs: %s') 4788 parser.error('Can\'t trigger try jobs: %s')
4764 4789
4765 if not options.name: 4790 if not options.name:
4766 options.name = cl.GetBranch() 4791 options.name = cl.GetBranch()
4767 4792
4768 if options.bot and not options.master: 4793 if options.bucket and options.master:
4794 parser.error('Only one of --bucket and --master may be used.')
4795
4796 if options.bot and not options.master and not options.bucket:
4769 options.master, err_msg = GetBuilderMaster(options.bot) 4797 options.master, err_msg = GetBuilderMaster(options.bot)
4770 if err_msg: 4798 if err_msg:
4771 parser.error('Tryserver master cannot be found because: %s\n' 4799 parser.error('Tryserver master cannot be found because: %s\n'
4772 'Please manually specify the tryserver master' 4800 'Please manually specify the tryserver master'
4773 ', e.g. "-m tryserver.chromium.linux".' % err_msg) 4801 ', e.g. "-m tryserver.chromium.linux".' % err_msg)
4774 4802
4775 def GetMasterMap(): 4803 def GetMasterMap():
4776 # Process --bot. 4804 # Process --bot.
4777 if not options.bot: 4805 if not options.bot:
4778 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None) 4806 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
4815 parser.error('Specify one bot per --bot flag') 4843 parser.error('Specify one bot per --bot flag')
4816 else: 4844 else:
4817 builders_and_tests.setdefault(bot, []) 4845 builders_and_tests.setdefault(bot, [])
4818 4846
4819 for bot, tests in new_style: 4847 for bot, tests in new_style:
4820 builders_and_tests.setdefault(bot, []).extend(tests) 4848 builders_and_tests.setdefault(bot, []).extend(tests)
4821 4849
4822 # Return a master map with one master to be backwards compatible. The 4850 # Return a master map with one master to be backwards compatible. The
4823 # master name defaults to an empty string, which will cause the master 4851 # master name defaults to an empty string, which will cause the master
4824 # not to be set on rietveld (deprecated). 4852 # not to be set on rietveld (deprecated).
4825 return {options.master: builders_and_tests} 4853 bucket = ''
4854 if options.master:
4855 # Add the "master." prefix to the master name to obtain the bucket name.
4856 bucket = _prefix_master(options.master)
4857 return {bucket: builders_and_tests}
4826 4858
4827 masters = GetMasterMap() 4859 if options.bucket:
4828 if not masters: 4860 builders_and_tests = {}
4829 # Default to triggering Dry Run (see http://crbug.com/625697). 4861 old_style = filter(lambda x: isinstance(x, basestring), options.bot)
nodir 2016/10/17 17:56:02 I think this is noop since options.bot cannot be t
borenet 2016/10/17 17:59:45 D'oh. Yeah, totally not needed when we're not mixi
4830 if options.verbose: 4862 for bot in old_style:
4831 print('git cl try with no bots now defaults to CQ Dry Run.') 4863 builders_and_tests.setdefault(bot, [])
4832 try: 4864 buckets = {options.bucket: builders_and_tests}
4833 cl.SetCQState(_CQState.DRY_RUN) 4865 else:
4834 print('scheduled CQ Dry Run on %s' % cl.GetIssueURL()) 4866 buckets = GetMasterMap()
4835 return 0 4867 if not buckets:
4836 except KeyboardInterrupt: 4868 # Default to triggering Dry Run (see http://crbug.com/625697).
4837 raise 4869 if options.verbose:
4838 except: 4870 print('git cl try with no bots now defaults to CQ Dry Run.')
4839 print('WARNING: failed to trigger CQ Dry Run.\n' 4871 try:
4840 'Either:\n' 4872 cl.SetCQState(_CQState.DRY_RUN)
4841 ' * your project has no CQ\n' 4873 print('scheduled CQ Dry Run on %s' % cl.GetIssueURL())
4842 ' * you don\'t have permission to trigger Dry Run\n' 4874 return 0
4843 ' * bug in this code (see stack trace below).\n' 4875 except KeyboardInterrupt:
4844 'Consider specifying which bots to trigger manually ' 4876 raise
4845 'or asking your project owners for permissions ' 4877 except:
4846 'or contacting Chrome Infrastructure team at ' 4878 print('WARNING: failed to trigger CQ Dry Run.\n'
4847 'https://www.chromium.org/infra\n\n') 4879 'Either:\n'
4848 # Still raise exception so that stack trace is printed. 4880 ' * your project has no CQ\n'
4849 raise 4881 ' * you don\'t have permission to trigger Dry Run\n'
4882 ' * bug in this code (see stack trace below).\n'
4883 'Consider specifying which bots to trigger manually '
4884 'or asking your project owners for permissions '
4885 'or contacting Chrome Infrastructure team at '
4886 'https://www.chromium.org/infra\n\n')
4887 # Still raise exception so that stack trace is printed.
4888 raise
4850 4889
4851 for builders in masters.itervalues(): 4890 for builders in buckets.itervalues():
4852 if any('triggered' in b for b in builders): 4891 if any('triggered' in b for b in builders):
4853 print('ERROR You are trying to send a job to a triggered bot. This type ' 4892 print('ERROR You are trying to send a job to a triggered bot. This type '
4854 'of bot requires an initial job from a parent (usually a builder). ' 4893 'of bot requires an initial job from a parent (usually a builder). '
4855 'Instead send your job to the parent.\n' 4894 'Instead send your job to the parent.\n'
4856 'Bot list: %s' % builders, file=sys.stderr) 4895 'Bot list: %s' % builders, file=sys.stderr)
4857 return 1 4896 return 1
4858 4897
4859 patchset = cl.GetMostRecentPatchset() 4898 patchset = cl.GetMostRecentPatchset()
4860 if patchset != cl.GetPatchset(): 4899 if patchset != cl.GetPatchset():
4861 print('Warning: Codereview server has newer patchsets (%s) than most ' 4900 print('Warning: Codereview server has newer patchsets (%s) than most '
4862 'recent upload from local checkout (%s). Did a previous upload ' 4901 'recent upload from local checkout (%s). Did a previous upload '
4863 'fail?\n' 4902 'fail?\n'
4864 'By default, git cl try uses the latest patchset from ' 4903 'By default, git cl try uses the latest patchset from '
4865 'codereview, continuing to use patchset %s.\n' % 4904 'codereview, continuing to use patchset %s.\n' %
4866 (patchset, cl.GetPatchset(), patchset)) 4905 (patchset, cl.GetPatchset(), patchset))
4867 try: 4906 try:
4868 _trigger_try_jobs(auth_config, cl, masters, options, 'git_cl_try', 4907 _trigger_try_jobs(auth_config, cl, buckets, options, 'git_cl_try',
4869 patchset) 4908 patchset)
4870 except BuildbucketResponseException as ex: 4909 except BuildbucketResponseException as ex:
4871 print('ERROR: %s' % ex) 4910 print('ERROR: %s' % ex)
4872 return 1 4911 return 1
4873 return 0 4912 return 0
4874 4913
4875 4914
4876 def CMDtry_results(parser, args): 4915 def CMDtry_results(parser, args):
4877 """Prints info about try jobs associated with current CL.""" 4916 """Prints info about try jobs associated with current CL."""
4878 group = optparse.OptionGroup(parser, 'Try job results options') 4917 group = optparse.OptionGroup(parser, 'Try job results options')
4879 group.add_option( 4918 group.add_option(
(...skipping 485 matching lines...) Expand 10 before | Expand all | Expand 10 after
5365 if __name__ == '__main__': 5404 if __name__ == '__main__':
5366 # These affect sys.stdout so do it outside of main() to simplify mocks in 5405 # These affect sys.stdout so do it outside of main() to simplify mocks in
5367 # unit testing. 5406 # unit testing.
5368 fix_encoding.fix_encoding() 5407 fix_encoding.fix_encoding()
5369 setup_color.init() 5408 setup_color.init()
5370 try: 5409 try:
5371 sys.exit(main(sys.argv[1:])) 5410 sys.exit(main(sys.argv[1:]))
5372 except KeyboardInterrupt: 5411 except KeyboardInterrupt:
5373 sys.stderr.write('interrupted\n') 5412 sys.stderr.write('interrupted\n')
5374 sys.exit(1) 5413 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | tests/git_cl_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698