예제 #1
0
    def prompt_rb_repository(self, tool_name, repository_info, api_root):
        """Interactively prompt to select a matching repository.

        The user is prompted to choose a matching repository found on the
        Review Board server.
        """
        # Go through each matching repo and prompt for a selection. If a
        # selection is made, immediately return the selected repo.
        for repository_page in api_root.get_repositories().all_pages:
            repo_paths = {}

            for repository in repository_page:
                if repository.tool != tool_name:
                    continue

                repo_paths[repository['path']] = repository

                if 'mirror_path' in repository:
                    repo_paths[repository['mirror_path']] = repository

            closest_path = difflib.get_close_matches(repository_info.path,
                                                     six.iterkeys(repo_paths),
                                                     n=1)

            for path in closest_path:
                repo = repo_paths[path]
                question = ('Use the %s repository "%s" (%s)?'
                            % (tool_name, repo['name'], repo['path']))

                if confirm(question):
                    return repo

        return None
예제 #2
0
    def _ask_review_request_match(self, review_request):
        question = ('Stamp with Review Request #%s: "%s"? '
                    % (review_request.id,
                       get_draft_or_current_value(
                           'summary', review_request)))

        return confirm(question)
예제 #3
0
    def prompt_rb_repository(self, tool_name, repository_info, api_root):
        """Interactively prompt to select a matching repository.

        The user is prompted to choose a matching repository found on the
        Review Board server.
        """
        repositories = api_root.get_repositories()

        # Go through each matching repo and prompt for a selection. If a
        # selection is made, immediately return the selected repo.
        try:
            while True:
                repo_paths = {}
                for repository in repositories:
                    if repository.tool != tool_name:
                        continue

                    repo_paths[repository["path"]] = repository
                    if "mirror_path" in repository:
                        repo_paths[repository["mirror_path"]] = repository

                closest_path = difflib.get_close_matches(repository_info.path, repo_paths.keys(), n=1)

                for path in closest_path:
                    repo = repo_paths[path]
                    question = "Use the %s repository '%s' (%s)?" % (tool_name, repo["name"], repo["path"])

                    if confirm(question):
                        return repo

                repositories = repositories.get_next()
        except StopIteration:
            pass

        return None
예제 #4
0
    def _ask_review_request_match(self, review_request):
        question = ("Update Review Request #%s: '%s'? "
                    % (review_request.id,
                       get_draft_or_current_value(
                           'summary', review_request)))

        return confirm(question)
예제 #5
0
파일: setup_repo.py 프로젝트: mhahn/rbtools
    def prompt_rb_repository(self, tool_name, repository_info, api_root):
        """Interactively prompt to select a matching repository.

        The user is prompted to choose a matching repository found on the
        Review Board server.
        """
        repositories = api_root.get_repositories()

        # Go through each matching repo and prompt for a selection. If a
        # selection is made, immediately return the selected repo.
        try:
            while True:
                for repo in repositories:
                    is_match = (
                        tool_name == repo.tool and
                        repository_info.path in
                        (repo['path'], getattr(repo, 'mirror_path', '')))

                    if is_match:
                        question = (
                            "Use the %s repository '%s' (%s)?"
                            % (tool_name, repo['name'], repo['path']))

                        if confirm(question):
                            return repo

                repositories = repositories.get_next()
        except StopIteration:
            pass

        return None
예제 #6
0
    def prompt_rb_repository(self, tool_name, repository_info, api_root):
        """Interactively prompt to select a matching repository.

        The user is prompted to choose a matching repository found on the
        Review Board server.
        """
        repositories = api_root.get_repositories()

        # Go through each matching repo and prompt for a selection. If a
        # selection is made, immediately return the selected repo.
        try:
            while True:
                for repo in repositories:
                    is_match = (tool_name == repo.tool and repository_info.path
                                in (repo['path'],
                                    getattr(repo, 'mirror_path', '')))

                    if is_match:
                        question = ("Use the %s repository '%s' (%s)?" %
                                    (tool_name, repo['name'], repo['path']))

                        if confirm(question):
                            return repo

                repositories = repositories.get_next()
        except StopIteration:
            pass

        return None
