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

Side by Side Diff: git_cl.py

Issue 2419113002: Add -B/--bucket flag to git-cl try (Closed)
Patch Set: Fix indent 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 4304 matching lines...) Expand 10 before | Expand all | Expand 10 after
4706 """ 4729 """
4707 group = optparse.OptionGroup(parser, 'Try job options') 4730 group = optparse.OptionGroup(parser, 'Try job options')
4708 group.add_option( 4731 group.add_option(
4709 '-b', '--bot', action='append', 4732 '-b', '--bot', action='append',
4710 help=('IMPORTANT: specify ONE builder per --bot flag. Use it multiple ' 4733 help=('IMPORTANT: specify ONE builder per --bot flag. Use it multiple '
4711 'times to specify multiple builders. ex: ' 4734 'times to specify multiple builders. ex: '
4712 '"-b win_rel -b win_layout". See ' 4735 '"-b win_rel -b win_layout". See '
4713 'the try server waterfall for the builders name and the tests ' 4736 'the try server waterfall for the builders name and the tests '
4714 'available.')) 4737 'available.'))
4715 group.add_option( 4738 group.add_option(
4739 '-B', '--bucket', default='',
4740 help=('Buildbucket bucket to send the try requests.'))
4741 group.add_option(
4716 '-m', '--master', default='', 4742 '-m', '--master', default='',
4717 help=('Specify a try master where to run the tries.')) 4743 help=('Specify a try master where to run the tries.'))
4718 # TODO(tandrii,nodir): add -B --bucket flag.
4719 group.add_option( 4744 group.add_option(
4720 '-r', '--revision', 4745 '-r', '--revision',
4721 help='Revision to use for the try job; default: the revision will ' 4746 help='Revision to use for the try job; default: the revision will '
4722 'be determined by the try recipe that builder runs, which usually ' 4747 'be determined by the try recipe that builder runs, which usually '
4723 'defaults to HEAD of origin/master') 4748 'defaults to HEAD of origin/master')
4724 group.add_option( 4749 group.add_option(
4725 '-c', '--clobber', action='store_true', default=False, 4750 '-c', '--clobber', action='store_true', default=False,
4726 help='Force a clobber before building; that is don\'t do an ' 4751 help='Force a clobber before building; that is don\'t do an '
4727 'incremental build') 4752 'incremental build')
4728 group.add_option( 4753 group.add_option(
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
4766 'If your project has Commit Queue, dry run is a workaround:\n' 4791 'If your project has Commit Queue, dry run is a workaround:\n'
4767 ' git cl set-commit --dry-run') 4792 ' git cl set-commit --dry-run')
4768 4793
4769 error_message = cl.CannotTriggerTryJobReason() 4794 error_message = cl.CannotTriggerTryJobReason()
4770 if error_message: 4795 if error_message:
4771 parser.error('Can\'t trigger try jobs: %s') 4796 parser.error('Can\'t trigger try jobs: %s')
4772 4797
4773 if not options.name: 4798 if not options.name:
4774 options.name = cl.GetBranch() 4799 options.name = cl.GetBranch()
4775 4800
4776 if options.bot and not options.master: 4801 if options.bucket and options.master:
4802 parser.error('Only one of --bucket and --master may be used.')
4803
4804 if options.bot and not options.master and not options.bucket:
4777 options.master, err_msg = GetBuilderMaster(options.bot) 4805 options.master, err_msg = GetBuilderMaster(options.bot)
4778 if err_msg: 4806 if err_msg:
4779 parser.error('Tryserver master cannot be found because: %s\n' 4807 parser.error('Tryserver master cannot be found because: %s\n'
4780 'Please manually specify the tryserver master' 4808 'Please manually specify the tryserver master'
4781 ', e.g. "-m tryserver.chromium.linux".' % err_msg) 4809 ', e.g. "-m tryserver.chromium.linux".' % err_msg)
4782 4810
4783 def GetMasterMap(): 4811 def GetMasterMap():
4784 # Process --bot. 4812 # Process --bot.
4785 if not options.bot: 4813 if not options.bot:
4786 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None) 4814 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
4823 parser.error('Specify one bot per --bot flag') 4851 parser.error('Specify one bot per --bot flag')
4824 else: 4852 else:
4825 builders_and_tests.setdefault(bot, []) 4853 builders_and_tests.setdefault(bot, [])
4826 4854
4827 for bot, tests in new_style: 4855 for bot, tests in new_style:
4828 builders_and_tests.setdefault(bot, []).extend(tests) 4856 builders_and_tests.setdefault(bot, []).extend(tests)
4829 4857
4830 # Return a master map with one master to be backwards compatible. The 4858 # Return a master map with one master to be backwards compatible. The
4831 # master name defaults to an empty string, which will cause the master 4859 # master name defaults to an empty string, which will cause the master
4832 # not to be set on rietveld (deprecated). 4860 # not to be set on rietveld (deprecated).
4833 return {options.master: builders_and_tests} 4861 bucket = ''
4862 if options.master:
4863 # Add the "master." prefix to the master name to obtain the bucket name.
4864 bucket = _prefix_master(options.master)
4865 return {bucket: builders_and_tests}
4834 4866
4835 masters = GetMasterMap() 4867 if options.bucket:
4836 if not masters: 4868 buckets = {options.bucket: {b: [] for b in options.bot}}
4837 # Default to triggering Dry Run (see http://crbug.com/625697). 4869 else:
4838 if options.verbose: 4870 buckets = GetMasterMap()
4839 print('git cl try with no bots now defaults to CQ Dry Run.') 4871 if not buckets:
4840 try: 4872 # Default to triggering Dry Run (see http://crbug.com/625697).
4841 cl.SetCQState(_CQState.DRY_RUN) 4873 if options.verbose:
4842 print('scheduled CQ Dry Run on %s' % cl.GetIssueURL()) 4874 print('git cl try with no bots now defaults to CQ Dry Run.')
4843 return 0 4875 try:
4844 except KeyboardInterrupt: 4876 cl.SetCQState(_CQState.DRY_RUN)
4845 raise 4877 print('scheduled CQ Dry Run on %s' % cl.GetIssueURL())
4846 except: 4878 return 0
4847 print('WARNING: failed to trigger CQ Dry Run.\n' 4879 except KeyboardInterrupt:
4848 'Either:\n' 4880 raise
4849 ' * your project has no CQ\n' 4881 except:
4850 ' * you don\'t have permission to trigger Dry Run\n' 4882 print('WARNING: failed to trigger CQ Dry Run.\n'
4851 ' * bug in this code (see stack trace below).\n' 4883 'Either:\n'
4852 'Consider specifying which bots to trigger manually ' 4884 ' * your project has no CQ\n'
4853 'or asking your project owners for permissions ' 4885 ' * you don\'t have permission to trigger Dry Run\n'
4854 'or contacting Chrome Infrastructure team at ' 4886 ' * bug in this code (see stack trace below).\n'
4855 'https://www.chromium.org/infra\n\n') 4887 'Consider specifying which bots to trigger manually '
4856 # Still raise exception so that stack trace is printed. 4888 'or asking your project owners for permissions '
4857 raise 4889 'or contacting Chrome Infrastructure team at '
4890 'https://www.chromium.org/infra\n\n')
4891 # Still raise exception so that stack trace is printed.
4892 raise
4858 4893
4859 for builders in masters.itervalues(): 4894 for builders in buckets.itervalues():
4860 if any('triggered' in b for b in builders): 4895 if any('triggered' in b for b in builders):
4861 print('ERROR You are trying to send a job to a triggered bot. This type ' 4896 print('ERROR You are trying to send a job to a triggered bot. This type '
4862 'of bot requires an initial job from a parent (usually a builder). ' 4897 'of bot requires an initial job from a parent (usually a builder). '
4863 'Instead send your job to the parent.\n' 4898 'Instead send your job to the parent.\n'
4864 'Bot list: %s' % builders, file=sys.stderr) 4899 'Bot list: %s' % builders, file=sys.stderr)
4865 return 1 4900 return 1
4866 4901
4867 patchset = cl.GetMostRecentPatchset() 4902 patchset = cl.GetMostRecentPatchset()
4868 if patchset != cl.GetPatchset(): 4903 if patchset != cl.GetPatchset():
4869 print('Warning: Codereview server has newer patchsets (%s) than most ' 4904 print('Warning: Codereview server has newer patchsets (%s) than most '
4870 'recent upload from local checkout (%s). Did a previous upload ' 4905 'recent upload from local checkout (%s). Did a previous upload '
4871 'fail?\n' 4906 'fail?\n'
4872 'By default, git cl try uses the latest patchset from ' 4907 'By default, git cl try uses the latest patchset from '
4873 'codereview, continuing to use patchset %s.\n' % 4908 'codereview, continuing to use patchset %s.\n' %
4874 (patchset, cl.GetPatchset(), patchset)) 4909 (patchset, cl.GetPatchset(), patchset))
4875 try: 4910 try:
4876 _trigger_try_jobs(auth_config, cl, masters, options, 'git_cl_try', 4911 _trigger_try_jobs(auth_config, cl, buckets, options, 'git_cl_try',
4877 patchset) 4912 patchset)
4878 except BuildbucketResponseException as ex: 4913 except BuildbucketResponseException as ex:
4879 print('ERROR: %s' % ex) 4914 print('ERROR: %s' % ex)
4880 return 1 4915 return 1
4881 return 0 4916 return 0
4882 4917
4883 4918
4884 def CMDtry_results(parser, args): 4919 def CMDtry_results(parser, args):
4885 """Prints info about try jobs associated with current CL.""" 4920 """Prints info about try jobs associated with current CL."""
4886 group = optparse.OptionGroup(parser, 'Try job results options') 4921 group = optparse.OptionGroup(parser, 'Try job results options')
4887 group.add_option( 4922 group.add_option(
(...skipping 485 matching lines...) Expand 10 before | Expand all | Expand 10 after
5373 if __name__ == '__main__': 5408 if __name__ == '__main__':
5374 # These affect sys.stdout so do it outside of main() to simplify mocks in 5409 # These affect sys.stdout so do it outside of main() to simplify mocks in
5375 # unit testing. 5410 # unit testing.
5376 fix_encoding.fix_encoding() 5411 fix_encoding.fix_encoding()
5377 setup_color.init() 5412 setup_color.init()
5378 try: 5413 try:
5379 sys.exit(main(sys.argv[1:])) 5414 sys.exit(main(sys.argv[1:]))
5380 except KeyboardInterrupt: 5415 except KeyboardInterrupt:
5381 sys.stderr.write('interrupted\n') 5416 sys.stderr.write('interrupted\n')
5382 sys.exit(1) 5417 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