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

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: fixes 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 1430 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 GetCQState(self):
1455 state = self._codereview_impl.GetCQState()
1456 assert state in _CQState.ALL_STATES
1457 return state
1458
1451 def GetCodereviewServer(self): 1459 def GetCodereviewServer(self):
1452 return self._codereview_impl.GetCodereviewServer() 1460 return self._codereview_impl.GetCodereviewServer()
1453 1461
1454 def GetApprovingReviewers(self): 1462 def GetApprovingReviewers(self):
1455 return self._codereview_impl.GetApprovingReviewers() 1463 return self._codereview_impl.GetApprovingReviewers()
1456 1464
1457 def GetMostRecentPatchset(self): 1465 def GetMostRecentPatchset(self):
1458 return self._codereview_impl.GetMostRecentPatchset() 1466 return self._codereview_impl.GetMostRecentPatchset()
1459 1467
1460 def __getattr__(self, attr): 1468 def __getattr__(self, attr):
(...skipping 15 matching lines...) Expand all
1476 return getattr(self._changelist, attr) 1484 return getattr(self._changelist, attr)
1477 1485
1478 def GetStatus(self): 1486 def GetStatus(self):
1479 """Apply a rough heuristic to give a simple summary of an issue's review 1487 """Apply a rough heuristic to give a simple summary of an issue's review
1480 or CQ status, assuming adherence to a common workflow. 1488 or CQ status, assuming adherence to a common workflow.
1481 1489
1482 Returns None if no issue for this branch, or specific string keywords. 1490 Returns None if no issue for this branch, or specific string keywords.
1483 """ 1491 """
1484 raise NotImplementedError() 1492 raise NotImplementedError()
1485 1493
1494 def IsOpen(self):
1495 """Returns True iff the issue is open."""
1496 raise NotImplementedError()
1497
1498 def GetCQState(self):
1499 """Returns CQ state (see _CQState)."""
1500 raise NotImplementedError()
1501
1486 def GetCodereviewServer(self): 1502 def GetCodereviewServer(self):
1487 """Returns server URL without end slash, like "https://codereview.com".""" 1503 """Returns server URL without end slash, like "https://codereview.com"."""
1488 raise NotImplementedError() 1504 raise NotImplementedError()
1489 1505
1490 def FetchDescription(self): 1506 def FetchDescription(self):
1491 """Fetches and returns description from the codereview server.""" 1507 """Fetches and returns description from the codereview server."""
1492 raise NotImplementedError() 1508 raise NotImplementedError()
1493 1509
1494 def GetCodereviewServerSetting(self): 1510 def GetCodereviewServerSetting(self):
1495 """Returns git config setting for the codereview server.""" 1511 """Returns git config setting for the codereview server."""
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
1674 * 'closed' - closed 1690 * 'closed' - closed
1675 """ 1691 """
1676 if not self.GetIssue(): 1692 if not self.GetIssue():
1677 return None 1693 return None
1678 1694
1679 try: 1695 try:
1680 props = self.GetIssueProperties() 1696 props = self.GetIssueProperties()
1681 except urllib2.HTTPError: 1697 except urllib2.HTTPError:
1682 return 'error' 1698 return 'error'
1683 1699
1684 if props.get('closed'): 1700 if not self.IsOpen():
1685 # Issue is closed.
1686 return 'closed' 1701 return 'closed'
1687 if props.get('commit') and not props.get('cq_dry_run', False): 1702 if self.GetCQState() == _CQState.COMMIT:
1688 # Issue is in the commit queue.
1689 return 'commit' 1703 return 'commit'
1690 1704
1691 try: 1705 try:
1692 reviewers = self.GetApprovingReviewers() 1706 reviewers = self.GetApprovingReviewers()
1693 except urllib2.HTTPError: 1707 except urllib2.HTTPError:
1694 return 'error' 1708 return 'error'
1695 1709
1696 if reviewers: 1710 if reviewers:
1697 # Was LGTM'ed. 1711 # Was LGTM'ed.
1698 return 'lgtm' 1712 return 'lgtm'
(...skipping 13 matching lines...) Expand all
1712 break 1726 break
1713 1727
1714 if not messages: 1728 if not messages:
1715 # No message was sent. 1729 # No message was sent.
1716 return 'unsent' 1730 return 'unsent'
1717 if messages[-1]['sender'] != props.get('owner_email'): 1731 if messages[-1]['sender'] != props.get('owner_email'):
1718 # Non-LGTM reply from non-owner and not CQ bot. 1732 # Non-LGTM reply from non-owner and not CQ bot.
1719 return 'reply' 1733 return 'reply'
1720 return 'waiting' 1734 return 'waiting'
1721 1735
1736 def IsOpen(self, props=None):
1737 if not props:
1738 props = self.GetIssueProperties()
1739 return not props.get('closed')
1740
1741 def GetCQState(self, props=None):
1742 if not props:
1743 props = self.GetIssueProperties()
1744 if props.get('commit') and not props.get('cq_dry_run'):
1745 return _CQState.COMMIT
1746 if props.get('cq_dry_run'):
1747 return _CQState.DRY_RUN
1748 return _CQState.NONE
1749
1722 def UpdateDescriptionRemote(self, description): 1750 def UpdateDescriptionRemote(self, description):
1723 return self.RpcServer().update_description( 1751 return self.RpcServer().update_description(
1724 self.GetIssue(), self.description) 1752 self.GetIssue(), self.description)
1725 1753
1726 def CloseIssue(self): 1754 def CloseIssue(self):
1727 return self.RpcServer().close_issue(self.GetIssue()) 1755 return self.RpcServer().close_issue(self.GetIssue())
1728 1756
1729 def SetFlag(self, flag, value): 1757 def SetFlag(self, flag, value):
1730 return self.SetFlags({flag: value}) 1758 return self.SetFlags({flag: value})
1731 1759
(...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after
2184 * 'closed' - abandoned 2212 * 'closed' - abandoned
2185 """ 2213 """
2186 if not self.GetIssue(): 2214 if not self.GetIssue():
2187 return None 2215 return None
2188 2216
2189 try: 2217 try:
2190 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION']) 2218 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION'])
2191 except httplib.HTTPException: 2219 except httplib.HTTPException:
2192 return 'error' 2220 return 'error'
2193 2221
2194 if data['status'] in ('ABANDONED', 'MERGED'): 2222 if not self.IsOpen(data=data):
2195 return 'closed' 2223 return 'closed'
2196 2224
2197 cq_label = data['labels'].get('Commit-Queue', {}) 2225 if self.GetCQState(data=data) == _CQState.COMMIT:
2198 if cq_label: 2226 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 2227
2205 lgtm_label = data['labels'].get('Code-Review', {}) 2228 lgtm_label = data['labels'].get('Code-Review', {})
2206 if lgtm_label: 2229 if lgtm_label:
2207 if 'rejected' in lgtm_label: 2230 if 'rejected' in lgtm_label:
2208 return 'not lgtm' 2231 return 'not lgtm'
2209 if 'approved' in lgtm_label: 2232 if 'approved' in lgtm_label:
2210 return 'lgtm' 2233 return 'lgtm'
2211 2234
2212 if not data.get('reviewers', {}).get('REVIEWER', []): 2235 if not data.get('reviewers', {}).get('REVIEWER', []):
2213 return 'unsent' 2236 return 'unsent'
2214 2237
2215 messages = data.get('messages', []) 2238 messages = data.get('messages', [])
2216 if messages: 2239 if messages:
2217 owner = data['owner'].get('_account_id') 2240 owner = data['owner'].get('_account_id')
2218 last_message_author = messages[-1].get('author', {}).get('_account_id') 2241 last_message_author = messages[-1].get('author', {}).get('_account_id')
2219 if owner != last_message_author: 2242 if owner != last_message_author:
2220 # Some reply from non-owner. 2243 # Some reply from non-owner.
2221 return 'reply' 2244 return 'reply'
2222 2245
2223 return 'waiting' 2246 return 'waiting'
2224 2247
2248 def IsOpen(self, data=None):
2249 if not data:
2250 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION'])
2251 return data['status'] not in ('ABANDONED', 'MERGED')
2252
2253 def GetCQState(self, data=None):
2254 if not data:
2255 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION'])
2256 cq_label = data['labels'].get('Commit-Queue', {})
2257 if cq_label:
2258 # Vote value is a stringified integer, which we expect from 0 to 2.
2259 vote_value = cq_label.get('value', '0')
2260 vote_text = cq_label.get('values', {}).get(vote_value, '')
2261 if vote_text.lower() == 'commit':
2262 return _CQState.COMMIT
2263 if vote_test.lower() == 'dry run':
2264 return _CQState.DRY_RUN
2265 return _CQState.NONE
2266
2225 def GetMostRecentPatchset(self): 2267 def GetMostRecentPatchset(self):
2226 data = self._GetChangeDetail(['CURRENT_REVISION']) 2268 data = self._GetChangeDetail(['CURRENT_REVISION'])
2227 return data['revisions'][data['current_revision']]['_number'] 2269 return data['revisions'][data['current_revision']]['_number']
2228 2270
2229 def FetchDescription(self): 2271 def FetchDescription(self):
2230 data = self._GetChangeDetail(['CURRENT_REVISION']) 2272 data = self._GetChangeDetail(['CURRENT_REVISION'])
2231 current_rev = data['current_revision'] 2273 current_rev = data['current_revision']
2232 url = data['revisions'][current_rev]['fetch']['http']['url'] 2274 url = data['revisions'][current_rev]['fetch']['http']['url']
2233 return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev) 2275 return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev)
2234 2276
(...skipping 2817 matching lines...) Expand 10 before | Expand all | Expand 10 after
5052 def CMDlol(parser, args): 5094 def CMDlol(parser, args):
5053 # This command is intentionally undocumented. 5095 # This command is intentionally undocumented.
5054 print(zlib.decompress(base64.b64decode( 5096 print(zlib.decompress(base64.b64decode(
5055 'eNptkLEOwyAMRHe+wupCIqW57v0Vq84WqWtXyrcXnCBsmgMJ+/SSAxMZgRB6NzE' 5097 'eNptkLEOwyAMRHe+wupCIqW57v0Vq84WqWtXyrcXnCBsmgMJ+/SSAxMZgRB6NzE'
5056 'E2ObgCKJooYdu4uAQVffUEoE1sRQLxAcqzd7uK2gmStrll1ucV3uZyaY5sXyDd9' 5098 'E2ObgCKJooYdu4uAQVffUEoE1sRQLxAcqzd7uK2gmStrll1ucV3uZyaY5sXyDd9'
5057 'JAnN+lAXsOMJ90GANAi43mq5/VeeacylKVgi8o6F1SC63FxnagHfJUTfUYdCR/W' 5099 'JAnN+lAXsOMJ90GANAi43mq5/VeeacylKVgi8o6F1SC63FxnagHfJUTfUYdCR/W'
5058 'Ofe+0dHL7PicpytKP750Fh1q2qnLVof4w8OZWNY'))) 5100 'Ofe+0dHL7PicpytKP750Fh1q2qnLVof4w8OZWNY')))
5059 return 0 5101 return 0
5060 5102
5061 5103
5104 def CMDplumbing(parser, args):
5105 """Returns info about the CL in machine-readable form."""
5106 parser.add_option('--json', help='Path to output JSON file')
5107
5108 auth.add_auth_options(parser)
5109
5110 _add_codereview_select_options(parser)
5111 options, args = parser.parse_args(args)
5112 _process_codereview_select_options(parser, options)
5113
5114 auth_config = auth.extract_auth_config_from_options(options)
5115
5116 cl = Changelist(auth_config=auth_config, codereview=options.forced_codereview)
5117
5118 result = {
5119 'open': cl.IsOpen(),
5120 'commit_queue': cl.GetCQState(),
5121 }
5122
5123 if options.json:
5124 with open(options.json, 'w') as f:
5125 json.dump(result, f)
5126 else:
5127 print(json.dumps(result, sort_keys=True, indent=4))
5128
5129 return 0
5130
5131
5062 class OptionParser(optparse.OptionParser): 5132 class OptionParser(optparse.OptionParser):
5063 """Creates the option parse and add --verbose support.""" 5133 """Creates the option parse and add --verbose support."""
5064 def __init__(self, *args, **kwargs): 5134 def __init__(self, *args, **kwargs):
5065 optparse.OptionParser.__init__( 5135 optparse.OptionParser.__init__(
5066 self, *args, prog='git cl', version=__version__, **kwargs) 5136 self, *args, prog='git cl', version=__version__, **kwargs)
5067 self.add_option( 5137 self.add_option(
5068 '-v', '--verbose', action='count', default=0, 5138 '-v', '--verbose', action='count', default=0,
5069 help='Use 2 times for more debugging info') 5139 help='Use 2 times for more debugging info')
5070 5140
5071 def parse_args(self, args=None, values=None): 5141 def parse_args(self, args=None, values=None):
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
5103 if __name__ == '__main__': 5173 if __name__ == '__main__':
5104 # These affect sys.stdout so do it outside of main() to simplify mocks in 5174 # These affect sys.stdout so do it outside of main() to simplify mocks in
5105 # unit testing. 5175 # unit testing.
5106 fix_encoding.fix_encoding() 5176 fix_encoding.fix_encoding()
5107 setup_color.init() 5177 setup_color.init()
5108 try: 5178 try:
5109 sys.exit(main(sys.argv[1:])) 5179 sys.exit(main(sys.argv[1:]))
5110 except KeyboardInterrupt: 5180 except KeyboardInterrupt:
5111 sys.stderr.write('interrupted\n') 5181 sys.stderr.write('interrupted\n')
5112 sys.exit(1) 5182 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