예제 #7
0
    def _ask_review_request_match(self, review_request):
        question = ("Update Review Request #%s: '%s'? "
                    % (review_request.id,
                       get_draft_or_current_value(
                           'summary', review_request)))

        return confirm(question)
예제 #8
0
    def prompt_rb_repository(self, tool_name, repository_info, api_root):
        """Interactively prompt to select a matching repository.

        The user is prompted to choose a matching repository found on the
        Review Board server.
        """
        # Go through each matching repo and prompt for a selection. If a
        # selection is made, immediately return the selected repo.
        for repository_page in api_root.get_repositories().all_pages:
            repo_paths = {}

            for repository in repository_page:
                if repository.tool != tool_name:
                    continue

                repo_paths[repository['path']] = repository

                if 'mirror_path' in repository:
                    repo_paths[repository['mirror_path']] = repository

            closest_path = difflib.get_close_matches(repository_info.path,
                                                     six.iterkeys(repo_paths),
                                                     n=1)

            for path in closest_path:
                repo = repo_paths[path]
                question = ('Use the %s repository "%s" (%s)?' %
                            (tool_name, repo['name'], repo['path']))

                if confirm(question):
                    return repo

        return None
예제 #9
0
    def main(self, *args):
        server = self.options.server

        if not server:
            server = input('Enter the Review Board server URL: ')

        repository_info, tool = self.initialize_scm_tool()
        api_client, api_root = self.get_api(server)
        self.setup_tool(tool, api_root=api_root)

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        selected_repo = self.prompt_rb_repository(
            tool.name, repository_info, api_root)

        if not selected_repo:
            print('No %s repository found or selected for %s. %s not created.'
                  % (tool.name, server, CONFIG_FILE))
            return

        config = [
            ('REVIEWBOARD_URL', server),
            ('REPOSITORY', selected_repo['name']),
            ('REPOSITORY_TYPE', tool.entrypoint_name),
        ]

        try:
            branch = tool.get_current_branch()
            config.append(('BRANCH', branch))
            config.append(('LAND_DEST_BRANCH', branch))
        except NotImplementedError:
            pass

        outfile_path = os.path.join(os.getcwd(), CONFIG_FILE)
        output = self._get_output(config)

        if not os.path.exists(outfile_path):
            question = ('Create "%s" with the following?\n\n%s\n'
                        % (outfile_path, output))
        else:
            question = ('"%s" exists. Overwrite with the following?\n\n%s\n'
                        % (outfile_path, output))

        if not confirm(question):
            return

        self.generate_config_file(outfile_path, config)
예제 #10
0
    def main(self, *args):
        server = self.options.server

        if not server:
            server = input('Enter the Review Board server URL: ')

        repository_info, tool = self.initialize_scm_tool()
        api_client, api_root = self.get_api(server)
        self.setup_tool(tool, api_root=api_root)

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        selected_repo = self.prompt_rb_repository(
            tool.name, repository_info, api_root)

        if not selected_repo:
            print('No %s repository found or selected for %s. %s not created.'
                  % (tool.name, server, CONFIG_FILE))
            return

        config = [
            ('REVIEWBOARD_URL', server),
            ('REPOSITORY', selected_repo['name']),
            ('REPOSITORY_TYPE', tool.entrypoint_name),
        ]

        try:
            branch = tool.get_current_branch()
            config.append(('BRANCH', branch))
            config.append(('LAND_DEST_BRANCH', branch))
        except NotImplementedError:
            pass

        outfile_path = os.path.join(os.getcwd(), CONFIG_FILE)
        output = self._get_output(config)

        if not os.path.exists(outfile_path):
            question = ('Create "%s" with the following?\n\n%s\n'
                        % (outfile_path, output))
        else:
            question = ('"%s" exists. Overwrite with the following?\n\n%s\n'
                        % (outfile_path, output))

        if not confirm(question):
            return

        self.generate_config_file(outfile_path, config)
