def create_from_12x_gf_client_name(p4, gf_client_name): ''' Upgrade from Git Fusion 12.x. Given the name of an existing Git Fusion 12.x client spec "git-fusion-{view- name}", copy its view into a Git Fusion 13.1 p4gf_config file, add and submit that file to Perforce. NOP if that p4gf_config file already exists. ''' # Extract repo_name (nee view_name) from client spect's name. assert gf_client_name.startswith(p4gf_const.P4GF_CLIENT_PREFIX) repo_name = gf_client_name[len(p4gf_const.P4GF_CLIENT_PREFIX):] # NOP if repo's p4gf_config already exists and is not deleted at head. depot_path = depot_path_repo(repo_name) if p4gf_util.depot_file_exists(p4, depot_path): return # Extract View lines from client spec, use them to create a new config. client_spec = p4.fetch_client(gf_client_name) view = client_spec['View'] config = default_config_repo_for_view(p4, repo_name, view) # Write config to Perforce. config_file_content = file_content_repo(config) depot_path = depot_path_repo(repo_name) _add_file(p4, depot_path, config_file_content)
def ssh_key_add(p4, depot_path, keys, action=None): """Read the contents of the named file and use it to produce a fingerprint of the presumed SSH key, formatting the results into a line suitable for adding to the SSH configuration file. The line is added to the set of keys, keyed by a generated fingerprint. Keyword arguments: p4 -- P4 API object depot_path -- path to keys file keys -- instance of KeyKeeper action -- string describing the action being performed (e.g. 'edit'), defaults to ADD. For debug log only. """ user, key, fp = extract_key_data(p4, depot_path) if not user: _print_warn( _('Could not extract user name from unrecognized depot path: {depot_path}' ).format(depot_path=depot_path)) return if not fp: if p4gf_util.depot_file_exists(p4, depot_path): _print_warn( _("File '{depot_path}' does not conform to a valid SSH key, ignoring..." ).format(depot_path=depot_path)) return if not action: action = _ADD _print_debug( _('action {}, user {}, key {}, FP {}').format(action, user, key, fp)) # $SSH[2]_ORIGINAL_COMMAND is there to get the command being invoked # by the client via SSH (e.g. git-upload-pack 'foo') -- we need that # in order to take the appropriate action, and for auditing purposes. if Ssh2: fname = os.path.join(KEYS_DIR, user, fp.replace(':', '') + NTR('.pub')) fpath = os.path.join(SshDirectory, fname) fdir = os.path.dirname(fpath) if not os.path.exists(fdir): os.makedirs(fdir) with open(fpath, 'w') as f: f.write(SSH2_HEADER_LINE + '\n') keydata = key while keydata: f.write(keydata[:72] + '\n') keydata = keydata[72:] f.write(SSH2_FOOTER_LINE + '\n') ln = NTR( 'Key {file}\nOptions command="p4gf_auth_server.py --user={user} --keyfp={keyfp}' ' $SSH2_ORIGINAL_COMMAND"').format(file=fname, user=user, keyfp=fp) # No options are included since not all SSH2 implementations support them. else: ln = generate_openssh_key(user, fp, key) keys.add(fp, user, ln)
def get_branch_dict(self): """Get a branch dictionary for this repo. If the p4gf_config exists, use that. Else if the p4 client exists create a branch dict containing a branch from the client views. Else return None """ LOG.debug("get_branch_dict for {0}".format(self.view_name)) # Repo config file already checked into Perforce? # Use that. config_path = p4gf_config.depot_path_repo(self.view_name) config_exists = p4gf_util.depot_file_exists(self.p4, config_path) if config_exists: self.config = p4gf_config.get_repo(self.p4, self.view_name) if self.config: return p4gf_branch.dict_from_config(self.config, self.p4) else: return None else: LOG.debug("checking if client {0} exists.".format(self.view_name)) if not p4gf_util.spec_exists(self.p4, 'client', self.view_name): LOG.debug(" client {0} NOT exists.".format( self.view_name)) return None view_lines = p4gf_util.first_value_for_key( self.p4.run('client', '-o', '-t', self.view_name, self.p4client), 'View') if not view_lines: return None else: # create a Branch object to manage this client view if isinstance(view_lines, str): view_lines = view_lines.splitlines() LOG.debug( "create branch from client views: {0}".format(view_lines)) branch = p4gf_branch.Branch() branch.branch_id = 'master' branch.git_branch_name = 'master' branch.view_p4map = P4.Map(view_lines) branch.view_lines = view_lines LOG.debug( "create branch from client branch view_p4map: {0}".format( branch.view_p4map)) LOG.debug( "create branch from client branch view_lines: {0}".format( branch.view_lines)) branch_dict = {} branch_dict[branch.branch_id] = branch return branch_dict
def get_branch_dict(self): """Get a branch dictionary for this repo. If the p4gf_config exists, use that. Else if the p4 client exists create a branch dict containing a branch from the client views. Else return None """ LOG.debug("get_branch_dict for {0}".format(self.view_name)) # Repo config file already checked into Perforce? # Use that. config_path = p4gf_config.depot_path_repo(self.view_name) config_exists = p4gf_util.depot_file_exists(self.p4, config_path) if config_exists: self.config = p4gf_config.get_repo(self.p4, self.view_name) if self.config: return p4gf_branch.dict_from_config(self.config, self.p4) else: return None else: LOG.debug("checking if client {0} exists.".format(self.view_name)) if not p4gf_util.spec_exists(self.p4, 'client', self.view_name): LOG.debug(" client {0} NOT exists.".format(self.view_name)) return None view_lines = p4gf_util.first_value_for_key( self.p4.run('client', '-o', '-t', self.view_name, self.p4client), 'View') if not view_lines: return None else: # create a Branch object to manage this client view if isinstance(view_lines, str): view_lines = view_lines.splitlines() LOG.debug("create branch from client views: {0}".format(view_lines)) branch = p4gf_branch.Branch() branch.branch_id = 'master' branch.git_branch_name = 'master' branch.view_p4map = P4.Map(view_lines) branch.view_lines = view_lines LOG.debug("create branch from client branch view_p4map: {0}". format(branch.view_p4map)) LOG.debug("create branch from client branch view_lines: {0}". format(branch.view_lines)) branch_dict = {} branch_dict[branch.branch_id] = branch return branch_dict
def _create_p4_client(p4, view_name, client_name, client_root, enable_mismatched_rhs, view_name_p4client, handle_imports=True): """Create the p4 client to contain Git meta-data mirror. Keyword arguments: p4 -- Perforce client API view_name -- client view of repository to clone - internal (translated) viewname client_name -- client that will be created client_root -- path for client workspace enable_mismatched_rhs -- allow branches to have differing RHS? view_name_p4client -- name of actual p4 client on which to base this new repo if None - will be determined from view_name if needed Returns one of the INIT_REPO_* constants. """ # Ensure the client root directory has been created. if not os.path.exists(client_root): os.makedirs(client_root) view_name_git = p4gf_translate.TranslateReponame.repo_to_git(view_name) LOG.debug("_create_p4_client(): view_name_git {0} view_name {1} view_name_p4client {2}". format(view_name_git, view_name ,view_name_p4client)) # If a Git Fusion client for this view already exists, use that, # no need to init the repo. # Do make sure that the client root is correct. if p4gf_util.spec_exists(p4, 'client', client_name): LOG.debug("%s client already exists for %s", client_name, view_name) p4gf_util.ensure_spec_values(p4, 'client', client_name , { 'Root' : client_root , 'Options' : CLIENT_OPTIONS }) return INIT_REPO_EXISTS # Repo config file already checked into Perforce? # Use that. config_path = p4gf_config.depot_path_repo(view_name) config_exists = p4gf_util.depot_file_exists(p4, config_path) if config_exists: return repo_from_config( p4, view_name, client_name, client_root , enable_mismatched_rhs) # Client exist with the same name as this Git Fusion repo? # Build a new config, check it into Perforce, and use that. if not view_name_p4client: view_name_p4client = p4gf_translate.TranslateReponame.git_to_p4client(view_name_git) nop4client = '' if p4gf_util.spec_exists(p4, 'client', view_name_p4client): return repo_from_template_client( p4, view_name, view_name_p4client, client_name , client_root , enable_mismatched_rhs) else: nop4client = _("p4 client '{0}' does not exist\n").format(view_name_p4client) # creating repo from stream? # note that we can't pass '//depot/stream' because git would be confused # but it's ok to pass 'depot/stream' and add the leading '//' here stream_name = '//'+view_name_git if p4gf_util.spec_exists(p4, 'stream', stream_name): return _repo_from_stream(p4, view_name, stream_name, client_name, client_root, enable_mismatched_rhs, handle_imports) # We don't have, and cannot create, a config for this repo. # Say so and give up. msg = p4gf_const.NO_REPO_MSG_TEMPLATE.format(view_name=view_name ,view_name_p4client=view_name_p4client ,nop4client=nop4client) LOG.warn(msg) _print_stderr(msg) return INIT_REPO_NOVIEW
def add_cl(self, *, branch, p4change): """Find .gitattributes files on branch@p4change and cache any lfs lines.""" # pylint:disable=too-many-branches LOG.debug('add_cl {} on {}'.format(p4change.change, branch.branch_id)) # if first change on this branch, add dict change# -> P4ChangeAttribute branch_name = branch.git_branch_name if branch_name in self.change_gitattributes_dict: # not the first change on this branch if p4change.change in self.change_gitattributes_dict[branch_name]: raise RuntimeError( _("Change {change} seen more than once in branch {branch}" ).format(change=p4change.change, branch=branch_name)) # find the parent change and its .gitattributes prev_change = max( self.change_gitattributes_dict[branch_name].keys()) init_dict = self.change_gitattributes_dict[branch_name][prev_change]\ .dirpath_attributes_dict else: # first change on this branch self.change_gitattributes_dict[branch_name] = {} # first, use git-lfs-initial-track config option, if present init_dict = {} initial_content = p4gf_lfs_attributes.generate_initial_lfs_attrs( self.ctx) if initial_content: init_dict[''] = parse_gitattributes(initial_content) # then add in any .gitattributes in effect prior to this change r = self.ctx.p4run( 'files', '//.../.gitattributes@{}'.format(int(p4change.change) - 1)) for change_file in r: depot_path = change_file['depotFile'] gwt_dir = _get_gwt_path(self.ctx, depot_path, branch, p4change.change) init_dict[gwt_dir] = parse_gitattributes( p4gf_util.print_depot_path_raw(self.ctx.p4, depot_path, p4change.change)) prev_change = p4change.change - 1 self.change_gitattributes_dict[branch_name][prev_change] = \ P4ChangeAttribute(prev_change, init_dict) # get .gitattributes delta for p4change change_dict = {} for change_file in p4change.files: depot_path = change_file.depot_path if not depot_path.endswith('/.gitattributes'): continue is_delete = change_file.action == 'delete' gwt_dir = _get_gwt_path(self.ctx, depot_path, branch, p4change.change) # If top level .gitattributes in first change doesn't exist in P4, # that means we inserted it with contents of initial tracking. # Since we took care of that above, skip it here. if (len(self.change_gitattributes_dict[branch_name]) == 1 and gwt_dir == '' and not is_delete and not p4gf_util.depot_file_exists(self.ctx.p4, depot_path)): continue # When .gitattributes is deleted, we want to remove the path from # the dict rather than set it to an empty pattern list. if is_delete: if gwt_dir in change_dict: del init_dict[gwt_dir] else: change_dict[gwt_dir] = parse_gitattributes( p4gf_util.print_depot_path_raw(self.ctx.p4, depot_path, p4change.change)) # if nothing changed, just ref the previous dict, saving a bit of memory # otherwise apply this change's delta to the previous changes attrs if change_dict: updated_dict = copy.deepcopy(init_dict) updated_dict.update(change_dict) else: updated_dict = init_dict self.change_gitattributes_dict[branch_name][p4change.change] = \ P4ChangeAttribute(p4change.change, updated_dict)
def delete_client(args, p4, client_name, metrics, prune_objs=True): """Delete the named Perforce client and its workspace. Raises P4Exception if the client is not present, or the client configuration is not set up as expected. Keyword arguments: args -- parsed command line arguments p4 -- Git user's Perforce client client_name -- name of client to be deleted metrics -- DeletionMetrics for collecting resulting metrics prune_objs -- if True, delete associated objects from cache """ # pylint: disable=R0912,R0915 group_list = [ p4gf_const.P4GF_GROUP_VIEW_PULL, p4gf_const.P4GF_GROUP_VIEW_PUSH ] p4.user = p4gf_const.P4GF_USER print_verbose(args, _("Checking for client '{}'...").format(client_name)) if not p4gf_util.spec_exists(p4, 'client', client_name): raise P4.P4Exception( _("No such client '{}' defined").format(client_name)) view_name = p4gf_util.client_to_view_name(client_name) p4gf_dir = p4gf_util.p4_to_p4gf_dir(p4) view_dirs = p4gf_view_dirs.from_p4gf_dir(p4gf_dir, view_name) p4gf_util.ensure_spec_values(p4, 'client', client_name, {'Root': view_dirs.p4root}) view_lock = None # We're clobbering and deleting. Overrule locks. with p4gf_context.create_context(view_name, view_lock) as ctx: command_path = ctx.client_view_path() homedir = os.path.expanduser('~') raise_if_homedir(homedir, view_name, view_dirs.view_container) # Scan for objects associated only with this view so we can remove them. objects_to_delete = [] if prune_objs: objects_to_delete = _find_client_commit_objects( args, p4, view_name) # Do we have a repo config file to delete? config_file = p4gf_config.depot_path_repo(view_name) + '*' config_file_exists = p4gf_util.depot_file_exists(p4, config_file) # What counters shall we delete? counter_list = [] counter_list.append( p4gf_context.calc_last_copied_change_counter_name( view_name, p4gf_util.get_server_id())) for spec in p4.run('counters', '-u', '-e', "git-fusion-index-last-{},*".format(view_name)): counter_list.append(spec['counter']) for spec in p4.run('counters', '-u', '-e', "git-fusion-index-branch-{},*".format(view_name)): counter_list.append(spec['counter']) if not args.delete: print(NTR('p4 sync -f {}#none').format(command_path)) print(NTR('p4 client -f -d {}').format(client_name)) print(NTR('rm -rf {}').format(view_dirs.view_container)) print( NTR('Deleting {} objects from //{}/objects/...').format( len(objects_to_delete), p4gf_const.P4GF_DEPOT)) for group_template in group_list: group = group_template.format(view=view_name) print(NTR('p4 group -a -d {}').format(group)) for c in counter_list: print(NTR('p4 counter -u -d {}').format(c)) if config_file_exists: print(NTR('p4 sync -f {}').format(config_file)) print(NTR('p4 delete {}').format(config_file)) print( NTR('p4 submit -d "Delete repo config for {view_name}" {config_file}' ).format(view_name=view_name, config_file=config_file)) else: print_verbose( args, NTR('Removing client files for {}...').format(client_name)) ctx.p4.run('sync', '-fq', command_path + '#none') print_verbose(args, NTR('Deleting client {}...').format(client_name)) p4.run('client', '-df', client_name) metrics.clients += 1 print_verbose( args, NTR("Deleting repo {0}'s directory {1}...").format( view_name, view_dirs.view_container)) _remove_tree(view_dirs.view_container, contents_only=False) metrics.files += _delete_files(p4, objects_to_delete, view_name) for group_template in group_list: _delete_group(args, p4, group_template.format(view=view_name), metrics) for c in counter_list: _delete_counter(p4, c, metrics) if config_file_exists: p4gf_util.p4run_logged(p4, ['sync', '-fq', config_file]) with p4gf_util.NumberedChangelist( p4=p4, description=_("Delete repo config for '{}'").format( view_name)) as nc: nc.p4run(["delete", config_file]) nc.submit()
def delete_client(args, p4, client_name, metrics, prune_objs=True): """Delete the named Perforce client and its workspace. Raises P4Exception if the client is not present, or the client configuration is not set up as expected. Keyword arguments: args -- parsed command line arguments p4 -- Git user's Perforce client client_name -- name of client to be deleted metrics -- DeletionMetrics for collecting resulting metrics prune_objs -- if True, delete associated objects from cache """ # pylint: disable=R0912,R0915 group_list = [p4gf_const.P4GF_GROUP_VIEW_PULL, p4gf_const.P4GF_GROUP_VIEW_PUSH] p4.user = p4gf_const.P4GF_USER print_verbose(args, _("Checking for client '{}'...").format(client_name)) if not p4gf_util.spec_exists(p4, 'client', client_name): raise P4.P4Exception(_("No such client '{}' defined") .format(client_name)) view_name = p4gf_util.client_to_view_name(client_name) p4gf_dir = p4gf_util.p4_to_p4gf_dir(p4) view_dirs = p4gf_view_dirs.from_p4gf_dir(p4gf_dir, view_name) p4gf_util.ensure_spec_values(p4, 'client', client_name, {'Root': view_dirs.p4root}) view_lock = None # We're clobbering and deleting. Overrule locks. with p4gf_context.create_context(view_name, view_lock) as ctx: command_path = ctx.client_view_path() homedir = os.path.expanduser('~') raise_if_homedir(homedir, view_name, view_dirs.view_container) # Scan for objects associated only with this view so we can remove them. objects_to_delete = [] if prune_objs: objects_to_delete = _find_client_commit_objects(args, p4, view_name) # Do we have a repo config file to delete? config_file = p4gf_config.depot_path_repo(view_name) + '*' config_file_exists = p4gf_util.depot_file_exists(p4, config_file) # What counters shall we delete? counter_list = [] counter_list.append(p4gf_context.calc_last_copied_change_counter_name( view_name, p4gf_util.get_server_id())) for spec in p4.run('counters', '-u', '-e', "git-fusion-index-last-{},*" .format(view_name)): counter_list.append(spec['counter']) for spec in p4.run('counters', '-u', '-e', "git-fusion-index-branch-{},*" .format(view_name)): counter_list.append(spec['counter']) if not args.delete: print(NTR('p4 sync -f {}#none').format(command_path)) print(NTR('p4 client -f -d {}').format(client_name)) print(NTR('rm -rf {}').format(view_dirs.view_container)) print(NTR('Deleting {} objects from //{}/objects/...').format( len(objects_to_delete), p4gf_const.P4GF_DEPOT)) for group_template in group_list: group = group_template.format(view=view_name) print(NTR('p4 group -a -d {}').format(group)) for c in counter_list: print(NTR('p4 counter -u -d {}').format(c)) if config_file_exists: print(NTR('p4 sync -f {}').format(config_file)) print(NTR('p4 delete {}').format(config_file)) print(NTR('p4 submit -d "Delete repo config for {view_name}" {config_file}') .format(view_name=view_name, config_file=config_file)) else: print_verbose(args, NTR('Removing client files for {}...').format(client_name)) ctx.p4.run('sync', '-fq', command_path + '#none') print_verbose(args, NTR('Deleting client {}...').format(client_name)) p4.run('client', '-df', client_name) metrics.clients += 1 print_verbose(args, NTR("Deleting repo {0}'s directory {1}...").format(view_name, view_dirs.view_container)) _remove_tree(view_dirs.view_container, contents_only=False) metrics.files += _delete_files(p4, objects_to_delete, view_name) for group_template in group_list: _delete_group(args, p4, group_template.format(view=view_name), metrics) for c in counter_list: _delete_counter(p4, c, metrics) if config_file_exists: p4gf_util.p4run_logged(p4, ['sync', '-fq', config_file]) with p4gf_util.NumberedChangelist( p4=p4, description=_("Delete repo config for '{}'") .format(view_name)) as nc: nc.p4run(["delete", config_file]) nc.submit()
def _create_p4_client(p4, view_name, client_name, client_root, enable_mismatched_rhs, view_name_p4client, handle_imports=True): """Create the p4 client to contain Git meta-data mirror. Keyword arguments: p4 -- Perforce client API view_name -- client view of repository to clone - internal (translated) viewname client_name -- client that will be created client_root -- path for client workspace enable_mismatched_rhs -- allow branches to have differing RHS? view_name_p4client -- name of actual p4 client on which to base this new repo if None - will be determined from view_name if needed Returns one of the INIT_REPO_* constants. """ # Ensure the client root directory has been created. if not os.path.exists(client_root): os.makedirs(client_root) view_name_git = p4gf_translate.TranslateReponame.repo_to_git(view_name) LOG.debug( "_create_p4_client(): view_name_git {0} view_name {1} view_name_p4client {2}" .format(view_name_git, view_name, view_name_p4client)) # If a Git Fusion client for this view already exists, use that, # no need to init the repo. # Do make sure that the client root is correct. if p4gf_util.spec_exists(p4, 'client', client_name): LOG.debug("%s client already exists for %s", client_name, view_name) p4gf_util.ensure_spec_values(p4, 'client', client_name, { 'Root': client_root, 'Options': CLIENT_OPTIONS }) return INIT_REPO_EXISTS # Repo config file already checked into Perforce? # Use that. config_path = p4gf_config.depot_path_repo(view_name) config_exists = p4gf_util.depot_file_exists(p4, config_path) if config_exists: return repo_from_config(p4, view_name, client_name, client_root, enable_mismatched_rhs) # Client exist with the same name as this Git Fusion repo? # Build a new config, check it into Perforce, and use that. if not view_name_p4client: view_name_p4client = p4gf_translate.TranslateReponame.git_to_p4client( view_name_git) nop4client = '' if p4gf_util.spec_exists(p4, 'client', view_name_p4client): return repo_from_template_client(p4, view_name, view_name_p4client, client_name, client_root, enable_mismatched_rhs) else: nop4client = _("p4 client '{0}' does not exist\n").format( view_name_p4client) # creating repo from stream? # note that we can't pass '//depot/stream' because git would be confused # but it's ok to pass 'depot/stream' and add the leading '//' here stream_name = '//' + view_name_git if p4gf_util.spec_exists(p4, 'stream', stream_name): return _repo_from_stream(p4, view_name, stream_name, client_name, client_root, enable_mismatched_rhs, handle_imports) # We don't have, and cannot create, a config for this repo. # Say so and give up. msg = p4gf_const.NO_REPO_MSG_TEMPLATE.format( view_name=view_name, view_name_p4client=view_name_p4client, nop4client=nop4client) LOG.warn(msg) _print_stderr(msg) return INIT_REPO_NOVIEW
def _repo_config_exists(p4, repo_name): """Return true is repo p4gf_config exists.""" config_path = p4gf_config.depot_path_repo(repo_name) return p4gf_util.depot_file_exists(p4, config_path)