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

Side by Side Diff: git_cl.py

Issue 2468263005: Implement and test git cl try for Gerrit. (Closed)
Patch Set: review Created 4 years, 1 month 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 409 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 420
421 codereview_host = urlparse.urlparse(codereview_url).hostname 421 codereview_host = urlparse.urlparse(codereview_url).hostname
422 authenticator = auth.get_authenticator_for_host(codereview_host, auth_config) 422 authenticator = auth.get_authenticator_for_host(codereview_host, auth_config)
423 http = authenticator.authorize(httplib2.Http()) 423 http = authenticator.authorize(httplib2.Http())
424 http.force_exception_to_status_code = True 424 http.force_exception_to_status_code = True
425 425
426 # TODO(tandrii): consider caching Gerrit CL details just like 426 # TODO(tandrii): consider caching Gerrit CL details just like
427 # _RietveldChangelistImpl does, then caching values in these two variables 427 # _RietveldChangelistImpl does, then caching values in these two variables
428 # won't be necessary. 428 # won't be necessary.
429 owner_email = changelist.GetIssueOwner() 429 owner_email = changelist.GetIssueOwner()
430 project = changelist.GetIssueProject()
431 430
432 buildbucket_put_url = ( 431 buildbucket_put_url = (
433 'https://{hostname}/_ah/api/buildbucket/v1/builds/batch'.format( 432 'https://{hostname}/_ah/api/buildbucket/v1/builds/batch'.format(
434 hostname=options.buildbucket_host)) 433 hostname=options.buildbucket_host))
435 buildset = 'patch/{codereview}/{hostname}/{issue}/{patch}'.format( 434 buildset = 'patch/{codereview}/{hostname}/{issue}/{patch}'.format(
436 codereview='gerrit' if changelist.IsGerrit() else 'rietveld', 435 codereview='gerrit' if changelist.IsGerrit() else 'rietveld',
437 hostname=codereview_host, 436 hostname=codereview_host,
438 issue=changelist.GetIssue(), 437 issue=changelist.GetIssue(),
439 patch=patchset) 438 patch=patchset)
439
440 shared_parameters_properties = changelist.GetTryjobProperties(patchset)
441 shared_parameters_properties['category'] = category
442 if options.clobber:
443 shared_parameters_properties['clobber'] = True
440 extra_properties = _get_properties_from_options(options) 444 extra_properties = _get_properties_from_options(options)
445 if extra_properties:
446 shared_parameters_properties.update(extra_properties)
441 447
442 batch_req_body = {'builds': []} 448 batch_req_body = {'builds': []}
443 print_text = [] 449 print_text = []
444 print_text.append('Tried jobs on:') 450 print_text.append('Tried jobs on:')
445 for bucket, builders_and_tests in sorted(buckets.iteritems()): 451 for bucket, builders_and_tests in sorted(buckets.iteritems()):
446 print_text.append('Bucket: %s' % bucket) 452 print_text.append('Bucket: %s' % bucket)
447 master = None 453 master = None
448 if bucket.startswith(MASTER_PREFIX): 454 if bucket.startswith(MASTER_PREFIX):
449 master = _unprefix_master(bucket) 455 master = _unprefix_master(bucket)
450 for builder, tests in sorted(builders_and_tests.iteritems()): 456 for builder, tests in sorted(builders_and_tests.iteritems()):
451 print_text.append(' %s: %s' % (builder, tests)) 457 print_text.append(' %s: %s' % (builder, tests))
452 parameters = { 458 parameters = {
453 'builder_name': builder, 459 'builder_name': builder,
454 'changes': [{ 460 'changes': [{
455 'author': {'email': owner_email}, 461 'author': {'email': owner_email},
456 'revision': options.revision, 462 'revision': options.revision,
457 }], 463 }],
458 'properties': { 464 'properties': shared_parameters_properties.copy(),
459 'category': category,
460 'issue': changelist.GetIssue(),
461 'patch_project': project,
462 'patch_storage': 'rietveld',
463 'patchset': patchset,
464 'rietveld': codereview_url,
465 },
466 } 465 }
467 if 'presubmit' in builder.lower(): 466 if 'presubmit' in builder.lower():
468 parameters['properties']['dry_run'] = 'true' 467 parameters['properties']['dry_run'] = 'true'
469 if tests: 468 if tests:
470 parameters['properties']['testfilter'] = tests 469 parameters['properties']['testfilter'] = tests
471 if extra_properties:
472 parameters['properties'].update(extra_properties)
473 if options.clobber:
474 parameters['properties']['clobber'] = True
475 470
476 tags = [ 471 tags = [
477 'builder:%s' % builder, 472 'builder:%s' % builder,
478 'buildset:%s' % buildset, 473 'buildset:%s' % buildset,
479 'user_agent:git_cl_try', 474 'user_agent:git_cl_try',
480 ] 475 ]
481 if master: 476 if master:
482 parameters['properties']['master'] = master 477 parameters['properties']['master'] = master
483 tags.append('master:%s' % master) 478 tags.append('master:%s' % master)
484 479
(...skipping 1199 matching lines...) Expand 10 before | Expand all | Expand 10 after
1684 def GetStatus(self): 1679 def GetStatus(self):
1685 return self._codereview_impl.GetStatus() 1680 return self._codereview_impl.GetStatus()
1686 1681
1687 def GetCodereviewServer(self): 1682 def GetCodereviewServer(self):
1688 return self._codereview_impl.GetCodereviewServer() 1683 return self._codereview_impl.GetCodereviewServer()
1689 1684
1690 def GetIssueOwner(self): 1685 def GetIssueOwner(self):
1691 """Get owner from codereview, which may differ from this checkout.""" 1686 """Get owner from codereview, which may differ from this checkout."""
1692 return self._codereview_impl.GetIssueOwner() 1687 return self._codereview_impl.GetIssueOwner()
1693 1688
1694 def GetIssueProject(self):
1695 """Get project from codereview, which may differ from what this
1696 checkout's codereview.settings or gerrit project URL say.
1697 """
1698 return self._codereview_impl.GetIssueProject()
1699
1700 def GetApprovingReviewers(self): 1689 def GetApprovingReviewers(self):
1701 return self._codereview_impl.GetApprovingReviewers() 1690 return self._codereview_impl.GetApprovingReviewers()
1702 1691
1703 def GetMostRecentPatchset(self): 1692 def GetMostRecentPatchset(self):
1704 return self._codereview_impl.GetMostRecentPatchset() 1693 return self._codereview_impl.GetMostRecentPatchset()
1705 1694
1706 def CannotTriggerTryJobReason(self): 1695 def CannotTriggerTryJobReason(self):
1707 """Returns reason (str) if unable trigger tryjobs on this CL or None.""" 1696 """Returns reason (str) if unable trigger tryjobs on this CL or None."""
1708 return self._codereview_impl.CannotTriggerTryJobReason() 1697 return self._codereview_impl.CannotTriggerTryJobReason()
1709 1698
1699 def GetTryjobProperties(self, patchset=None):
1700 """Returns dictionary of properties to launch tryjob."""
1701 return self._codereview_impl.GetTryjobProperties(patchset=patchset)
1702
1710 def __getattr__(self, attr): 1703 def __getattr__(self, attr):
1711 # This is because lots of untested code accesses Rietveld-specific stuff 1704 # This is because lots of untested code accesses Rietveld-specific stuff
1712 # directly, and it's hard to fix for sure. So, just let it work, and fix 1705 # directly, and it's hard to fix for sure. So, just let it work, and fix
1713 # on a case by case basis. 1706 # on a case by case basis.
1714 # Note that child method defines __getattr__ as well, and forwards it here, 1707 # Note that child method defines __getattr__ as well, and forwards it here,
1715 # because _RietveldChangelistImpl is not cleaned up yet, and given 1708 # because _RietveldChangelistImpl is not cleaned up yet, and given
1716 # deprecation of Rietveld, it should probably be just removed. 1709 # deprecation of Rietveld, it should probably be just removed.
1717 # Until that time, avoid infinite recursion by bypassing __getattr__ 1710 # Until that time, avoid infinite recursion by bypassing __getattr__
1718 # of implementation class. 1711 # of implementation class.
1719 return self._codereview_impl.__getattribute__(attr) 1712 return self._codereview_impl.__getattribute__(attr)
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1832 """ 1825 """
1833 raise NotImplementedError() 1826 raise NotImplementedError()
1834 1827
1835 def CannotTriggerTryJobReason(self): 1828 def CannotTriggerTryJobReason(self):
1836 """Returns reason (str) if unable trigger tryjobs on this CL or None.""" 1829 """Returns reason (str) if unable trigger tryjobs on this CL or None."""
1837 raise NotImplementedError() 1830 raise NotImplementedError()
1838 1831
1839 def GetIssueOwner(self): 1832 def GetIssueOwner(self):
1840 raise NotImplementedError() 1833 raise NotImplementedError()
1841 1834
1842 def GetIssueProject(self): 1835 def GetTryjobProperties(self, patchset=None):
1843 raise NotImplementedError() 1836 raise NotImplementedError()
1844 1837
1845 1838
1846 class _RietveldChangelistImpl(_ChangelistCodereviewBase): 1839 class _RietveldChangelistImpl(_ChangelistCodereviewBase):
1847 def __init__(self, changelist, auth_config=None, rietveld_server=None): 1840 def __init__(self, changelist, auth_config=None, rietveld_server=None):
1848 super(_RietveldChangelistImpl, self).__init__(changelist) 1841 super(_RietveldChangelistImpl, self).__init__(changelist)
1849 assert settings, 'must be initialized in _ChangelistCodereviewBase' 1842 assert settings, 'must be initialized in _ChangelistCodereviewBase'
1850 if not rietveld_server: 1843 if not rietveld_server:
1851 settings.GetDefaultServerUrl() 1844 settings.GetDefaultServerUrl()
1852 1845
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1913 def CannotTriggerTryJobReason(self): 1906 def CannotTriggerTryJobReason(self):
1914 props = self.GetIssueProperties() 1907 props = self.GetIssueProperties()
1915 if not props: 1908 if not props:
1916 return 'Rietveld doesn\'t know about your issue %s' % self.GetIssue() 1909 return 'Rietveld doesn\'t know about your issue %s' % self.GetIssue()
1917 if props.get('closed'): 1910 if props.get('closed'):
1918 return 'CL %s is closed' % self.GetIssue() 1911 return 'CL %s is closed' % self.GetIssue()
1919 if props.get('private'): 1912 if props.get('private'):
1920 return 'CL %s is private' % self.GetIssue() 1913 return 'CL %s is private' % self.GetIssue()
1921 return None 1914 return None
1922 1915
1916 def GetTryjobProperties(self, patchset=None):
1917 """Returns dictionary of properties to launch tryjob."""
1918 project = (self.GetIssueProperties() or {}).get('project')
1919 return {
1920 'issue': self.GetIssue(),
1921 'patch_project': project,
1922 'patch_storage': 'rietveld',
1923 'patchset': patchset or self.GetPatchset(),
1924 'rietveld': self.GetCodereviewServer(),
1925 }
1926
1923 def GetApprovingReviewers(self): 1927 def GetApprovingReviewers(self):
1924 return get_approving_reviewers(self.GetIssueProperties()) 1928 return get_approving_reviewers(self.GetIssueProperties())
1925 1929
1926 def GetIssueOwner(self): 1930 def GetIssueOwner(self):
1927 return (self.GetIssueProperties() or {}).get('owner_email') 1931 return (self.GetIssueProperties() or {}).get('owner_email')
1928 1932
1929 def GetIssueProject(self):
1930 return (self.GetIssueProperties() or {}).get('project')
1931
1932 def AddComment(self, message): 1933 def AddComment(self, message):
1933 return self.RpcServer().add_comment(self.GetIssue(), message) 1934 return self.RpcServer().add_comment(self.GetIssue(), message)
1934 1935
1935 def GetStatus(self): 1936 def GetStatus(self):
1936 """Apply a rough heuristic to give a simple summary of an issue's review 1937 """Apply a rough heuristic to give a simple summary of an issue's review
1937 or CQ status, assuming adherence to a common workflow. 1938 or CQ status, assuming adherence to a common workflow.
1938 1939
1939 Returns None if no issue for this branch, or one of the following keywords: 1940 Returns None if no issue for this branch, or one of the following keywords:
1940 * 'error' - error from review tool (including deleted issues) 1941 * 'error' - error from review tool (including deleted issues)
1941 * 'unsent' - not sent for review 1942 * 'unsent' - not sent for review
(...skipping 919 matching lines...) Expand 10 before | Expand all | Expand 10 after
2861 """Sets the Commit-Queue label assuming canonical CQ config for Gerrit.""" 2862 """Sets the Commit-Queue label assuming canonical CQ config for Gerrit."""
2862 vote_map = { 2863 vote_map = {
2863 _CQState.NONE: 0, 2864 _CQState.NONE: 0,
2864 _CQState.DRY_RUN: 1, 2865 _CQState.DRY_RUN: 1,
2865 _CQState.COMMIT : 2, 2866 _CQState.COMMIT : 2,
2866 } 2867 }
2867 gerrit_util.SetReview(self._GetGerritHost(), self.GetIssue(), 2868 gerrit_util.SetReview(self._GetGerritHost(), self.GetIssue(),
2868 labels={'Commit-Queue': vote_map[new_state]}) 2869 labels={'Commit-Queue': vote_map[new_state]})
2869 2870
2870 def CannotTriggerTryJobReason(self): 2871 def CannotTriggerTryJobReason(self):
2871 # TODO(tandrii): implement for Gerrit. 2872 try:
2872 raise NotImplementedError() 2873 data = self._GetChangeDetail()
2874 except GerritIssueNotExists:
2875 return 'Gerrit doesn\'t know about your issue %s' % self.GetIssue()
2876
2877 if data['status'] in ('ABANDONED', 'MERGED'):
2878 return 'CL %s is closed' % self.GetIssue()
2879
2880 def GetTryjobProperties(self, patchset=None):
2881 """Returns dictionary of properties to launch tryjob."""
2882 data = self._GetChangeDetail(['ALL_REVISIONS'])
2883 patchset = int(patchset or self.GetPatchset())
2884 assert patchset
2885 revision_data = None # Pylint wants it to be defined.
2886 for revision_data in data['revisions'].itervalues():
2887 if int(revision_data['_number']) == patchset:
2888 break
2889 else:
2890 raise Exception('Patchset %d is not known in Gerrit issue %d' %
2891 (patchset, self.GetIssue()))
2892 return {
2893 'patch_issue': self.GetIssue(),
2894 'patch_set': patchset or self.GetPatchset(),
2895 'patch_project': data['project'],
2896 'patch_storage': 'gerrit',
2897 'patch_ref': revision_data['fetch']['http']['ref'],
2898 'patch_repository_url': revision_data['fetch']['http']['url'],
2899 'patch_gerrit_url': self.GetCodereviewServer(),
2900 }
2873 2901
2874 def GetIssueOwner(self): 2902 def GetIssueOwner(self):
2875 # TODO(tandrii): implement for Gerrit. 2903 return self._GetChangeDetail(['DETAILED_ACCOUNTS'])['owner']['email']
2876 raise NotImplementedError()
2877
2878 def GetIssueProject(self):
2879 # TODO(tandrii): implement for Gerrit.
2880 raise NotImplementedError()
2881 2904
2882 2905
2883 _CODEREVIEW_IMPLEMENTATIONS = { 2906 _CODEREVIEW_IMPLEMENTATIONS = {
2884 'rietveld': _RietveldChangelistImpl, 2907 'rietveld': _RietveldChangelistImpl,
2885 'gerrit': _GerritChangelistImpl, 2908 'gerrit': _GerritChangelistImpl,
2886 } 2909 }
2887 2910
2888 2911
2889 def _add_codereview_issue_select_options(parser, extra=""): 2912 def _add_codereview_issue_select_options(parser, extra=""):
2890 _add_codereview_select_options(parser) 2913 _add_codereview_select_options(parser)
(...skipping 1930 matching lines...) Expand 10 before | Expand all | Expand 10 after
4821 if bad_params: 4844 if bad_params:
4822 parser.error('Got properties with missing "=": %s' % bad_params) 4845 parser.error('Got properties with missing "=": %s' % bad_params)
4823 4846
4824 if args: 4847 if args:
4825 parser.error('Unknown arguments: %s' % args) 4848 parser.error('Unknown arguments: %s' % args)
4826 4849
4827 cl = Changelist(auth_config=auth_config) 4850 cl = Changelist(auth_config=auth_config)
4828 if not cl.GetIssue(): 4851 if not cl.GetIssue():
4829 parser.error('Need to upload first') 4852 parser.error('Need to upload first')
4830 4853
4831 if cl.IsGerrit():
4832 parser.error(
4833 'Not yet supported for Gerrit (http://crbug.com/599931).\n'
4834 'If your project has Commit Queue, dry run is a workaround:\n'
4835 ' git cl set-commit --dry-run')
4836
4837 error_message = cl.CannotTriggerTryJobReason() 4854 error_message = cl.CannotTriggerTryJobReason()
4838 if error_message: 4855 if error_message:
4839 parser.error('Can\'t trigger try jobs: %s' % error_message) 4856 parser.error('Can\'t trigger try jobs: %s' % error_message)
4840 4857
4841 if options.bucket and options.master: 4858 if options.bucket and options.master:
4842 parser.error('Only one of --bucket and --master may be used.') 4859 parser.error('Only one of --bucket and --master may be used.')
4843 4860
4844 buckets = _get_bucket_map(cl, options, parser) 4861 buckets = _get_bucket_map(cl, options, parser)
4845 4862
4846 # If no bots are listed and we couldn't get a list based on PRESUBMIT files, 4863 # If no bots are listed and we couldn't get a list based on PRESUBMIT files,
(...skipping 522 matching lines...) Expand 10 before | Expand all | Expand 10 after
5369 if __name__ == '__main__': 5386 if __name__ == '__main__':
5370 # These affect sys.stdout so do it outside of main() to simplify mocks in 5387 # These affect sys.stdout so do it outside of main() to simplify mocks in
5371 # unit testing. 5388 # unit testing.
5372 fix_encoding.fix_encoding() 5389 fix_encoding.fix_encoding()
5373 setup_color.init() 5390 setup_color.init()
5374 try: 5391 try:
5375 sys.exit(main(sys.argv[1:])) 5392 sys.exit(main(sys.argv[1:]))
5376 except KeyboardInterrupt: 5393 except KeyboardInterrupt:
5377 sys.stderr.write('interrupted\n') 5394 sys.stderr.write('interrupted\n')
5378 sys.exit(1) 5395 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