예제 #11
0
파일: setup_repo.py 프로젝트: mhahn/rbtools
    def main(self, *args):
        server = self.options.server

        if not server:
            server = raw_input('Enter the Review Board server URL: ')

        repository_info, tool = self.initialize_scm_tool()
        api_client, api_root = self.get_api(server)
        self.setup_tool(tool, api_root=api_root)

        selected_repo = self.prompt_rb_repository(
            tool.name, repository_info, api_root)

        if not selected_repo:
            print ("No %s repository found or selected for %s. %s not created."
                   % (tool.name, server, CONFIG_FILE))
            return

        config = [
            ('REVIEWBOARD_URL', server),
            ('REPOSITORY', selected_repo['name'])
        ]

        try:
            branch = tool.get_current_branch()
            config.append(('BRANCH', branch))
        except NotImplementedError:
            pass

        outfile_path = os.path.join(os.getcwd(), CONFIG_FILE)
        output = self._get_output(config)

        if not os.path.exists(outfile_path):
            question = ("Create '%s' with the following?\n\n%s\n"
                        % (outfile_path, output))
        else:
            question = ("'%s' exists. Overwrite with the following?\n\n%s\n"
                        % (outfile_path, output))

        if not confirm(question):
            return

        self.generate_config_file(outfile_path, config)
예제 #12
0
    def main(self, *args):
        server = self.options.server

        if not server:
            server = raw_input('Enter the Review Board server URL: ')

        repository_info, tool = self.initialize_scm_tool()
        api_client, api_root = self.get_api(server)
        self.setup_tool(tool, api_root=api_root)

        selected_repo = self.prompt_rb_repository(tool.name, repository_info,
                                                  api_root)

        if not selected_repo:
            print(
                "No %s repository found or selected for %s. %s not created." %
                (tool.name, server, CONFIG_FILE))
            return

        config = [('REVIEWBOARD_URL', server),
                  ('REPOSITORY', selected_repo['name'])]

        try:
            branch = tool.get_current_branch()
            config.append(('BRANCH', branch))
        except NotImplementedError:
            pass

        outfile_path = os.path.join(os.getcwd(), CONFIG_FILE)
        output = self._get_output(config)

        if not os.path.exists(outfile_path):
            question = ("Create '%s' with the following?\n\n%s\n" %
                        (outfile_path, output))
        else:
            question = ("'%s' exists. Overwrite with the following?\n\n%s\n" %
                        (outfile_path, output))

        if not confirm(question):
            return

        self.generate_config_file(outfile_path, config)
예제 #13
0
    def prompt_rb_repository(self, tool_name, repository_info, api_root):
        """Interactively prompt to select a matching repository.

        The user is prompted to choose a matching repository found on the
        Review Board server.
        """
        repositories = api_root.get_repositories()

        # Go through each matching repo and prompt for a selection. If a
        # selection is made, immediately return the selected repo.
        try:
            while True:
                repo_paths = {}
                for repository in repositories:
                    if repository.tool != tool_name:
                        continue

                    repo_paths[repository['path']] = repository
                    if 'mirror_path' in repository:
                        repo_paths[repository['mirror_path']] = repository

                closest_path = difflib.get_close_matches(repository_info.path,
                                                         repo_paths.keys(),
                                                         n=1)

                for path in closest_path:
                    repo = repo_paths[path]
                    question = (
                        "Use the %s repository '%s' (%s)?"
                        % (tool_name, repo['name'], repo['path']))

                    if confirm(question):
                        return repo

                repositories = repositories.get_next()
        except StopIteration:
            pass

        return None
