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 distutils.version import LooseVersion | 10 from distutils.version import LooseVersion |
(...skipping 817 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
828 """Returns current branch or None. | 828 """Returns current branch or None. |
829 | 829 |
830 For refs/heads/* branches, returns just last part. For others, full ref. | 830 For refs/heads/* branches, returns just last part. For others, full ref. |
831 """ | 831 """ |
832 branchref = GetCurrentBranchRef() | 832 branchref = GetCurrentBranchRef() |
833 if branchref: | 833 if branchref: |
834 return ShortBranchName(branchref) | 834 return ShortBranchName(branchref) |
835 return None | 835 return None |
836 | 836 |
837 | 837 |
838 class _CQState(object): | |
839 """Enum for states of CL with respect to Commit Queue.""" | |
840 NONE = 'none' | |
841 DRY_RUN = 'dry_run' | |
842 COMMIT = 'commit' | |
843 | |
844 ALL_STATES = [NONE, DRY_RUN, COMMIT] | |
845 | |
846 | |
838 class _ParsedIssueNumberArgument(object): | 847 class _ParsedIssueNumberArgument(object): |
839 def __init__(self, issue=None, patchset=None, hostname=None): | 848 def __init__(self, issue=None, patchset=None, hostname=None): |
840 self.issue = issue | 849 self.issue = issue |
841 self.patchset = patchset | 850 self.patchset = patchset |
842 self.hostname = hostname | 851 self.hostname = hostname |
843 | 852 |
844 @property | 853 @property |
845 def valid(self): | 854 def valid(self): |
846 return self.issue is not None | 855 return self.issue is not None |
847 | 856 |
(...skipping 542 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1390 print | 1399 print |
1391 print '--dependencies has been specified.' | 1400 print '--dependencies has been specified.' |
1392 print 'All dependent local branches will be re-uploaded.' | 1401 print 'All dependent local branches will be re-uploaded.' |
1393 print | 1402 print |
1394 # Remove the dependencies flag from args so that we do not end up in a | 1403 # Remove the dependencies flag from args so that we do not end up in a |
1395 # loop. | 1404 # loop. |
1396 orig_args.remove('--dependencies') | 1405 orig_args.remove('--dependencies') |
1397 ret = upload_branch_deps(self, orig_args) | 1406 ret = upload_branch_deps(self, orig_args) |
1398 return ret | 1407 return ret |
1399 | 1408 |
1409 def SetCQState(self, new_state): | |
1410 """Update the CQ state for latest patchset. | |
1411 | |
1412 Issue must have been already uploaded and known. | |
1413 """ | |
1414 assert new_state in _CQState.ALL_STATES | |
1415 assert self.GetIssue() | |
1416 return self._codereview_impl.SetCQState(new_state) | |
1417 | |
1400 # Forward methods to codereview specific implementation. | 1418 # Forward methods to codereview specific implementation. |
1401 | 1419 |
1402 def CloseIssue(self): | 1420 def CloseIssue(self): |
1403 return self._codereview_impl.CloseIssue() | 1421 return self._codereview_impl.CloseIssue() |
1404 | 1422 |
1405 def GetStatus(self): | 1423 def GetStatus(self): |
1406 return self._codereview_impl.GetStatus() | 1424 return self._codereview_impl.GetStatus() |
1407 | 1425 |
1408 def GetCodereviewServer(self): | 1426 def GetCodereviewServer(self): |
1409 return self._codereview_impl.GetCodereviewServer() | 1427 return self._codereview_impl.GetCodereviewServer() |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1515 | 1533 |
1516 Arguments: | 1534 Arguments: |
1517 force: whether to skip confirmation questions. | 1535 force: whether to skip confirmation questions. |
1518 """ | 1536 """ |
1519 raise NotImplementedError() | 1537 raise NotImplementedError() |
1520 | 1538 |
1521 def CMDUploadChange(self, options, args, change): | 1539 def CMDUploadChange(self, options, args, change): |
1522 """Uploads a change to codereview.""" | 1540 """Uploads a change to codereview.""" |
1523 raise NotImplementedError() | 1541 raise NotImplementedError() |
1524 | 1542 |
1543 def SetCQState(self, new_state): | |
1544 """Update the CQ state for latest patchset. | |
1545 | |
1546 Issue must have been already uploaded and known. | |
1547 """ | |
1548 raise NotImplementedError() | |
1549 | |
1525 | 1550 |
1526 class _RietveldChangelistImpl(_ChangelistCodereviewBase): | 1551 class _RietveldChangelistImpl(_ChangelistCodereviewBase): |
1527 def __init__(self, changelist, auth_config=None, rietveld_server=None): | 1552 def __init__(self, changelist, auth_config=None, rietveld_server=None): |
1528 super(_RietveldChangelistImpl, self).__init__(changelist) | 1553 super(_RietveldChangelistImpl, self).__init__(changelist) |
1529 assert settings, 'must be initialized in _ChangelistCodereviewBase' | 1554 assert settings, 'must be initialized in _ChangelistCodereviewBase' |
1530 settings.GetDefaultServerUrl() | 1555 settings.GetDefaultServerUrl() |
1531 | 1556 |
1532 self._rietveld_server = rietveld_server | 1557 self._rietveld_server = rietveld_server |
1533 self._auth_config = auth_config | 1558 self._auth_config = auth_config |
1534 self._props = None | 1559 self._props = None |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1692 def GetCodereviewServerSetting(self): | 1717 def GetCodereviewServerSetting(self): |
1693 """Returns the git setting that stores this change's rietveld server.""" | 1718 """Returns the git setting that stores this change's rietveld server.""" |
1694 branch = self.GetBranch() | 1719 branch = self.GetBranch() |
1695 if branch: | 1720 if branch: |
1696 return 'branch.%s.rietveldserver' % branch | 1721 return 'branch.%s.rietveldserver' % branch |
1697 return None | 1722 return None |
1698 | 1723 |
1699 def GetRieveldObjForPresubmit(self): | 1724 def GetRieveldObjForPresubmit(self): |
1700 return self.RpcServer() | 1725 return self.RpcServer() |
1701 | 1726 |
1727 def SetCQState(self, new_state): | |
1728 props = self.GetIssueProperties() | |
1729 if props.get('private'): | |
1730 DieWithError('Cannot set-commit on private issue') | |
1731 | |
1732 if new_state == _CQState.COMMIT: | |
1733 self.SetFlag('commit', '1') | |
1734 elif new_state == _CQState.NONE: | |
1735 self.SetFlag('commit', '0') | |
1736 else: | |
1737 raise NotImplementedError() | |
Sergiy Byelozyorov
2016/04/13 17:15:02
What about DRY_RUN?
tandrii(chromium)
2016/04/13 17:17:16
wasn't supported before, so whatever.
| |
1738 | |
1739 | |
1702 def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit, | 1740 def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit, |
1703 directory): | 1741 directory): |
1704 # TODO(maruel): Use apply_issue.py | 1742 # TODO(maruel): Use apply_issue.py |
1705 | 1743 |
1706 # PatchIssue should never be called with a dirty tree. It is up to the | 1744 # PatchIssue should never be called with a dirty tree. It is up to the |
1707 # caller to check this, but just in case we assert here since the | 1745 # caller to check this, but just in case we assert here since the |
1708 # consequences of the caller not checking this could be dire. | 1746 # consequences of the caller not checking this could be dire. |
1709 assert(not git_common.is_dirty_git_tree('apply')) | 1747 assert(not git_common.is_dirty_git_tree('apply')) |
1710 assert(parsed_issue_arg.valid) | 1748 assert(parsed_issue_arg.valid) |
1711 self._changelist.issue = parsed_issue_arg.issue | 1749 self._changelist.issue = parsed_issue_arg.issue |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1929 backup_file = open(backup_path, 'w') | 1967 backup_file = open(backup_path, 'w') |
1930 backup_file.write(change_desc.description) | 1968 backup_file.write(change_desc.description) |
1931 backup_file.close() | 1969 backup_file.close() |
1932 raise | 1970 raise |
1933 | 1971 |
1934 if not self.GetIssue(): | 1972 if not self.GetIssue(): |
1935 self.SetIssue(issue) | 1973 self.SetIssue(issue) |
1936 self.SetPatchset(patchset) | 1974 self.SetPatchset(patchset) |
1937 | 1975 |
1938 if options.use_commit_queue: | 1976 if options.use_commit_queue: |
1939 self.SetFlag('commit', '1') | 1977 self.SetCQState(_CQState.COMMIT) |
1940 return 0 | 1978 return 0 |
1941 | 1979 |
1942 | 1980 |
1943 class _GerritChangelistImpl(_ChangelistCodereviewBase): | 1981 class _GerritChangelistImpl(_ChangelistCodereviewBase): |
1944 def __init__(self, changelist, auth_config=None): | 1982 def __init__(self, changelist, auth_config=None): |
1945 # auth_config is Rietveld thing, kept here to preserve interface only. | 1983 # auth_config is Rietveld thing, kept here to preserve interface only. |
1946 super(_GerritChangelistImpl, self).__init__(changelist) | 1984 super(_GerritChangelistImpl, self).__init__(changelist) |
1947 self._change_id = None | 1985 self._change_id = None |
1948 # Lazily cached values. | 1986 # Lazily cached values. |
1949 self._gerrit_server = None # e.g. https://chromium-review.googlesource.com | 1987 self._gerrit_server = None # e.g. https://chromium-review.googlesource.com |
(...skipping 493 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2443 log_desc = options.message or CreateDescriptionFromLog(args) | 2481 log_desc = options.message or CreateDescriptionFromLog(args) |
2444 git_command = ['commit', '--amend', '-m', log_desc] | 2482 git_command = ['commit', '--amend', '-m', log_desc] |
2445 RunGit(git_command) | 2483 RunGit(git_command) |
2446 new_log_desc = CreateDescriptionFromLog(args) | 2484 new_log_desc = CreateDescriptionFromLog(args) |
2447 if git_footers.get_footer_change_id(new_log_desc): | 2485 if git_footers.get_footer_change_id(new_log_desc): |
2448 print 'git-cl: Added Change-Id to commit message.' | 2486 print 'git-cl: Added Change-Id to commit message.' |
2449 return new_log_desc | 2487 return new_log_desc |
2450 else: | 2488 else: |
2451 print >> sys.stderr, 'ERROR: Gerrit commit-msg hook not available.' | 2489 print >> sys.stderr, 'ERROR: Gerrit commit-msg hook not available.' |
2452 | 2490 |
2491 def SetCQState(self, new_state): | |
2492 """Sets the Commit-Queue label assuming canonical CQ config for Gerrit.""" | |
2493 # TODO(tandrii): maybe allow configurability in codereview.settings or by | |
2494 # self-discovery of label config for this CL using REST API. | |
2495 vote_map = { | |
2496 _CQState.NONE: 0, | |
2497 _CQState.DRY_RUN: 1, | |
2498 _CQState.COMMIT : 2, | |
2499 } | |
2500 gerrit_util.SetReview(self._GetGerritHost(), self.GetIssue(), | |
2501 labels={'Commit-Queue': vote_map[new_state]}) | |
2502 | |
2453 | 2503 |
2454 _CODEREVIEW_IMPLEMENTATIONS = { | 2504 _CODEREVIEW_IMPLEMENTATIONS = { |
2455 'rietveld': _RietveldChangelistImpl, | 2505 'rietveld': _RietveldChangelistImpl, |
2456 'gerrit': _GerritChangelistImpl, | 2506 'gerrit': _GerritChangelistImpl, |
2457 } | 2507 } |
2458 | 2508 |
2459 | 2509 |
2460 class ChangeDescription(object): | 2510 class ChangeDescription(object): |
2461 """Contains a parsed form of the change description.""" | 2511 """Contains a parsed form of the change description.""" |
2462 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' | 2512 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' |
(...skipping 1596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4059 | 4109 |
4060 print "The tree is %s" % status | 4110 print "The tree is %s" % status |
4061 print | 4111 print |
4062 print GetTreeStatusReason() | 4112 print GetTreeStatusReason() |
4063 if status != 'open': | 4113 if status != 'open': |
4064 return 1 | 4114 return 1 |
4065 return 0 | 4115 return 0 |
4066 | 4116 |
4067 | 4117 |
4068 def CMDtry(parser, args): | 4118 def CMDtry(parser, args): |
4069 """Triggers a try job through BuildBucket.""" | 4119 """Triggers try jobs through BuildBucket.""" |
4070 group = optparse.OptionGroup(parser, "Try job options") | 4120 group = optparse.OptionGroup(parser, "Try job options") |
4071 group.add_option( | 4121 group.add_option( |
4072 "-b", "--bot", action="append", | 4122 "-b", "--bot", action="append", |
4073 help=("IMPORTANT: specify ONE builder per --bot flag. Use it multiple " | 4123 help=("IMPORTANT: specify ONE builder per --bot flag. Use it multiple " |
4074 "times to specify multiple builders. ex: " | 4124 "times to specify multiple builders. ex: " |
4075 "'-b win_rel -b win_layout'. See " | 4125 "'-b win_rel -b win_layout'. See " |
4076 "the try server waterfall for the builders name and the tests " | 4126 "the try server waterfall for the builders name and the tests " |
4077 "available.")) | 4127 "available.")) |
4078 group.add_option( | 4128 group.add_option( |
4079 "-m", "--master", default='', | 4129 "-m", "--master", default='', |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4118 if bad_params: | 4168 if bad_params: |
4119 parser.error('Got properties with missing "=": %s' % bad_params) | 4169 parser.error('Got properties with missing "=": %s' % bad_params) |
4120 | 4170 |
4121 if args: | 4171 if args: |
4122 parser.error('Unknown arguments: %s' % args) | 4172 parser.error('Unknown arguments: %s' % args) |
4123 | 4173 |
4124 cl = Changelist(auth_config=auth_config) | 4174 cl = Changelist(auth_config=auth_config) |
4125 if not cl.GetIssue(): | 4175 if not cl.GetIssue(): |
4126 parser.error('Need to upload first') | 4176 parser.error('Need to upload first') |
4127 | 4177 |
4178 if cl.IsGerrit(): | |
4179 parser.error( | |
4180 'Not yet supported for Gerrit (http://crbug.com/599931).\n' | |
4181 'If your project has Commit Queue, dry run is a workaround:\n' | |
4182 ' git cl set-commit --dry-run') | |
4183 # Code below assumes Rietveld issue. | |
4184 # TODO(tandrii): actually implement for Gerrit http://crbug.com/599931. | |
4185 | |
4128 props = cl.GetIssueProperties() | 4186 props = cl.GetIssueProperties() |
4129 if props.get('closed'): | 4187 if props.get('closed'): |
4130 parser.error('Cannot send tryjobs for a closed CL') | 4188 parser.error('Cannot send tryjobs for a closed CL') |
4131 | 4189 |
4132 if props.get('private'): | 4190 if props.get('private'): |
4133 parser.error('Cannot use trybots with private issue') | 4191 parser.error('Cannot use trybots with private issue') |
4134 | 4192 |
4135 if not options.name: | 4193 if not options.name: |
4136 options.name = cl.GetBranch() | 4194 options.name = cl.GetBranch() |
4137 | 4195 |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4339 if not issue_url: | 4397 if not issue_url: |
4340 print >> sys.stderr, 'ERROR No issue to open' | 4398 print >> sys.stderr, 'ERROR No issue to open' |
4341 return 1 | 4399 return 1 |
4342 | 4400 |
4343 webbrowser.open(issue_url) | 4401 webbrowser.open(issue_url) |
4344 return 0 | 4402 return 0 |
4345 | 4403 |
4346 | 4404 |
4347 def CMDset_commit(parser, args): | 4405 def CMDset_commit(parser, args): |
4348 """Sets the commit bit to trigger the Commit Queue.""" | 4406 """Sets the commit bit to trigger the Commit Queue.""" |
4407 parser.add_option('-d', '--dry-run', action='store_true', | |
4408 help='trigger in dry run mode') | |
4409 parser.add_option('-c', '--clear', action='store_true', | |
4410 help='stop CQ run, if any') | |
4349 auth.add_auth_options(parser) | 4411 auth.add_auth_options(parser) |
4350 options, args = parser.parse_args(args) | 4412 options, args = parser.parse_args(args) |
4351 auth_config = auth.extract_auth_config_from_options(options) | 4413 auth_config = auth.extract_auth_config_from_options(options) |
4352 if args: | 4414 if args: |
4353 parser.error('Unrecognized args: %s' % ' '.join(args)) | 4415 parser.error('Unrecognized args: %s' % ' '.join(args)) |
4416 if options.dry_run and options.clear: | |
4417 parser.error('Make up your mind: both --dry-run and --clear not allowed') | |
4418 | |
4354 cl = Changelist(auth_config=auth_config) | 4419 cl = Changelist(auth_config=auth_config) |
4355 props = cl.GetIssueProperties() | 4420 if options.clear: |
4356 if props.get('private'): | 4421 state = _CQState.CLEAR |
4357 parser.error('Cannot set commit on private issue') | 4422 elif options.dry_run: |
4358 cl.SetFlag('commit', '1') | 4423 state = _CQState.DRY_RUN |
4424 else: | |
4425 state = _CQState.COMMIT | |
4426 if not cl.GetIssue(): | |
4427 parser.error('Must upload the issue first') | |
4428 cl.SetCQState(state) | |
4359 return 0 | 4429 return 0 |
4360 | 4430 |
4361 | 4431 |
4362 def CMDset_close(parser, args): | 4432 def CMDset_close(parser, args): |
4363 """Closes the issue.""" | 4433 """Closes the issue.""" |
4364 auth.add_auth_options(parser) | 4434 auth.add_auth_options(parser) |
4365 options, args = parser.parse_args(args) | 4435 options, args = parser.parse_args(args) |
4366 auth_config = auth.extract_auth_config_from_options(options) | 4436 auth_config = auth.extract_auth_config_from_options(options) |
4367 if args: | 4437 if args: |
4368 parser.error('Unrecognized args: %s' % ' '.join(args)) | 4438 parser.error('Unrecognized args: %s' % ' '.join(args)) |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4708 if __name__ == '__main__': | 4778 if __name__ == '__main__': |
4709 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4779 # These affect sys.stdout so do it outside of main() to simplify mocks in |
4710 # unit testing. | 4780 # unit testing. |
4711 fix_encoding.fix_encoding() | 4781 fix_encoding.fix_encoding() |
4712 setup_color.init() | 4782 setup_color.init() |
4713 try: | 4783 try: |
4714 sys.exit(main(sys.argv[1:])) | 4784 sys.exit(main(sys.argv[1:])) |
4715 except KeyboardInterrupt: | 4785 except KeyboardInterrupt: |
4716 sys.stderr.write('interrupted\n') | 4786 sys.stderr.write('interrupted\n') |
4717 sys.exit(1) | 4787 sys.exit(1) |
OLD | NEW |