Esempio n. 1
0
    def add_reviewer(cls, change_id, account_id, force=False):
        """
        Send a POST request to the Gerrit Code Review server to add one user or
        all members of one group as reviewer to the change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)
            account_id: any identification string for an account (name,
                username, email)
            force: do not prompt the user for confirmation if gerrit needs a
                confirmation to add multiple reviewers at once (group).
                Defaults to False

        RETURNS
            The list of newly added reviewers on success, None otherwise

        RAISES
            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 = Api.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 PyCRError('unexpected error', 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 PyCRError('unexpected error', why)

        assert 'reviewers' in response, '"reviewers" not in HTTP response'
        return [AccountInfo.parse(r) for r in response['reviewers']]
Esempio n. 2
0
def parse_command_line(builtin_type):
    """Parse the command-line arguments

    Returns a tuple containing the action to execute, and the arguments to use
    with that action.

    :param builtin_type: the type of Builtin to look for
    :type builtin_type: Builtin
    :rtype: Function, list[str]
    """

    parser = build_cmdline_parser(builtin_type)

    # Parse the command-line
    cmdline, remaining = parser.parse_known_args(sys.argv[1:])

    # Activate logging if requested
    if cmdline.debug:
        logging.basicConfig(format='[%(asctime)s %(name)-20s] %(message)s',
                            datefmt='%H:%M:%S',
                            level=logging.DEBUG)

    # Display help if requested (display_help exits the program)
    if cmdline.builtins == 'help':
        display_help(builtin_type, cmdline.builtin)

    # Configure the HTTP request engine
    RequestFactory.set_unsecure_connection(cmdline.unsecure)

    if cmdline.username is not None:
        # Reset the pair (username, password) if --username is supplied on the
        # command line. The user will be prompted its password.
        RequestFactory.set_auth_token(cmdline.username, None)

    return cmdline.command, remaining
Esempio n. 3
0
def parse_command_line(builtin_type):
    """Parse the command-line arguments

    Returns a tuple containing the action to execute, and the arguments to use
    with that action.

    :param builtin_type: the type of Builtin to look for
    :type builtin_type: Builtin
    :rtype: Function, list[str]
    """

    parser = build_cmdline_parser(builtin_type)

    # Parse the command-line
    cmdline, remaining = parser.parse_known_args(sys.argv[1:])

    # Activate logging if requested
    if cmdline.debug:
        logging.basicConfig(
            format='[%(asctime)s %(name)-20s] %(message)s', datefmt='%H:%M:%S',
            level=logging.DEBUG)

    # Display help if requested (display_help exits the program)
    if cmdline.builtins == 'help':
        display_help(builtin_type, cmdline.builtin)

    # Configure the HTTP request engine
    RequestFactory.set_unsecure_connection(cmdline.unsecure)

    if cmdline.username is not None:
        # Reset the pair (username, password) if --username is supplied on the
        # command line. The user will be prompted its password.
        RequestFactory.set_auth_token(cmdline.username, None)

    return cmdline.command, remaining
Esempio n. 4
0
    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']])
Esempio n. 5
0
    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']])
Esempio n. 6
0
    def get_reviewer(cls, change_id, account_id):
        """Fetch a reviewer info

        Sends a GET request to Gerrit to fetch details about a reviewer of a
        change. Returns None if the reviewer does not exists or is not a
        reviewer of 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
        :rtype: ReviewerInfo | None
        :raise: PyCRError if the Gerrit server returns an error
        """

        cls.log.debug('Reviewer lookup: "%s" for %s', account_id, change_id)

        try:
            endpoint = changes.reviewer(change_id, account_id)
            _, response = RequestFactory.get(endpoint)

        except RequestError as why:
            if why.status_code == 404:
                # The user does not exist or is not a reviewer of the change
                return None

            raise UnexpectedError(why)

        return ReviewerInfo.parse(response)
Esempio n. 7
0
def accounts_query():
    """Return an URL to Gerrit

    :rtype: str
    """

    return '{}/accounts/'.format(RequestFactory.get_remote_base_url())
Esempio n. 8
0
def base_query():
    """Return an URL to Gerrit

    :rtype: str
    """

    return '{}/changes/'.format(RequestFactory.get_remote_base_url())
Esempio n. 9
0
    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)
Esempio n. 10
0
    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
Esempio n. 11
0
    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)
Esempio n. 12
0
    def list_changes(cls, status='open', owner='self'):
        """List changes

        Sends a GET request to Gerrit to fetch the list of changes with the
        given STATUS and from the given OWNER.

        :param status: the status of the change (open, merged, ...)
        :type status: str
        :param owner: the account_id of the owner of the changes
        :type owner: str
        :rtype: tuple[ChangeInfo]
        :raise: NoSuchChangeError if no change match the query criterion
        :raise: PyCRError on any other error
        """

        cls.log.debug(
            'Changes lookup with status:%s & owner:%s', status, owner)

        try:
            endpoint = changes.search_query(status=status, owner=owner)

            # DETAILED_ACCOUNTS option ensures that the owner email address is
            # sent in the response
            extra_params = {'o': 'DETAILED_ACCOUNTS'}

            _, response = RequestFactory.get(endpoint, params=extra_params)

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no result for query criterion')

            raise UnexpectedError(why)

        return tuple([ChangeInfo.parse(c) for c in response])
Esempio n. 13
0
    def get_reviewer(cls, change_id, account_id):
        """Fetch a reviewer info

        Sends a GET request to Gerrit to fetch details about a reviewer of a
        change. Returns None if the reviewer does not exists or is not a
        reviewer of 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
        :rtype: ReviewerInfo | None
        :raise: PyCRError if the Gerrit server returns an error
        """

        cls.log.debug('Reviewer lookup: "%s" for %s', account_id, change_id)

        try:
            endpoint = changes.reviewer(change_id, account_id)
            _, response = RequestFactory.get(endpoint)

        except RequestError as why:
            if why.status_code == 404:
                # The user does not exist or is not a reviewer of the change
                return None

            raise UnexpectedError(why)

        return ReviewerInfo.parse(response)
Esempio n. 14
0
    def get_patch(cls, change_id, revision_id='current'):
        """
        Send a GET request to the Gerrit Code Review server to fetch the
        patch for the given change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)
            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)

        RETURNS
            the diff as a string

        RAISES
            NoSuchChangeError if the change does not exist
            PyCRError on any other error
        """

        cls.log.debug('Fetch diff: %s (revision: %s)' %
                      (change_id, revision_id))

        try:
            endpoint = Api.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 PyCRError('unexpected error', why)

        return patch
Esempio n. 15
0
    def get_reviewer(cls, change_id, account_id):
        """
        Send a GET request to the Gerrit Code Review server to fetch details
        about a reviewer of a change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)
            account_id: any identification string for an account (name,
                username, email)

        RETURNS
            a ReviewInfo object, None if the user is not a reviewer

        RAISES
            PyCRError if the Gerrit server returns an error
        """

        cls.log.debug('Reviewer lookup: "%s" for %s' %
                      (account_id, change_id))

        try:
            endpoint = Api.reviewer(change_id, account_id)
            _, response = RequestFactory.get(endpoint)

        except RequestError as why:
            if why.status_code == 404:
                # The user does not exist or is not a reviewer of the change
                return None

            raise PyCRError('unexpected error', why)

        return ReviewerInfo.parse(response)
Esempio n. 16
0
    def list_watched_changes(cls, status='open'):
        """
        Send a GET request to the Gerrit Code Review server to fetch the list
        of changes with the given STATUS and from the given OWNER.

        PARAMETERS
            status: the status of the change (open, merged, ...)
            owner: the account_id of the owner of the changes

        RETURNS
            a list of ChangeInfo

        RAISES
            NoSuchChangeError if no change match the query criterion
            PyCRError on any error
        """

        cls.log.debug('Watched changes lookup with status:%s' % status)

        try:
            endpoint = Api.search_query(status=status, watched=True)

            # DETAILED_ACCOUNTS option ensures that the owner email address is
            # sent in the response
            extra_params = {'o': 'DETAILED_ACCOUNTS'}

            _, response = RequestFactory.get(endpoint, params=extra_params)

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no result for query criterion')

            raise PyCRError('cannot fetch change details', why)

        return [ChangeInfo.parse(c) for c in response]
Esempio n. 17
0
    def get_change(cls, change_id):
        """
        Send a GET request to the Gerrit Code Review server to fetch the data
        on the given change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID

        RETURNS
            a ChangeInfo

        RAISES
            NoSuchChangeError if the change does not exist
            PyCRError on any other error
        """

        cls.log.debug('Change lookup: %s' % change_id)

        try:
            endpoint = Api.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 PyCRError('cannot fetch change details', why)

        return ChangeInfo.parse(response)
Esempio n. 18
0
    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)
Esempio n. 19
0
    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
Esempio n. 20
0
    def list_changes(cls, status='open', owner='self'):
        """List changes

        Sends a GET request to Gerrit to fetch the list of changes with the
        given STATUS and from the given OWNER.

        :param status: the status of the change (open, merged, ...)
        :type status: str
        :param owner: the account_id of the owner of the changes
        :type owner: str
        :rtype: tuple[ChangeInfo]
        :raise: NoSuchChangeError if no change match the query criterion
        :raise: PyCRError on any other error
        """

        cls.log.debug('Changes lookup with status:%s & owner:%s', status,
                      owner)

        try:
            endpoint = changes.search_query(status=status, owner=owner)

            # DETAILED_ACCOUNTS option ensures that the owner email address is
            # sent in the response
            extra_params = {'o': 'DETAILED_ACCOUNTS'}

            _, response = RequestFactory.get(endpoint, params=extra_params)

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no result for query criterion')

            raise UnexpectedError(why)

        return tuple([ChangeInfo.parse(c) for c in response])
Esempio n. 21
0
    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)
Esempio n. 22
0
    def rebase(cls, change_id):
        """
        Send a POST request to the Gerrit Code Review server to rebase the
        given change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)

        RETURNS
            a ChangeInfo

        RAISES
            NoSuchChangeError if the change does not exist
            ConflictError if could not submit the change
            PyCRError on any other error
        """

        cls.log.debug('rebase: %s' % change_id)

        try:
            _, change = RequestFactory.post(Api.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 PyCRError('unexpected error', why)

        return ChangeInfo.parse(change)
Esempio n. 23
0
    def changes_query():
        """
        Return an URL to the Gerrit Code Review server.

        RETURNS
            the URL as a string
        """

        return '%s/changes/' % RequestFactory.get_remote_base_url()
Esempio n. 24
0
    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)
Esempio n. 25
0
    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)
Esempio n. 26
0
    def delete_reviewer(cls, change_id, account_id):
        """
        Send a DELETE request to the Gerrit Code Review server to delete one
        user from the reviewer's list of a change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)
            account_id: any identification string for an account (name,
                username, email)

        RETURNS
            a ReviewInfo

        RAISES
            PyCRError if the Gerrit server returns an error
        """

        cls.log.debug('Delete reviewer: "%s" for %s' %
                      (account_id, change_id))

        try:
            endpoint = Api.reviewer(change_id, account_id)

            _, response = RequestFactory.get(endpoint)
            RequestFactory.delete(endpoint)

        except RequestError as why:
            if why.status_code == 403:
                raise PyCRError(
                    'no sufficient permissions or already submitted')

            if why.status_code == 404:
                # The user does not exist or is not a reviewer of the change
                return None

            raise PyCRError('unexpected error', why)

        assert len(response) == 1
        return ReviewerInfo.parse(response[0])
Esempio n. 27
0
    def delete_reviewer(cls, change_id, account_id):
        """Remove a reviewer from the list of reviewer of a change

        Sends a DELETE request to Gerrit to delete one user from the reviewer's
        list of a change. Returns None if the reviewer does not exists or is
        not a reviewer of 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
        :rtype: ReviewerInfo | None
        :raise: PyCRError if the Gerrit server returns an error
        """

        cls.log.debug('Delete reviewer: "%s" for %s', account_id, change_id)

        try:
            endpoint = changes.reviewer(change_id, account_id)

            _, response = RequestFactory.get(endpoint)
            RequestFactory.delete(endpoint)

        except RequestError as why:
            if why.status_code == 403:
                raise PyCRError(
                    'no sufficient permissions or already submitted')

            if why.status_code == 404:
                # The user does not exist or is not a reviewer of the change
                return None

            raise UnexpectedError(why)

        assert len(response) == 1
        return ReviewerInfo.parse(response[0])
Esempio n. 28
0
    def delete_reviewer(cls, change_id, account_id):
        """Remove a reviewer from the list of reviewer of a change

        Sends a DELETE request to Gerrit to delete one user from the reviewer's
        list of a change. Returns None if the reviewer does not exists or is
        not a reviewer of 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
        :rtype: ReviewerInfo | None
        :raise: PyCRError if the Gerrit server returns an error
        """

        cls.log.debug('Delete reviewer: "%s" for %s', account_id, change_id)

        try:
            endpoint = changes.reviewer(change_id, account_id)

            _, response = RequestFactory.get(endpoint)
            RequestFactory.delete(endpoint)

        except RequestError as why:
            if why.status_code == 403:
                raise PyCRError(
                    'no sufficient permissions or already submitted')

            if why.status_code == 404:
                # The user does not exist or is not a reviewer of the change
                return None

            raise UnexpectedError(why)

        assert len(response) == 1
        return ReviewerInfo.parse(response[0])
Esempio n. 29
0
def parse_command_line(argv):
    """
    Parse the command-line arguments, and return a tuple containing the action
    to execute, and the arguments to use with that action.

    PARAMETERS
        argv: the argument array to parse

    RETURNS
        a tuple containing both the command callback to execute and any
        remaining arguments that need more parsing.
    """

    parser = build_cmdline_parser()

    # Parse the command-line
    cmdline, remaining = parser.parse_known_args(argv[1:])

    # Activate logging if requested
    if cmdline.debug:
        logging.basicConfig(
            format='[%(asctime)s %(name)-20s] %(message)s', datefmt='%H:%M:%S',
            level=logging.DEBUG)

    # Display help if requested (display_help exits the program)
    if cmdline.builtins == 'help':
        display_help(cmdline.builtin)

    # Configure the HTTP request engine
    RequestFactory.set_unsecure_connection(cmdline.unsecure)

    if cmdline.username is not None:
        # Reset the pair (username, password) if --username is supplied on the
        # command line. The user will be prompted its password.
        RequestFactory.set_auth_token(cmdline.username, None)

    return cmdline.command, remaining
Esempio n. 30
0
    def set_review(cls, score, message, change_id, revision_id='current'):
        """
        Send a POST request to the Gerrit Code Review server to review the
        given change.

        PARAMETERS
            score: the score (-2, -1, 0, +1, +2)
            message: the review message
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)
            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)

        RETURNS
            a ChangeInfo

        RAISES
            NoSuchChangeError if the change does not exist
            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('Message: %s', message)

        assert score in Gerrit.SCORES

        payload = {
            'message': message,
            'labels': {'Code-Review': score}
        }
        headers = {'content-type': 'application/json'}

        try:
            endpoint = Api.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)

            raise PyCRError('unexpected error', why)

        return ReviewInfo.parse(review)