예제 #14
0
    def main(self, *args):
        server = self.options.server
        api_client = None
        api_root = None

        if not server:
            print()
            print(textwrap.fill(
                'This command is intended to help users create a %s file in '
                'the current directory to connect a repository and Review '
                'Board server.')
                % CONFIG_FILE)
            print()
            print(textwrap.fill(
                'Repositories must currently exist on your server (either '
                'hosted internally or via RBCommons) to successfully '
                'generate this file.'))
            print(textwrap.fill(
                'Repositories can be added using the Admin Dashboard in '
                'Review Board or under your team administration settings in '
                'RBCommons.'))
            print()
            print(textwrap.fill(
                'Press CTRL + C anytime during this command to cancel '
                'generating your config file.'))
            print()

            while True:
                server = input('Enter the Review Board server URL: ')

                if server:
                    try:
                        # Validate the inputted server.
                        api_client, api_root = self.get_api(server)
                        break
                    except CommandError as e:
                        print()
                        print('%s' % e)
                        print('Please try again.')
                        print()

        repository_info, tool = self.initialize_scm_tool()

        # If we run the `--server` option or if we run `rbt setup-repo` with
        # a server URL already defined in our config file, neither `api_root`
        # nor `api_client` will be defined. We must re-validate our server
        # again.
        if not api_root and not api_client:
            api_client, api_root = self.get_api(server)

        self.setup_tool(tool, api_root=api_root)

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        # While a repository is not chosen, keep the repository selection
        # prompt displayed until the prompt is cancelled.
        while True:
            print()
            print('Current server: %s' % server)
            selected_repo = self.prompt_rb_repository(
                tool.name, repository_info, api_root)

            if not selected_repo:
                print()
                print('No %s repository found for the Review Board server %s'
                      % (tool.name, server))
                print()
                print('Cancelling %s creation...' % CONFIG_FILE)
                print()
                print(textwrap.fill(
                    'Please make sure your repositories currently exist on '
                    'your server. Repositories can be configured using the '
                    'Review Board Admin Dashboard or under your team '
                    'administration settings in RBCommons. For more '
                    'information, see `rbt help setup-repo` or the official '
                    'docs at https://www.reviewboard.org/docs/.'))
                return

            config = [
                ('REVIEWBOARD_URL', server),
                ('REPOSITORY', selected_repo['name']),
                ('REPOSITORY_TYPE', tool.entrypoint_name),
            ]

            try:
                branch = tool.get_current_branch()
                config.append(('BRANCH', branch))
                config.append(('LAND_DEST_BRANCH', branch))
            except NotImplementedError:
                pass

            outfile_path = os.path.join(os.getcwd(), CONFIG_FILE)
            output = self._get_output(config)

            if not os.path.exists(outfile_path):
                question = ('Create "%s" with the following?\n\n%s\n'
                            % (outfile_path, output))
            else:
                question = (
                    '"%s" exists. Overwrite with the following?\n\n%s\n'
                    % (outfile_path, output)
                )

            if confirm(question):
                break

        self.generate_config_file(outfile_path, config)
예제 #15
0
파일: land.py 프로젝트: abaelhe/rbtools
 def _ask_review_request_match(self, review_request):
     return confirm(
         'Land Review Request #%s: "%s"? '
         % (review_request.id,
            get_draft_or_current_value('summary', review_request)))
예제 #16
0
파일: land.py 프로젝트: drbr/rbtools
 def _ask_review_request_match(self, review_request):
     return confirm(
         'Land Review Request #%s: "%s"? '
         % (review_request.id,
            get_draft_or_current_value('summary', review_request)))
