Example #1
0
  def __init__(self, files, local_root, author,
               fopen, os_path,
               email_postfix='@chromium.org',
               disable_color=False,
               override_files=None):
    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.db = owners_module.Database(local_root, fopen, os_path)
    self.db.override_files = override_files or {}
    self.db.load_data_needed_for(files)

    self.os_path = os_path

    self.author = author

    filtered_files = files

    # Eliminate files that the author can review.
    filtered_files = list(self.db.files_not_covered_by(
      filtered_files, [author] if author else []))

    # If some files are eliminated.
    if len(filtered_files) != len(files):
      files = filtered_files
      # Reload the database.
      self.db = owners_module.Database(local_root, fopen, os_path)
      self.db.override_files = override_files or {}
      self.db.load_data_needed_for(files)

    self.all_possible_owners = self.db.all_possible_owners(files, None)

    self.owners_to_files = {}
    self._map_owners_to_files(files)

    self.files_to_owners = {}
    self._map_files_to_owners()

    self.owners_score = self.db.total_costs_by_owner(
        self.all_possible_owners, files)

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

    # 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()
Example #2
0
def resolve_owners(flags):
  """Resolves sets of owners for every flag in the provided list.

  Given a list of flags, for each flag, resolves owners for that flag. Resolving
  owners means, for each entry in a flag's owners list:
  * Turning owners files references into the transitive set of owners listed in
    those files
  * Turning bare usernames into @chromium.org email addresses
  * Passing any other type of entry through unmodified
  """

  owners_db = owners.Database(ROOT_PATH, open, os.path)

  new_flags = []
  for f in flags:
    new_flag = f.copy()
    new_owners = []
    for o in f['owners']:
      if o.startswith('//') or '/' in o:
        new_owners += owners_db.owners_rooted_at_file(re.sub('//', '', o))
      elif '@' not in o:
        new_owners.append(o + '@chromium.org')
      else:
        new_owners.append(o)
    new_flag['resolved_owners'] = new_owners
    new_flags.append(new_flag)
  return new_flags
Example #3
0
    def __init__(self, change, presubmit_path, is_committing, rietveld_obj,
                 verbose):
        """Builds an InputApi object.

    Args:
      change: A presubmit.Change object.
      presubmit_path: The path to the presubmit script being processed.
      is_committing: True if the change is about to be committed.
      rietveld_obj: rietveld.Rietveld client object
    """
        # Version number of the presubmit_support script.
        self.version = [int(x) for x in __version__.split('.')]
        self.change = change
        self.is_committing = is_committing
        self.rietveld = rietveld_obj
        # TBD
        self.host_url = 'http://codereview.chromium.org'
        if self.rietveld:
            self.host_url = self.rietveld.url

        # We expose various modules and functions as attributes of the input_api
        # so that presubmit scripts don't have to import them.
        self.basename = os.path.basename
        self.cPickle = cPickle
        self.cStringIO = cStringIO
        self.json = json
        self.logging = logging.getLogger('PRESUBMIT')
        self.os_listdir = os.listdir
        self.os_walk = os.walk
        self.os_path = os.path
        self.pickle = pickle
        self.marshal = marshal
        self.re = re
        self.subprocess = subprocess
        self.tempfile = tempfile
        self.time = time
        self.traceback = traceback
        self.unittest = unittest
        self.urllib2 = urllib2

        # To easily fork python.
        self.python_executable = sys.executable
        self.environ = os.environ

        # InputApi.platform is the platform you're currently running on.
        self.platform = sys.platform

        # The local path of the currently-being-processed presubmit script.
        self._current_presubmit_path = os.path.dirname(presubmit_path)

        # We carry the canned checks so presubmit scripts can easily use them.
        self.canned_checks = presubmit_canned_checks

        # TODO(dpranke): figure out a list of all approved owners for a repo
        # in order to be able to handle wildcard OWNERS files?
        self.owners_db = owners.Database(change.RepositoryRoot(),
                                         fopen=file,
                                         os_path=self.os_path)
        self.verbose = verbose
