def rebase(cls, change_id): """Rebase a change Sends a POST request to Gerrit to rebase the given change. :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID) :type change_id: str :rtype: ChangeInfo :raise: NoSuchChangeError if the change does not exists :raise: ConflictError if could not rebase the change :raise: PyCRError on any other error """ cls.log.debug('rebase: %s', change_id) try: _, change = RequestFactory.post(changes.rebase(change_id)) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) if why.status_code == 409: # There was a conflict rebasing the change # Error message is return as PLAIN text raise ConflictError(why.response.text.strip()) raise UnexpectedError(why) return ChangeInfo.parse(change)
def get_patch(cls, change_id, revision_id='current'): """Fetch a patch content Sends a GET request to Gerrit to fetch the patch for the given change. Returns the diff content. :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID) :type change_id: str :param revision_id: identifier that uniquely identifies one revision of a change (current, a commit ID (SHA1) or abbreviated commit ID, or a legacy numeric patch number) :type revision_id: str :rtype: str :raise: NoSuchChangeError if the change does not exists :raise: PyCRError on any other error """ cls.log.debug('Fetch diff: %s (revision: %s)', change_id, revision_id) try: endpoint = changes.patch(change_id, revision_id) _, patch = RequestFactory.get(endpoint, encoding=BASE64) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) raise UnexpectedError(why) return patch
def get_change(cls, change_id): """Fetch a change details Sends a GET request to Gerrit to fetch the data on the given change. :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID :type change_id: str :rtype: ChangeInfo :raise: NoSuchChangeError if the change does not exists :raise: PyCRError on any other error """ cls.log.debug('Change lookup: %s', change_id) try: endpoint = changes.detailed_changes(change_id) # CURRENT_REVISION describe the current revision (patch set) of the # change, including the commit SHA-1 and URLs to fetch from extra_params = {'o': 'CURRENT_REVISION'} _, response = RequestFactory.get(endpoint, params=extra_params) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) raise UnexpectedError(why) return ChangeInfo.parse(response)
def add_reviewer(cls, change_id, account_id, force=False): """Add a reviewer Sends a POST request to Gerrit to add one user or all members of one group as reviewer to the change. :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID) :type change_id: str :param account_id: any identification string for an account (name, username, email) :type account_id: str :param force: do not prompt the user for confirmation if gerrit needs a confirmation to add multiple reviewers at once (group). Defaults to False :type force: bool :rtype: tuple(AccountInfo) :raise: PyCRError if the Gerrit server returns an error """ cls.log.debug('Assign review to %s: %s', account_id, change_id) payload = {'reviewer': account_id} headers = {'content-type': 'application/json'} try: endpoint = changes.reviewers(change_id) _, response = RequestFactory.post(endpoint, data=json.dumps(payload), headers=headers) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) raise UnexpectedError(why) if 'confirm' in response: assert 'error' in response, 'missing "error" field in response' cls.log.debug('Assigning review: confirmation requested') do_add_reviewers = True if force else confirm(response['error']) if not do_add_reviewers: info('reviewer not added, aborting...') return None try: payload['confirmed'] = True _, response = RequestFactory.post(endpoint, data=json.dumps(payload), headers=headers) except RequestError as why: raise UnexpectedError(why) assert 'reviewers' in response, '"reviewers" not in HTTP response' return tuple([AccountInfo.parse(r) for r in response['reviewers']])
def set_review(cls, score, message, change_id, label, revision_id='current'): """Set a review score Sends a POST request to Gerrit to review the given change. :param score: the score (-2, -1, 0, +1, +2) :type score: str :param message: the review message :type message: str :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID) :type change_id: str :param label: the label to score :type label: str :param revision_id: identifier that uniquely identifies one revision of a change (current, a commit ID (SHA1) or abbreviated commit ID, or a legacy numeric patch number) :type revision_id: str :rtype: ChangeInfo :raise: NoSuchChangeError if the change does not exists :raise: PyCRError on any other error """ cls.log.debug('Set review: %s (revision: %s)', change_id, revision_id) cls.log.debug('Score: %s', score) cls.log.debug('Label: %s', label) cls.log.debug('Message: %s', message) assert score in Gerrit.SCORES payload = {'message': message, 'labels': {label: score}} headers = {'content-type': 'application/json'} try: endpoint = changes.review(change_id, revision_id) _, review = RequestFactory.post(endpoint, data=json.dumps(payload), headers=headers) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) if why.status_code == 400: raise QueryError('invalid score "%s" for label "%s"' % (score, label)) raise UnexpectedError(why) return ReviewInfo.parse(review)
def get_reviews(cls, change_id): """Fetch the reviews for a change Sends a GET request to Gerrit to fetch the reviews for the given change. :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID) :type change_id: str :rtype: tuple[ReviewerInfo] :raise: NoSuchChangeError if the change does not exists :raise: PyCRError on any other error """ cls.log.debug('Reviews lookup: %s', change_id) try: endpoint = changes.reviewers(change_id) _, response = RequestFactory.get(endpoint) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) raise UnexpectedError(why) # If 'approvals' field is missing, then there is no reviewer # NOTE: This seems to be against the specifications for this method: # https://gerrit-review.googlesource.com/Documentation/ # rest-api-changes.html#list-reviewers # "As result a list of ReviewerInfo entries is returned." # A ReviewerInfo entry is expected to have an "approvals" field, but # experiences show that it's not always the case, and that the change # owner can also be in the list although not a reviewers. return tuple( [ReviewerInfo.parse(r) for r in response if 'approvals' in r])
def submit(cls, change_id): """Submit a change Sends a POST request to Gerrit to submit the given change. Returns True if the change was successfully merged, False otherwise. :param change_id: any identification number for the change (UUID, Change-Id, or legacy numeric change ID) :type change_id: str :rtype: bool :raise: NoSuchChangeError if the change does not exists :raise: ConflictError if could not submit the change :raise: PyCRError on any other error """ cls.log.debug('submit: %s', change_id) payload = {'wait_for_merge': True} headers = {'content-type': 'application/json'} try: _, change = RequestFactory.post(changes.submit(change_id), data=json.dumps(payload), headers=headers) except RequestError as why: if why.status_code == 404: raise NoSuchChangeError(change_id) if why.status_code == 409: # There was a conflict rebasing the change # Error message is return as PLAIN text raise ConflictError(why.response.text.strip()) raise UnexpectedError(why) return ChangeInfo.parse(change).status == ChangeInfo.MERGED