예제 #17
0
class Post(Command):
    """Create and update review requests."""
    name = "post"
    author = "The Review Board Project"
    description = "Uploads diffs to create and update review requests."
    args = "[revisions]"

    GUESS_AUTO = 'auto'
    GUESS_YES = 'yes'
    GUESS_NO = 'no'
    GUESS_YES_INPUT_VALUES = (True, 'yes', 1, '1')
    GUESS_NO_INPUT_VALUES = (False, 'no', 0, '0')
    GUESS_CHOICES = (GUESS_AUTO, GUESS_YES, GUESS_NO)

    option_list = [
        OptionGroup(
            name='Posting Options',
            description='Controls the behavior of a post, including what '
                        'review request gets posted and how, and what '
                        'happens after it is posted.',
            option_list=[
                Option('-r', '--review-request-id',
                       dest='rid',
                       metavar='ID',
                       default=None,
                       help='Specifies the existing review request ID to '
                            'update.'),
                Option('-u', '--update',
                       dest='update',
                       action='store_true',
                       default=False,
                       help='Automatically determines the existing review '
                            'request to update.'),
                Option('-p', '--publish',
                       dest='publish',
                       action='store_true',
                       default=False,
                       config_key='PUBLISH',
                       help='Immediately publishes the review request after '
                            'posting.'),
                Option('-o', '--open',
                       dest='open_browser',
                       action='store_true',
                       config_key='OPEN_BROWSER',
                       default=False,
                       help='Opens a web browser to the review request '
                            'after posting.'),
                Option('--submit-as',
                       dest='submit_as',
                       metavar='USERNAME',
                       config_key='SUBMIT_AS',
                       default=None,
                       help='The user name to use as the author of the '
                            'review request, instead of the logged in user.'),
                Option('--change-only',
                       dest='change_only',
                       action='store_true',
                       default=False,
                       help='Updates fields from the change description, '
                            'but does not upload a new diff '
                            '(Perforce/Plastic only).'),
                Option('--diff-only',
                       dest='diff_only',
                       action='store_true',
                       default=False,
                       help='Uploads a new diff, but does not update '
                            'fields from the change description '
                            '(Perforce/Plastic only).'),
            ]
        ),
        Command.server_options,
        Command.repository_options,
        OptionGroup(
            name='Review Request Field Options',
            description='Options for setting the contents of fields in the '
                        'review request.',
            option_list=[
                Option('-g', '--guess-fields',
                       dest='guess_fields',
                       action='store',
                       config_key='GUESS_FIELDS',
                       nargs='?',
                       default=GUESS_AUTO,
                       const=GUESS_YES,
                       choices=GUESS_CHOICES,
                       help='Short-hand for --guess-summary '
                            '--guess-description.'),
                Option('--guess-summary',
                       dest='guess_summary',
                       action='store',
                       config_key='GUESS_SUMMARY',
                       nargs='?',
                       default=GUESS_AUTO,
                       const=GUESS_YES,
                       choices=GUESS_CHOICES,
                       help='Generates the Summary field based on the '
                            'commit messages (Bazaar/Git/Mercurial only).'),
                Option('--guess-description',
                       dest='guess_description',
                       action='store',
                       config_key='GUESS_DESCRIPTION',
                       nargs='?',
                       default=GUESS_AUTO,
                       const=GUESS_YES,
                       choices=GUESS_CHOICES,
                       help='Generates the Description field based on the '
                            'commit messages (Bazaar/Git/Mercurial only).'),
                Option('--change-description',
                       default=None,
                       help='A description of what changed in this update '
                            'of the review request. This is ignored for new '
                            'review requests.'),
                Option('--summary',
                       dest='summary',
                       default=None,
                       help='The new contents for the Summary field.'),
                Option('--description',
                       dest='description',
                       default=None,
                       help='The new contents for the Description field.'),
                Option('--description-file',
                       dest='description_file',
                       default=None,
                       metavar='FILENAME',
                       help='A text file containing the new contents for the '
                            'Description field.'),
                Option('--testing-done',
                       dest='testing_done',
                       default=None,
                       help='The new contents for the Testing Done field.'),
                Option('--testing-done-file',
                       dest='testing_file',
                       default=None,
                       metavar='FILENAME',
                       help='A text file containing the new contents for the '
                            'Testing Done field.'),
                Option('--branch',
                       dest='branch',
                       config_key='BRANCH',
                       default=None,
                       help='The branch the change will be committed on.'),
                Option('--bugs-closed',
                       dest='bugs_closed',
                       default=None,
                       help='The comma-separated list of bug IDs closed.'),
                Option('--target-groups',
                       dest='target_groups',
                       config_key='TARGET_GROUPS',
                       default=None,
                       help='The names of the groups that should perform the '
                            'review.'),
                Option('--target-people',
                       dest='target_people',
                       config_key='TARGET_PEOPLE',
                       default=None,
                       help='The usernames of the people who should perform '
                            'the review.'),
                Option('--depends-on',
                       dest='depends_on',
                       config_key='DEPENDS_ON',
                       default=None,
                       help='The new contents for the Depends On field.'),
                Option('--markdown',
                       dest='markdown',
                       action='store_true',
                       config_key='MARKDOWN',
                       default=False,
                       help='Specifies if the summary and description should '
                            'be interpreted as Markdown-formatted text '
                            '(Review Board 2.0+ only).'),
            ]
        ),
        Command.diff_options,
        Command.perforce_options,
        Command.subversion_options,
    ]

    def post_process_options(self):
        # -g implies --guess-summary and --guess-description
        if self.options.guess_fields:
            self.options.guess_fields = self.normalize_guess_value(
                self.options.guess_fields, '--guess-fields')

            self.options.guess_summary = self.options.guess_fields
            self.options.guess_description = self.options.guess_fields

        if self.options.revision_range:
            raise CommandError(
                'The --revision-range argument has been removed. To post a '
                'diff for one or more specific revisions, pass those '
                'revisions as arguments. For more information, see the '
                'RBTools 0.6 Release Notes.')

        if self.options.svn_changelist:
            raise CommandError(
                'The --svn-changelist argument has been removed. To use a '
                'Subversion changelist, pass the changelist name as an '
                'additional argument after the command.')

        # Only one of --description and --description-file can be used
        if self.options.description and self.options.description_file:
            raise CommandError("The --description and --description-file "
                               "options are mutually exclusive.\n")

        # If --description-file is used, read that file
        if self.options.description_file:
            if os.path.exists(self.options.description_file):
                fp = open(self.options.description_file, "r")
                self.options.description = fp.read()
                fp.close()
            else:
                raise CommandError(
                    "The description file %s does not exist.\n" %
                    self.options.description_file)

        # Only one of --testing-done and --testing-done-file can be used
        if self.options.testing_done and self.options.testing_file:
            raise CommandError("The --testing-done and --testing-done-file "
                               "options are mutually exclusive.\n")

        # If --testing-done-file is used, read that file
        if self.options.testing_file:
            if os.path.exists(self.options.testing_file):
                fp = open(self.options.testing_file, "r")
                self.options.testing_done = fp.read()
                fp.close()
            else:
                raise CommandError("The testing file %s does not exist.\n" %
                                   self.options.testing_file)

        # If we have an explicitly specified summary, override
        # --guess-summary
        if self.options.summary:
            self.options.guess_summary = self.GUESS_NO
        else:
            self.options.guess_summary = self.normalize_guess_value(
                self.options.guess_summary, '--guess-summary')

        # If we have an explicitly specified description, override
        # --guess-description
        if self.options.description:
            self.options.guess_description = self.GUESS_NO
        else:
            self.options.guess_description = self.normalize_guess_value(
                self.options.guess_description, '--guess-description')

        # If we have an explicitly specified review request ID, override
        # --update
        if self.options.rid and self.options.update:
            self.options.update = False

    def normalize_guess_value(self, guess, arg_name):
        if guess in self.GUESS_YES_INPUT_VALUES:
            return self.GUESS_YES
        elif guess in self.GUESS_NO_INPUT_VALUES:
            return self.GUESS_NO
        elif guess == self.GUESS_AUTO:
            return guess
        else:
            raise CommandError('Invalid value "%s" for argument "%s"'
                               % (guess, arg_name))

    def get_repository_path(self, repository_info, api_root):
        """Get the repository path from the server.

        This will compare the paths returned by the SCM client
        with those one the server, and return the first match.
        """
        if isinstance(repository_info.path, list):
            repositories = api_root.get_repositories()

            try:
                while True:
                    for repo in repositories:
                        if repo['path'] in repository_info.path:
                            repository_info.path = repo['path']
                            raise StopIteration()

                    repositories = repositories.get_next()
            except StopIteration:
                pass

        if isinstance(repository_info.path, list):
            error_str = [
                'There was an error creating this review request.\n',
                '\n',
                'There was no matching repository path found on the server.\n',
                'Unknown repository paths found:\n',
            ]

            for foundpath in repository_info.path:
                error_str.append('\t%s\n' % foundpath)

            error_str += [
                'Ask the administrator to add one of these repositories\n',
                'to the Review Board server.\n',
            ]

            raise CommandError(''.join(error_str))

        return repository_info.path

    def get_draft_or_current_value(self, field_name, review_request):
        """Returns the draft or current field value from a review request.

        If a draft exists for the supplied review request, return the draft's
        field value for the supplied field name, otherwise return the review
        request's field value for the supplied field name.
        """
        if review_request.draft:
            fields = review_request.draft[0]
        else:
            fields = review_request

        return fields[field_name]

    def get_possible_matches(self, review_requests, summary, description,
                             limit=5):
        """Returns a sorted list of tuples of score and review request.

        Each review request is given a score based on the summary and
        description provided. The result is a sorted list of tuples containing
        the score and the corresponding review request, sorted by the highest
        scoring review request first.
        """
        candidates = []

        # Get all potential matches.
        try:
            while True:
                for review_request in review_requests:
                    summary_pair = (
                        self.get_draft_or_current_value(
                            'summary', review_request),
                        summary)
                    description_pair = (
                        self.get_draft_or_current_value(
                            'description', review_request),
                        description)
                    score = Score.get_match(summary_pair, description_pair)
                    candidates.append((score, review_request))

                review_requests = review_requests.get_next()
        except StopIteration:
            pass

        # Sort by summary and description on descending rank.
        sorted_candidates = sorted(
            candidates,
            key=lambda m: (m[0].summary_score, m[0].description_score),
            reverse=True
        )

        return sorted_candidates[:limit]

    def num_exact_matches(self, possible_matches):
        """Returns the number of exact matches in the possible match list."""
        count = 0

        for score, request in possible_matches:
            if score.is_exact_match():
                count += 1

        return count

    def guess_existing_review_request_id(self, repository_info, api_root,
                                         api_client):
        """Try to guess the existing review request ID if it is available.

        The existing review request is guessed by comparing the existing
        summary and description to the current post's summary and description,
        respectively. The current post's summary and description are guessed if
        they are not provided.

        If the summary and description exactly match those of an existing
        review request, the ID for which is immediately returned. Otherwise,
        the user is prompted to select from a list of potential matches,
        sorted by the highest ranked match first.
        """
        user = get_user(api_client, api_root, auth_required=True)
        repository_id = get_repository_id(
            repository_info, api_root, self.options.repository_name)

        try:
            # Get only pending requests by the current user for this
            # repository.
            review_requests = api_root.get_review_requests(
                repository=repository_id, from_user=user.username,
                status='pending', expand='draft')

            if not review_requests:
                raise CommandError('No existing review requests to update for '
                                   'user %s.'
                                   % user.username)
        except APIError, e:
            raise CommandError('Error getting review requests for user '
                               '%s: %s' % (user.username, e))

        summary = self.options.summary
        description = self.options.description

        if not summary or not description:
            try:
                commit_message = self.get_commit_message()

                if commit_message:
                    if not summary:
                        summary = commit_message['summary']

                    if not description:
                        description = commit_message['description']
            except NotImplementedError:
                raise CommandError('--summary and --description are required.')

        possible_matches = self.get_possible_matches(review_requests, summary,
                                                     description)
        exact_match_count = self.num_exact_matches(possible_matches)

        for score, review_request in possible_matches:
            # If the score is the only exact match, return the review request
            # ID without confirmation, otherwise prompt.
            if score.is_exact_match() and exact_match_count == 1:
                return review_request.id
            else:
                question = ("Update Review Request #%s: '%s'? "
                            % (review_request.id,
                               self.get_draft_or_current_value(
                                   'summary', review_request)))

                if confirm(question):
                    return review_request.id

        return None
