diff --git a/git_cl.py b/git_cl.py index ea03734590..34c96c0581 100755 --- a/git_cl.py +++ b/git_cl.py @@ -835,6 +835,15 @@ def GetCurrentBranch(): return None +class _CQState(object): + """Enum for states of CL with respect to Commit Queue.""" + NONE = 'none' + DRY_RUN = 'dry_run' + COMMIT = 'commit' + + ALL_STATES = [NONE, DRY_RUN, COMMIT] + + class _ParsedIssueNumberArgument(object): def __init__(self, issue=None, patchset=None, hostname=None): self.issue = issue @@ -1397,6 +1406,15 @@ class Changelist(object): ret = upload_branch_deps(self, orig_args) return ret + def SetCQState(self, new_state): + """Update the CQ state for latest patchset. + + Issue must have been already uploaded and known. + """ + assert new_state in _CQState.ALL_STATES + assert self.GetIssue() + return self._codereview_impl.SetCQState(new_state) + # Forward methods to codereview specific implementation. def CloseIssue(self): @@ -1522,6 +1540,13 @@ class _ChangelistCodereviewBase(object): """Uploads a change to codereview.""" raise NotImplementedError() + def SetCQState(self, new_state): + """Update the CQ state for latest patchset. + + Issue must have been already uploaded and known. + """ + raise NotImplementedError() + class _RietveldChangelistImpl(_ChangelistCodereviewBase): def __init__(self, changelist, auth_config=None, rietveld_server=None): @@ -1699,6 +1724,19 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase): def GetRieveldObjForPresubmit(self): return self.RpcServer() + def SetCQState(self, new_state): + props = self.GetIssueProperties() + if props.get('private'): + DieWithError('Cannot set-commit on private issue') + + if new_state == _CQState.COMMIT: + self.SetFlag('commit', '1') + elif new_state == _CQState.NONE: + self.SetFlag('commit', '0') + else: + raise NotImplementedError() + + def CMDPatchWithParsedIssue(self, parsed_issue_arg, reject, nocommit, directory): # TODO(maruel): Use apply_issue.py @@ -1936,7 +1974,7 @@ class _RietveldChangelistImpl(_ChangelistCodereviewBase): self.SetPatchset(patchset) if options.use_commit_queue: - self.SetFlag('commit', '1') + self.SetCQState(_CQState.COMMIT) return 0 @@ -2450,6 +2488,18 @@ class _GerritChangelistImpl(_ChangelistCodereviewBase): else: print >> sys.stderr, 'ERROR: Gerrit commit-msg hook not available.' + def SetCQState(self, new_state): + """Sets the Commit-Queue label assuming canonical CQ config for Gerrit.""" + # TODO(tandrii): maybe allow configurability in codereview.settings or by + # self-discovery of label config for this CL using REST API. + vote_map = { + _CQState.NONE: 0, + _CQState.DRY_RUN: 1, + _CQState.COMMIT : 2, + } + gerrit_util.SetReview(self._GetGerritHost(), self.GetIssue(), + labels={'Commit-Queue': vote_map[new_state]}) + _CODEREVIEW_IMPLEMENTATIONS = { 'rietveld': _RietveldChangelistImpl, @@ -4066,7 +4116,7 @@ def CMDtree(parser, args): def CMDtry(parser, args): - """Triggers a try job through BuildBucket.""" + """Triggers try jobs through BuildBucket.""" group = optparse.OptionGroup(parser, "Try job options") group.add_option( "-b", "--bot", action="append", @@ -4125,6 +4175,14 @@ def CMDtry(parser, args): if not cl.GetIssue(): parser.error('Need to upload first') + if cl.IsGerrit(): + parser.error( + 'Not yet supported for Gerrit (http://crbug.com/599931).\n' + 'If your project has Commit Queue, dry run is a workaround:\n' + ' git cl set-commit --dry-run') + # Code below assumes Rietveld issue. + # TODO(tandrii): actually implement for Gerrit http://crbug.com/599931. + props = cl.GetIssueProperties() if props.get('closed'): parser.error('Cannot send tryjobs for a closed CL') @@ -4346,16 +4404,28 @@ def CMDweb(parser, args): def CMDset_commit(parser, args): """Sets the commit bit to trigger the Commit Queue.""" + parser.add_option('-d', '--dry-run', action='store_true', + help='trigger in dry run mode') + parser.add_option('-c', '--clear', action='store_true', + help='stop CQ run, if any') auth.add_auth_options(parser) options, args = parser.parse_args(args) auth_config = auth.extract_auth_config_from_options(options) if args: parser.error('Unrecognized args: %s' % ' '.join(args)) + if options.dry_run and options.clear: + parser.error('Make up your mind: both --dry-run and --clear not allowed') + cl = Changelist(auth_config=auth_config) - props = cl.GetIssueProperties() - if props.get('private'): - parser.error('Cannot set commit on private issue') - cl.SetFlag('commit', '1') + if options.clear: + state = _CQState.CLEAR + elif options.dry_run: + state = _CQState.DRY_RUN + else: + state = _CQState.COMMIT + if not cl.GetIssue(): + parser.error('Must upload the issue first') + cl.SetCQState(state) return 0 diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py index 4797288258..8eacfbbfe7 100755 --- a/tests/git_cl_test.py +++ b/tests/git_cl_test.py @@ -1300,6 +1300,36 @@ class TestGitCl(TestCase): }) self.assertIsNone(cl.EnsureAuthenticated(force=False)) + def test_cmd_set_commit_rietveld(self): + self.mock(git_cl._RietveldChangelistImpl, 'SetFlag', + lambda _, f, v: self._mocked_call(['SetFlag', f, v])) + self.calls = [ + ((['git', 'symbolic-ref', 'HEAD'],), 'feature'), + ((['git', 'config', 'branch.feature.rietveldissue'],), '123'), + ((['git', 'config', 'rietveld.autoupdate'],), ''), + ((['git', 'config', 'rietveld.server'],), ''), + ((['git', 'config', 'rietveld.server'],), ''), + ((['git', 'config', 'branch.feature.rietveldserver'],), + 'https://codereview.chromium.org'), + ((['SetFlag', 'commit', '1'], ), ''), + ] + self.assertEqual(0, git_cl.main(['set-commit'])) + + def test_cmd_set_commit_gerrit(self): + self.mock(git_cl.gerrit_util, 'SetReview', + lambda h, i, labels: self._mocked_call( + ['SetReview', h, i, labels])) + self.calls = [ + ((['git', 'symbolic-ref', 'HEAD'],), 'feature'), + ((['git', 'config', 'branch.feature.rietveldissue'],), ''), + ((['git', 'config', 'branch.feature.gerritissue'],), '123'), + ((['git', 'config', 'branch.feature.gerritserver'],), + 'https://chromium-review.googlesource.com'), + ((['SetReview', 'chromium-review.googlesource.com', 123, + {'Commit-Queue': 1}],), ''), + ] + self.assertEqual(0, git_cl.main(['set-commit', '-d'])) + if __name__ == '__main__': git_cl.logging.basicConfig(