1from __future__ import print_function, unicode_literals 2 3import logging 4import subprocess 5 6from rbtools.api.client import RBClient 7from rbtools.api.errors import APIError, ServerInterfaceError 8 9 10SUBMITTED = 'submitted' 11 12 13class HookError(Exception): 14 pass 15 16 17def get_api(server_url, **kwargs): 18 """Returns an RBClient instance and the associated root resource. 19 20 Hooks should use this method to gain access to the API, instead of 21 instantiating their own client. 22 23 Args: 24 server_url (unicode): 25 The server URL to retrieve. 26 27 **kwargs (dict): 28 Additional keyword arguments to pass to the 29 :py:class:`~rbtools.api.client.RBClient` constructor. See 30 :py:meth:`SyncTransport.__init__() 31 <rbtools.api.transport.sync.SyncTransport.__init__>` for arguments 32 that are accepted. 33 34 Returns: 35 tuple: 36 This returns a 2-tuple of the :py:class:`~rbtools.api.client.RBClient` 37 and :py:class:`<root resource> rbtools.api.resource.Resource`. 38 """ 39 api_client = RBClient(server_url, **kwargs) 40 41 try: 42 api_root = api_client.get_root() 43 except ServerInterfaceError as e: 44 raise HookError('Could not reach the Review Board server at %s: %s' 45 % (server_url, e)) 46 except APIError as e: 47 raise HookError('Unexpected API Error: %s' % e) 48 49 return api_client, api_root 50 51 52def execute(command): 53 """Executes the specified command and returns the stdout output.""" 54 process = subprocess.Popen(command, stdout=subprocess.PIPE) 55 output = process.communicate()[0].strip() 56 57 if process.returncode: 58 logging.warning('Failed to execute command: %s', command) 59 return None 60 61 return output 62 63 64def initialize_logging(): 65 """Sets up a log handler to format log messages. 66 67 Warning, error, and critical messages will show the level name as a prefix, 68 followed by the message. 69 """ 70 root = logging.getLogger() 71 72 handler = logging.StreamHandler() 73 handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) 74 handler.setLevel(logging.WARNING) 75 root.addHandler(handler) 76 77 78def get_review_request_id(regex, commit_message): 79 """Returns the review request ID referenced in the commit message. 80 81 We assume there is at most one review request associated with each commit. 82 If a matching review request cannot be found, we return 0. 83 """ 84 match = regex.search(commit_message) 85 return (match and int(match.group('id'))) or 0 86 87 88def get_review_request(review_request_id, api_root): 89 """Returns the review request resource for the given ID.""" 90 try: 91 review_request = api_root.get_review_request( 92 review_request_id=review_request_id) 93 except APIError as e: 94 raise HookError('Error getting review request: %s' % e) 95 96 return review_request 97 98 99def close_review_request(server_url, username, password, review_request_id, 100 description): 101 """Closes the specified review request as submitted.""" 102 api_client, api_root = get_api(server_url, username, password) 103 review_request = get_review_request(review_request_id, api_root) 104 105 if review_request.status == SUBMITTED: 106 logging.warning('Review request #%s is already %s.', 107 review_request_id, SUBMITTED) 108 return 109 110 if description: 111 review_request = review_request.update(status=SUBMITTED, 112 description=description) 113 else: 114 review_request = review_request.update(status=SUBMITTED) 115 116 print('Review request #%s is set to %s.' % 117 (review_request_id, review_request.status)) 118 119 120def get_review_request_approval(server_url, username, password, 121 review_request_id): 122 """Returns the approval information for the given review request.""" 123 api_client, api_root = get_api(server_url, username, password) 124 review_request = get_review_request(review_request_id, api_root) 125 126 return review_request.approved, review_request.approval_failure 127