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()
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
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
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
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()
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)
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()
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
def __init__(self, host, root): super(DepotToolsClient, self).__init__(host) self._root = root self._db = owners.Database(root, open, os.path)
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()