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

Side by Side Diff: git_cl.py

Issue 2250093002: git cl: add plumbing command to get metadata about CLs in machine-readable form (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 4 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | 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 839 matching lines...) Expand 10 before | Expand all | Expand 10 after
850 return ShortBranchName(branchref) 850 return ShortBranchName(branchref)
851 return None 851 return None
852 852
853 853
854 class _CQState(object): 854 class _CQState(object):
855 """Enum for states of CL with respect to Commit Queue.""" 855 """Enum for states of CL with respect to Commit Queue."""
856 NONE = 'none' 856 NONE = 'none'
857 DRY_RUN = 'dry_run' 857 DRY_RUN = 'dry_run'
858 COMMIT = 'commit' 858 COMMIT = 'commit'
859 859
860 ALL_STATES = [NONE, DRY_RUN, COMMIT] 860 ALL_STATES = [NONE, DRY_RUN, COMMIT]
tandrii(chromium) 2016/08/16 14:52:10 can you make use of these states?
861 861
862 862
863 class _ParsedIssueNumberArgument(object): 863 class _ParsedIssueNumberArgument(object):
864 def __init__(self, issue=None, patchset=None, hostname=None): 864 def __init__(self, issue=None, patchset=None, hostname=None):
865 self.issue = issue 865 self.issue = issue
866 self.patchset = patchset 866 self.patchset = patchset
867 self.hostname = hostname 867 self.hostname = hostname
868 868
869 @property 869 @property
870 def valid(self): 870 def valid(self):
(...skipping 570 matching lines...) Expand 10 before | Expand all | Expand 10 after
1441 return self._codereview_impl.SetCQState(new_state) 1441 return self._codereview_impl.SetCQState(new_state)
1442 1442
1443 # Forward methods to codereview specific implementation. 1443 # Forward methods to codereview specific implementation.
1444 1444
1445 def CloseIssue(self): 1445 def CloseIssue(self):
1446 return self._codereview_impl.CloseIssue() 1446 return self._codereview_impl.CloseIssue()
1447 1447
1448 def GetStatus(self): 1448 def GetStatus(self):
1449 return self._codereview_impl.GetStatus() 1449 return self._codereview_impl.GetStatus()
1450 1450
1451 def IsOpen(self):
1452 return self._codereview_impl.IsOpen()
1453
1454 def IsInCQ(self):
tandrii(chromium) 2016/08/16 14:52:10 s/IsInCQ/GetCQState and return CQ status from abo
1455 return self._codereview_impl.IsInCQ()
1456
1451 def GetCodereviewServer(self): 1457 def GetCodereviewServer(self):
1452 return self._codereview_impl.GetCodereviewServer() 1458 return self._codereview_impl.GetCodereviewServer()
1453 1459
1454 def GetApprovingReviewers(self): 1460 def GetApprovingReviewers(self):
1455 return self._codereview_impl.GetApprovingReviewers() 1461 return self._codereview_impl.GetApprovingReviewers()
1456 1462
1457 def GetMostRecentPatchset(self): 1463 def GetMostRecentPatchset(self):
1458 return self._codereview_impl.GetMostRecentPatchset() 1464 return self._codereview_impl.GetMostRecentPatchset()
1459 1465
1460 def __getattr__(self, attr): 1466 def __getattr__(self, attr):
(...skipping 15 matching lines...) Expand all
1476 return getattr(self._changelist, attr) 1482 return getattr(self._changelist, attr)
1477 1483
1478 def GetStatus(self): 1484 def GetStatus(self):
1479 """Apply a rough heuristic to give a simple summary of an issue's review 1485 """Apply a rough heuristic to give a simple summary of an issue's review
1480 or CQ status, assuming adherence to a common workflow. 1486 or CQ status, assuming adherence to a common workflow.
1481 1487
1482 Returns None if no issue for this branch, or specific string keywords. 1488 Returns None if no issue for this branch, or specific string keywords.
1483 """ 1489 """
1484 raise NotImplementedError() 1490 raise NotImplementedError()
1485 1491
1492 def IsOpen(self):
1493 """Returns True iff the issue is open."""
1494 raise NotImplementedError()
1495
1496 def IsInCQ(self):
1497 """Returns True iff the issue is in CQ (does not include dry run)."""
1498 raise NotImplementedError()
1499
1486 def GetCodereviewServer(self): 1500 def GetCodereviewServer(self):
1487 """Returns server URL without end slash, like "https://codereview.com".""" 1501 """Returns server URL without end slash, like "https://codereview.com"."""
1488 raise NotImplementedError() 1502 raise NotImplementedError()
1489 1503
1490 def FetchDescription(self): 1504 def FetchDescription(self):
1491 """Fetches and returns description from the codereview server.""" 1505 """Fetches and returns description from the codereview server."""
1492 raise NotImplementedError() 1506 raise NotImplementedError()
1493 1507
1494 def GetCodereviewServerSetting(self): 1508 def GetCodereviewServerSetting(self):
1495 """Returns git config setting for the codereview server.""" 1509 """Returns git config setting for the codereview server."""
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
1674 * 'closed' - closed 1688 * 'closed' - closed
1675 """ 1689 """
1676 if not self.GetIssue(): 1690 if not self.GetIssue():
1677 return None 1691 return None
1678 1692
1679 try: 1693 try:
1680 props = self.GetIssueProperties() 1694 props = self.GetIssueProperties()
1681 except urllib2.HTTPError: 1695 except urllib2.HTTPError:
1682 return 'error' 1696 return 'error'
1683 1697
1684 if props.get('closed'): 1698 if not self.IsOpen():
1685 # Issue is closed.
1686 return 'closed' 1699 return 'closed'
1687 if props.get('commit') and not props.get('cq_dry_run', False): 1700 if self.IsInCQ():
tandrii(chromium) 2016/08/16 14:52:10 and this is great fix.
1688 # Issue is in the commit queue.
1689 return 'commit' 1701 return 'commit'
1690 1702
1691 try: 1703 try:
1692 reviewers = self.GetApprovingReviewers() 1704 reviewers = self.GetApprovingReviewers()
1693 except urllib2.HTTPError: 1705 except urllib2.HTTPError:
1694 return 'error' 1706 return 'error'
1695 1707
1696 if reviewers: 1708 if reviewers:
1697 # Was LGTM'ed. 1709 # Was LGTM'ed.
1698 return 'lgtm' 1710 return 'lgtm'
(...skipping 13 matching lines...) Expand all
1712 break 1724 break
1713 1725
1714 if not messages: 1726 if not messages:
1715 # No message was sent. 1727 # No message was sent.
1716 return 'unsent' 1728 return 'unsent'
1717 if messages[-1]['sender'] != props.get('owner_email'): 1729 if messages[-1]['sender'] != props.get('owner_email'):
1718 # Non-LGTM reply from non-owner and not CQ bot. 1730 # Non-LGTM reply from non-owner and not CQ bot.
1719 return 'reply' 1731 return 'reply'
1720 return 'waiting' 1732 return 'waiting'
1721 1733
1734 def IsOpen(self, props=None):
1735 if not props:
1736 props = self.GetIssueProperties()
1737 return not props.get('closed')
1738
1739 def IsInCQ(self, props=None):
1740 if not props:
1741 props = self.GetIssueProperties()
1742 return props.get('commit') and not props.get('cq_dry_run')
1743
1722 def UpdateDescriptionRemote(self, description): 1744 def UpdateDescriptionRemote(self, description):
1723 return self.RpcServer().update_description( 1745 return self.RpcServer().update_description(
1724 self.GetIssue(), self.description) 1746 self.GetIssue(), self.description)
1725 1747
1726 def CloseIssue(self): 1748 def CloseIssue(self):
1727 return self.RpcServer().close_issue(self.GetIssue()) 1749 return self.RpcServer().close_issue(self.GetIssue())
1728 1750
1729 def SetFlag(self, flag, value): 1751 def SetFlag(self, flag, value):
1730 return self.SetFlags({flag: value}) 1752 return self.SetFlags({flag: value})
1731 1753
(...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after
2184 * 'closed' - abandoned 2206 * 'closed' - abandoned
2185 """ 2207 """
2186 if not self.GetIssue(): 2208 if not self.GetIssue():
2187 return None 2209 return None
2188 2210
2189 try: 2211 try:
2190 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION']) 2212 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION'])
2191 except httplib.HTTPException: 2213 except httplib.HTTPException:
2192 return 'error' 2214 return 'error'
2193 2215
2194 if data['status'] in ('ABANDONED', 'MERGED'): 2216 if not self.IsOpen(data=data):
2195 return 'closed' 2217 return 'closed'
2196 2218
2197 cq_label = data['labels'].get('Commit-Queue', {}) 2219 if self.IsInCQ(data=data):
2198 if cq_label: 2220 return 'commit'
2199 # Vote value is a stringified integer, which we expect from 0 to 2.
2200 vote_value = cq_label.get('value', '0')
2201 vote_text = cq_label.get('values', {}).get(vote_value, '')
2202 if vote_text.lower() == 'commit':
2203 return 'commit'
2204 2221
2205 lgtm_label = data['labels'].get('Code-Review', {}) 2222 lgtm_label = data['labels'].get('Code-Review', {})
2206 if lgtm_label: 2223 if lgtm_label:
2207 if 'rejected' in lgtm_label: 2224 if 'rejected' in lgtm_label:
2208 return 'not lgtm' 2225 return 'not lgtm'
2209 if 'approved' in lgtm_label: 2226 if 'approved' in lgtm_label:
2210 return 'lgtm' 2227 return 'lgtm'
2211 2228
2212 if not data.get('reviewers', {}).get('REVIEWER', []): 2229 if not data.get('reviewers', {}).get('REVIEWER', []):
2213 return 'unsent' 2230 return 'unsent'
2214 2231
2215 messages = data.get('messages', []) 2232 messages = data.get('messages', [])
2216 if messages: 2233 if messages:
2217 owner = data['owner'].get('_account_id') 2234 owner = data['owner'].get('_account_id')
2218 last_message_author = messages[-1].get('author', {}).get('_account_id') 2235 last_message_author = messages[-1].get('author', {}).get('_account_id')
2219 if owner != last_message_author: 2236 if owner != last_message_author:
2220 # Some reply from non-owner. 2237 # Some reply from non-owner.
2221 return 'reply' 2238 return 'reply'
2222 2239
2223 return 'waiting' 2240 return 'waiting'
2224 2241
2242 def IsOpen(self, data=None):
2243 if not data:
2244 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION'])
2245 return data['status'] not in ('ABANDONED', 'MERGED')
2246
2247 def IsInCQ(self, data=None):
2248 if not data:
2249 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION'])
2250 cq_label = data['labels'].get('Commit-Queue', {})
2251 if cq_label:
2252 # Vote value is a stringified integer, which we expect from 0 to 2.
2253 vote_value = cq_label.get('value', '0')
2254 vote_text = cq_label.get('values', {}).get(vote_value, '')
2255 if vote_text.lower() == 'commit':
2256 return True
2257 return False
2258
2225 def GetMostRecentPatchset(self): 2259 def GetMostRecentPatchset(self):
2226 data = self._GetChangeDetail(['CURRENT_REVISION']) 2260 data = self._GetChangeDetail(['CURRENT_REVISION'])
2227 return data['revisions'][data['current_revision']]['_number'] 2261 return data['revisions'][data['current_revision']]['_number']
2228 2262
2229 def FetchDescription(self): 2263 def FetchDescription(self):
2230 data = self._GetChangeDetail(['CURRENT_REVISION']) 2264 data = self._GetChangeDetail(['CURRENT_REVISION'])
2231 current_rev = data['current_revision'] 2265 current_rev = data['current_revision']
2232 url = data['revisions'][current_rev]['fetch']['http']['url'] 2266 url = data['revisions'][current_rev]['fetch']['http']['url']
2233 return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev) 2267 return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev)
2234 2268
(...skipping 2817 matching lines...) Expand 10 before | Expand all | Expand 10 after
5052 def CMDlol(parser, args): 5086 def CMDlol(parser, args):
5053 # This command is intentionally undocumented. 5087 # This command is intentionally undocumented.
5054 print(zlib.decompress(base64.b64decode( 5088 print(zlib.decompress(base64.b64decode(
5055 'eNptkLEOwyAMRHe+wupCIqW57v0Vq84WqWtXyrcXnCBsmgMJ+/SSAxMZgRB6NzE' 5089 'eNptkLEOwyAMRHe+wupCIqW57v0Vq84WqWtXyrcXnCBsmgMJ+/SSAxMZgRB6NzE'
5056 'E2ObgCKJooYdu4uAQVffUEoE1sRQLxAcqzd7uK2gmStrll1ucV3uZyaY5sXyDd9' 5090 'E2ObgCKJooYdu4uAQVffUEoE1sRQLxAcqzd7uK2gmStrll1ucV3uZyaY5sXyDd9'
5057 'JAnN+lAXsOMJ90GANAi43mq5/VeeacylKVgi8o6F1SC63FxnagHfJUTfUYdCR/W' 5091 'JAnN+lAXsOMJ90GANAi43mq5/VeeacylKVgi8o6F1SC63FxnagHfJUTfUYdCR/W'
5058 'Ofe+0dHL7PicpytKP750Fh1q2qnLVof4w8OZWNY'))) 5092 'Ofe+0dHL7PicpytKP750Fh1q2qnLVof4w8OZWNY')))
5059 return 0 5093 return 0
5060 5094
5061 5095
5096 def CMDplumbing(parser, args):
5097 """Returns info about the CL in machine-readable form."""
5098 parser.add_option('--json', help='Path to output JSON file')
5099
5100 auth.add_auth_options(parser)
5101
5102 _add_codereview_select_options(parser)
5103 options, args = parser.parse_args(args)
5104 _process_codereview_select_options(parser, options)
5105
5106 auth_config = auth.extract_auth_config_from_options(options)
5107
5108 cl = Changelist(auth_config=auth_config, codereview=options.forced_codereview)
5109
5110 result = {
5111 'open': cl.IsOpen(),
5112 'commit_queue': cl.IsInCQ(),
5113 }
5114
5115 if options.json:
5116 with open(options.json, 'w') as f:
5117 json.dump(result, f)
5118 else:
5119 print(json.dumps(result, sort_keys=True, indent=4))
5120
5121 return 0
5122
5123
5062 class OptionParser(optparse.OptionParser): 5124 class OptionParser(optparse.OptionParser):
5063 """Creates the option parse and add --verbose support.""" 5125 """Creates the option parse and add --verbose support."""
5064 def __init__(self, *args, **kwargs): 5126 def __init__(self, *args, **kwargs):
5065 optparse.OptionParser.__init__( 5127 optparse.OptionParser.__init__(
5066 self, *args, prog='git cl', version=__version__, **kwargs) 5128 self, *args, prog='git cl', version=__version__, **kwargs)
5067 self.add_option( 5129 self.add_option(
5068 '-v', '--verbose', action='count', default=0, 5130 '-v', '--verbose', action='count', default=0,
5069 help='Use 2 times for more debugging info') 5131 help='Use 2 times for more debugging info')
5070 5132
5071 def parse_args(self, args=None, values=None): 5133 def parse_args(self, args=None, values=None):
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
5103 if __name__ == '__main__': 5165 if __name__ == '__main__':
5104 # These affect sys.stdout so do it outside of main() to simplify mocks in 5166 # These affect sys.stdout so do it outside of main() to simplify mocks in
5105 # unit testing. 5167 # unit testing.
5106 fix_encoding.fix_encoding() 5168 fix_encoding.fix_encoding()
5107 setup_color.init() 5169 setup_color.init()
5108 try: 5170 try:
5109 sys.exit(main(sys.argv[1:])) 5171 sys.exit(main(sys.argv[1:]))
5110 except KeyboardInterrupt: 5172 except KeyboardInterrupt:
5111 sys.stderr.write('interrupted\n') 5173 sys.stderr.write('interrupted\n')
5112 sys.exit(1) 5174 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698