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 1564 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1575 print( | 1575 print( |
1576 'You aren\'t using Rietveld at the moment, but Gerrit.\n' | 1576 'You aren\'t using Rietveld at the moment, but Gerrit.\n' |
1577 'Using Rietveld in your PRESUBMIT scripts won\'t work.\n' | 1577 'Using Rietveld in your PRESUBMIT scripts won\'t work.\n' |
1578 'Please, either change your PRESUBIT to not use rietveld_obj.%s,\n' | 1578 'Please, either change your PRESUBIT to not use rietveld_obj.%s,\n' |
1579 'or use Rietveld for codereview.\n' | 1579 'or use Rietveld for codereview.\n' |
1580 'See also http://crbug.com/579160.' % attr) | 1580 'See also http://crbug.com/579160.' % attr) |
1581 raise NotImplementedError() | 1581 raise NotImplementedError() |
1582 return ThisIsNotRietveldIssue() | 1582 return ThisIsNotRietveldIssue() |
1583 | 1583 |
1584 def GetStatus(self): | 1584 def GetStatus(self): |
1585 # TODO(tandrii) | 1585 """Apply a rough heuristic to give a simple summary of an issue's review |
1586 raise NotImplementedError() | 1586 or CQ status, assuming adherence to a common workflow. |
| 1587 |
| 1588 Returns None if no issue for this branch, or one of the following keywords: |
| 1589 * 'error' - error from review tool (including deleted issues) |
| 1590 * 'unsent' - no reviewers added |
| 1591 * 'waiting' - waiting for review |
| 1592 * 'reply' - waiting for owner to reply to review |
| 1593 * 'not lgtm' - Code-Review -2 from at least one approved reviewer |
| 1594 * 'lgtm' - Code-Review +2 from at least one approved reviewer |
| 1595 * 'commit' - in the commit queue |
| 1596 * 'closed' - abandoned |
| 1597 """ |
| 1598 if not self.GetIssue(): |
| 1599 return None |
| 1600 |
| 1601 try: |
| 1602 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION']) |
| 1603 except httplib.HTTPException: |
| 1604 return 'error' |
| 1605 |
| 1606 if data['status'] == 'ABANDONED': |
| 1607 return 'closed' |
| 1608 |
| 1609 cq_label = data['labels'].get('Commit-Queue', {}) |
| 1610 if cq_label: |
| 1611 # Vote value is a stringified integer, which we expect from 0 to 2. |
| 1612 vote_value = cq_label.get('value', '0') |
| 1613 vote_text = cq_label.get('values', {}).get(vote_value, '') |
| 1614 if vote_text.lower() == 'commit': |
| 1615 return 'commit' |
| 1616 |
| 1617 lgtm_label = data['labels'].get('Code-Review', {}) |
| 1618 if lgtm_label: |
| 1619 if 'rejected' in lgtm_label: |
| 1620 return 'not lgtm' |
| 1621 if 'approved' in lgtm_label: |
| 1622 return 'lgtm' |
| 1623 |
| 1624 if not data.get('reviewers', {}).get('REVIEWER', []): |
| 1625 return 'unsent' |
| 1626 |
| 1627 messages = data.get('messages', []) |
| 1628 if messages: |
| 1629 owner = data['owner'].get('_account_id') |
| 1630 last_message_author = messages[-1].get('author', {}).get('_account_id') |
| 1631 if owner != last_message_author: |
| 1632 # Some reply from non-owner. |
| 1633 return 'reply' |
| 1634 |
| 1635 return 'waiting' |
1587 | 1636 |
1588 def GetMostRecentPatchset(self): | 1637 def GetMostRecentPatchset(self): |
1589 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), | 1638 data = self._GetChangeDetail(['CURRENT_REVISION']) |
1590 ['CURRENT_REVISION']) | |
1591 return data['revisions'][data['current_revision']]['_number'] | 1639 return data['revisions'][data['current_revision']]['_number'] |
1592 | 1640 |
1593 def FetchDescription(self): | 1641 def FetchDescription(self): |
1594 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), | 1642 data = self._GetChangeDetail(['COMMIT_FOOTERS', 'CURRENT_REVISION']) |
1595 ['COMMIT_FOOTERS', 'CURRENT_REVISION']) | |
1596 return data['revisions'][data['current_revision']]['commit_with_footers'] | 1643 return data['revisions'][data['current_revision']]['commit_with_footers'] |
1597 | 1644 |
1598 def UpdateDescriptionRemote(self, description): | 1645 def UpdateDescriptionRemote(self, description): |
1599 # TODO(tandrii) | 1646 # TODO(tandrii) |
1600 raise NotImplementedError() | 1647 raise NotImplementedError() |
1601 | 1648 |
1602 def CloseIssue(self): | 1649 def CloseIssue(self): |
1603 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') | 1650 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') |
1604 | 1651 |
1605 | 1652 |
| 1653 def _GetChangeDetail(self, options): |
| 1654 return gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), |
| 1655 options) |
| 1656 |
| 1657 |
1606 class ChangeDescription(object): | 1658 class ChangeDescription(object): |
1607 """Contains a parsed form of the change description.""" | 1659 """Contains a parsed form of the change description.""" |
1608 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' | 1660 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' |
1609 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' | 1661 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' |
1610 | 1662 |
1611 def __init__(self, description): | 1663 def __init__(self, description): |
1612 self._description_lines = (description or '').strip().splitlines() | 1664 self._description_lines = (description or '').strip().splitlines() |
1613 | 1665 |
1614 @property # www.logilab.org/ticket/89786 | 1666 @property # www.logilab.org/ticket/89786 |
1615 def description(self): # pylint: disable=E0202 | 1667 def description(self): # pylint: disable=E0202 |
(...skipping 2657 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4273 if __name__ == '__main__': | 4325 if __name__ == '__main__': |
4274 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4326 # These affect sys.stdout so do it outside of main() to simplify mocks in |
4275 # unit testing. | 4327 # unit testing. |
4276 fix_encoding.fix_encoding() | 4328 fix_encoding.fix_encoding() |
4277 colorama.init() | 4329 colorama.init() |
4278 try: | 4330 try: |
4279 sys.exit(main(sys.argv[1:])) | 4331 sys.exit(main(sys.argv[1:])) |
4280 except KeyboardInterrupt: | 4332 except KeyboardInterrupt: |
4281 sys.stderr.write('interrupted\n') | 4333 sys.stderr.write('interrupted\n') |
4282 sys.exit(1) | 4334 sys.exit(1) |
OLD | NEW |