예제 #18
0
    def main(self, *args):
        server = self.options.server
        api_client = None
        api_root = None

        self.stdout.new_line()
        self.stdout.write(
            textwrap.fill(
                'This command is intended to help users create a %s file in '
                'the current directory to connect a repository and Review '
                'Board server.') % CONFIG_FILE)
        self.stdout.new_line()
        self.stdout.write(
            textwrap.fill(
                'Repositories must currently exist on your server (either '
                'hosted internally or via RBCommons) to successfully '
                'generate this file.'))
        self.stdout.write(
            textwrap.fill(
                'Repositories can be added using the Admin Dashboard in '
                'Review Board or under your team administration settings in '
                'RBCommons.'))
        self.stdout.new_line()
        self.stdout.write(
            textwrap.fill(
                'Press CTRL + C anytime during this command to cancel '
                'generating your config file.'))
        self.stdout.new_line()

        while True:
            if server:
                try:
                    # Validate the inputted server.
                    api_client, api_root = self.get_api(server)
                    break
                except CommandError as e:
                    self.stdout.new_line()
                    self.stdout.write('%s' % e)
                    self.stdout.write('Please try again.')
                    self.stdout.new_line()

            server = input('Enter the Review Board server URL: ')

        repository_info, tool = self.initialize_scm_tool()

        self.capabilities = self.get_capabilities(api_root)
        tool.capabilities = self.capabilities

        # Go through standard detection mechanism first. If we find a match
        # this way, we'll set the local repository_info path to be the same as
        # the remote, which will improve matching.
        repository, info = get_repository_resource(
            api_root, tool=tool, repository_paths=repository_info.path)

        if repository:
            repository_info.update_from_remote(repository, info)

        # While a repository is not chosen, keep the repository selection
        # prompt displayed until the prompt is cancelled.
        while True:
            self.stdout.new_line()
            self.stdout.write('Current server: %s' % server)
            selected_repo = self.prompt_rb_repository(
                local_tool_name=tool.name,
                server_tool_names=tool.server_tool_names,
                repository_paths=repository_info.path,
                api_root=api_root)

            if not selected_repo:
                self.stdout.new_line()
                self.stdout.write('No %s repository found for the Review '
                                  'Board server %s' % (tool.name, server))
                self.stdout.new_line()
                self.stdout.write('Cancelling %s creation...' % CONFIG_FILE)
                self.stdout.new_line()
                self.stdout.write(
                    textwrap.fill('Please make sure your repositories '
                                  'currently exist on your server. '
                                  'Repositories can be configured using the '
                                  'Review Board Admin Dashboard or under your '
                                  'team administration settings in RBCommons. '
                                  'For more information, see `rbt help '
                                  'setup-repo` or the official docs at '
                                  'https://www.reviewboard.org/docs/.'))
                return

            config = [
                ('REVIEWBOARD_URL', server),
                ('REPOSITORY', selected_repo['name']),
                ('REPOSITORY_TYPE', tool.entrypoint_name),
            ]

            try:
                branch = tool.get_current_branch()
                config.append(('BRANCH', branch))
                config.append(('LAND_DEST_BRANCH', branch))
            except NotImplementedError:
                pass

            outfile_path = os.path.join(os.getcwd(), CONFIG_FILE)
            output = self._get_output(config)

            if not os.path.exists(outfile_path):
                question = ('Create "%s" with the following?\n\n%s\n' %
                            (outfile_path, output))
            else:
                question = (
                    '"%s" exists. Overwrite with the following?\n\n%s\n' %
                    (outfile_path, output))

            if confirm(question):
                break

        self.generate_config_file(outfile_path, config)