def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') emails = Gerrit.get_emails(account_id or 'self') except PyCRError as why: fail('cannot list account emails', why) table = PrettyTable(['Email', 'Preferred', 'Confirmed']) table.align = 'l' for email in emails: table.add_row([ email.email, checkmark(True) if email.preferred else '', 'No' if email.pending_confirmation else 'Yes' ]) with Pager(command=self.name): print 'Account: {}'.format(account.username) if emails: print table else: print 'No email address'
def parse_command_line(arguments): """Parse the SHOW command command-line arguments Returns a tuple containing three lists: - the list of ChangeInfo - the list of reviewers to add - the list of reviewers to delete :param arguments: a list of command-line arguments to parse :type arguments: list[str] :rtype: tuple[ChangeInfo, list[str], list[str]] """ changes, to_add, to_del = [], [], [] for argument in arguments: if argument in ('-h', '--help'): # Manually handle the --help flag Assign.display_help() if argument[0] == '+': to_add.append(argument[1:]) elif argument[0] == '-': to_del.append(argument[1:]) else: changes.append(argument) if not to_add and not to_del: fail('please specify reviewer(s) to add or delete') return fetch_change_list_or_fail(changes), to_add, to_del
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') keys = Gerrit.get_ssh_keys(account_id or 'self') except PyCRError as why: fail('cannot list account SSH keys', why) table = PrettyTable( ['Id', 'Algorithm', 'Comment', 'Valid', 'Encoded key']) table.align = 'l' for key in keys: table.add_row([ key.seq, key.algorithm, key.comment, checkmark(key.valid), key.encoded_key ]) with Pager(command=self.name): print 'Account: {}'.format(account.username) if keys: print table else: print 'No SSH keys'
def get_remote_base_url(cls): """ Return the Gerrit Code Review server base URL. RETURNS the Gerrit Code Review server base URL as a string """ url = Config.get('gerrit.host') unsecure = Config.get('gerrit.unsecure', False) if url is None: fail('gerrit.host not set') # From Gerrit Code Review Documentation (section Authentication): # https://gerrit-review.googlesource.com/Documentation/rest-api.html # # Users (and programs) may authenticate using HTTP authentication by # supplying the HTTP password from the user's account settings page. # Gerrit by default uses HTTP digest authentication. To authenticate, # prefix the endpoint URL with /a/. For example to authenticate to # /projects/ request URL /a/projects/. if RequestFactory.require_auth(): url = '%s/a' % url if not url.startswith('http://') or not url.startswith('https://'): return '%s://%s' % ('http' if unsecure else 'https', url) return url
def parse_command_line(arguments): """ Parse the LIST command command-line arguments. PARAMETERS arguments: a list of command-line arguments to parse RETURNS a list of ChangeInfo """ parser = argparse.ArgumentParser( description='List changes by owner and status.') parser.add_argument( '--status', default='open', help='the status of the changes (default: open)') exclusive = parser.add_mutually_exclusive_group() exclusive.add_argument( '--owner', default='self', help='the owner of the changes (default: self)') exclusive.add_argument( '--watched', default=False, action='store_true', help='list only watched changes') cmdline = parser.parse_args(arguments) if cmdline.status not in Gerrit.get_all_statuses(): fail('argument --status: invalid choice "%s" (choose from %s)' % (cmdline.status, ', '.join(["'%s'" % st for st in Gerrit.get_all_statuses()]))) # Fetch changes details return cmdline.owner, cmdline.status, cmdline.watched
def get_remote_base_url(cls): """Return the Gerrit Code Review server base URL :rtype: str """ url = Config.get("gerrit.host") unsecure = Config.get("gerrit.unsecure", False) if url is None: fail("gerrit.host not set") # From Gerrit Code Review Documentation (section Authentication): # https://gerrit-review.googlesource.com/Documentation/rest-api.html # # Users (and programs) may authenticate using HTTP authentication by # supplying the HTTP password from the user's account settings page. # Gerrit by default uses HTTP digest authentication. To authenticate, # prefix the endpoint URL with /a/. For example to authenticate to # /projects/ request URL /a/projects/. if RequestFactory.require_auth(): url = "{}/a".format(url) if not url.startswith("http://") or not url.startswith("https://"): return "{}://{}".format("http" if unsecure else "https", url) return url
def run(self, arguments, *args, **kwargs): change_id, score, message, label = self.parse_command_line(arguments) try: change = Gerrit.get_change(change_id) if message is None: initial_content = [ '', ('# Please enter the comment message for your review. ' 'Lines starting'), "# with '#' will be ignored.", '#' ] initial_content.extend( ['# %s' % line for line in change.raw_str().splitlines()]) initial_content.append('#') message = raw_input_editor(os.linesep.join(initial_content)) message = strip_comments(message) if score is None: score = ask('Please enter your review score', Gerrit.SCORES) review = Gerrit.set_review(score, message, change.uuid, label) except NoSuchChangeError as why: self.log.debug(str(why)) fail('invalid change') except PyCRError as why: fail('cannot post review', why) print Formatter.format(self.tokenize(change, review))
def parse_command_line(arguments): """ Parse the SHOW command command-line arguments. PARAMETERS arguments: a list of command-line arguments to parse RETURNS a tuple containing three lists: - the list of ChangeInfo - the list of reviewers to add - the list of reviewers to delete """ changes, to_add, to_del = [], [], [] for argument in arguments: if argument in ('-h', '--help'): # Manually handle the --help flag Assign.display_help() if argument[0] == '+': to_add.append(argument[1:]) elif argument[0] == '-': to_del.append(argument[1:]) else: changes.append(argument) if not to_add and not to_del: fail('please specify reviewer(s) to add or delete') return fetch_change_list_or_fail(changes), to_add, to_del
def get_remote_base_url(cls): """Return the Gerrit Code Review server base URL :rtype: str """ url = Config.get('gerrit.host') unsecure = Config.get('gerrit.unsecure', False) if url is None: fail('gerrit.host not set') # From Gerrit Code Review Documentation (section Authentication): # https://gerrit-review.googlesource.com/Documentation/rest-api.html # # Users (and programs) may authenticate using HTTP authentication by # supplying the HTTP password from the user's account settings page. # Gerrit by default uses HTTP digest authentication. To authenticate, # prefix the endpoint URL with /a/. For example to authenticate to # /projects/ request URL /a/projects/. if RequestFactory.require_auth(): url = '{}/a'.format(url) if not url.startswith('http://') or not url.startswith('https://'): return '{}://{}'.format('http' if unsecure else 'https', url) return url
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: changes = Gerrit.get_starred_changes(account_id or 'self') except PyCRError as why: fail('cannot list account starred changes', why) with Pager(command=self.name): for idx, change in enumerate(changes): print Formatter.format(self.tokenize(idx, change))
def run(self, arguments, *args, **kwargs): change_id = self.parse_command_line(arguments) try: change = Gerrit.rebase(change_id) except NoSuchChangeError as why: self.log.debug(str(why)) fail('invalid change') except PyCRError as why: fail('cannot rebase', why) print Formatter.format(self.tokenize(change))
def run_get(arguments, *args, **kwargs): """???""" del args, kwargs parser = argparse.ArgumentParser(description='Display ssh key') parser.add_argument('account', type=str, help='account ID') parser.add_argument('uuid', type=int, help='SSH key ID') cmdline = parser.parse_args(arguments) try: key = Gerrit.get_ssh_key(cmdline.account, cmdline.uuid) except PyCRError as why: fail('cannot list account SSH keys', why) print key.ssh_public_key.strip()
def run(self, arguments, *args, **kwargs): account_ids = self.parse_command_line(arguments) try: accounts = (Gerrit.get_account(a) for a in account_ids or ['self']) except PyCRError as why: fail('cannot list accounts', why) table = PrettyTable(['Username', 'Name', 'Email']) table.align = 'l' for account in set(accounts): table.add_row([account.username, account.name, account.email]) with Pager(command=self.name): print table
def fetch_change_list_or_fail(change_list): """Same as fetch_change_list, but fail if the final change list is empty :param change_list: the list of changes :type change_list: list[str] :rtype: list[ChangeInfo] """ changes = fetch_change_list(change_list) # If no correct changes found if not changes: message = 'no valid change-id provided' if not RequestFactory.require_auth(): message += ' (missing authentication?)' fail(message) return changes
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') prefs = Gerrit.get_diff_prefs(account_id or 'self') except PyCRError as why: fail('cannot list account diff preferences', why) table = PrettyTable(['Preference', 'Value']) table.align['Preference'] = 'l' table.align['Value'] = 'c' table.add_row(['Context', prefs.context]) table.add_row( ['Expand all comments', checkmark(prefs.expand_all_comments)]) table.add_row(['Ignore whitespace', prefs.ignore_whitespace]) table.add_row( ['Intraline difference', checkmark(prefs.intraline_difference)]) table.add_row(['Line length', prefs.line_length]) table.add_row(['Manual review', checkmark(prefs.manual_review)]) table.add_row(['Retain header', checkmark(prefs.retain_header)]) table.add_row( ['Show line endings', checkmark(prefs.show_line_endings)]) table.add_row(['Show tabs', checkmark(prefs.show_tabs)]) table.add_row([ 'Show whitespace errors', checkmark(prefs.show_whitespace_errors) ]) table.add_row(['Skip deleted', checkmark(prefs.skip_deleted)]) table.add_row(['Skip uncommented', checkmark(prefs.skip_uncommented)]) table.add_row( ['Syntax highlighting', checkmark(prefs.syntax_highlighting)]) table.add_row(['Tab size', prefs.tab_size]) with Pager(command=self.name): print 'Account: {}'.format(account.username) print table
def run(self, arguments, *args, **kwargs): if not arguments: fail('No command given.') command = arguments[0] arguments = arguments[1:] if command in ('-h', '--help'): sys.exit(self.get_usage()) if command not in self.subcommands: fail('Unknown subcommand: {}'.format(command)) if command == 'get': self.run_get(arguments, *args, **kwargs) elif command == 'add': self.run_add(arguments, *args, **kwargs) elif command == 'remove': self.run_remove(arguments, *args, **kwargs)
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: groups = Gerrit.get_groups(account_id or 'self') except PyCRError as why: fail('cannot list account groups', why) table = PrettyTable(['Group', 'Description', 'Visible to all']) table.align = 'l' table.align['Visible to all'] = 'c' for group in groups: table.add_row([group.name, group.description or '', checkmark(group.options.visible_to_all)]) with Pager(command=self.name): print table
def run(self, arguments, *args, **kwargs): owner, status, watched = self.parse_command_line(arguments) try: if watched: changes = Gerrit.list_watched_changes(status=status) else: changes = Gerrit.list_changes(status=status, owner=owner) except QueryError as why: # No result, not an error self.log.debug(str(why)) return except PyCRError as why: fail('cannot list changes', why) with Pager(command=self.name): for idx, change in enumerate(changes): print Formatter.format(self.tokenize(idx, change))
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: groups = Gerrit.get_groups(account_id or 'self') except PyCRError as why: fail('cannot list account groups', why) table = PrettyTable(['Group', 'Description', 'Visible to all']) table.align = 'l' table.align['Visible to all'] = 'c' for group in groups: table.add_row([ group.name, group.description or '', checkmark(group.options.visible_to_all) ]) with Pager(command=self.name): print table
def run(self, arguments, *args, **kwargs): """ The entry point for the SUBMIT command. Rebase revision. PARAMETERS arguments: a list of command-line arguments to parse """ change_id = Submit.parse_command_line(arguments) try: change = Gerrit.get_change(change_id) if not Gerrit.submit(change.uuid): fail('submit could not be merged') except NoSuchChangeError as why: self.log.debug(str(why)) fail('invalid change') except PyCRError as why: fail('cannot submit', why) print Formatter.format(Submit.tokenize(change))
def load(cls, filename, quiet=False): """Parse a configuration file and extract this script's configuration :param filename: the path to the config file :type filename: str """ if not os.path.isfile(filename): if not quiet: warn('{}: No such file or directory'.format(filename)) return parser = SafeConfigParser() try: with open(filename, 'r') as config: parser.readfp(config, filename) except ParsingError as why: fail('failed to parse configuration: {}'.format(config), why) cls._store_config(parser)
def fetch_change_list_or_fail(change_list): """ Same as fetch_change_list, but fail if the final change list is empty. PARAMETERS change_list: list of string RETURNS a tuple with a list of ChangeInfo, and a list of unknown changes """ changes = fetch_change_list(change_list) # If no correct change found if not changes: message = 'no valid change-id provided' if not RequestFactory.require_auth(): message += ' (missing authentication?)' fail(message) return changes
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') prefs = Gerrit.get_diff_prefs(account_id or 'self') except PyCRError as why: fail('cannot list account diff preferences', why) table = PrettyTable(['Preference', 'Value']) table.align['Preference'] = 'l' table.align['Value'] = 'c' table.add_row(['Context', prefs.context]) table.add_row(['Expand all comments', checkmark(prefs.expand_all_comments)]) table.add_row(['Ignore whitespace', prefs.ignore_whitespace]) table.add_row(['Intraline difference', checkmark(prefs.intraline_difference)]) table.add_row(['Line length', prefs.line_length]) table.add_row(['Manual review', checkmark(prefs.manual_review)]) table.add_row(['Retain header', checkmark(prefs.retain_header)]) table.add_row(['Show line endings', checkmark(prefs.show_line_endings)]) table.add_row(['Show tabs', checkmark(prefs.show_tabs)]) table.add_row(['Show whitespace errors', checkmark(prefs.show_whitespace_errors)]) table.add_row(['Skip deleted', checkmark(prefs.skip_deleted)]) table.add_row(['Skip uncommented', checkmark(prefs.skip_uncommented)]) table.add_row(['Syntax highlighting', checkmark(prefs.syntax_highlighting)]) table.add_row(['Tab size', prefs.tab_size]) with Pager(command=self.name): print 'Account: {}'.format(account.username) print table
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') capabilities = Gerrit.get_capabilities(account_id or 'self') except PyCRError as why: fail('cannot list account capabilities', why) table = PrettyTable(['Capability', 'Value']) table.align['Capability'] = 'l' table.align['Value'] = 'c' table.add_row(['Administrate server', checkmark(capabilities.administrate_server)]) table.add_row(['Min Query limit', capabilities.query_limit.min]) table.add_row(['Max Query limit', capabilities.query_limit.max]) table.add_row(['Create account', checkmark(capabilities.create_account)]) table.add_row(['Create group', checkmark(capabilities.create_group)]) table.add_row(['Create project', checkmark(capabilities.create_project)]) table.add_row(['Email reviewers', checkmark(capabilities.email_reviewers)]) table.add_row(['Kill task', checkmark(capabilities.kill_task)]) table.add_row(['View caches', checkmark(capabilities.view_caches)]) table.add_row(['Flush caches', checkmark(capabilities.flush_caches)]) table.add_row(['View connections', checkmark(capabilities.view_connections)]) table.add_row(['View queue', checkmark(capabilities.view_queue)]) table.add_row(['Run GC', checkmark(capabilities.run_gc)]) with Pager(command=self.name): print 'Account: {}'.format(account.username) print table
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') emails = Gerrit.get_emails(account_id or 'self') except PyCRError as why: fail('cannot list account emails', why) table = PrettyTable(['Email', 'Preferred', 'Confirmed']) table.align = 'l' for email in emails: table.add_row([email.email, checkmark(True) if email.preferred else '', 'No' if email.pending_confirmation else 'Yes']) with Pager(command=self.name): print 'Account: {}'.format(account.username) if emails: print table else: print 'No email address'
def run(self, arguments, *args, **kwargs): account_id = self.parse_command_line(arguments) try: account = Gerrit.get_account(account_id or 'self') keys = Gerrit.get_ssh_keys(account_id or 'self') except PyCRError as why: fail('cannot list account SSH keys', why) table = PrettyTable( ['Id', 'Algorithm', 'Comment', 'Valid', 'Encoded key']) table.align = 'l' for key in keys: table.add_row([key.seq, key.algorithm, key.comment, checkmark(key.valid), key.encoded_key]) with Pager(command=self.name): print 'Account: {}'.format(account.username) if keys: print table else: print 'No SSH keys'
def run(self, arguments, *args, **kwargs): change_id = self.parse_command_line(arguments) try: change = Gerrit.get_change(change_id) if not Gerrit.submit(change.uuid): fail('change could not be merged') except NoSuchChangeError as why: self.log.debug(str(why)) fail('invalid change') except PyCRError as why: fail('cannot submit', why) print Formatter.format(self.tokenize(change))
def send(cls, endpoint, method=GET, encoding=JSON, **kwargs): """ Return the result of a HTTP request. PARAMETERS endpoint: the endpoint to the request method: HTTP protocol method to use (either GET or POST) encoding: expected response format (JSON, base64 or plain text) **kwargs: any additional arguments to the underlying API call RETURNS a tuple of two elements: the raw response (stripped from magic for the JSON format) and the decoded object of that response, or None if expected_encoding is PLAIN. If response is no content (status code 204), returns a tuple of None values RAISES requests.exceptions.RequestException on error """ cls.log.debug('Query URL: %s' % endpoint) try: response = cls.get_session().request(method, endpoint, **kwargs) if response.status_code == 204: # No content return None, None if response.status_code != 200: response.raise_for_status() except ConnectionError as why: fail('Unable to connect to %s' % urlparse(endpoint).netloc) except RequestException as why: raise RequestError( response.status_code, response, 'HTTP %s request failed: %s' % (method, endpoint), why) if encoding == BASE64: encoded = response.text cls.log.debug('%d bytes to decode', len(encoded)) pad = -len(encoded) % 4 if pad == 3: b64response = encoded[:-1] else: b64response = encoded + b'=' * pad cls.log.debug('%d padded bytes to decode', len(b64response)) cls.log.debug(b64response) try: decoded = base64.decodestring(b64response) except TypeError: # TypeError: incorrect padding cls.log.exception('cannot decode base64 stream') fail('invalid response stream (could not decode base64)') elif encoding == JSON: if not response.text.startswith(GERRIT_MAGIC): fail('invalid response stream (magic prefix not found)') json_response = response.text[len(GERRIT_MAGIC):] decoded = json.loads(json_response) else: decoded = None return response.text, decoded
def send(cls, endpoint, method=GET, encoding=JSON, **kwargs): """Return the result of a HTTP request Returns a tuple of two elements: the raw response (stripped from magic for the JSON format) and the decoded object of that response, or None if encoding is PLAIN. If response is no content (status code 204), returns a tuple of None values. :param endpoint: the endpoint to the request :type endpoint: str :param method: HTTP protocol method to use (either GET or POST) :type method: str :param encoding: expected response format (JSON, base64 or plain text) :type encoding: str :param **kwargs: any additional arguments to the underlying API call :type **kwargs: dict :rtype: str, dict | None :raise: requests.exceptions.RequestException on error """ cls.log.debug("Query URL: %s", endpoint) if cls.log.isEnabledFor(logging.DEBUG) and "data" in kwargs: if "headers" in kwargs and kwargs["headers"].get("content-type", "").endswith("json"): # Dump the JSON-encoded payload data = json.loads(kwargs["data"]) cls.log.debug("JSON-encoded query payload") cls.log.debug(json.dumps(data, indent=2)) try: response = cls.get_session().request(method, endpoint, **kwargs) if response.status_code == 204: # No content return None, None if response.status_code != 200: response.raise_for_status() except ConnectionError as why: fail("Unable to connect to %s" % urlparse(endpoint).netloc) except RequestException as why: raise RequestError(response.status_code, response, "HTTP %s request failed: %s" % (method, endpoint), why) if encoding == BASE64: encoded = response.text cls.log.debug("%d bytes to decode", len(encoded)) pad = -len(encoded) % 4 if pad == 3: b64response = encoded[:-1] else: b64response = encoded + b"=" * pad cls.log.debug("%d padded bytes to decode", len(b64response)) cls.log.debug(b64response) try: decoded = base64.decodestring(b64response) except TypeError: # TypeError: incorrect padding cls.log.exception("cannot decode base64 stream") fail("invalid response stream (could not decode base64)") elif encoding == JSON: if not response.text.startswith(GERRIT_MAGIC): fail("invalid response stream (magic prefix not found)") json_response = response.text[len(GERRIT_MAGIC) :] decoded = json.loads(json_response) if cls.log.isEnabledFor(logging.DEBUG): cls.log.debug("JSON-encoded server reply") cls.log.debug(json.dumps(decoded, indent=2)) else: decoded = None return response.text, decoded
def send(cls, endpoint, method=GET, encoding=JSON, **kwargs): """Return the result of a HTTP request Returns a tuple of two elements: the raw response (stripped from magic for the JSON format) and the decoded object of that response, or None if encoding is PLAIN. If response is no content (status code 204), returns a tuple of None values. :param endpoint: the endpoint to the request :type endpoint: str :param method: HTTP protocol method to use (either GET or POST) :type method: str :param encoding: expected response format (JSON, base64 or plain text) :type encoding: str :param **kwargs: any additional arguments to the underlying API call :type **kwargs: dict :rtype: str, dict | None :raise: requests.exceptions.RequestException on error """ cls.log.debug('Query URL: %s', endpoint) if cls.log.isEnabledFor(logging.DEBUG) and 'data' in kwargs: if ('headers' in kwargs and kwargs['headers'].get( 'content-type', '').endswith('json')): # Dump the JSON-encoded payload data = json.loads(kwargs['data']) cls.log.debug('JSON-encoded query payload') cls.log.debug(json.dumps(data, indent=2)) try: response = cls.get_session().request(method, endpoint, **kwargs) if response.status_code == 204: # No content return None, None if response.status_code != 200: response.raise_for_status() except ConnectionError as why: fail('Unable to connect to %s' % urlparse(endpoint).netloc) except RequestException as why: raise RequestError( response.status_code, response, 'HTTP %s request failed: %s' % (method, endpoint), why) if encoding == BASE64: encoded = response.text cls.log.debug('%d bytes to decode', len(encoded)) pad = -len(encoded) % 4 if pad == 3: b64response = encoded[:-1] else: b64response = encoded + b'=' * pad cls.log.debug('%d padded bytes to decode', len(b64response)) cls.log.debug(b64response) try: decoded = base64.decodestring(b64response) except TypeError: # TypeError: incorrect padding cls.log.exception('cannot decode base64 stream') fail('invalid response stream (could not decode base64)') elif encoding == JSON: if not response.text.startswith(GERRIT_MAGIC): fail('invalid response stream (magic prefix not found)') json_response = response.text[len(GERRIT_MAGIC):] decoded = json.loads(json_response) if cls.log.isEnabledFor(logging.DEBUG): cls.log.debug('JSON-encoded server reply') cls.log.debug(json.dumps(decoded, indent=2)) else: decoded = None return response.text, decoded