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 __future__ import print_function | 10 from __future__ import print_function |
(...skipping 959 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
970 parsed_url = urlparse.urlparse(url) | 970 parsed_url = urlparse.urlparse(url) |
971 except ValueError: | 971 except ValueError: |
972 return fail_result | 972 return fail_result |
973 for cls in _CODEREVIEW_IMPLEMENTATIONS.itervalues(): | 973 for cls in _CODEREVIEW_IMPLEMENTATIONS.itervalues(): |
974 tmp = cls.ParseIssueURL(parsed_url) | 974 tmp = cls.ParseIssueURL(parsed_url) |
975 if tmp is not None: | 975 if tmp is not None: |
976 return tmp | 976 return tmp |
977 return fail_result | 977 return fail_result |
978 | 978 |
979 | 979 |
980 class GerritIssueNotExists(Exception): | |
Sergiy Byelozyorov
2016/10/10 11:41:11
IMHO it should be either GerritIssueExistsNot or G
tandrii(chromium)
2016/10/10 15:10:14
I appreciate your review, but I think NotExists is
| |
981 def __init__(self, issue, url): | |
982 self.issue = issue | |
983 self.url = url | |
984 super(GerritIssueNotExists, self).__init__() | |
985 | |
986 def __str__(self): | |
987 return 'issue %s at %s does not exist or you have no access to it' % ( | |
988 self.issue, self.url) | |
989 | |
990 | |
980 class Changelist(object): | 991 class Changelist(object): |
981 """Changelist works with one changelist in local branch. | 992 """Changelist works with one changelist in local branch. |
982 | 993 |
983 Supports two codereview backends: Rietveld or Gerrit, selected at object | 994 Supports two codereview backends: Rietveld or Gerrit, selected at object |
984 creation. | 995 creation. |
985 | 996 |
986 Notes: | 997 Notes: |
987 * Not safe for concurrent multi-{thread,process} use. | 998 * Not safe for concurrent multi-{thread,process} use. |
988 * Caches values from current branch. Therefore, re-use after branch change | 999 * Caches values from current branch. Therefore, re-use after branch change |
989 with great care. | 1000 with great care. |
(...skipping 1261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2251 | 2262 |
2252 def GetStatus(self): | 2263 def GetStatus(self): |
2253 """Apply a rough heuristic to give a simple summary of an issue's review | 2264 """Apply a rough heuristic to give a simple summary of an issue's review |
2254 or CQ status, assuming adherence to a common workflow. | 2265 or CQ status, assuming adherence to a common workflow. |
2255 | 2266 |
2256 Returns None if no issue for this branch, or one of the following keywords: | 2267 Returns None if no issue for this branch, or one of the following keywords: |
2257 * 'error' - error from review tool (including deleted issues) | 2268 * 'error' - error from review tool (including deleted issues) |
2258 * 'unsent' - no reviewers added | 2269 * 'unsent' - no reviewers added |
2259 * 'waiting' - waiting for review | 2270 * 'waiting' - waiting for review |
2260 * 'reply' - waiting for owner to reply to review | 2271 * 'reply' - waiting for owner to reply to review |
2261 * 'not lgtm' - Code-Review -2 from at least one approved reviewer | 2272 * 'not lgtm' - Code-Review disaproval from at least one valid reviewer |
2262 * 'lgtm' - Code-Review +2 from at least one approved reviewer | 2273 * 'lgtm' - Code-Review approval from at least one valid reviewer |
2263 * 'commit' - in the commit queue | 2274 * 'commit' - in the commit queue |
2264 * 'closed' - abandoned | 2275 * 'closed' - abandoned |
2265 """ | 2276 """ |
2266 if not self.GetIssue(): | 2277 if not self.GetIssue(): |
2267 return None | 2278 return None |
2268 | 2279 |
2269 try: | 2280 try: |
2270 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION']) | 2281 data = self._GetChangeDetail(['DETAILED_LABELS', 'CURRENT_REVISION']) |
2271 except httplib.HTTPException: | 2282 except (httplib.HTTPException, GerritIssueNotExists): |
2272 return 'error' | 2283 return 'error' |
2273 | 2284 |
2274 if data['status'] in ('ABANDONED', 'MERGED'): | 2285 if data['status'] in ('ABANDONED', 'MERGED'): |
2275 return 'closed' | 2286 return 'closed' |
2276 | 2287 |
2277 cq_label = data['labels'].get('Commit-Queue', {}) | 2288 cq_label = data['labels'].get('Commit-Queue', {}) |
2278 if cq_label: | 2289 if cq_label: |
2279 # Vote value is a stringified integer, which we expect from 0 to 2. | 2290 # Vote value is a stringified integer, which we expect from 0 to 2. |
2280 vote_value = cq_label.get('value', '0') | 2291 vote_value = cq_label.get('value', '0') |
2281 vote_text = cq_label.get('values', {}).get(vote_value, '') | 2292 vote_text = cq_label.get('values', {}).get(vote_value, '') |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2336 """ | 2347 """ |
2337 raise NotImplementedError() | 2348 raise NotImplementedError() |
2338 | 2349 |
2339 def SubmitIssue(self, wait_for_merge=True): | 2350 def SubmitIssue(self, wait_for_merge=True): |
2340 gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(), | 2351 gerrit_util.SubmitChange(self._GetGerritHost(), self.GetIssue(), |
2341 wait_for_merge=wait_for_merge) | 2352 wait_for_merge=wait_for_merge) |
2342 | 2353 |
2343 def _GetChangeDetail(self, options=None, issue=None): | 2354 def _GetChangeDetail(self, options=None, issue=None): |
2344 options = options or [] | 2355 options = options or [] |
2345 issue = issue or self.GetIssue() | 2356 issue = issue or self.GetIssue() |
2346 assert issue, 'issue required to query Gerrit' | 2357 assert issue, 'issue is required to query Gerrit' |
2347 return gerrit_util.GetChangeDetail(self._GetGerritHost(), str(issue), | 2358 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), str(issue), |
2348 options) | 2359 options) |
2360 if not data: | |
2361 raise GerritIssueNotExists(issue, self.GetCodereviewServer()) | |
2362 return data | |
2349 | 2363 |
2350 def CMDLand(self, force, bypass_hooks, verbose): | 2364 def CMDLand(self, force, bypass_hooks, verbose): |
2351 if git_common.is_dirty_git_tree('land'): | 2365 if git_common.is_dirty_git_tree('land'): |
2352 return 1 | 2366 return 1 |
2353 detail = self._GetChangeDetail(['CURRENT_REVISION', 'LABELS']) | 2367 detail = self._GetChangeDetail(['CURRENT_REVISION', 'LABELS']) |
2354 if u'Commit-Queue' in detail.get('labels', {}): | 2368 if u'Commit-Queue' in detail.get('labels', {}): |
2355 if not force: | 2369 if not force: |
2356 ask_for_data('\nIt seems this repository has a Commit Queue, ' | 2370 ask_for_data('\nIt seems this repository has a Commit Queue, ' |
2357 'which can test and land changes for you. ' | 2371 'which can test and land changes for you. ' |
2358 'Are you sure you wish to bypass it?\n' | 2372 'Are you sure you wish to bypass it?\n' |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2393 assert not nocommit | 2407 assert not nocommit |
2394 assert not directory | 2408 assert not directory |
2395 assert parsed_issue_arg.valid | 2409 assert parsed_issue_arg.valid |
2396 | 2410 |
2397 self._changelist.issue = parsed_issue_arg.issue | 2411 self._changelist.issue = parsed_issue_arg.issue |
2398 | 2412 |
2399 if parsed_issue_arg.hostname: | 2413 if parsed_issue_arg.hostname: |
2400 self._gerrit_host = parsed_issue_arg.hostname | 2414 self._gerrit_host = parsed_issue_arg.hostname |
2401 self._gerrit_server = 'https://%s' % self._gerrit_host | 2415 self._gerrit_server = 'https://%s' % self._gerrit_host |
2402 | 2416 |
2403 detail = self._GetChangeDetail(['ALL_REVISIONS']) | 2417 try: |
2418 detail = self._GetChangeDetail(['ALL_REVISIONS']) | |
2419 except GerritIssueNotExists as e: | |
2420 DieWithError(str(e)) | |
2404 | 2421 |
2405 if not parsed_issue_arg.patchset: | 2422 if not parsed_issue_arg.patchset: |
2406 # Use current revision by default. | 2423 # Use current revision by default. |
2407 revision_info = detail['revisions'][detail['current_revision']] | 2424 revision_info = detail['revisions'][detail['current_revision']] |
2408 patchset = int(revision_info['_number']) | 2425 patchset = int(revision_info['_number']) |
2409 else: | 2426 else: |
2410 patchset = parsed_issue_arg.patchset | 2427 patchset = parsed_issue_arg.patchset |
2411 for revision_info in detail['revisions'].itervalues(): | 2428 for revision_info in detail['revisions'].itervalues(): |
2412 if int(revision_info['_number']) == parsed_issue_arg.patchset: | 2429 if int(revision_info['_number']) == parsed_issue_arg.patchset: |
2413 break | 2430 break |
(...skipping 2903 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5317 if __name__ == '__main__': | 5334 if __name__ == '__main__': |
5318 # These affect sys.stdout so do it outside of main() to simplify mocks in | 5335 # These affect sys.stdout so do it outside of main() to simplify mocks in |
5319 # unit testing. | 5336 # unit testing. |
5320 fix_encoding.fix_encoding() | 5337 fix_encoding.fix_encoding() |
5321 setup_color.init() | 5338 setup_color.init() |
5322 try: | 5339 try: |
5323 sys.exit(main(sys.argv[1:])) | 5340 sys.exit(main(sys.argv[1:])) |
5324 except KeyboardInterrupt: | 5341 except KeyboardInterrupt: |
5325 sys.stderr.write('interrupted\n') | 5342 sys.stderr.write('interrupted\n') |
5326 sys.exit(1) | 5343 sys.exit(1) |
OLD | NEW |