Esempio n. 31
0
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
Esempio n. 32
0
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
Esempio n. 33
0
    def get_capabilities(cls, account_id='self'):
        """Fetch Gerrit account capabilities

        :param account_id: identifier that uniquely identifies one account
        :type account: str
        :rtype: CapabilityInfo
        :raise: PyCRError on any other error
        """

        cls.log.debug('List Gerrit account capabilities')

        try:
            _, response = RequestFactory.get(accounts.capabilities(account_id))

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no such account')

            raise UnexpectedError(why)

        return CapabilityInfo.parse(response)
Esempio n. 34
0
    def get_groups(cls, account_id='self'):
        """Fetch Gerrit account groups

        :param account_id: identifier that uniquely identifies one account
        :type account: str
        :rtype: tuple[GroupInfo]
        :raise: PyCRError on any other error
        """

        cls.log.debug('List Gerrit account group')

        try:
            _, response = RequestFactory.get(accounts.groups(account_id))

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no such account')

            raise UnexpectedError(why)

        return tuple([GroupInfo.parse(g) for g in response])
Esempio n. 35
0
    def get_groups(cls, account_id='self'):
        """Fetch Gerrit account groups

        :param account_id: identifier that uniquely identifies one account
        :type account: str
        :rtype: tuple[GroupInfo]
        :raise: PyCRError on any other error
        """

        cls.log.debug('List Gerrit account group')

        try:
            _, response = RequestFactory.get(accounts.groups(account_id))

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no such account')

            raise UnexpectedError(why)

        return tuple([GroupInfo.parse(g) for g in response])
