Example #1
0
 def setUp(self):
     self.repo = filesystem_mock.MockFileSystem(
         files={
             '/OWNERS':
             '\n'.join([
                 'per-file [email protected]',
                 'per-file [email protected]',
                 '*****@*****.**',
             ]),
             '/approved.cc':
             '',
             '/reviewed.h':
             '',
             '/bar/insufficient_reviewers.py':
             '',
             '/bar/everyone/OWNERS':
             '*',
             '/bar/everyone/foo.txt':
             '',
         })
     self.root = '/'
     self.fopen = self.repo.open_for_reading
     mock.patch('owners_client.DepotToolsClient._GetOriginalOwnersFiles',
                return_value={}).start()
     self.addCleanup(mock.patch.stopall)
     self.client = owners_client.DepotToolsClient('host', '/', 'branch',
                                                  self.fopen, self.repo)
Example #2
0
def SplitCl(description_file, comment_file, changelist, cmd_upload, dry_run,
            cq_dry_run, enable_auto_submit, repository_root):
    """"Splits a branch into smaller branches and uploads CLs.

  Args:
    description_file: File containing the description of uploaded CLs.
    comment_file: File containing the comment of uploaded CLs.
    changelist: The Changelist class.
    cmd_upload: The function associated with the git cl upload command.
    dry_run: Whether this is a dry run (no branches or CLs created).
    cq_dry_run: If CL uploads should also do a cq dry run.
    enable_auto_submit: If CL uploads should also enable auto submit.

  Returns:
    0 in case of success. 1 in case of error.
  """
    description = AddUploadedByGitClSplitToDescription(
        gclient_utils.FileRead(description_file))
    comment = gclient_utils.FileRead(comment_file) if comment_file else None

    try:
        EnsureInGitRepository()

        cl = changelist()
        upstream = cl.GetCommonAncestorWithUpstream()
        files = [
            (action.strip(), f)
            for action, f in scm.GIT.CaptureStatus(repository_root, upstream)
        ]

        if not files:
            print('Cannot split an empty CL.')
            return 1

        author = git.run('config', 'user.email').strip() or None
        refactor_branch = git.current_branch()
        assert refactor_branch, "Can't run from detached branch."
        refactor_branch_upstream = git.upstream(refactor_branch)
        assert refactor_branch_upstream, \
            "Branch %s must have an upstream." % refactor_branch

        client = owners_client.DepotToolsClient(
            root=repository_root, branch=refactor_branch_upstream)

        files_split_by_owners = GetFilesSplitByOwners(files)

        num_cls = len(files_split_by_owners)
        print('Will split current branch (' + refactor_branch + ') into ' +
              str(num_cls) + ' CLs.\n')
        if cq_dry_run and num_cls > CL_SPLIT_FORCE_LIMIT:
            print(
                'This will generate "%r" CLs. This many CLs can potentially generate'
                ' too much load on the build infrastructure. Please email'
                ' [email protected] to ensure that this won\'t  break anything.'
                ' The infra team reserves the right to cancel your jobs if they are'
                ' overloading the CQ.' % num_cls)
            answer = gclient_utils.AskForData('Proceed? (y/n):')
            if answer.lower() != 'y':
                return 0

        for cl_index, (directory, files) in \
            enumerate(files_split_by_owners.items(), 1):
            # Use '/' as a path separator in the branch name and the CL description
            # and comment.
            directory = directory.replace(os.path.sep, '/')
            file_paths = [f for _, f in files]
            reviewers = client.SuggestOwners(
                file_paths,
                exclude=[author, owners_client.OwnersClient.EVERYONE])
            if dry_run:
                PrintClInfo(cl_index, num_cls, directory, file_paths,
                            description, reviewers)
            else:
                UploadCl(refactor_branch, refactor_branch_upstream, directory,
                         files, description, comment, reviewers, changelist,
                         cmd_upload, cq_dry_run, enable_auto_submit,
                         repository_root)

        # Go back to the original branch.
        git.run('checkout', refactor_branch)

    except subprocess2.CalledProcessError as cpe:
        sys.stderr.write(cpe.stderr)
        return 1
    return 0
    def __init__(self,
                 files,
                 local_root,
                 author,
                 reviewers,
                 fopen,
                 os_path,
                 email_postfix='@chromium.org',
                 disable_color=False,
                 override_files=None,
                 ignore_author=False):
        self.email_postfix = email_postfix

        if os.name == 'nt' or disable_color:
            self.COLOR_LINK = ''
            self.COLOR_BOLD = ''
            self.COLOR_GREY = ''
            self.COLOR_RESET = ''

        self.os_path = os_path

        self.author = author

        filtered_files = files

        reviewers = list(reviewers)
        if author and not ignore_author:
            reviewers.append(author)

        # Eliminate files that existing reviewers can review.
        self.client = owners_client.DepotToolsClient(
            root=local_root,
            branch=git_common.current_branch(),
            fopen=fopen,
            os_path=os_path)

        approval_status = self.client.GetFilesApprovalStatus(
            filtered_files, reviewers, [])
        filtered_files = [
            f for f in filtered_files
            if approval_status[f] != owners_client.OwnersClient.APPROVED
        ]

        # If some files are eliminated.
        if len(filtered_files) != len(files):
            files = filtered_files

        self.files_to_owners = self.client.BatchListOwners(files)

        self.owners_to_files = {}
        self._map_owners_to_files()

        self.original_files_to_owners = copy.deepcopy(self.files_to_owners)

        # This is the queue that will be shown in the interactive questions.
        # It is initially sorted by the score in descending order. In the
        # interactive questions a user can choose to "defer" its decision, then the
        # owner will be put to the end of the queue and shown later.
        self.owners_queue = []

        self.unreviewed_files = set()
        self.reviewed_by = {}
        self.selected_owners = set()
        self.deselected_owners = set()
        self.reset()