Example #4
0
def SplitCl(description_file, comment_file, changelist, cmd_upload):
    """"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.

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

    try:
        EnsureInGitRepository()

        cl = changelist()
        change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
        files = change.AffectedFiles()

        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

        owners_database = owners.Database(change.RepositoryRoot(), file,
                                          os.path)
        owners_database.load_data_needed_for([f.LocalPath() for f in files])

        files_split_by_owners = GetFilesSplitByOwners(owners_database, files)

        print('Will split current branch (' + refactor_branch + ') in ' +
              str(len(files_split_by_owners)) + ' CLs.\n')

        for directory, files in files_split_by_owners.iteritems():
            # Use '/' as a path separator in the branch name and the CL description
            # and comment.
            directory = directory.replace(os.path.sep, '/')
            # Upload the CL.
            UploadCl(refactor_branch, refactor_branch_upstream, directory,
                     files, author, description, comment, owners_database,
                     changelist, cmd_upload)

        # 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
Example #5
0
 def __init__(self, host, root, branch, fopen=open, os_path=os.path):
     super(DepotToolsClient, self).__init__(host)
     self._root = root
     self._fopen = fopen
     self._os_path = os_path
     self._branch = branch
     self._db = owners_db.Database(root, fopen, os_path)
     self._db.override_files = self._GetOriginalOwnersFiles()
Example #6
0
    def ValidateOwnersConfig(self, change_id):
        data = gerrit_util.GetChange(self._host, change_id, [
            'DETAILED_ACCOUNTS', 'DETAILED_LABELS', 'CURRENT_FILES',
            'CURRENT_REVISION'
        ])

        files = data['revisions'][data['current_revision']]['files']

        db = owners_db.Database(self._root, self._fopen, self._os_path)
        try:
            db.load_data_needed_for(
                [f for f in files if os.path.basename(f) == 'OWNERS'])
        except Exception as e:
            raise InvalidOwnersConfig('Error parsing OWNERS files:\n%s' % e)
Example #7
0
 def db(self, root=None, fopen=None, os_path=None, glob=None):
   root = root or self.root
   fopen = fopen or self.fopen
   os_path = os_path or self.repo
   glob = glob or self.glob
   return owners.Database(root, fopen, os_path, glob)
 def db(self, root=None, fopen=None, os_path=None):
     root = root or self.root
     fopen = fopen or self.fopen
     os_path = os_path or self.repo
     # pylint: disable=no-value-for-parameter
     return owners.Database(root, fopen, os_path)
 def _ensure_db(self):
   if self._db is not None:
     return
   self._db = owners_db.Database(self._root, self._fopen, self._os_path)
   self._db.override_files = self._GetOriginalOwnersFiles()
Example #10
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(
        ReadFile(description_file))
    comment = ReadFile(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

        owners_database = owners.Database(repository_root, open, os.path)
        owners_database.load_data_needed_for([f for _, f in files])

        files_split_by_owners = GetFilesSplitByOwners(owners_database, 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 = raw_input('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 = owners_database.reviewers_for(file_paths, author)

            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
Example #11
0
 def __init__(self, host, root):
     super(DepotToolsClient, self).__init__(host)
     self._root = root
     self._db = owners.Database(root, open, os.path)
Example #12
0
    def __init__(self,
                 files,
                 local_root,
                 author,
                 fopen,
                 os_path,
                 glob,
                 email_postfix='@chromium.org',
                 disable_color=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.db = owners_module.Database(local_root, fopen, os_path, glob)
        self.db.load_data_needed_for(files)

        self.os_path = os_path

        self.author = author

        filtered_files = files

        # Eliminate files that author himself can review.
        if author:
            if author in self.db.owned_by:
                for dir_name in self.db.owned_by[author]:
                    filtered_files = [
                        file_name for file_name in filtered_files
                        if not file_name.startswith(dir_name)
                    ]

                filtered_files = list(filtered_files)

        # Eliminate files that everyone can review.
        if owners_module.EVERYONE in self.db.owned_by:
            for dir_name in self.db.owned_by[owners_module.EVERYONE]:
                filtered_files = filter(
                    lambda file_name: not file_name.startswith(dir_name),
                    filtered_files)

        # If some files are eliminated.
        if len(filtered_files) != len(files):
            files = filtered_files
            # Reload the database.
            self.db = owners_module.Database(local_root, fopen, os_path, glob)
            self.db.load_data_needed_for(files)

        self.all_possible_owners = self.db.all_possible_owners(files, None)

        self.owners_to_files = {}
        self._map_owners_to_files(files)

        self.files_to_owners = {}
        self._map_files_to_owners()

        self.owners_score = self.db.total_costs_by_owner(
            self.all_possible_owners, files)

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

        # 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()