def merge(self): auth = HTTPDigestAuthFromNetrc(url=GERRIT_URL) rest = GerritRestAPI(url=GERRIT_URL, auth=auth, verify=GERRIT_VERIFY) url_path = '/changes/{}/submit'.format(self.id) rest.post(url_path)
class Gerrit(object): def __init__(self, url, netrc=None, use_internal=False): auth = AuthFromNetrc(netrc, url, use_internal) self.timeout = 90 self.rest = GerritRestAPI(url=url, auth=auth) self.url = url self.change_options = [ 'CURRENT_REVISION', 'MESSAGES', 'DETAILED_LABELS', 'DETAILED_ACCOUNTS', 'COMMIT_FOOTERS' ] def get_change(self, change_id, rev_num=None): options = self.change_options if rev_num != None: options += ['ALL_REVISIONS'] uri = '/changes/{}?o={}'.format(change_id, '&o='.join(options)) rest = self.rest.get(uri, timeout=self.timeout) c = GerritChange(self.url, rest) # The modifications to change here shouldn't be relied upon, but rolling # back to a previous revision is useful for testing. So we'll do our best # to act like the requested revision is the current_revision and hope # nothing downstream of us gets too confused if rev_num != None: uri = '/changes/{}/revisions/{}/commit'.format(change_id, rev_num) rest = self.rest.get(uri, timeout=self.timeout) for r in c.revisions: if int(r.number) != int(rev_num): continue r.commit_message = rest['message'] c.subject = rest['subject'] c.current_revision = r uri = '/changes/{}/comments/'.format(change_id) rest = self.rest.get(uri, timeout=self.timeout) #pprint.PrettyPrinter(indent=4).pprint(rest) c.add_comments(rest) return c def get_ancestor_changes(self, change): uri = '/changes/{}/revisions/current/related'.format(change.id) related_changes = self.rest.get(uri, timeout=self.timeout)['changes'] changes = [] parents = [] for c in related_changes: if c['change_id'] == change.change_id: parents = c['commit']['parents'] break while True: new_parents = [] for p in parents: for c in related_changes: if c['commit']['commit'] == p['commit']: new_parents += c['commit']['parents'] changes.append(self.get_change(c['_change_number'])) break if new_parents: parents = new_parents else: break return changes def query_changes(self, status=None, message=None, after=None, age_days=None, change_id=None, change_num=None, project=None, owner=None, branches=None): query = [] if message: query.append('message:"{}"'.format(urllib.parse.quote(message))) if status: query.append('status:{}'.format(status)) if after: query.append('after:"{}"'.format(after.isoformat())) if age_days: query.append('age:{}d'.format(age_days)) if change_id: query.append('change:{}'.format(change_id)) if change_num: query.append('change:{}'.format(change_num)) if project: query.append('project:{}'.format(project)) if owner: query.append('owner:{}'.format(owner)) if branches: if len(branches) == 1: q = 'branch:{}'.format(branches[0]) else: q = '(branch:' q += ' OR branch:'.join(branches) q += ')' query.append(q) uri = '/changes/?q={}&o={}'.format('+'.join(query), '&o='.join(self.change_options)) changes = [] for c in self.rest.get(uri, timeout=self.timeout): changes.append(GerritChange(self.url, c)) return changes def get_patch(self, change): uri = '/changes/{}/revisions/{}/patch'.format( change.id, change.current_revision.id) return self.rest.get(uri, timeout=self.timeout) def get_messages(self, change): uri = '/changes/{}/messages'.format(change.id) return self.rest.get(uri, timeout=self.timeout) def set_topic(self, change): # https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#set-topic uri = '/changes/{}/topic'.format(change.id) options = {'topic': change.topic} try: self.rest.put(uri, data=options, timeout=self.timeout) return True except requests.exceptions.HTTPError: return False def remove_reviewer(self, change): uri = '/changes/{}/reviewers/self/delete'.format(change.id) options = { 'notify': 'NONE', } try: self.rest.post(uri, data=options, timeout=self.timeout) return True except requests.exceptions.HTTPError: return False def abandon(self, change): uri = '/changes/{}/abandon'.format(change.id) try: self.rest.post(uri, timeout=self.timeout) return True except requests.exceptions.HTTPError: return False def review(self, change, tag, message, notify_owner, vote_code_review=None, vote_verified=None, vote_cq_ready=None, inline_comments=None): review = { 'tag': tag, 'message': message, 'notify': 'OWNER' if notify_owner else 'NONE', 'omit_duplicate_comments': True, } labels = {} if vote_code_review != None: labels['Code-Review'] = vote_code_review if vote_verified != None: labels['Verified'] = vote_verified if vote_cq_ready != None: labels['Commit-Queue'] = vote_cq_ready if labels: review['labels'] = labels if inline_comments: review['comments'] = inline_comments #pprint.PrettyPrinter(indent=4).pprint(review) #pprint.PrettyPrinter(indent=4).pprint(json.dumps(review)) uri = "changes/{}/revisions/{}/review".format( change.id, change.current_revision.id) return self.rest.post(uri, data=json.dumps(review), headers={"Content-Type": "application/json"}, timeout=self.timeout)
class Gerrit(object): def __init__(self, url, use_internal=False): auth = AuthFromNetrc(url, use_internal) self.rest = GerritRestAPI(url=url, auth=auth) self.url = url self.change_options = [ 'CURRENT_REVISION', 'MESSAGES', 'DETAILED_LABELS', 'DETAILED_ACCOUNTS', 'COMMIT_FOOTERS' ] def get_change(self, change_id, rev_num=None): options = self.change_options if rev_num != None: options += ['ALL_REVISIONS'] uri = '/changes/{}?o={}'.format(change_id, '&o='.join(options)) rest = self.rest.get(uri) c = GerritChange(self.url, rest) # The modifications to change here shouldn't be relied upon, but rolling # back to a previous revision is useful for testing. So we'll do our best # to act like the requested revision is the current_revision and hope # nothing downstream of us gets too confused if rev_num != None: uri = '/changes/{}/revisions/{}/commit'.format(change_id, rev_num) rest = self.rest.get(uri) for r in c.revisions: if int(r.number) != int(rev_num): continue r.commit_message = rest['message'] c.subject = rest['subject'] c.current_revision = r return c def get_related_changes(self, change): uri = '/changes/{}/revisions/current/related'.format(change.id) changes = [] for c in self.rest.get(uri)['changes']: changes.append(self.get_change(c['change_id'])) return changes def query_changes(self, status=None, message=None, after=None, age_days=None, change_id=None, change_num=None, project=None): query = [] if message: query.append('message:"{}"'.format(urllib.parse.quote(message))) if status: query.append('status:{}'.format(status)) if after: query.append('after:"{}"'.format(after.isoformat())) if age_days: query.append('age:{}d'.format(age_days)) if change_id: query.append('change:{}'.format(change_id)) if change_num: query.append('change:{}'.format(change_num)) if project: query.append('project:{}'.format(project)) uri = '/changes/?q={}&o={}'.format('+'.join(query), '&o='.join(self.change_options)) changes = [] for c in self.rest.get(uri): changes.append(GerritChange(self.url, c)) return changes def get_patch(self, change): uri = '/changes/{}/revisions/{}/patch'.format( change.id, change.current_revision.id) return self.rest.get(uri) def get_messages(self, change): uri = '/changes/{}/messages'.format(change.id) return self.rest.get(uri) def remove_reviewer(self, change): uri = '/changes/{}/reviewers/self/delete'.format(change.id) options = { 'notify': 'NONE', } try: self.rest.post(uri, data=options) return True except requests.exceptions.HTTPError as e: return False def review(self, change, tag, message, notify_owner, vote_code_review=None, vote_verified=None, vote_cq_ready=None, inline_comments=None): review = { 'tag': tag, 'message': message, 'notify': 'OWNER' if notify_owner else 'NONE', 'omit_duplicate_comments': True, } labels = {} if vote_code_review != None: labels['Code-Review'] = vote_code_review if vote_verified != None: labels['Verified'] = vote_verified if vote_cq_ready != None: labels['Commit-Queue'] = vote_cq_ready if labels: review['labels'] = labels if inline_comments: review['comments'] = inline_comments #pprint.PrettyPrinter(indent=4).pprint(review) #pprint.PrettyPrinter(indent=4).pprint(json.dumps(review)) return self.rest.review(change.id, change.current_revision.id, json.dumps(review))
class Reindexer: """Class for reindexing Gerrit changes""" def __init__(self): self.options = _parse_options() self._init_logger() credentials = self._authenticate() if self.options.cert: certs = os.path.expanduser(self.options.cert) self.api = GerritRestAPI(url=self.options.url, auth=credentials, verify=certs) else: self.api = GerritRestAPI(url=self.options.url, auth=credentials) def _init_logger(self): self.logger = logging.getLogger("Reindexer") self.logger.setLevel(logging.DEBUG) h = logging.StreamHandler() if self.options.verbose: h.setLevel(logging.DEBUG) else: h.setLevel(logging.INFO) formatter = logging.Formatter("%(message)s") h.setFormatter(formatter) self.logger.addHandler(h) def _authenticate(self): username = password = None if self.options.netrc: auth = HTTPBasicAuthFromNetrc(url=self.options.url) username = auth.username password = auth.password if not username: username = os.environ.get("USERNAME") if not password: password = os.environ.get("PASSWORD") while not username: username = input("user: "******"password: "******"since:{self.options.time}&start={start}&skip-visibility" for change in self.api.get(f"changes/?q={query}"): more_changes = change.get("_more_changes") is not None start += 1 yield change.get("_number") break def _query_to_file(self): self.logger.debug( f"writing changes since {self.options.time} to file {self.options.file}:" ) with open(self.options.file, "w") as output: for id in self._query(): self.logger.debug(id) output.write(f"{id}\n") def _reindex_chunk(self, chunk): self.logger.debug(f"indexing {chunk}") response = self.api.post( "/config/server/index.changes", chunk, ) self.logger.debug(f"response: {response}") def _reindex(self): self.logger.debug(f"indexing changes from file {self.options.file}") with open(self.options.file, "r") as f: with tqdm(unit="changes", desc="Indexed") as pbar: for chunk in _chunker(f, self.options.chunksize): self._reindex_chunk(chunk) pbar.update(len(chunk)) def execute(self): if self.options.time: self._query_to_file() else: self._reindex()