Esempio n. 36
0
    def get_capabilities(cls, account_id='self'):
        """Fetch Gerrit account capabilities

        :param account_id: identifier that uniquely identifies one account
        :type account: str
        :rtype: CapabilityInfo
        :raise: PyCRError on any other error
        """

        cls.log.debug('List Gerrit account capabilities')

        try:
            _, response = RequestFactory.get(accounts.capabilities(account_id))

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no such account')

            raise UnexpectedError(why)

        return CapabilityInfo.parse(response)
Esempio n. 37
0
    def get_reviews(cls, change_id):
        """
        Send a GET request to the Gerrit Code Review server to fetch the
        reviews for the given change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)

        RETURNS
            a ChangeInfo

        RAISES
            NoSuchChangeError if the change does not exist
            PyCRError on any other error
        """

        cls.log.debug('Reviews lookup: %s' % change_id)

        try:
            endpoint = Api.reviewers(change_id)
            _, response = RequestFactory.get(endpoint)

        except RequestError as why:
            if why.status_code == 404:
                raise NoSuchChangeError(change_id)

            raise PyCRError('unexpected error', why)

        # If 'approvals' field is missing, then this 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 [ReviewerInfo.parse(r) for r in response if 'approvals' in r]
Esempio n. 38
0
    def submit(cls, change_id):
        """
        Send a POST request to the Gerrit Code Review server to submit the
        given change.

        PARAMETERS
            change_id: any identification number for the change (UUID,
                Change-Id, or legacy numeric change ID)

        RETURNS
            True if the change was successfully merged, False otherwise

        RAISES
            NoSuchChangeError if the change does not exist
            ConflictError if could not submit the change
            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(Api.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 PyCRError('unexpected error', why)

        return ChangeInfo.parse(change).status == ChangeInfo.MERGED
Esempio n. 39
0
    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])
Esempio n. 40
0
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
Esempio n. 41
0
    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
Esempio n. 42
0
    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
Esempio n. 43
0
    def get_ssh_key(cls, account_id='self', ssh_key_id='0'):
        """Fetch Gerrit account SSH key

        :param account_id: identifier that uniquely identifies one account
        :type account: str
        :param ssh_key_id: unique identifier to a SSH key
        :type ssh_key_id: int
        :rtype: SshKeyInfo
        :raise: PyCRError on any other error
        """

        cls.log.debug('List Gerrit account SSH keys')

        try:
            _, response = RequestFactory.get(
                accounts.ssh_key(account_id, ssh_key_id))

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no such account or ssh key')

            raise UnexpectedError(why)

        return SshKeyInfo.parse(response)
Esempio n. 44
0
    def get_ssh_key(cls, account_id='self', ssh_key_id='0'):
        """Fetch Gerrit account SSH key

        :param account_id: identifier that uniquely identifies one account
        :type account: str
        :param ssh_key_id: unique identifier to a SSH key
        :type ssh_key_id: int
        :rtype: SshKeyInfo
        :raise: PyCRError on any other error
        """

        cls.log.debug('List Gerrit account SSH keys')

        try:
            _, response = RequestFactory.get(
                accounts.ssh_key(account_id, ssh_key_id))

        except RequestError as why:
            if why.status_code == 404:
                raise QueryError('no such account or ssh key')

            raise UnexpectedError(why)

        return SshKeyInfo.parse(response)