mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-05 16:43:55 +00:00
adding "remove" option for #47 and fix a few issues/bugs
This commit is contained in:
@@ -19,6 +19,9 @@ from dotdrop.logger import Logger
|
||||
from dotdrop.utils import strip_home
|
||||
|
||||
|
||||
TILD = '~'
|
||||
|
||||
|
||||
class CfgAggregator:
|
||||
|
||||
file_prefix = 'f'
|
||||
@@ -99,9 +102,9 @@ class CfgAggregator:
|
||||
|
||||
# patch trans_w/trans_r in dotfiles
|
||||
self._patch_keys_to_objs(self.dotfiles,
|
||||
"trans_r", self.get_trans_r)
|
||||
"trans_r", self._get_trans_r)
|
||||
self._patch_keys_to_objs(self.dotfiles,
|
||||
"trans_w", self.get_trans_w)
|
||||
"trans_w", self._get_trans_w)
|
||||
|
||||
def _patch_keys_to_objs(self, containers, keys, get_by_key):
|
||||
"""
|
||||
@@ -128,6 +131,14 @@ class CfgAggregator:
|
||||
self.log.dbg('patching {}.{} with {}'.format(c, keys, objects))
|
||||
setattr(c, keys, objects)
|
||||
|
||||
def del_dotfile(self, dotfile):
|
||||
"""remove this dotfile from the config"""
|
||||
return self.cfgyaml.del_dotfile(dotfile.key)
|
||||
|
||||
def del_dotfile_from_profile(self, dotfile, profile):
|
||||
"""remove this dotfile from this profile"""
|
||||
return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key)
|
||||
|
||||
def new(self, src, dst, link, profile_key):
|
||||
"""
|
||||
import a new dotfile
|
||||
@@ -136,10 +147,9 @@ class CfgAggregator:
|
||||
@link: LinkType
|
||||
@profile_key: to which profile
|
||||
"""
|
||||
home = os.path.expanduser('~')
|
||||
dst = dst.replace(home, '~', 1)
|
||||
dst = self.path_to_dotfile_dst(dst)
|
||||
|
||||
dotfile = self._get_dotfile_by_dst(dst)
|
||||
dotfile = self.get_dotfile_by_dst(dst)
|
||||
if not dotfile:
|
||||
# get a new dotfile with a unique key
|
||||
key = self._get_new_dotfile_key(dst)
|
||||
@@ -228,8 +238,22 @@ class CfgAggregator:
|
||||
cnt += 1
|
||||
return newkey
|
||||
|
||||
def _get_dotfile_by_dst(self, dst):
|
||||
def path_to_dotfile_dst(self, path):
|
||||
"""normalize the path to match dotfile dst"""
|
||||
path = os.path.expanduser(path)
|
||||
path = os.path.expandvars(path)
|
||||
path = os.path.abspath(path)
|
||||
home = os.path.expanduser(TILD) + os.sep
|
||||
|
||||
# normalize the path
|
||||
if path.startswith(home):
|
||||
path = path[len(home):]
|
||||
path = os.path.join(TILD, path)
|
||||
return path
|
||||
|
||||
def get_dotfile_by_dst(self, dst):
|
||||
"""get a dotfile by dst"""
|
||||
dst = self.path_to_dotfile_dst(dst)
|
||||
try:
|
||||
return next(d for d in self.dotfiles if d.dst == dst)
|
||||
except StopIteration:
|
||||
@@ -262,12 +286,24 @@ class CfgAggregator:
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def get_profiles_by_dotfile_key(self, key):
|
||||
"""return all profiles having this dotfile"""
|
||||
res = []
|
||||
for p in self.profiles:
|
||||
keys = [d.key for d in p.dotfiles]
|
||||
if key in keys:
|
||||
res.append(p)
|
||||
return res
|
||||
|
||||
def get_dotfiles(self, profile=None):
|
||||
"""return dotfiles dict for this profile key"""
|
||||
if not profile:
|
||||
return self.dotfiles
|
||||
try:
|
||||
return next(x.dotfiles for x in self.profiles if x.key == profile)
|
||||
pro = self.get_profile(profile)
|
||||
if not pro:
|
||||
return []
|
||||
return pro.dotfiles
|
||||
except StopIteration:
|
||||
return []
|
||||
|
||||
@@ -278,7 +314,7 @@ class CfgAggregator:
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def get_action(self, key):
|
||||
def _get_action(self, key):
|
||||
"""return action by key"""
|
||||
try:
|
||||
return next(x for x in self.actions if x.key == key)
|
||||
@@ -293,19 +329,19 @@ class CfgAggregator:
|
||||
key, *args = fields
|
||||
if self.debug:
|
||||
self.log.dbg('action with parm: {} and {}'.format(key, args))
|
||||
action = self.get_action(key).copy(args)
|
||||
action = self._get_action(key).copy(args)
|
||||
else:
|
||||
action = self.get_action(key)
|
||||
action = self._get_action(key)
|
||||
return action
|
||||
|
||||
def get_trans_r(self, key):
|
||||
def _get_trans_r(self, key):
|
||||
"""return the trans_r with this key"""
|
||||
try:
|
||||
return next(x for x in self.trans_r if x.key == key)
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def get_trans_w(self, key):
|
||||
def _get_trans_w(self, key):
|
||||
"""return the trans_w with this key"""
|
||||
try:
|
||||
return next(x for x in self.trans_w if x.key == key)
|
||||
|
||||
@@ -8,6 +8,7 @@ handle lower level of the config file
|
||||
import os
|
||||
import yaml
|
||||
import glob
|
||||
from copy import deepcopy
|
||||
|
||||
# local imports
|
||||
from dotdrop.settings import Settings
|
||||
@@ -97,9 +98,10 @@ class CfgYaml:
|
||||
|
||||
def _parse_main_yaml(self, dic):
|
||||
"""parse the different blocks"""
|
||||
self.ori_settings = self._get_entry(self.yaml_dict, self.key_settings)
|
||||
self.ori_settings = self._get_entry(dic, self.key_settings)
|
||||
self.settings = Settings(None).serialize().get(self.key_settings)
|
||||
self.settings.update(self.ori_settings)
|
||||
|
||||
# resolve settings paths
|
||||
p = self._resolve_path(self.settings[self.key_settings_dotpath])
|
||||
self.settings[self.key_settings_dotpath] = p
|
||||
@@ -109,50 +111,60 @@ class CfgYaml:
|
||||
self.log.dbg('settings: {}'.format(self.settings))
|
||||
|
||||
# dotfiles
|
||||
self.dotfiles = self._get_entry(self.yaml_dict, self.key_dotfiles)
|
||||
self.ori_dotfiles = self._get_entry(dic, self.key_dotfiles)
|
||||
self.dotfiles = deepcopy(self.ori_dotfiles)
|
||||
keys = self.dotfiles.keys()
|
||||
if len(keys) != len(list(set(keys))):
|
||||
dups = [x for x in keys if x not in list(set(keys))]
|
||||
raise Exception('duplicate dotfile keys found: {}'.format(dups))
|
||||
self.dotfiles = self._norm_dotfiles(self.dotfiles)
|
||||
if self.debug:
|
||||
self.log.dbg('dotfiles: {}'.format(self.dotfiles))
|
||||
|
||||
# profiles
|
||||
self.profiles = self._get_entry(self.yaml_dict, self.key_profiles)
|
||||
self.ori_profiles = self._get_entry(dic, self.key_profiles)
|
||||
self.profiles = deepcopy(self.ori_profiles)
|
||||
if self.debug:
|
||||
self.log.dbg('profiles: {}'.format(self.profiles))
|
||||
|
||||
# actions
|
||||
self.actions = self._get_entry(self.yaml_dict, self.key_actions,
|
||||
mandatory=False)
|
||||
self.ori_actions = self._get_entry(dic, self.key_actions,
|
||||
mandatory=False)
|
||||
self.actions = deepcopy(self.ori_actions)
|
||||
self.actions = self._norm_actions(self.actions)
|
||||
if self.debug:
|
||||
self.log.dbg('actions: {}'.format(self.actions))
|
||||
|
||||
# trans_r
|
||||
if self.old_key_trans_r in self.yaml_dict:
|
||||
key = self.key_trans_r
|
||||
if self.old_key_trans_r in dic:
|
||||
self.log.warn('\"trans\" is deprecated, please use \"trans_read\"')
|
||||
self.yaml_dict[self.key_trans_r] = self.yaml_dict.pop(
|
||||
self.old_key_trans_r
|
||||
)
|
||||
self.dirty = True
|
||||
self.trans_r = self._get_entry(self.yaml_dict, self.key_trans_r,
|
||||
mandatory=False)
|
||||
key = self.old_key_trans_r
|
||||
self.ori_trans_r = self._get_entry(dic, key, mandatory=False)
|
||||
self.trans_r = deepcopy(self.ori_trans_r)
|
||||
if self.debug:
|
||||
self.log.dbg('trans_r: {}'.format(self.trans_r))
|
||||
|
||||
# trans_w
|
||||
self.trans_w = self._get_entry(self.yaml_dict, self.key_trans_w,
|
||||
mandatory=False)
|
||||
self.ori_trans_w = self._get_entry(dic, self.key_trans_w,
|
||||
mandatory=False)
|
||||
self.trans_w = deepcopy(self.ori_trans_w)
|
||||
if self.debug:
|
||||
self.log.dbg('trans_w: {}'.format(self.trans_w))
|
||||
|
||||
# variables
|
||||
self.variables = self._get_entry(self.yaml_dict, self.key_variables,
|
||||
mandatory=False)
|
||||
self.ori_variables = self._get_entry(dic,
|
||||
self.key_variables,
|
||||
mandatory=False)
|
||||
self.variables = deepcopy(self.ori_variables)
|
||||
if self.debug:
|
||||
self.log.dbg('variables: {}'.format(self.variables))
|
||||
|
||||
# dynvariables
|
||||
self.dvariables = self._get_entry(self.yaml_dict, self.key_dvariables,
|
||||
mandatory=False)
|
||||
self.ori_dvariables = self._get_entry(dic,
|
||||
self.key_dvariables,
|
||||
mandatory=False)
|
||||
self.dvariables = deepcopy(self.ori_dvariables)
|
||||
if self.debug:
|
||||
self.log.dbg('dvariables: {}'.format(self.dvariables))
|
||||
|
||||
@@ -493,7 +505,7 @@ class CfgYaml:
|
||||
values[self.key_profiles_dotfiles] = uniq_list(current)
|
||||
if self.debug:
|
||||
dfs = values[self.key_profiles_dotfiles]
|
||||
self.log.dbg('profile dfs after include: {}'.format(dfs))
|
||||
self.log.dbg('{} dfs after include: {}'.format(profile, dfs))
|
||||
return values.get(self.key_profiles_dotfiles, [])
|
||||
|
||||
def _resolve_path(self, path):
|
||||
@@ -538,21 +550,21 @@ class CfgYaml:
|
||||
"""merge low into high"""
|
||||
# won't work in python3.4
|
||||
# return {**low, **high}
|
||||
new = low.copy()
|
||||
new = deepcopy(low)
|
||||
new.update(high)
|
||||
return new
|
||||
|
||||
def _get_entry(self, yaml_dict, key, mandatory=True):
|
||||
def _get_entry(self, dic, key, mandatory=True):
|
||||
"""return entry from yaml dictionary"""
|
||||
if key not in yaml_dict:
|
||||
if key not in dic:
|
||||
if mandatory:
|
||||
raise Exception('invalid config: no {} found'.format(key))
|
||||
yaml_dict[key] = {}
|
||||
return yaml_dict[key]
|
||||
if mandatory and not yaml_dict[key]:
|
||||
dic[key] = {}
|
||||
return dic[key]
|
||||
if mandatory and not dic[key]:
|
||||
# ensure is not none
|
||||
yaml_dict[key] = {}
|
||||
return yaml_dict[key]
|
||||
dic[key] = {}
|
||||
return dic[key]
|
||||
|
||||
def _load_yaml(self, path):
|
||||
"""load a yaml file to a dict"""
|
||||
@@ -609,6 +621,40 @@ class CfgYaml:
|
||||
self.yaml_dict[self.key_dotfiles][key] = df_dict
|
||||
self.dirty = True
|
||||
|
||||
def del_dotfile(self, key):
|
||||
"""remove this dotfile from config"""
|
||||
if key not in self.yaml_dict[self.key_dotfiles]:
|
||||
self.log.err('key not in dotfiles: {}'.format(key))
|
||||
return False
|
||||
if self.debug:
|
||||
self.log.dbg('remove dotfile: {}'.format(key))
|
||||
del self.yaml_dict[self.key_dotfiles][key]
|
||||
if self.debug:
|
||||
dfs = self.yaml_dict[self.key_dotfiles]
|
||||
self.log.dbg('new dotfiles: {}'.format(dfs))
|
||||
self.dirty = True
|
||||
return True
|
||||
|
||||
def del_dotfile_from_profile(self, df_key, pro_key):
|
||||
"""remove this dotfile from that profile"""
|
||||
if df_key not in self.dotfiles.keys():
|
||||
self.log.err('key not in dotfiles: {}'.format(df_key))
|
||||
return False
|
||||
if pro_key not in self.profiles.keys():
|
||||
self.log.err('key not in profiles: {}'.format(pro_key))
|
||||
return False
|
||||
profiles = self.yaml_dict[self.key_profiles][pro_key]
|
||||
if self.debug:
|
||||
dfs = profiles[self.key_profiles_dotfiles]
|
||||
self.log.dbg('{} profile dotfiles: {}'.format(pro_key, dfs))
|
||||
self.log.dbg('remove {} from profile {}'.format(df_key, pro_key))
|
||||
profiles[self.key_profiles_dotfiles].remove(df_key)
|
||||
if self.debug:
|
||||
dfs = profiles[self.key_profiles_dotfiles]
|
||||
self.log.dbg('{} profile dotfiles: {}'.format(pro_key, dfs))
|
||||
self.dirty = True
|
||||
return True
|
||||
|
||||
def _fix_deprecated(self, yamldict):
|
||||
"""fix deprecated entries"""
|
||||
self._fix_deprecated_link_by_default(yamldict)
|
||||
@@ -671,9 +717,9 @@ class CfgYaml:
|
||||
newv = v
|
||||
if isinstance(v, dict):
|
||||
newv = self._clear_none(v)
|
||||
if v is None:
|
||||
if newv is None:
|
||||
continue
|
||||
if not v:
|
||||
if not newv:
|
||||
continue
|
||||
new[k] = newv
|
||||
return new
|
||||
|
||||
@@ -268,7 +268,10 @@ def cmd_update(o):
|
||||
if o.debug:
|
||||
LOG.dbg('dotfile to update: {}'.format(paths))
|
||||
|
||||
updater = Updater(o.dotpath, o.dotfiles, o.variables,
|
||||
updater = Updater(o.dotpath, o.variables,
|
||||
o.conf.get_dotfile,
|
||||
o.conf.get_dotfile_by_dst,
|
||||
o.conf.path_to_dotfile_dst,
|
||||
dry=o.dry, safe=o.safe, debug=o.debug,
|
||||
ignore=ignore, showpatch=showpatch)
|
||||
if not iskey:
|
||||
@@ -403,6 +406,67 @@ def cmd_detail(o):
|
||||
LOG.log('')
|
||||
|
||||
|
||||
def cmd_remove(o):
|
||||
"""remove dotfile from dotpath and from config"""
|
||||
paths = o.remove_path
|
||||
iskey = o.remove_iskey
|
||||
|
||||
if not paths:
|
||||
LOG.log('no dotfile to remove')
|
||||
return False
|
||||
if o.debug:
|
||||
LOG.dbg('dotfile to remove: {}'.format(paths))
|
||||
|
||||
removed = []
|
||||
for key in paths:
|
||||
if o.debug:
|
||||
LOG.dbg('removing {}'.format(key))
|
||||
if not iskey:
|
||||
# by path
|
||||
dotfile = o.conf.get_dotfile_by_dst(key)
|
||||
if not dotfile:
|
||||
LOG.warn('{} ignored, does not exist'.format(key))
|
||||
continue
|
||||
k = dotfile.key
|
||||
else:
|
||||
# by key
|
||||
dotfile = o.conf.get_dotfile(key)
|
||||
k = key
|
||||
# make sure is part of the profile
|
||||
if dotfile.key not in [d.key for d in o.dotfiles]:
|
||||
LOG.warn('{} ignored, not associated to this profile'.format(key))
|
||||
continue
|
||||
profiles = o.conf.get_profiles_by_dotfile_key(k)
|
||||
pkeys = ','.join([p.key for p in profiles])
|
||||
if o.dry:
|
||||
LOG.dry('would remove {} from {}'.format(dotfile, pkeys))
|
||||
continue
|
||||
msg = 'Remove dotfile from all these profiles: {}'.format(pkeys)
|
||||
if o.safe and not LOG.ask(msg):
|
||||
return False
|
||||
if o.debug:
|
||||
LOG.dbg('remove dotfile: {}'.format(dotfile))
|
||||
|
||||
for profile in profiles:
|
||||
if not o.conf.del_dotfile_from_profile(dotfile, profile):
|
||||
return False
|
||||
if not o.conf.del_dotfile(dotfile):
|
||||
return False
|
||||
|
||||
# remove dotfile from dotpath
|
||||
dtpath = os.path.join(o.dotpath, dotfile.src)
|
||||
remove(dtpath)
|
||||
removed.append(dotfile.key)
|
||||
|
||||
if o.dry:
|
||||
LOG.dry('new config file would be:')
|
||||
LOG.raw(o.conf.dump())
|
||||
else:
|
||||
o.conf.save()
|
||||
LOG.log('\ndotfile(s) removed: {}'.format(','.join(removed)))
|
||||
return True
|
||||
|
||||
|
||||
###########################################################
|
||||
# helpers
|
||||
###########################################################
|
||||
@@ -444,8 +508,10 @@ def _select(selections, dotfiles):
|
||||
|
||||
|
||||
def apply_trans(dotpath, dotfile, debug=False):
|
||||
"""apply the read transformation to the dotfile
|
||||
return None if fails and new source if succeed"""
|
||||
"""
|
||||
apply the read transformation to the dotfile
|
||||
return None if fails and new source if succeed
|
||||
"""
|
||||
src = dotfile.src
|
||||
new_src = '{}.{}'.format(src, TRANS_SUFFIX)
|
||||
for trans in dotfile.trans_r:
|
||||
@@ -523,6 +589,12 @@ def main():
|
||||
LOG.dbg('running cmd: detail')
|
||||
cmd_detail(o)
|
||||
|
||||
elif o.cmd_remove:
|
||||
# remove dotfile
|
||||
if o.debug:
|
||||
LOG.dbg('running cmd: remove')
|
||||
cmd_remove(o)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
LOG.err('interrupted')
|
||||
ret = False
|
||||
|
||||
@@ -57,6 +57,7 @@ Usage:
|
||||
[-o <opts>] [-C <file>...] [-i <pattern>...]
|
||||
dotdrop update [-VbfdkP] [-c <path>] [-p <profile>]
|
||||
[-i <pattern>...] [<path>...]
|
||||
dotdrop remove [-Vbfdk] [-c <path>] [-p <profile>] [<path>...]
|
||||
dotdrop listfiles [-VbT] [-c <path>] [-p <profile>]
|
||||
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
||||
dotdrop list [-Vb] [-c <path>]
|
||||
@@ -193,6 +194,7 @@ class Options(AttrMonitor):
|
||||
self.cmd_import = self.args['import']
|
||||
self.cmd_update = self.args['update']
|
||||
self.cmd_detail = self.args['detail']
|
||||
self.cmd_remove = self.args['remove']
|
||||
|
||||
# adapt attributes based on arguments
|
||||
self.dry = self.args['--dry']
|
||||
@@ -236,6 +238,9 @@ class Options(AttrMonitor):
|
||||
self.update_showpatch = self.args['--show-patch']
|
||||
# "detail" specifics
|
||||
self.detail_keys = self.args['<key>']
|
||||
# "remove" specifics
|
||||
self.remove_path = self.args['<path>']
|
||||
self.remove_iskey = self.args['--key']
|
||||
|
||||
def _fill_attr(self):
|
||||
"""create attributes from conf"""
|
||||
|
||||
@@ -20,12 +20,17 @@ TILD = '~'
|
||||
|
||||
class Updater:
|
||||
|
||||
def __init__(self, dotpath, dotfiles, variables, dry=False, safe=True,
|
||||
def __init__(self, dotpath, variables,
|
||||
dotfile_key_getter, dotfile_dst_getter,
|
||||
dotfile_path_normalizer,
|
||||
dry=False, safe=True,
|
||||
debug=False, ignore=[], showpatch=False):
|
||||
"""constructor
|
||||
@dotpath: path where dotfiles are stored
|
||||
@dotfiles: dotfiles for this profile
|
||||
@variables: dictionary of variables for the templates
|
||||
@dotfile_key_getter: func to get a dotfile by key
|
||||
@dotfile_dst_getter: func to get a dotfile by dst
|
||||
@dotfile_path_normalizer: func to normalize dotfile dst
|
||||
@dry: simulate
|
||||
@safe: ask for overwrite if True
|
||||
@debug: enable debug
|
||||
@@ -33,8 +38,10 @@ class Updater:
|
||||
@showpatch: show patch if dotfile to update is a template
|
||||
"""
|
||||
self.dotpath = dotpath
|
||||
self.dotfiles = dotfiles
|
||||
self.variables = variables
|
||||
self.dotfile_key_getter = dotfile_key_getter
|
||||
self.dotfile_dst_getter = dotfile_dst_getter
|
||||
self.dotfile_path_normalizer = dotfile_path_normalizer
|
||||
self.dry = dry
|
||||
self.safe = safe
|
||||
self.debug = debug
|
||||
@@ -48,8 +55,7 @@ class Updater:
|
||||
if not os.path.lexists(path):
|
||||
self.log.err('\"{}\" does not exist!'.format(path))
|
||||
return False
|
||||
path = self._normalize(path)
|
||||
dotfile = self._get_dotfile_by_path(path)
|
||||
dotfile = self.dotfile_dst_getter(path)
|
||||
if not dotfile:
|
||||
return False
|
||||
if self.debug:
|
||||
@@ -58,12 +64,12 @@ class Updater:
|
||||
|
||||
def update_key(self, key):
|
||||
"""update the dotfile referenced by key"""
|
||||
dotfile = self._get_dotfile_by_key(key)
|
||||
dotfile = self.dotfile_key_getter(key)
|
||||
if not dotfile:
|
||||
return False
|
||||
if self.debug:
|
||||
self.log.dbg('updating {} from key \"{}\"'.format(dotfile, key))
|
||||
path = self._normalize(dotfile.dst)
|
||||
path = self.dotfile_path_normalizer(dotfile.dst)
|
||||
return self._update(path, dotfile)
|
||||
|
||||
def _update(self, path, dotfile):
|
||||
@@ -111,45 +117,6 @@ class Updater:
|
||||
return None
|
||||
return tmp
|
||||
|
||||
def _normalize(self, path):
|
||||
"""normalize the path to match dotfile"""
|
||||
path = os.path.expanduser(path)
|
||||
path = os.path.expandvars(path)
|
||||
path = os.path.abspath(path)
|
||||
home = os.path.expanduser(TILD) + os.sep
|
||||
|
||||
# normalize the path
|
||||
if path.startswith(home):
|
||||
path = path[len(home):]
|
||||
path = os.path.join(TILD, path)
|
||||
return path
|
||||
|
||||
def _get_dotfile_by_key(self, key):
|
||||
"""get the dotfile matching this key"""
|
||||
dotfiles = self.dotfiles
|
||||
subs = [d for d in dotfiles if d.key == key]
|
||||
if not subs:
|
||||
self.log.err('key \"{}\" not found!'.format(key))
|
||||
return None
|
||||
if len(subs) > 1:
|
||||
found = ','.join([d.src for d in dotfiles])
|
||||
self.log.err('multiple dotfiles found: {}'.format(found))
|
||||
return None
|
||||
return subs[0]
|
||||
|
||||
def _get_dotfile_by_path(self, path):
|
||||
"""get the dotfile matching this path"""
|
||||
dotfiles = self.dotfiles
|
||||
subs = [d for d in dotfiles if d.dst == path]
|
||||
if not subs:
|
||||
self.log.err('\"{}\" is not managed!'.format(path))
|
||||
return None
|
||||
if len(subs) > 1:
|
||||
found = ','.join([d.src for d in dotfiles])
|
||||
self.log.err('multiple dotfiles found: {}'.format(found))
|
||||
return None
|
||||
return subs[0]
|
||||
|
||||
def _is_template(self, path):
|
||||
if not Templategen.is_template(path):
|
||||
if self.debug:
|
||||
|
||||
Reference in New Issue
Block a user