mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-12 20:25:17 +00:00
linting
This commit is contained in:
@@ -3,6 +3,7 @@ author: deadc0de6 (https://github.com/deadc0de6)
|
|||||||
Copyright (c) 2017, deadc0de6
|
Copyright (c) 2017, deadc0de6
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# pylint: disable=C0415
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from dotdrop.exceptions import YamlException, UndefinedException
|
|||||||
|
|
||||||
|
|
||||||
class CfgYaml:
|
class CfgYaml:
|
||||||
|
"""yaml config file parser"""
|
||||||
|
|
||||||
# global entries
|
# global entries
|
||||||
key_settings = Settings.key_yaml
|
key_settings = Settings.key_yaml
|
||||||
@@ -437,7 +438,6 @@ class CfgYaml:
|
|||||||
self._log.warn(warn)
|
self._log.warn(warn)
|
||||||
|
|
||||||
self._dirty = False
|
self._dirty = False
|
||||||
self.cfg_updated = False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
@@ -464,20 +464,20 @@ class CfgYaml:
|
|||||||
self._check_minversion(minversion)
|
self._check_minversion(minversion)
|
||||||
|
|
||||||
# normalize paths
|
# normalize paths
|
||||||
p = self._norm_path(settings[self.key_settings_dotpath])
|
paths = self._norm_path(settings[self.key_settings_dotpath])
|
||||||
settings[self.key_settings_dotpath] = p
|
settings[self.key_settings_dotpath] = paths
|
||||||
p = self._norm_path(settings[self.key_settings_workdir])
|
paths = self._norm_path(settings[self.key_settings_workdir])
|
||||||
settings[self.key_settings_workdir] = p
|
settings[self.key_settings_workdir] = paths
|
||||||
p = [
|
paths = [
|
||||||
self._norm_path(p)
|
self._norm_path(path)
|
||||||
for p in settings[Settings.key_filter_file]
|
for path in settings[Settings.key_filter_file]
|
||||||
]
|
]
|
||||||
settings[Settings.key_filter_file] = p
|
settings[Settings.key_filter_file] = paths
|
||||||
p = [
|
paths = [
|
||||||
self._norm_path(p)
|
self._norm_path(path)
|
||||||
for p in settings[Settings.key_func_file]
|
for path in settings[Settings.key_func_file]
|
||||||
]
|
]
|
||||||
settings[Settings.key_func_file] = p
|
settings[Settings.key_func_file] = paths
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('settings block:', settings)
|
self._debug_dict('settings block:', settings)
|
||||||
return settings
|
return settings
|
||||||
@@ -1166,11 +1166,11 @@ class CfgYaml:
|
|||||||
new = []
|
new = []
|
||||||
if not entries:
|
if not entries:
|
||||||
return new
|
return new
|
||||||
for e in entries:
|
for entry in entries:
|
||||||
et = self._template_item(e)
|
newe = self._template_item(entry)
|
||||||
if self._debug and e != et:
|
if self._debug and entry != newe:
|
||||||
self._dbg('resolved: {} -> {}'.format(e, et))
|
self._dbg('resolved: {} -> {}'.format(entry, newe))
|
||||||
new.append(et)
|
new.append(newe)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _template_dict(self, entries):
|
def _template_dict(self, entries):
|
||||||
@@ -1178,11 +1178,11 @@ class CfgYaml:
|
|||||||
new = {}
|
new = {}
|
||||||
if not entries:
|
if not entries:
|
||||||
return new
|
return new
|
||||||
for k, v in entries.items():
|
for k, val in entries.items():
|
||||||
vt = self._template_item(v)
|
newv = self._template_item(val)
|
||||||
if self._debug and v != vt:
|
if self._debug and val != newv:
|
||||||
self._dbg('resolved: {} -> {}'.format(v, vt))
|
self._dbg('resolved: {} -> {}'.format(val, newv))
|
||||||
new[k] = vt
|
new[k] = newv
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _template_dotfiles_entries(self):
|
def _template_dotfiles_entries(self):
|
||||||
@@ -1226,9 +1226,9 @@ class CfgYaml:
|
|||||||
if self.key_all not in pdfs:
|
if self.key_all not in pdfs:
|
||||||
# take a subset of the dotfiles
|
# take a subset of the dotfiles
|
||||||
newdotfiles = {}
|
newdotfiles = {}
|
||||||
for k, v in dotfiles.items():
|
for k, val in dotfiles.items():
|
||||||
if k in pdfs:
|
if k in pdfs:
|
||||||
newdotfiles[k] = v
|
newdotfiles[k] = val
|
||||||
dotfiles = newdotfiles
|
dotfiles = newdotfiles
|
||||||
|
|
||||||
for dotfile in dotfiles.values():
|
for dotfile in dotfiles.values():
|
||||||
@@ -1246,26 +1246,29 @@ class CfgYaml:
|
|||||||
var = self._enrich_vars(variables, self._profile)
|
var = self._enrich_vars(variables, self._profile)
|
||||||
# use a separated templategen to handle variables
|
# use a separated templategen to handle variables
|
||||||
# resolved outside the main config
|
# resolved outside the main config
|
||||||
t = Templategen(variables=var,
|
func_files = self.settings[Settings.key_func_file]
|
||||||
func_file=self.settings[Settings.key_func_file],
|
filter_files = self.settings[Settings.key_filter_file]
|
||||||
filter_file=self.settings[Settings.key_filter_file])
|
templ = Templategen(variables=var,
|
||||||
|
func_file=func_files,
|
||||||
|
filter_file=filter_files)
|
||||||
for k in variables.keys():
|
for k in variables.keys():
|
||||||
val = variables[k]
|
val = variables[k]
|
||||||
while Templategen.var_is_template(val):
|
while Templategen.var_is_template(val):
|
||||||
val = t.generate_string(val)
|
val = templ.generate_string(val)
|
||||||
variables[k] = val
|
variables[k] = val
|
||||||
t.update_variables(variables)
|
templ.update_variables(variables)
|
||||||
if variables is self.variables:
|
if variables is self.variables:
|
||||||
self._redefine_templater()
|
self._redefine_templater()
|
||||||
|
|
||||||
def _get_profile_included_vars(self):
|
def _get_profile_included_vars(self):
|
||||||
"""resolve profile included variables/dynvariables"""
|
"""resolve profile included variables/dynvariables"""
|
||||||
for k, v in self.profiles.items():
|
for _, val in self.profiles.items():
|
||||||
if self.key_profile_include in v and v[self.key_profile_include]:
|
if self.key_profile_include in val and \
|
||||||
|
val[self.key_profile_include]:
|
||||||
new = []
|
new = []
|
||||||
for x in v[self.key_profile_include]:
|
for entry in val[self.key_profile_include]:
|
||||||
new.append(self._tmpl.generate_string(x))
|
new.append(self._tmpl.generate_string(entry))
|
||||||
v[self.key_profile_include] = new
|
val[self.key_profile_include] = new
|
||||||
|
|
||||||
# now get the included ones
|
# now get the included ones
|
||||||
pro_var = self._get_profile_included_item(self.key_profile_variables)
|
pro_var = self._get_profile_included_item(self.key_profile_variables)
|
||||||
@@ -1291,7 +1294,8 @@ class CfgYaml:
|
|||||||
"""
|
"""
|
||||||
if not dic:
|
if not dic:
|
||||||
return
|
return
|
||||||
[dic.pop(k, None) for k in self._profilevarskeys]
|
for k in self._profilevarskeys:
|
||||||
|
dic.pop(k, None)
|
||||||
|
|
||||||
def _parse_extended_import_path(self, path_entry):
|
def _parse_extended_import_path(self, path_entry):
|
||||||
"""Parse an import path in a tuple (path, fatal_not_found)."""
|
"""Parse an import path in a tuple (path, fatal_not_found)."""
|
||||||
@@ -1369,7 +1373,8 @@ class CfgYaml:
|
|||||||
processed_paths = (self._process_path(p) for p in paths)
|
processed_paths = (self._process_path(p) for p in paths)
|
||||||
return list(chain.from_iterable(processed_paths))
|
return list(chain.from_iterable(processed_paths))
|
||||||
|
|
||||||
def _merge_dict(self, high, low):
|
@classmethod
|
||||||
|
def _merge_dict(cls, high, low):
|
||||||
"""merge high and low dict"""
|
"""merge high and low dict"""
|
||||||
if not high:
|
if not high:
|
||||||
high = {}
|
high = {}
|
||||||
@@ -1377,7 +1382,8 @@ class CfgYaml:
|
|||||||
low = {}
|
low = {}
|
||||||
return {**low, **high}
|
return {**low, **high}
|
||||||
|
|
||||||
def _get_entry(self, dic, key, mandatory=True):
|
@classmethod
|
||||||
|
def _get_entry(cls, dic, key, mandatory=True):
|
||||||
"""return copy of entry from yaml dictionary"""
|
"""return copy of entry from yaml dictionary"""
|
||||||
if key not in dic:
|
if key not in dic:
|
||||||
if mandatory:
|
if mandatory:
|
||||||
@@ -1393,19 +1399,19 @@ class CfgYaml:
|
|||||||
def _clear_none(self, dic):
|
def _clear_none(self, dic):
|
||||||
"""recursively delete all none/empty values in a dictionary."""
|
"""recursively delete all none/empty values in a dictionary."""
|
||||||
new = {}
|
new = {}
|
||||||
for k, v in dic.items():
|
for k, val in dic.items():
|
||||||
if k == self.key_dotfile_src:
|
if k == self.key_dotfile_src:
|
||||||
# allow empty dotfile src
|
# allow empty dotfile src
|
||||||
new[k] = v
|
new[k] = val
|
||||||
continue
|
continue
|
||||||
if k == self.key_dotfile_dst:
|
if k == self.key_dotfile_dst:
|
||||||
# allow empty dotfile dst
|
# allow empty dotfile dst
|
||||||
new[k] = v
|
new[k] = val
|
||||||
continue
|
continue
|
||||||
newv = v
|
newv = val
|
||||||
if isinstance(v, dict):
|
if isinstance(val, dict):
|
||||||
# recursive travers dict
|
# recursive travers dict
|
||||||
newv = self._clear_none(v)
|
newv = self._clear_none(val)
|
||||||
if not newv:
|
if not newv:
|
||||||
# no empty dict
|
# no empty dict
|
||||||
continue
|
continue
|
||||||
@@ -1418,7 +1424,8 @@ class CfgYaml:
|
|||||||
new[k] = newv
|
new[k] = newv
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def _is_glob(self, path):
|
@classmethod
|
||||||
|
def _is_glob(cls, path):
|
||||||
"""Quick test if path is a glob."""
|
"""Quick test if path is a glob."""
|
||||||
return '*' in path or '?' in path
|
return '*' in path or '?' in path
|
||||||
|
|
||||||
@@ -1435,8 +1442,8 @@ class CfgYaml:
|
|||||||
return path
|
return path
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
if not os.path.isabs(path):
|
if not os.path.isabs(path):
|
||||||
d = os.path.dirname(self._path)
|
dirn = os.path.dirname(self._path)
|
||||||
ret = os.path.join(d, path)
|
ret = os.path.join(dirn, path)
|
||||||
if self._debug:
|
if self._debug:
|
||||||
msg = 'normalizing relative to cfg: {} -> {}'
|
msg = 'normalizing relative to cfg: {} -> {}'
|
||||||
self._dbg(msg.format(path, ret))
|
self._dbg(msg.format(path, ret))
|
||||||
@@ -1446,30 +1453,31 @@ class CfgYaml:
|
|||||||
self._dbg('normalizing: {} -> {}'.format(path, ret))
|
self._dbg('normalizing: {} -> {}'.format(path, ret))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _shell_exec_dvars(self, dic, keys=[]):
|
def _shell_exec_dvars(self, dic, keys=None):
|
||||||
"""shell execute dynvariables in-place"""
|
"""shell execute dynvariables in-place"""
|
||||||
if not keys:
|
if not keys:
|
||||||
keys = dic.keys()
|
keys = dic.keys()
|
||||||
for k in keys:
|
for k in keys:
|
||||||
v = dic[k]
|
val = dic[k]
|
||||||
ret, out = shell(v, debug=self._debug)
|
ret, out = shell(val, debug=self._debug)
|
||||||
if not ret:
|
if not ret:
|
||||||
err = 'var \"{}: {}\" failed: {}'.format(k, v, out)
|
err = 'var \"{}: {}\" failed: {}'.format(k, val, out)
|
||||||
self._log.err(err)
|
self._log.err(err)
|
||||||
raise YamlException(err)
|
raise YamlException(err)
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._dbg('{}: `{}` -> {}'.format(k, v, out))
|
self._dbg('{}: `{}` -> {}'.format(k, val, out))
|
||||||
dic[k] = out
|
dic[k] = out
|
||||||
|
|
||||||
def _check_minversion(self, minversion):
|
@classmethod
|
||||||
|
def _check_minversion(cls, minversion):
|
||||||
if not minversion:
|
if not minversion:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
cur = tuple([int(x) for x in VERSION.split('.')])
|
cur = ([int(x) for x in VERSION.split('.')])
|
||||||
cfg = tuple([int(x) for x in minversion.split('.')])
|
cfg = ([int(x) for x in minversion.split('.')])
|
||||||
except Exception:
|
except Exception as exc:
|
||||||
err = 'bad version: \"{}\" VS \"{}\"'.format(VERSION, minversion)
|
err = 'bad version: \"{}\" VS \"{}\"'.format(VERSION, minversion)
|
||||||
raise YamlException(err)
|
raise YamlException(err) from exc
|
||||||
if cur < cfg:
|
if cur < cfg:
|
||||||
err = 'current dotdrop version is too old for that config file.'
|
err = 'current dotdrop version is too old for that config file.'
|
||||||
err += ' Please update.'
|
err += ' Please update.'
|
||||||
@@ -1495,8 +1503,8 @@ class CfgYaml:
|
|||||||
self._dbg('{}:'.format(title))
|
self._dbg('{}:'.format(title))
|
||||||
if not elems:
|
if not elems:
|
||||||
return
|
return
|
||||||
for k, v in elems.items():
|
for k, val in elems.items():
|
||||||
self._dbg('\t- \"{}\": {}'.format(k, v))
|
self._dbg('\t- \"{}\": {}'.format(k, val))
|
||||||
|
|
||||||
def _dbg(self, content):
|
def _dbg(self, content):
|
||||||
pre = os.path.basename(self._path)
|
pre = os.path.basename(self._path)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ from dotdrop.utils import get_tmpdir, removepath, \
|
|||||||
uniq_list, patch_ignores, dependencies_met, \
|
uniq_list, patch_ignores, dependencies_met, \
|
||||||
adapt_workers
|
adapt_workers
|
||||||
from dotdrop.linktypes import LinkTypes
|
from dotdrop.linktypes import LinkTypes
|
||||||
from dotdrop.exceptions import YamlException, UndefinedException
|
from dotdrop.exceptions import YamlException, \
|
||||||
|
UndefinedException, UnmetDependency
|
||||||
|
|
||||||
LOG = Logger()
|
LOG = Logger()
|
||||||
TRANS_SUFFIX = 'trans'
|
TRANS_SUFFIX = 'trans'
|
||||||
@@ -32,7 +33,7 @@ TRANS_SUFFIX = 'trans'
|
|||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
|
|
||||||
def action_executor(o, actions, defactions, templater, post=False):
|
def action_executor(opts, actions, defactions, templater, post=False):
|
||||||
"""closure for action execution"""
|
"""closure for action execution"""
|
||||||
def execute():
|
def execute():
|
||||||
"""
|
"""
|
||||||
@@ -40,70 +41,71 @@ def action_executor(o, actions, defactions, templater, post=False):
|
|||||||
True, None if ok
|
True, None if ok
|
||||||
False, errstring if issue
|
False, errstring if issue
|
||||||
"""
|
"""
|
||||||
s = 'pre' if not post else 'post'
|
actiontype = 'pre' if not post else 'post'
|
||||||
|
|
||||||
# execute default actions
|
# execute default actions
|
||||||
for action in defactions:
|
for action in defactions:
|
||||||
if o.dry:
|
if opts.dry:
|
||||||
LOG.dry('would execute def-{}-action: {}'.format(s,
|
LOG.dry('would execute def-{}-action: {}'.format(actiontype,
|
||||||
action))
|
action))
|
||||||
continue
|
continue
|
||||||
LOG.dbg('executing def-{}-action: {}'.format(s, action))
|
LOG.dbg('executing def-{}-action: {}'.format(actiontype, action))
|
||||||
ret = action.execute(templater=templater, debug=o.debug)
|
ret = action.execute(templater=templater, debug=opts.debug)
|
||||||
if not ret:
|
if not ret:
|
||||||
err = 'def-{}-action \"{}\" failed'.format(s, action.key)
|
err = 'def-{}-action \"{}\" failed'
|
||||||
LOG.err(err)
|
LOG.err(err.format(actiontype, action.key))
|
||||||
return False, err
|
return False, err
|
||||||
|
|
||||||
# execute actions
|
# execute actions
|
||||||
for action in actions:
|
for action in actions:
|
||||||
if o.dry:
|
if opts.dry:
|
||||||
LOG.dry('would execute {}-action: {}'.format(s, action))
|
err = 'would execute {}-action: {}'
|
||||||
|
LOG.dry(err.format(actiontype, action))
|
||||||
continue
|
continue
|
||||||
LOG.dbg('executing {}-action: {}'.format(s, action))
|
LOG.dbg('executing {}-action: {}'.format(actiontype, action))
|
||||||
ret = action.execute(templater=templater, debug=o.debug)
|
ret = action.execute(templater=templater, debug=opts.debug)
|
||||||
if not ret:
|
if not ret:
|
||||||
err = '{}-action \"{}\" failed'.format(s, action.key)
|
err = '{}-action \"{}\" failed'.format(actiontype, action.key)
|
||||||
LOG.err(err)
|
LOG.err(err)
|
||||||
return False, err
|
return False, err
|
||||||
return True, None
|
return True, None
|
||||||
return execute
|
return execute
|
||||||
|
|
||||||
|
|
||||||
def _dotfile_update(o, path, key=False):
|
def _dotfile_update(opts, path, key=False):
|
||||||
"""
|
"""
|
||||||
update a dotfile pointed by path
|
update a dotfile pointed by path
|
||||||
if key is false or by key (in path)
|
if key is false or by key (in path)
|
||||||
"""
|
"""
|
||||||
updater = Updater(o.dotpath, o.variables, o.conf,
|
updater = Updater(opts.dotpath, opts.variables, opts.conf,
|
||||||
dry=o.dry, safe=o.safe, debug=o.debug,
|
dry=opts.dry, safe=opts.safe, debug=opts.debug,
|
||||||
ignore=o.update_ignore,
|
ignore=opts.update_ignore,
|
||||||
showpatch=o.update_showpatch,
|
showpatch=opts.update_showpatch,
|
||||||
ignore_missing_in_dotdrop=o.ignore_missing_in_dotdrop)
|
ignore_missing_in_dotdrop=opts.ignore_missing_in_dotdrop)
|
||||||
if key:
|
if key:
|
||||||
return updater.update_key(path)
|
return updater.update_key(path)
|
||||||
return updater.update_path(path)
|
return updater.update_path(path)
|
||||||
|
|
||||||
|
|
||||||
def _dotfile_compare(o, dotfile, tmp):
|
def _dotfile_compare(opts, dotfile, tmp):
|
||||||
"""
|
"""
|
||||||
compare a dotfile
|
compare a dotfile
|
||||||
returns True if same
|
returns True if same
|
||||||
"""
|
"""
|
||||||
t = _get_templater(o)
|
templ = _get_templater(opts)
|
||||||
ignore_missing_in_dotdrop = o.ignore_missing_in_dotdrop or \
|
ignore_missing_in_dotdrop = opts.ignore_missing_in_dotdrop or \
|
||||||
dotfile.ignore_missing_in_dotdrop
|
dotfile.ignore_missing_in_dotdrop
|
||||||
inst = Installer(create=o.create, backup=o.backup,
|
inst = Installer(create=opts.create, backup=opts.backup,
|
||||||
dry=o.dry, base=o.dotpath,
|
dry=opts.dry, base=opts.dotpath,
|
||||||
workdir=o.workdir, debug=o.debug,
|
workdir=opts.workdir, debug=opts.debug,
|
||||||
backup_suffix=o.install_backup_suffix,
|
backup_suffix=opts.install_backup_suffix,
|
||||||
diff_cmd=o.diff_command)
|
diff_cmd=opts.diff_command)
|
||||||
comp = Comparator(diff_cmd=o.diff_command, debug=o.debug,
|
comp = Comparator(diff_cmd=opts.diff_command, debug=opts.debug,
|
||||||
ignore_missing_in_dotdrop=ignore_missing_in_dotdrop)
|
ignore_missing_in_dotdrop=ignore_missing_in_dotdrop)
|
||||||
|
|
||||||
# add dotfile variables
|
# add dotfile variables
|
||||||
newvars = dotfile.get_dotfile_variables()
|
newvars = dotfile.get_dotfile_variables()
|
||||||
t.add_tmp_vars(newvars=newvars)
|
templ.add_tmp_vars(newvars=newvars)
|
||||||
|
|
||||||
# dotfiles does not exist / not installed
|
# dotfiles does not exist / not installed
|
||||||
LOG.dbg('comparing {}'.format(dotfile))
|
LOG.dbg('comparing {}'.format(dotfile))
|
||||||
@@ -118,14 +120,14 @@ def _dotfile_compare(o, dotfile, tmp):
|
|||||||
tmpsrc = None
|
tmpsrc = None
|
||||||
if dotfile.trans_r:
|
if dotfile.trans_r:
|
||||||
LOG.dbg('applying transformation before comparing')
|
LOG.dbg('applying transformation before comparing')
|
||||||
tmpsrc = apply_trans(o.dotpath, dotfile, t, debug=o.debug)
|
tmpsrc = apply_trans(opts.dotpath, dotfile, templ, debug=opts.debug)
|
||||||
if not tmpsrc:
|
if not tmpsrc:
|
||||||
# could not apply trans
|
# could not apply trans
|
||||||
return False
|
return False
|
||||||
src = tmpsrc
|
src = tmpsrc
|
||||||
|
|
||||||
# is a symlink pointing to itself
|
# is a symlink pointing to itself
|
||||||
asrc = os.path.join(o.dotpath, os.path.expanduser(src))
|
asrc = os.path.join(opts.dotpath, os.path.expanduser(src))
|
||||||
adst = os.path.expanduser(dotfile.dst)
|
adst = os.path.expanduser(dotfile.dst)
|
||||||
if os.path.samefile(asrc, adst):
|
if os.path.samefile(asrc, adst):
|
||||||
line = '=> compare {}: diffing with \"{}\"'
|
line = '=> compare {}: diffing with \"{}\"'
|
||||||
@@ -133,13 +135,13 @@ def _dotfile_compare(o, dotfile, tmp):
|
|||||||
LOG.dbg('points to itself')
|
LOG.dbg('points to itself')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
ignores = list(set(o.compare_ignore + dotfile.cmpignore))
|
ignores = list(set(opts.compare_ignore + dotfile.cmpignore))
|
||||||
ignores = patch_ignores(ignores, dotfile.dst, debug=o.debug)
|
ignores = patch_ignores(ignores, dotfile.dst, debug=opts.debug)
|
||||||
|
|
||||||
insttmp = None
|
insttmp = None
|
||||||
if dotfile.template and Templategen.is_template(src, ignore=ignores):
|
if dotfile.template and Templategen.is_template(src, ignore=ignores):
|
||||||
# install dotfile to temporary dir for compare
|
# install dotfile to temporary dir for compare
|
||||||
ret, err, insttmp = inst.install_to_temp(t, tmp, src, dotfile.dst,
|
ret, err, insttmp = inst.install_to_temp(templ, tmp, src, dotfile.dst,
|
||||||
is_template=True,
|
is_template=True,
|
||||||
chmod=dotfile.chmod)
|
chmod=dotfile.chmod)
|
||||||
if not ret:
|
if not ret:
|
||||||
@@ -151,28 +153,29 @@ def _dotfile_compare(o, dotfile, tmp):
|
|||||||
src = insttmp
|
src = insttmp
|
||||||
|
|
||||||
# compare
|
# compare
|
||||||
|
# need to be executed before cleaning
|
||||||
diff = comp.compare(src, dotfile.dst, ignore=ignores)
|
diff = comp.compare(src, dotfile.dst, ignore=ignores)
|
||||||
|
|
||||||
# clean tmp transformed dotfile if any
|
# clean tmp transformed dotfile if any
|
||||||
if tmpsrc:
|
if tmpsrc:
|
||||||
tmpsrc = os.path.join(o.dotpath, tmpsrc)
|
tmpsrc = os.path.join(opts.dotpath, tmpsrc)
|
||||||
if os.path.exists(tmpsrc):
|
if os.path.exists(tmpsrc):
|
||||||
removepath(tmpsrc, LOG)
|
removepath(tmpsrc, LOG)
|
||||||
|
|
||||||
# clean tmp template dotfile if any
|
# clean tmp template dotfile if any
|
||||||
if insttmp:
|
if insttmp and os.path.exists(insttmp):
|
||||||
if os.path.exists(insttmp):
|
|
||||||
removepath(insttmp, LOG)
|
removepath(insttmp, LOG)
|
||||||
|
|
||||||
if diff != '':
|
if diff != '':
|
||||||
# print diff results
|
# print diff results
|
||||||
line = '=> compare {}: diffing with \"{}\"'
|
line = '=> compare {}: diffing with \"{}\"'
|
||||||
LOG.log(line.format(dotfile.key, dotfile.dst))
|
LOG.log(line.format(dotfile.key, dotfile.dst))
|
||||||
if o.compare_fileonly:
|
if opts.compare_fileonly:
|
||||||
LOG.raw('<files are different>')
|
LOG.raw('<files are different>')
|
||||||
else:
|
else:
|
||||||
LOG.emph(diff)
|
LOG.emph(diff)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# no difference
|
# no difference
|
||||||
line = '=> compare {}: diffing with \"{}\"'
|
line = '=> compare {}: diffing with \"{}\"'
|
||||||
LOG.dbg(line.format(dotfile.key, dotfile.dst))
|
LOG.dbg(line.format(dotfile.key, dotfile.dst))
|
||||||
@@ -180,33 +183,33 @@ def _dotfile_compare(o, dotfile, tmp):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _dotfile_install(o, dotfile, tmpdir=None):
|
def _dotfile_install(opts, dotfile, tmpdir=None):
|
||||||
"""
|
"""
|
||||||
install a dotfile
|
install a dotfile
|
||||||
returns <success, dotfile key, err>
|
returns <success, dotfile key, err>
|
||||||
"""
|
"""
|
||||||
# installer
|
# installer
|
||||||
inst = _get_install_installer(o, tmpdir=tmpdir)
|
inst = _get_install_installer(opts, tmpdir=tmpdir)
|
||||||
|
|
||||||
# templater
|
# templater
|
||||||
t = _get_templater(o)
|
templ = _get_templater(opts)
|
||||||
|
|
||||||
# add dotfile variables
|
# add dotfile variables
|
||||||
newvars = dotfile.get_dotfile_variables()
|
newvars = dotfile.get_dotfile_variables()
|
||||||
t.add_tmp_vars(newvars=newvars)
|
templ.add_tmp_vars(newvars=newvars)
|
||||||
|
|
||||||
preactions = []
|
preactions = []
|
||||||
if not o.install_temporary:
|
if not opts.install_temporary:
|
||||||
preactions.extend(dotfile.get_pre_actions())
|
preactions.extend(dotfile.get_pre_actions())
|
||||||
defactions = o.install_default_actions_pre
|
defactions = opts.install_default_actions_pre
|
||||||
pre_actions_exec = action_executor(o, preactions, defactions,
|
pre_actions_exec = action_executor(opts, preactions, defactions,
|
||||||
t, post=False)
|
templ, post=False)
|
||||||
|
|
||||||
LOG.dbg('installing dotfile: \"{}\"'.format(dotfile.key))
|
LOG.dbg('installing dotfile: \"{}\"'.format(dotfile.key))
|
||||||
LOG.dbg(dotfile.prt())
|
LOG.dbg(dotfile.prt())
|
||||||
|
|
||||||
ignores = list(set(o.install_ignore + dotfile.instignore))
|
ignores = list(set(opts.install_ignore + dotfile.instignore))
|
||||||
ignores = patch_ignores(ignores, dotfile.dst, debug=o.debug)
|
ignores = patch_ignores(ignores, dotfile.dst, debug=opts.debug)
|
||||||
|
|
||||||
is_template = dotfile.template and Templategen.is_template(
|
is_template = dotfile.template and Templategen.is_template(
|
||||||
dotfile.src,
|
dotfile.src,
|
||||||
@@ -214,29 +217,29 @@ def _dotfile_install(o, dotfile, tmpdir=None):
|
|||||||
)
|
)
|
||||||
if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.LINK:
|
if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.LINK:
|
||||||
# link
|
# link
|
||||||
r, err = inst.install(t, dotfile.src, dotfile.dst,
|
ret, err = inst.install(templ, dotfile.src, dotfile.dst,
|
||||||
dotfile.link,
|
dotfile.link,
|
||||||
actionexec=pre_actions_exec,
|
actionexec=pre_actions_exec,
|
||||||
is_template=is_template,
|
is_template=is_template,
|
||||||
ignore=ignores,
|
ignore=ignores,
|
||||||
chmod=dotfile.chmod,
|
chmod=dotfile.chmod,
|
||||||
force_chmod=o.install_force_chmod)
|
force_chmod=opts.install_force_chmod)
|
||||||
elif hasattr(dotfile, 'link') and \
|
elif hasattr(dotfile, 'link') and \
|
||||||
dotfile.link == LinkTypes.LINK_CHILDREN:
|
dotfile.link == LinkTypes.LINK_CHILDREN:
|
||||||
# link_children
|
# link_children
|
||||||
r, err = inst.install(t, dotfile.src, dotfile.dst,
|
ret, err = inst.install(templ, dotfile.src, dotfile.dst,
|
||||||
dotfile.link,
|
dotfile.link,
|
||||||
actionexec=pre_actions_exec,
|
actionexec=pre_actions_exec,
|
||||||
is_template=is_template,
|
is_template=is_template,
|
||||||
chmod=dotfile.chmod,
|
chmod=dotfile.chmod,
|
||||||
ignore=ignores,
|
ignore=ignores,
|
||||||
force_chmod=o.install_force_chmod)
|
force_chmod=opts.install_force_chmod)
|
||||||
else:
|
else:
|
||||||
# nolink
|
# nolink
|
||||||
src = dotfile.src
|
src = dotfile.src
|
||||||
tmp = None
|
tmp = None
|
||||||
if dotfile.trans_r:
|
if dotfile.trans_r:
|
||||||
tmp = apply_trans(o.dotpath, dotfile, t, debug=o.debug)
|
tmp = apply_trans(opts.dotpath, dotfile, templ, debug=opts.debug)
|
||||||
if not tmp:
|
if not tmp:
|
||||||
return False, dotfile.key, None
|
return False, dotfile.key, None
|
||||||
src = tmp
|
src = tmp
|
||||||
@@ -245,92 +248,92 @@ def _dotfile_install(o, dotfile, tmpdir=None):
|
|||||||
src,
|
src,
|
||||||
ignore=ignores,
|
ignore=ignores,
|
||||||
)
|
)
|
||||||
r, err = inst.install(t, src, dotfile.dst,
|
ret, err = inst.install(templ, src, dotfile.dst,
|
||||||
LinkTypes.NOLINK,
|
LinkTypes.NOLINK,
|
||||||
actionexec=pre_actions_exec,
|
actionexec=pre_actions_exec,
|
||||||
noempty=dotfile.noempty,
|
noempty=dotfile.noempty,
|
||||||
ignore=ignores,
|
ignore=ignores,
|
||||||
is_template=is_template,
|
is_template=is_template,
|
||||||
chmod=dotfile.chmod,
|
chmod=dotfile.chmod,
|
||||||
force_chmod=o.install_force_chmod)
|
force_chmod=opts.install_force_chmod)
|
||||||
if tmp:
|
if tmp:
|
||||||
tmp = os.path.join(o.dotpath, tmp)
|
tmp = os.path.join(opts.dotpath, tmp)
|
||||||
if os.path.exists(tmp):
|
if os.path.exists(tmp):
|
||||||
removepath(tmp, LOG)
|
removepath(tmp, LOG)
|
||||||
|
|
||||||
# check result of installation
|
# check result of installation
|
||||||
if r:
|
if ret:
|
||||||
# dotfile was installed
|
# dotfile was installed
|
||||||
if not o.install_temporary:
|
if not opts.install_temporary:
|
||||||
defactions = o.install_default_actions_post
|
defactions = opts.install_default_actions_post
|
||||||
postactions = dotfile.get_post_actions()
|
postactions = dotfile.get_post_actions()
|
||||||
post_actions_exec = action_executor(o, postactions, defactions,
|
post_actions_exec = action_executor(opts, postactions, defactions,
|
||||||
t, post=True)
|
templ, post=True)
|
||||||
post_actions_exec()
|
post_actions_exec()
|
||||||
else:
|
else:
|
||||||
# dotfile was NOT installed
|
# dotfile was NOT installed
|
||||||
if o.install_force_action:
|
if opts.install_force_action:
|
||||||
# pre-actions
|
# pre-actions
|
||||||
LOG.dbg('force pre action execution ...')
|
LOG.dbg('force pre action execution ...')
|
||||||
pre_actions_exec()
|
pre_actions_exec()
|
||||||
# post-actions
|
# post-actions
|
||||||
LOG.dbg('force post action execution ...')
|
LOG.dbg('force post action execution ...')
|
||||||
defactions = o.install_default_actions_post
|
defactions = opts.install_default_actions_post
|
||||||
postactions = dotfile.get_post_actions()
|
postactions = dotfile.get_post_actions()
|
||||||
post_actions_exec = action_executor(o, postactions, defactions,
|
post_actions_exec = action_executor(opts, postactions, defactions,
|
||||||
t, post=True)
|
templ, post=True)
|
||||||
post_actions_exec()
|
post_actions_exec()
|
||||||
|
|
||||||
return r, dotfile.key, err
|
return ret, dotfile.key, err
|
||||||
|
|
||||||
|
|
||||||
def cmd_install(o):
|
def cmd_install(opts):
|
||||||
"""install dotfiles for this profile"""
|
"""install dotfiles for this profile"""
|
||||||
dotfiles = o.dotfiles
|
dotfiles = opts.dotfiles
|
||||||
prof = o.conf.get_profile()
|
prof = opts.conf.get_profile()
|
||||||
|
|
||||||
adapt_workers(o, LOG)
|
adapt_workers(opts, LOG)
|
||||||
|
|
||||||
pro_pre_actions = prof.get_pre_actions() if prof else []
|
pro_pre_actions = prof.get_pre_actions() if prof else []
|
||||||
pro_post_actions = prof.get_post_actions() if prof else []
|
pro_post_actions = prof.get_post_actions() if prof else []
|
||||||
|
|
||||||
if o.install_keys:
|
if opts.install_keys:
|
||||||
# filtered dotfiles to install
|
# filtered dotfiles to install
|
||||||
uniq = uniq_list(o.install_keys)
|
uniq = uniq_list(opts.install_keys)
|
||||||
dotfiles = [d for d in dotfiles if d.key in uniq]
|
dotfiles = [d for d in dotfiles if d.key in uniq]
|
||||||
if not dotfiles:
|
if not dotfiles:
|
||||||
msg = 'no dotfile to install for this profile (\"{}\")'
|
msg = 'no dotfile to install for this profile (\"{}\")'
|
||||||
LOG.warn(msg.format(o.profile))
|
LOG.warn(msg.format(opts.profile))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# the installer
|
# the installer
|
||||||
tmpdir = None
|
tmpdir = None
|
||||||
if o.install_temporary:
|
if opts.install_temporary:
|
||||||
tmpdir = get_tmpdir()
|
tmpdir = get_tmpdir()
|
||||||
|
|
||||||
installed = []
|
installed = []
|
||||||
|
|
||||||
# execute profile pre-action
|
# execute profile pre-action
|
||||||
LOG.dbg('run {} profile pre actions'.format(len(pro_pre_actions)))
|
LOG.dbg('run {} profile pre actions'.format(len(pro_pre_actions)))
|
||||||
t = _get_templater(o)
|
templ = _get_templater(opts)
|
||||||
ret, err = action_executor(o, pro_pre_actions, [], t, post=False)()
|
ret, _ = action_executor(opts, pro_pre_actions, [], templ, post=False)()
|
||||||
if not ret:
|
if not ret:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# install each dotfile
|
# install each dotfile
|
||||||
if o.workers > 1:
|
if opts.workers > 1:
|
||||||
# in parallel
|
# in parallel
|
||||||
LOG.dbg('run with {} workers'.format(o.workers))
|
LOG.dbg('run with {} workers'.format(opts.workers))
|
||||||
ex = futures.ThreadPoolExecutor(max_workers=o.workers)
|
ex = futures.ThreadPoolExecutor(max_workers=opts.workers)
|
||||||
|
|
||||||
wait_for = []
|
wait_for = []
|
||||||
for dotfile in dotfiles:
|
for dotfile in dotfiles:
|
||||||
j = ex.submit(_dotfile_install, o, dotfile, tmpdir=tmpdir)
|
j = ex.submit(_dotfile_install, opts, dotfile, tmpdir=tmpdir)
|
||||||
wait_for.append(j)
|
wait_for.append(j)
|
||||||
# check result
|
# check result
|
||||||
for f in futures.as_completed(wait_for):
|
for fut in futures.as_completed(wait_for):
|
||||||
r, key, err = f.result()
|
tmpret, key, err = fut.result()
|
||||||
if r:
|
if tmpret:
|
||||||
installed.append(key)
|
installed.append(key)
|
||||||
elif err:
|
elif err:
|
||||||
LOG.err('installing \"{}\" failed: {}'.format(key,
|
LOG.err('installing \"{}\" failed: {}'.format(key,
|
||||||
@@ -338,42 +341,43 @@ def cmd_install(o):
|
|||||||
else:
|
else:
|
||||||
# sequentially
|
# sequentially
|
||||||
for dotfile in dotfiles:
|
for dotfile in dotfiles:
|
||||||
r, key, err = _dotfile_install(o, dotfile, tmpdir=tmpdir)
|
tmpret, key, err = _dotfile_install(opts, dotfile, tmpdir=tmpdir)
|
||||||
# check result
|
# check result
|
||||||
if r:
|
if tmpret:
|
||||||
installed.append(key)
|
installed.append(key)
|
||||||
elif err:
|
elif err:
|
||||||
LOG.err('installing \"{}\" failed: {}'.format(key,
|
LOG.err('installing \"{}\" failed: {}'.format(key,
|
||||||
err))
|
err))
|
||||||
|
|
||||||
# execute profile post-action
|
# execute profile post-action
|
||||||
if len(installed) > 0 or o.install_force_action:
|
if len(installed) > 0 or opts.install_force_action:
|
||||||
msg = 'run {} profile post actions'
|
msg = 'run {} profile post actions'
|
||||||
LOG.dbg(msg.format(len(pro_post_actions)))
|
LOG.dbg(msg.format(len(pro_post_actions)))
|
||||||
ret, err = action_executor(o, pro_post_actions, [], t, post=False)()
|
ret, _ = action_executor(opts, pro_post_actions,
|
||||||
|
[], templ, post=False)()
|
||||||
if not ret:
|
if not ret:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
LOG.dbg('install done: installed \"{}\"'.format(','.join(installed)))
|
LOG.dbg('install done: installed \"{}\"'.format(','.join(installed)))
|
||||||
|
|
||||||
if o.install_temporary:
|
if opts.install_temporary:
|
||||||
LOG.log('\ninstalled to tmp \"{}\".'.format(tmpdir))
|
LOG.log('\ninstalled to tmp \"{}\".'.format(tmpdir))
|
||||||
LOG.log('\n{} dotfile(s) installed.'.format(len(installed)))
|
LOG.log('\n{} dotfile(s) installed.'.format(len(installed)))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def cmd_compare(o, tmp):
|
def cmd_compare(opts, tmp):
|
||||||
"""compare dotfiles and return True if all identical"""
|
"""compare dotfiles and return True if all identical"""
|
||||||
dotfiles = o.dotfiles
|
dotfiles = opts.dotfiles
|
||||||
if not dotfiles:
|
if not dotfiles:
|
||||||
msg = 'no dotfile defined for this profile (\"{}\")'
|
msg = 'no dotfile defined for this profile (\"{}\")'
|
||||||
LOG.warn(msg.format(o.profile))
|
LOG.warn(msg.format(opts.profile))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# compare only specific files
|
# compare only specific files
|
||||||
selected = dotfiles
|
selected = dotfiles
|
||||||
if o.compare_focus:
|
if opts.compare_focus:
|
||||||
selected = _select(o.compare_focus, dotfiles)
|
selected = _select(opts.compare_focus, dotfiles)
|
||||||
|
|
||||||
if len(selected) < 1:
|
if len(selected) < 1:
|
||||||
LOG.log('\nno dotfile to compare')
|
LOG.log('\nno dotfile to compare')
|
||||||
@@ -381,20 +385,20 @@ def cmd_compare(o, tmp):
|
|||||||
|
|
||||||
same = True
|
same = True
|
||||||
cnt = 0
|
cnt = 0
|
||||||
if o.workers > 1:
|
if opts.workers > 1:
|
||||||
# in parallel
|
# in parallel
|
||||||
LOG.dbg('run with {} workers'.format(o.workers))
|
LOG.dbg('run with {} workers'.format(opts.workers))
|
||||||
ex = futures.ThreadPoolExecutor(max_workers=o.workers)
|
ex = futures.ThreadPoolExecutor(max_workers=opts.workers)
|
||||||
wait_for = []
|
wait_for = []
|
||||||
for dotfile in selected:
|
for dotfile in selected:
|
||||||
j = ex.submit(_dotfile_compare, o, dotfile, tmp)
|
|
||||||
wait_for.append(j)
|
|
||||||
# check result
|
|
||||||
for f in futures.as_completed(wait_for):
|
|
||||||
if not dotfile.src and not dotfile.dst:
|
if not dotfile.src and not dotfile.dst:
|
||||||
# ignore fake dotfile
|
# ignore fake dotfile
|
||||||
continue
|
continue
|
||||||
if not f.result():
|
j = ex.submit(_dotfile_compare, opts, dotfile, tmp)
|
||||||
|
wait_for.append(j)
|
||||||
|
# check result
|
||||||
|
for fut in futures.as_completed(wait_for):
|
||||||
|
if not fut.result():
|
||||||
same = False
|
same = False
|
||||||
cnt += 1
|
cnt += 1
|
||||||
else:
|
else:
|
||||||
@@ -403,7 +407,7 @@ def cmd_compare(o, tmp):
|
|||||||
if not dotfile.src and not dotfile.dst:
|
if not dotfile.src and not dotfile.dst:
|
||||||
# ignore fake dotfile
|
# ignore fake dotfile
|
||||||
continue
|
continue
|
||||||
if not _dotfile_compare(o, dotfile, tmp):
|
if not _dotfile_compare(opts, dotfile, tmp):
|
||||||
same = False
|
same = False
|
||||||
cnt += 1
|
cnt += 1
|
||||||
|
|
||||||
@@ -411,31 +415,32 @@ def cmd_compare(o, tmp):
|
|||||||
return same
|
return same
|
||||||
|
|
||||||
|
|
||||||
def cmd_update(o):
|
def cmd_update(opts):
|
||||||
"""update the dotfile(s) from path(s) or key(s)"""
|
"""update the dotfile(s) from path(s) or key(s)"""
|
||||||
cnt = 0
|
cnt = 0
|
||||||
paths = o.update_path
|
paths = opts.update_path
|
||||||
iskey = o.update_iskey
|
iskey = opts.update_iskey
|
||||||
|
|
||||||
if o.profile not in [p.key for p in o.profiles]:
|
if opts.profile not in [p.key for p in opts.profiles]:
|
||||||
LOG.err('no such profile \"{}\"'.format(o.profile))
|
LOG.err('no such profile \"{}\"'.format(opts.profile))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
adapt_workers(o, LOG)
|
adapt_workers(opts, LOG)
|
||||||
|
|
||||||
if not paths:
|
if not paths:
|
||||||
# update the entire profile
|
# update the entire profile
|
||||||
if iskey:
|
if iskey:
|
||||||
LOG.dbg('update by keys: {}'.format(paths))
|
LOG.dbg('update by keys: {}'.format(paths))
|
||||||
paths = [d.key for d in o.dotfiles]
|
paths = [d.key for d in opts.dotfiles]
|
||||||
else:
|
else:
|
||||||
LOG.dbg('update by paths: {}'.format(paths))
|
LOG.dbg('update by paths: {}'.format(paths))
|
||||||
paths = [d.dst for d in o.dotfiles]
|
paths = [d.dst for d in opts.dotfiles]
|
||||||
msg = 'Update all dotfiles for profile \"{}\"'.format(o.profile)
|
msg = 'Update all dotfiles for profile \"{}\"'.format(opts.profile)
|
||||||
if o.safe and not LOG.ask(msg):
|
if opts.safe and not LOG.ask(msg):
|
||||||
LOG.log('\n{} file(s) updated.'.format(cnt))
|
LOG.log('\n{} file(s) updated.'.format(cnt))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# check there's something to do
|
||||||
if not paths:
|
if not paths:
|
||||||
LOG.log('\nno dotfile to update')
|
LOG.log('\nno dotfile to update')
|
||||||
return True
|
return True
|
||||||
@@ -443,84 +448,87 @@ def cmd_update(o):
|
|||||||
LOG.dbg('dotfile to update: {}'.format(paths))
|
LOG.dbg('dotfile to update: {}'.format(paths))
|
||||||
|
|
||||||
# update each dotfile
|
# update each dotfile
|
||||||
if o.workers > 1:
|
if opts.workers > 1:
|
||||||
# in parallel
|
# in parallel
|
||||||
LOG.dbg('run with {} workers'.format(o.workers))
|
LOG.dbg('run with {} workers'.format(opts.workers))
|
||||||
ex = futures.ThreadPoolExecutor(max_workers=o.workers)
|
ex = futures.ThreadPoolExecutor(max_workers=opts.workers)
|
||||||
wait_for = []
|
wait_for = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
j = ex.submit(_dotfile_update, o, path, key=iskey)
|
j = ex.submit(_dotfile_update, opts, path, key=iskey)
|
||||||
wait_for.append(j)
|
wait_for.append(j)
|
||||||
# check result
|
# check result
|
||||||
for f in futures.as_completed(wait_for):
|
for fut in futures.as_completed(wait_for):
|
||||||
if f.result():
|
if fut.result():
|
||||||
cnt += 1
|
cnt += 1
|
||||||
else:
|
else:
|
||||||
# sequentially
|
# sequentially
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if _dotfile_update(o, path, key=iskey):
|
if _dotfile_update(opts, path, key=iskey):
|
||||||
cnt += 1
|
cnt += 1
|
||||||
|
|
||||||
LOG.log('\n{} file(s) updated.'.format(cnt))
|
LOG.log('\n{} file(s) updated.'.format(cnt))
|
||||||
return cnt == len(paths)
|
return cnt == len(paths)
|
||||||
|
|
||||||
|
|
||||||
def cmd_importer(o):
|
def cmd_importer(opts):
|
||||||
"""import dotfile(s) from paths"""
|
"""import dotfile(s) from paths"""
|
||||||
ret = True
|
ret = True
|
||||||
cnt = 0
|
cnt = 0
|
||||||
paths = o.import_path
|
paths = opts.import_path
|
||||||
importer = Importer(o.profile, o.conf, o.dotpath, o.diff_command,
|
importer = Importer(opts.profile, opts.conf,
|
||||||
dry=o.dry, safe=o.safe, debug=o.debug,
|
opts.dotpath, opts.diff_command,
|
||||||
keepdot=o.keepdot, ignore=o.import_ignore)
|
dry=opts.dry, safe=opts.safe,
|
||||||
|
debug=opts.debug,
|
||||||
|
keepdot=opts.keepdot,
|
||||||
|
ignore=opts.import_ignore)
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
r = importer.import_path(path, import_as=o.import_as,
|
tmpret = importer.import_path(path, import_as=opts.import_as,
|
||||||
import_link=o.import_link,
|
import_link=opts.import_link,
|
||||||
import_mode=o.import_mode)
|
import_mode=opts.import_mode)
|
||||||
if r < 0:
|
if tmpret < 0:
|
||||||
ret = False
|
ret = False
|
||||||
elif r > 0:
|
elif tmpret > 0:
|
||||||
cnt += 1
|
cnt += 1
|
||||||
|
|
||||||
if o.dry:
|
if opts.dry:
|
||||||
LOG.dry('new config file would be:')
|
LOG.dry('new config file would be:')
|
||||||
LOG.raw(o.conf.dump())
|
LOG.raw(opts.conf.dump())
|
||||||
else:
|
else:
|
||||||
o.conf.save()
|
opts.conf.save()
|
||||||
LOG.log('\n{} file(s) imported.'.format(cnt))
|
LOG.log('\n{} file(s) imported.'.format(cnt))
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def cmd_list_profiles(o):
|
def cmd_list_profiles(opts):
|
||||||
"""list all profiles"""
|
"""list all profiles"""
|
||||||
LOG.emph('Available profile(s):\n')
|
LOG.emph('Available profile(s):\n')
|
||||||
for p in o.profiles:
|
for profile in opts.profiles:
|
||||||
if o.profiles_grepable:
|
if opts.profiles_grepable:
|
||||||
fmt = '{}'.format(p.key)
|
fmt = '{}'.format(profile.key)
|
||||||
LOG.raw(fmt)
|
LOG.raw(fmt)
|
||||||
else:
|
else:
|
||||||
LOG.sub(p.key, end='')
|
LOG.sub(profile.key, end='')
|
||||||
LOG.log(' ({} dotfiles)'.format(len(p.dotfiles)))
|
LOG.log(' ({} dotfiles)'.format(len(profile.dotfiles)))
|
||||||
LOG.log('')
|
LOG.log('')
|
||||||
|
|
||||||
|
|
||||||
def cmd_files(o):
|
def cmd_files(opts):
|
||||||
"""list all dotfiles for a specific profile"""
|
"""list all dotfiles for a specific profile"""
|
||||||
if o.profile not in [p.key for p in o.profiles]:
|
if opts.profile not in [p.key for p in opts.profiles]:
|
||||||
LOG.warn('unknown profile \"{}\"'.format(o.profile))
|
LOG.warn('unknown profile \"{}\"'.format(opts.profile))
|
||||||
return
|
return
|
||||||
what = 'Dotfile(s)'
|
what = 'Dotfile(s)'
|
||||||
if o.files_templateonly:
|
if opts.files_templateonly:
|
||||||
what = 'Template(s)'
|
what = 'Template(s)'
|
||||||
LOG.emph('{} for profile \"{}\":\n'.format(what, o.profile))
|
LOG.emph('{} for profile \"{}\":\n'.format(what, opts.profile))
|
||||||
for dotfile in o.dotfiles:
|
for dotfile in opts.dotfiles:
|
||||||
if o.files_templateonly:
|
if opts.files_templateonly:
|
||||||
src = os.path.join(o.dotpath, dotfile.src)
|
src = os.path.join(opts.dotpath, dotfile.src)
|
||||||
if not Templategen.is_template(src):
|
if not Templategen.is_template(src):
|
||||||
continue
|
continue
|
||||||
if o.files_grepable:
|
if opts.files_grepable:
|
||||||
fmt = '{},dst:{},src:{},link:{}'
|
fmt = '{},dst:{},src:{},link:{}'
|
||||||
fmt = fmt.format(dotfile.key, dotfile.dst,
|
fmt = fmt.format(dotfile.key, dotfile.dst,
|
||||||
dotfile.src, dotfile.link.name.lower())
|
dotfile.src, dotfile.link.name.lower())
|
||||||
@@ -539,26 +547,26 @@ def cmd_files(o):
|
|||||||
LOG.log('')
|
LOG.log('')
|
||||||
|
|
||||||
|
|
||||||
def cmd_detail(o):
|
def cmd_detail(opts):
|
||||||
"""list details on all files for all dotfile entries"""
|
"""list details on all files for all dotfile entries"""
|
||||||
if o.profile not in [p.key for p in o.profiles]:
|
if opts.profile not in [p.key for p in opts.profiles]:
|
||||||
LOG.warn('unknown profile \"{}\"'.format(o.profile))
|
LOG.warn('unknown profile \"{}\"'.format(opts.profile))
|
||||||
return
|
return
|
||||||
dotfiles = o.dotfiles
|
dotfiles = opts.dotfiles
|
||||||
if o.detail_keys:
|
if opts.detail_keys:
|
||||||
# filtered dotfiles to install
|
# filtered dotfiles to install
|
||||||
uniq = uniq_list(o.details_keys)
|
uniq = uniq_list(opts.details_keys)
|
||||||
dotfiles = [d for d in dotfiles if d.key in uniq]
|
dotfiles = [d for d in dotfiles if d.key in uniq]
|
||||||
LOG.emph('dotfiles details for profile \"{}\":\n'.format(o.profile))
|
LOG.emph('dotfiles details for profile \"{}\":\n'.format(opts.profile))
|
||||||
for d in dotfiles:
|
for dotfile in dotfiles:
|
||||||
_detail(o.dotpath, d)
|
_detail(opts.dotpath, dotfile)
|
||||||
LOG.log('')
|
LOG.log('')
|
||||||
|
|
||||||
|
|
||||||
def cmd_remove(o):
|
def cmd_remove(opts):
|
||||||
"""remove dotfile from dotpath and from config"""
|
"""remove dotfile from dotpath and from config"""
|
||||||
paths = o.remove_path
|
paths = opts.remove_path
|
||||||
iskey = o.remove_iskey
|
iskey = opts.remove_iskey
|
||||||
|
|
||||||
if not paths:
|
if not paths:
|
||||||
LOG.log('no dotfile to remove')
|
LOG.log('no dotfile to remove')
|
||||||
@@ -569,13 +577,13 @@ def cmd_remove(o):
|
|||||||
for key in paths:
|
for key in paths:
|
||||||
if not iskey:
|
if not iskey:
|
||||||
# by path
|
# by path
|
||||||
dotfiles = o.conf.get_dotfile_by_dst(key)
|
dotfiles = opts.conf.get_dotfile_by_dst(key)
|
||||||
if not dotfiles:
|
if not dotfiles:
|
||||||
LOG.warn('{} ignored, does not exist'.format(key))
|
LOG.warn('{} ignored, does not exist'.format(key))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# by key
|
# by key
|
||||||
dotfile = o.conf.get_dotfile(key)
|
dotfile = opts.conf.get_dotfile(key)
|
||||||
if not dotfile:
|
if not dotfile:
|
||||||
LOG.warn('{} ignored, does not exist'.format(key))
|
LOG.warn('{} ignored, does not exist'.format(key))
|
||||||
continue
|
continue
|
||||||
@@ -592,46 +600,46 @@ def cmd_remove(o):
|
|||||||
LOG.dbg('removing {}'.format(key))
|
LOG.dbg('removing {}'.format(key))
|
||||||
|
|
||||||
# make sure is part of the profile
|
# make sure is part of the profile
|
||||||
if dotfile.key not in [d.key for d in o.dotfiles]:
|
if dotfile.key not in [d.key for d in opts.dotfiles]:
|
||||||
msg = '{} ignored, not associated to this profile'
|
msg = '{} ignored, not associated to this profile'
|
||||||
LOG.warn(msg.format(key))
|
LOG.warn(msg.format(key))
|
||||||
continue
|
continue
|
||||||
profiles = o.conf.get_profiles_by_dotfile_key(k)
|
profiles = opts.conf.get_profiles_by_dotfile_key(k)
|
||||||
pkeys = ','.join([p.key for p in profiles])
|
pkeys = ','.join([p.key for p in profiles])
|
||||||
if o.dry:
|
if opts.dry:
|
||||||
LOG.dry('would remove {} from {}'.format(dotfile, pkeys))
|
LOG.dry('would remove {} from {}'.format(dotfile, pkeys))
|
||||||
continue
|
continue
|
||||||
msg = 'Remove \"{}\" from all these profiles: {}'.format(k, pkeys)
|
msg = 'Remove \"{}\" from all these profiles: {}'.format(k, pkeys)
|
||||||
if o.safe and not LOG.ask(msg):
|
if opts.safe and not LOG.ask(msg):
|
||||||
return False
|
return False
|
||||||
LOG.dbg('remove dotfile: {}'.format(dotfile))
|
LOG.dbg('remove dotfile: {}'.format(dotfile))
|
||||||
|
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
if not o.conf.del_dotfile_from_profile(dotfile, profile):
|
if not opts.conf.del_dotfile_from_profile(dotfile, profile):
|
||||||
return False
|
return False
|
||||||
if not o.conf.del_dotfile(dotfile):
|
if not opts.conf.del_dotfile(dotfile):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# remove dotfile from dotpath
|
# remove dotfile from dotpath
|
||||||
dtpath = os.path.join(o.dotpath, dotfile.src)
|
dtpath = os.path.join(opts.dotpath, dotfile.src)
|
||||||
removepath(dtpath, LOG)
|
removepath(dtpath, LOG)
|
||||||
# remove empty directory
|
# remove empty directory
|
||||||
parent = os.path.dirname(dtpath)
|
parent = os.path.dirname(dtpath)
|
||||||
# remove any empty parent up to dotpath
|
# remove any empty parent up to dotpath
|
||||||
while parent != o.dotpath:
|
while parent != opts.dotpath:
|
||||||
if os.path.isdir(parent) and not os.listdir(parent):
|
if os.path.isdir(parent) and not os.listdir(parent):
|
||||||
msg = 'Remove empty dir \"{}\"'.format(parent)
|
msg = 'Remove empty dir \"{}\"'.format(parent)
|
||||||
if o.safe and not LOG.ask(msg):
|
if opts.safe and not LOG.ask(msg):
|
||||||
break
|
break
|
||||||
removepath(parent, LOG)
|
removepath(parent, LOG)
|
||||||
parent = os.path.dirname(parent)
|
parent = os.path.dirname(parent)
|
||||||
removed.append(dotfile)
|
removed.append(dotfile)
|
||||||
|
|
||||||
if o.dry:
|
if opts.dry:
|
||||||
LOG.dry('new config file would be:')
|
LOG.dry('new config file would be:')
|
||||||
LOG.raw(o.conf.dump())
|
LOG.raw(opts.conf.dump())
|
||||||
else:
|
else:
|
||||||
o.conf.save()
|
opts.conf.save()
|
||||||
if removed:
|
if removed:
|
||||||
LOG.log('\nFollowing dotfile(s) are not tracked anymore:')
|
LOG.log('\nFollowing dotfile(s) are not tracked anymore:')
|
||||||
entries = ['- \"{}\" (was tracked as \"{}\")'.format(r.dst, r.key)
|
entries = ['- \"{}\" (was tracked as \"{}\")'.format(r.dst, r.key)
|
||||||
@@ -647,25 +655,25 @@ def cmd_remove(o):
|
|||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
|
|
||||||
def _get_install_installer(o, tmpdir=None):
|
def _get_install_installer(opts, tmpdir=None):
|
||||||
"""get an installer instance for cmd_install"""
|
"""get an installer instance for cmd_install"""
|
||||||
inst = Installer(create=o.create, backup=o.backup,
|
inst = Installer(create=opts.create, backup=opts.backup,
|
||||||
dry=o.dry, safe=o.safe,
|
dry=opts.dry, safe=opts.safe,
|
||||||
base=o.dotpath, workdir=o.workdir,
|
base=opts.dotpath, workdir=opts.workdir,
|
||||||
diff=o.install_diff, debug=o.debug,
|
diff=opts.install_diff, debug=opts.debug,
|
||||||
totemp=tmpdir,
|
totemp=tmpdir,
|
||||||
showdiff=o.install_showdiff,
|
showdiff=opts.install_showdiff,
|
||||||
backup_suffix=o.install_backup_suffix,
|
backup_suffix=opts.install_backup_suffix,
|
||||||
diff_cmd=o.diff_command)
|
diff_cmd=opts.diff_command)
|
||||||
return inst
|
return inst
|
||||||
|
|
||||||
|
|
||||||
def _get_templater(o):
|
def _get_templater(opts):
|
||||||
"""get an templater instance"""
|
"""get an templater instance"""
|
||||||
t = Templategen(base=o.dotpath, variables=o.variables,
|
templ = Templategen(base=opts.dotpath, variables=opts.variables,
|
||||||
func_file=o.func_file, filter_file=o.filter_file,
|
func_file=opts.func_file, filter_file=opts.filter_file,
|
||||||
debug=o.debug)
|
debug=opts.debug)
|
||||||
return t
|
return templ
|
||||||
|
|
||||||
|
|
||||||
def _detail(dotpath, dotfile):
|
def _detail(dotpath, dotfile):
|
||||||
@@ -684,24 +692,24 @@ def _detail(dotpath, dotfile):
|
|||||||
LOG.sub('{} (template:{})'.format(path, template))
|
LOG.sub('{} (template:{})'.format(path, template))
|
||||||
else:
|
else:
|
||||||
for root, _, files in os.walk(path):
|
for root, _, files in os.walk(path):
|
||||||
for f in files:
|
for file in files:
|
||||||
p = os.path.join(root, f)
|
fpath = os.path.join(root, file)
|
||||||
template = 'no'
|
template = 'no'
|
||||||
if dotfile.template and Templategen.is_template(p):
|
if dotfile.template and Templategen.is_template(fpath):
|
||||||
template = 'yes'
|
template = 'yes'
|
||||||
LOG.sub('{} (template:{})'.format(p, template))
|
LOG.sub('{} (template:{})'.format(fpath, template))
|
||||||
|
|
||||||
|
|
||||||
def _select(selections, dotfiles):
|
def _select(selections, dotfiles):
|
||||||
selected = []
|
selected = []
|
||||||
for selection in selections:
|
for selection in selections:
|
||||||
df = next(
|
dotfile = next(
|
||||||
(x for x in dotfiles
|
(x for x in dotfiles
|
||||||
if os.path.expanduser(x.dst) == os.path.expanduser(selection)),
|
if os.path.expanduser(x.dst) == os.path.expanduser(selection)),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
if df:
|
if dotfile:
|
||||||
selected.append(df)
|
selected.append(dotfile)
|
||||||
else:
|
else:
|
||||||
LOG.err('no dotfile matches \"{}\"'.format(selection))
|
LOG.err('no dotfile matches \"{}\"'.format(selection))
|
||||||
return selected
|
return selected
|
||||||
@@ -716,9 +724,9 @@ def apply_trans(dotpath, dotfile, templater, debug=False):
|
|||||||
new_src = '{}.{}'.format(src, TRANS_SUFFIX)
|
new_src = '{}.{}'.format(src, TRANS_SUFFIX)
|
||||||
trans = dotfile.trans_r
|
trans = dotfile.trans_r
|
||||||
LOG.dbg('executing transformation: {}'.format(trans))
|
LOG.dbg('executing transformation: {}'.format(trans))
|
||||||
s = os.path.join(dotpath, src)
|
srcpath = os.path.join(dotpath, src)
|
||||||
temp = os.path.join(dotpath, new_src)
|
temp = os.path.join(dotpath, new_src)
|
||||||
if not trans.transform(s, temp, templater=templater, debug=debug):
|
if not trans.transform(srcpath, temp, templater=templater, debug=debug):
|
||||||
msg = 'transformation \"{}\" failed for {}'
|
msg = 'transformation \"{}\" failed for {}'
|
||||||
LOG.err(msg.format(trans.key, dotfile.key))
|
LOG.err(msg.format(trans.key, dotfile.key))
|
||||||
if new_src and os.path.exists(new_src):
|
if new_src and os.path.exists(new_src):
|
||||||
@@ -731,97 +739,103 @@ def apply_trans(dotpath, dotfile, templater, debug=False):
|
|||||||
# main
|
# main
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
|
def _exec_command(opts):
|
||||||
|
"""execute command"""
|
||||||
|
ret = True
|
||||||
|
command = ''
|
||||||
|
try:
|
||||||
|
|
||||||
|
if opts.cmd_profiles:
|
||||||
|
# list existing profiles
|
||||||
|
command = 'profiles'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
cmd_list_profiles(opts)
|
||||||
|
|
||||||
|
elif opts.cmd_files:
|
||||||
|
# list files for selected profile
|
||||||
|
command = 'files'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
cmd_files(opts)
|
||||||
|
|
||||||
|
elif opts.cmd_install:
|
||||||
|
# install the dotfiles stored in dotdrop
|
||||||
|
command = 'install'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
ret = cmd_install(opts)
|
||||||
|
|
||||||
|
elif opts.cmd_compare:
|
||||||
|
# compare local dotfiles with dotfiles stored in dotdrop
|
||||||
|
command = 'compare'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
tmp = get_tmpdir()
|
||||||
|
ret = cmd_compare(opts, tmp)
|
||||||
|
# clean tmp directory
|
||||||
|
removepath(tmp, LOG)
|
||||||
|
|
||||||
|
elif opts.cmd_import:
|
||||||
|
# import dotfile(s)
|
||||||
|
command = 'import'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
ret = cmd_importer(opts)
|
||||||
|
|
||||||
|
elif opts.cmd_update:
|
||||||
|
# update a dotfile
|
||||||
|
command = 'update'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
ret = cmd_update(opts)
|
||||||
|
|
||||||
|
elif opts.cmd_detail:
|
||||||
|
# detail files
|
||||||
|
command = 'detail'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
cmd_detail(opts)
|
||||||
|
|
||||||
|
elif opts.cmd_remove:
|
||||||
|
# remove dotfile
|
||||||
|
command = 'remove'
|
||||||
|
LOG.dbg('running cmd: {}'.format(command))
|
||||||
|
cmd_remove(opts)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
LOG.err('interrupted')
|
||||||
|
ret = False
|
||||||
|
|
||||||
|
return ret, command
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""entry point"""
|
"""entry point"""
|
||||||
# check dependencies are met
|
# check dependencies are met
|
||||||
try:
|
try:
|
||||||
dependencies_met()
|
dependencies_met()
|
||||||
except Exception as e:
|
except UnmetDependency as exc:
|
||||||
LOG.err(e)
|
LOG.err(exc)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
t0 = time.time()
|
time0 = time.time()
|
||||||
try:
|
try:
|
||||||
o = Options()
|
opts = Options()
|
||||||
except YamlException as e:
|
except YamlException as exc:
|
||||||
LOG.err('config error: {}'.format(str(e)))
|
LOG.err('config error: {}'.format(str(exc)))
|
||||||
return False
|
return False
|
||||||
except UndefinedException as e:
|
except UndefinedException as exc:
|
||||||
LOG.err('config error: {}'.format(str(e)))
|
LOG.err('config error: {}'.format(str(exc)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if o.debug:
|
if opts.debug:
|
||||||
LOG.debug = o.debug
|
LOG.debug = opts.debug
|
||||||
LOG.dbg('\n\n')
|
LOG.dbg('\n\n')
|
||||||
options_time = time.time() - t0
|
options_time = time.time() - time0
|
||||||
|
|
||||||
ret = True
|
time0 = time.time()
|
||||||
t0 = time.time()
|
ret, command = _exec_command(opts)
|
||||||
command = ''
|
cmd_time = time.time() - time0
|
||||||
try:
|
|
||||||
|
|
||||||
if o.cmd_profiles:
|
|
||||||
# list existing profiles
|
|
||||||
command = 'profiles'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
cmd_list_profiles(o)
|
|
||||||
|
|
||||||
elif o.cmd_files:
|
|
||||||
# list files for selected profile
|
|
||||||
command = 'files'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
cmd_files(o)
|
|
||||||
|
|
||||||
elif o.cmd_install:
|
|
||||||
# install the dotfiles stored in dotdrop
|
|
||||||
command = 'install'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
ret = cmd_install(o)
|
|
||||||
|
|
||||||
elif o.cmd_compare:
|
|
||||||
# compare local dotfiles with dotfiles stored in dotdrop
|
|
||||||
command = 'compare'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
tmp = get_tmpdir()
|
|
||||||
ret = cmd_compare(o, tmp)
|
|
||||||
# clean tmp directory
|
|
||||||
removepath(tmp, LOG)
|
|
||||||
|
|
||||||
elif o.cmd_import:
|
|
||||||
# import dotfile(s)
|
|
||||||
command = 'import'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
ret = cmd_importer(o)
|
|
||||||
|
|
||||||
elif o.cmd_update:
|
|
||||||
# update a dotfile
|
|
||||||
command = 'update'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
ret = cmd_update(o)
|
|
||||||
|
|
||||||
elif o.cmd_detail:
|
|
||||||
# detail files
|
|
||||||
command = 'detail'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
cmd_detail(o)
|
|
||||||
|
|
||||||
elif o.cmd_remove:
|
|
||||||
# remove dotfile
|
|
||||||
command = 'remove'
|
|
||||||
LOG.dbg('running cmd: {}'.format(command))
|
|
||||||
cmd_remove(o)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
LOG.err('interrupted')
|
|
||||||
ret = False
|
|
||||||
cmd_time = time.time() - t0
|
|
||||||
|
|
||||||
LOG.dbg('done executing command \"{}\"'.format(command))
|
LOG.dbg('done executing command \"{}\"'.format(command))
|
||||||
LOG.dbg('options loaded in {}'.format(options_time))
|
LOG.dbg('options loaded in {}'.format(options_time))
|
||||||
LOG.dbg('command executed in {}'.format(cmd_time))
|
LOG.dbg('command executed in {}'.format(cmd_time))
|
||||||
|
|
||||||
if ret and o.conf.save():
|
if ret and opts.conf.save():
|
||||||
LOG.log('config file updated')
|
LOG.log('config file updated')
|
||||||
|
|
||||||
LOG.dbg('return {}'.format(ret))
|
LOG.dbg('return {}'.format(ret))
|
||||||
|
|||||||
@@ -12,3 +12,7 @@ class YamlException(Exception):
|
|||||||
|
|
||||||
class UndefinedException(Exception):
|
class UndefinedException(Exception):
|
||||||
"""exception in templating"""
|
"""exception in templating"""
|
||||||
|
|
||||||
|
|
||||||
|
class UnmetDependency(Exception):
|
||||||
|
"""unmet dependency"""
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from dotdrop.exceptions import UndefinedException
|
|||||||
|
|
||||||
|
|
||||||
class Installer:
|
class Installer:
|
||||||
|
"""dotfile installer"""
|
||||||
|
|
||||||
def __init__(self, base='.', create=True, backup=True,
|
def __init__(self, base='.', create=True, backup=True,
|
||||||
dry=False, safe=False, workdir='~/.config/dotdrop',
|
dry=False, safe=False, workdir='~/.config/dotdrop',
|
||||||
@@ -65,7 +66,7 @@ class Installer:
|
|||||||
|
|
||||||
def install(self, templater, src, dst, linktype,
|
def install(self, templater, src, dst, linktype,
|
||||||
actionexec=None, noempty=False,
|
actionexec=None, noempty=False,
|
||||||
ignore=[], is_template=True,
|
ignore=None, is_template=True,
|
||||||
chmod=None, force_chmod=False):
|
chmod=None, force_chmod=False):
|
||||||
"""
|
"""
|
||||||
install src to dst
|
install src to dst
|
||||||
@@ -93,7 +94,7 @@ class Installer:
|
|||||||
return True, None
|
return True, None
|
||||||
msg = 'installing \"{}\" to \"{}\" (link: {})'
|
msg = 'installing \"{}\" to \"{}\" (link: {})'
|
||||||
self.log.dbg(msg.format(src, dst, str(linktype)))
|
self.log.dbg(msg.format(src, dst, str(linktype)))
|
||||||
src, dst, cont, err = self._check_paths(src, dst, chmod)
|
src, dst, cont, err = self._check_paths(src, dst)
|
||||||
if not cont:
|
if not cont:
|
||||||
return self._log_install(cont, err)
|
return self._log_install(cont, err)
|
||||||
|
|
||||||
@@ -108,10 +109,13 @@ class Installer:
|
|||||||
# install to temporary dir
|
# install to temporary dir
|
||||||
# and ignore any actions
|
# and ignore any actions
|
||||||
if self.totemp:
|
if self.totemp:
|
||||||
r, err, _ = self.install_to_temp(templater, self.totemp,
|
ret, err, _ = self.install_to_temp(templater,
|
||||||
src, dst, is_template=is_template,
|
self.totemp,
|
||||||
chmod=chmod, ignore=ignore)
|
src, dst,
|
||||||
return self._log_install(r, err)
|
is_template=is_template,
|
||||||
|
chmod=chmod,
|
||||||
|
ignore=ignore)
|
||||||
|
return self._log_install(ret, err)
|
||||||
|
|
||||||
isdir = os.path.isdir(src)
|
isdir = os.path.isdir(src)
|
||||||
self.log.dbg('install {} to {}'.format(src, dst))
|
self.log.dbg('install {} to {}'.format(src, dst))
|
||||||
@@ -120,20 +124,18 @@ class Installer:
|
|||||||
if linktype == LinkTypes.NOLINK:
|
if linktype == LinkTypes.NOLINK:
|
||||||
# normal file
|
# normal file
|
||||||
if isdir:
|
if isdir:
|
||||||
r, err = self._copy_dir(templater, src, dst,
|
ret, err = self._copy_dir(templater, src, dst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
noempty=noempty, ignore=ignore,
|
noempty=noempty, ignore=ignore,
|
||||||
is_template=is_template,
|
is_template=is_template)
|
||||||
chmod=chmod)
|
|
||||||
else:
|
else:
|
||||||
r, err = self._copy_file(templater, src, dst,
|
ret, err = self._copy_file(templater, src, dst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
noempty=noempty, ignore=ignore,
|
noempty=noempty, ignore=ignore,
|
||||||
is_template=is_template,
|
is_template=is_template)
|
||||||
chmod=chmod)
|
|
||||||
elif linktype == LinkTypes.LINK:
|
elif linktype == LinkTypes.LINK:
|
||||||
# symlink
|
# symlink
|
||||||
r, err = self._link(templater, src, dst,
|
ret, err = self._link(templater, src, dst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
is_template=is_template)
|
is_template=is_template)
|
||||||
elif linktype == LinkTypes.LINK_CHILDREN:
|
elif linktype == LinkTypes.LINK_CHILDREN:
|
||||||
@@ -142,17 +144,17 @@ class Installer:
|
|||||||
msg = 'symlink children of {} to {}'
|
msg = 'symlink children of {} to {}'
|
||||||
self.log.dbg(msg.format(src, dst))
|
self.log.dbg(msg.format(src, dst))
|
||||||
err = 'source dotfile is not a directory: {}'.format(src)
|
err = 'source dotfile is not a directory: {}'.format(src)
|
||||||
r = False
|
ret = False
|
||||||
else:
|
else:
|
||||||
r, err = self._link_children(templater, src, dst,
|
ret, err = self._link_children(templater, src, dst,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
is_template=is_template,
|
is_template=is_template,
|
||||||
ignore=ignore)
|
ignore=ignore)
|
||||||
|
|
||||||
self.log.dbg('before chmod: {} err:{}'.format(r, err))
|
self.log.dbg('before chmod: {} err:{}'.format(ret, err))
|
||||||
|
|
||||||
if self.dry:
|
if self.dry:
|
||||||
return self._log_install(r, err)
|
return self._log_install(ret, err)
|
||||||
|
|
||||||
# handle chmod
|
# handle chmod
|
||||||
# - on success (r, not err)
|
# - on success (r, not err)
|
||||||
@@ -160,7 +162,7 @@ class Installer:
|
|||||||
# but not when
|
# but not when
|
||||||
# - error (not r, err)
|
# - error (not r, err)
|
||||||
# - aborted (not r, err)
|
# - aborted (not r, err)
|
||||||
if (r or (not r and not err)):
|
if (ret or (not ret and not err)):
|
||||||
if not chmod:
|
if not chmod:
|
||||||
chmod = utils.get_file_perm(src)
|
chmod = utils.get_file_perm(src)
|
||||||
dstperms = utils.get_file_perm(dst)
|
dstperms = utils.get_file_perm(dst)
|
||||||
@@ -168,21 +170,21 @@ class Installer:
|
|||||||
# apply mode
|
# apply mode
|
||||||
msg = 'chmod {} to {:o}'.format(dst, chmod)
|
msg = 'chmod {} to {:o}'.format(dst, chmod)
|
||||||
if not force_chmod and self.safe and not self.log.ask(msg):
|
if not force_chmod and self.safe and not self.log.ask(msg):
|
||||||
r = False
|
ret = False
|
||||||
err = 'aborted'
|
err = 'aborted'
|
||||||
else:
|
else:
|
||||||
if not self.comparing:
|
if not self.comparing:
|
||||||
self.log.sub('chmod {} to {:o}'.format(dst, chmod))
|
self.log.sub('chmod {} to {:o}'.format(dst, chmod))
|
||||||
if utils.chmod(dst, chmod, debug=self.debug):
|
if utils.chmod(dst, chmod, debug=self.debug):
|
||||||
r = True
|
ret = True
|
||||||
else:
|
else:
|
||||||
r = False
|
ret = False
|
||||||
err = 'chmod failed'
|
err = 'chmod failed'
|
||||||
|
|
||||||
return self._log_install(r, err)
|
return self._log_install(ret, err)
|
||||||
|
|
||||||
def install_to_temp(self, templater, tmpdir, src, dst,
|
def install_to_temp(self, templater, tmpdir, src, dst,
|
||||||
is_template=True, chmod=None, ignore=[]):
|
is_template=True, chmod=None, ignore=None):
|
||||||
"""
|
"""
|
||||||
install a dotfile to a tempdir
|
install a dotfile to a tempdir
|
||||||
|
|
||||||
@@ -198,9 +200,10 @@ class Installer:
|
|||||||
- success, error-if-any, dotfile-installed-path
|
- success, error-if-any, dotfile-installed-path
|
||||||
"""
|
"""
|
||||||
self.log.dbg('tmp install {} (defined dst: {})'.format(src, dst))
|
self.log.dbg('tmp install {} (defined dst: {})'.format(src, dst))
|
||||||
src, dst, cont, err = self._check_paths(src, dst, chmod)
|
src, dst, cont, err = self._check_paths(src, dst)
|
||||||
if not cont:
|
if not cont:
|
||||||
return self._log_install(cont, err)
|
self._log_install(cont, err)
|
||||||
|
return cont, err, None
|
||||||
|
|
||||||
ret = False
|
ret = False
|
||||||
tmpdst = ''
|
tmpdst = ''
|
||||||
@@ -253,18 +256,18 @@ class Installer:
|
|||||||
self.log.dbg('is a template')
|
self.log.dbg('is a template')
|
||||||
self.log.dbg('install to {}'.format(self.workdir))
|
self.log.dbg('install to {}'.format(self.workdir))
|
||||||
tmp = self._pivot_path(dst, self.workdir, striphome=True)
|
tmp = self._pivot_path(dst, self.workdir, striphome=True)
|
||||||
r, err = self.install(templater, src, tmp,
|
ret, err = self.install(templater, src, tmp,
|
||||||
LinkTypes.NOLINK,
|
LinkTypes.NOLINK,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
is_template=is_template)
|
is_template=is_template)
|
||||||
if not r and not os.path.exists(tmp):
|
if not ret and not os.path.exists(tmp):
|
||||||
return r, err
|
return ret, err
|
||||||
src = tmp
|
src = tmp
|
||||||
r, err = self._symlink(src, dst, actionexec=actionexec)
|
ret, err = self._symlink(src, dst, actionexec=actionexec)
|
||||||
return r, err
|
return ret, err
|
||||||
|
|
||||||
def _link_children(self, templater, src, dst,
|
def _link_children(self, templater, src, dst,
|
||||||
actionexec=None, is_template=True, ignore=[]):
|
actionexec=None, is_template=True, ignore=None):
|
||||||
"""
|
"""
|
||||||
install link:link_children
|
install link:link_children
|
||||||
|
|
||||||
@@ -318,11 +321,11 @@ class Installer:
|
|||||||
self.log.dbg('install to {} and symlink'
|
self.log.dbg('install to {} and symlink'
|
||||||
.format(self.workdir))
|
.format(self.workdir))
|
||||||
tmp = self._pivot_path(subdst, self.workdir, striphome=True)
|
tmp = self._pivot_path(subdst, self.workdir, striphome=True)
|
||||||
r, e = self.install(templater, subsrc, tmp,
|
ret2, err2 = self.install(templater, subsrc, tmp,
|
||||||
LinkTypes.NOLINK,
|
LinkTypes.NOLINK,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
is_template=is_template)
|
is_template=is_template)
|
||||||
if not r and e and not os.path.exists(tmp):
|
if not ret2 and err2 and not os.path.exists(tmp):
|
||||||
continue
|
continue
|
||||||
subsrc = tmp
|
subsrc = tmp
|
||||||
|
|
||||||
@@ -369,8 +372,8 @@ class Installer:
|
|||||||
overwrite = True
|
overwrite = True
|
||||||
try:
|
try:
|
||||||
utils.removepath(dst)
|
utils.removepath(dst)
|
||||||
except OSError as e:
|
except OSError as exc:
|
||||||
err = 'something went wrong with {}: {}'.format(src, e)
|
err = 'something went wrong with {}: {}'.format(src, exc)
|
||||||
return False, err
|
return False, err
|
||||||
if self.dry:
|
if self.dry:
|
||||||
self.log.dry('would link {} to {}'.format(dst, src))
|
self.log.dry('would link {} to {}'.format(dst, src))
|
||||||
@@ -379,9 +382,9 @@ class Installer:
|
|||||||
if not self._create_dirs(base):
|
if not self._create_dirs(base):
|
||||||
err = 'error creating directory for {}'.format(dst)
|
err = 'error creating directory for {}'.format(dst)
|
||||||
return False, err
|
return False, err
|
||||||
r, e = self._exec_pre_actions(actionexec)
|
ret, err = self._exec_pre_actions(actionexec)
|
||||||
if not r:
|
if not ret:
|
||||||
return False, e
|
return False, err
|
||||||
# re-check in case action created the file
|
# re-check in case action created the file
|
||||||
if os.path.lexists(dst):
|
if os.path.lexists(dst):
|
||||||
msg = 'Remove "{}" for link creation?'.format(dst)
|
msg = 'Remove "{}" for link creation?'.format(dst)
|
||||||
@@ -389,8 +392,8 @@ class Installer:
|
|||||||
return False, 'aborted'
|
return False, 'aborted'
|
||||||
try:
|
try:
|
||||||
utils.removepath(dst)
|
utils.removepath(dst)
|
||||||
except OSError as e:
|
except OSError as exc:
|
||||||
err = 'something went wrong with {}: {}'.format(src, e)
|
err = 'something went wrong with {}: {}'.format(src, exc)
|
||||||
return False, err
|
return False, err
|
||||||
os.symlink(src, dst)
|
os.symlink(src, dst)
|
||||||
if not self.comparing:
|
if not self.comparing:
|
||||||
@@ -399,8 +402,7 @@ class Installer:
|
|||||||
|
|
||||||
def _copy_file(self, templater, src, dst,
|
def _copy_file(self, templater, src, dst,
|
||||||
actionexec=None, noempty=False,
|
actionexec=None, noempty=False,
|
||||||
ignore=[], is_template=True,
|
ignore=None, is_template=True):
|
||||||
chmod=None):
|
|
||||||
"""
|
"""
|
||||||
install src to dst when is a file
|
install src to dst when is a file
|
||||||
|
|
||||||
@@ -441,8 +443,8 @@ class Installer:
|
|||||||
saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst))
|
saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst))
|
||||||
try:
|
try:
|
||||||
content = templater.generate(src)
|
content = templater.generate(src)
|
||||||
except UndefinedException as e:
|
except UndefinedException as exc:
|
||||||
return False, str(e)
|
return False, str(exc)
|
||||||
finally:
|
finally:
|
||||||
templater.restore_vars(saved)
|
templater.restore_vars(saved)
|
||||||
# test is empty
|
# test is empty
|
||||||
@@ -456,8 +458,7 @@ class Installer:
|
|||||||
# write the file
|
# write the file
|
||||||
ret, err = self._write(src, dst,
|
ret, err = self._write(src, dst,
|
||||||
content=content,
|
content=content,
|
||||||
actionexec=actionexec,
|
actionexec=actionexec)
|
||||||
chmod=chmod)
|
|
||||||
if ret and not err:
|
if ret and not err:
|
||||||
if not self.dry and not self.comparing:
|
if not self.dry and not self.comparing:
|
||||||
self.log.sub('install {} to {}'.format(src, dst))
|
self.log.sub('install {} to {}'.format(src, dst))
|
||||||
@@ -465,7 +466,7 @@ class Installer:
|
|||||||
|
|
||||||
def _copy_dir(self, templater, src, dst,
|
def _copy_dir(self, templater, src, dst,
|
||||||
actionexec=None, noempty=False,
|
actionexec=None, noempty=False,
|
||||||
ignore=[], is_template=True, chmod=None):
|
ignore=None, is_template=True):
|
||||||
"""
|
"""
|
||||||
install src to dst when is a directory
|
install src to dst when is a directory
|
||||||
|
|
||||||
@@ -486,17 +487,16 @@ class Installer:
|
|||||||
|
|
||||||
# handle all files in dir
|
# handle all files in dir
|
||||||
for entry in os.listdir(src):
|
for entry in os.listdir(src):
|
||||||
f = os.path.join(src, entry)
|
fpath = os.path.join(src, entry)
|
||||||
self.log.dbg('deploy sub from {}: {}'.format(dst, entry))
|
self.log.dbg('deploy sub from {}: {}'.format(dst, entry))
|
||||||
if not os.path.isdir(f):
|
if not os.path.isdir(fpath):
|
||||||
# is file
|
# is file
|
||||||
res, err = self._copy_file(templater, f,
|
res, err = self._copy_file(templater, fpath,
|
||||||
os.path.join(dst, entry),
|
os.path.join(dst, entry),
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
noempty=noempty,
|
noempty=noempty,
|
||||||
ignore=ignore,
|
ignore=ignore,
|
||||||
is_template=is_template,
|
is_template=is_template)
|
||||||
chmod=None)
|
|
||||||
if not res and err:
|
if not res and err:
|
||||||
# error occured
|
# error occured
|
||||||
ret = res, err
|
ret = res, err
|
||||||
@@ -506,13 +506,12 @@ class Installer:
|
|||||||
ret = True, None
|
ret = True, None
|
||||||
else:
|
else:
|
||||||
# is directory
|
# is directory
|
||||||
res, err = self._copy_dir(templater, f,
|
res, err = self._copy_dir(templater, fpath,
|
||||||
os.path.join(dst, entry),
|
os.path.join(dst, entry),
|
||||||
actionexec=actionexec,
|
actionexec=actionexec,
|
||||||
noempty=noempty,
|
noempty=noempty,
|
||||||
ignore=ignore,
|
ignore=ignore,
|
||||||
is_template=is_template,
|
is_template=is_template)
|
||||||
chmod=None)
|
|
||||||
if not res and err:
|
if not res and err:
|
||||||
# error occured
|
# error occured
|
||||||
ret = res, err
|
ret = res, err
|
||||||
@@ -522,8 +521,33 @@ class Installer:
|
|||||||
ret = True, None
|
ret = True, None
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _write_content_to_file(cls, content, src, dst):
|
||||||
|
"""write content to file"""
|
||||||
|
if content:
|
||||||
|
# write content the file
|
||||||
|
try:
|
||||||
|
with open(dst, 'wb') as file:
|
||||||
|
file.write(content)
|
||||||
|
shutil.copymode(src, dst)
|
||||||
|
except NotADirectoryError as exc:
|
||||||
|
err = 'opening dest file: {}'.format(exc)
|
||||||
|
return False, err
|
||||||
|
except OSError as exc:
|
||||||
|
return False, str(exc)
|
||||||
|
except TypeError as exc:
|
||||||
|
return False, str(exc)
|
||||||
|
else:
|
||||||
|
# copy file
|
||||||
|
try:
|
||||||
|
shutil.copyfile(src, dst)
|
||||||
|
shutil.copymode(src, dst)
|
||||||
|
except OSError as exc:
|
||||||
|
return False, str(exc)
|
||||||
|
return True, None
|
||||||
|
|
||||||
def _write(self, src, dst, content=None,
|
def _write(self, src, dst, content=None,
|
||||||
actionexec=None, chmod=None):
|
actionexec=None):
|
||||||
"""
|
"""
|
||||||
copy dotfile / write content to file
|
copy dotfile / write content to file
|
||||||
|
|
||||||
@@ -541,8 +565,8 @@ class Installer:
|
|||||||
if os.path.lexists(dst):
|
if os.path.lexists(dst):
|
||||||
try:
|
try:
|
||||||
os.stat(dst)
|
os.stat(dst)
|
||||||
except OSError as e:
|
except OSError as exc:
|
||||||
if e.errno == errno.ENOENT:
|
if exc.errno == errno.ENOENT:
|
||||||
# broken symlink
|
# broken symlink
|
||||||
err = 'broken symlink {}'.format(dst)
|
err = 'broken symlink {}'.format(dst)
|
||||||
return False, err
|
return False, err
|
||||||
@@ -560,47 +584,38 @@ class Installer:
|
|||||||
if not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
if not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
||||||
return False, 'aborted'
|
return False, 'aborted'
|
||||||
overwrite = True
|
overwrite = True
|
||||||
|
|
||||||
if self.backup and os.path.lexists(dst):
|
if self.backup and os.path.lexists(dst):
|
||||||
self._backup(dst)
|
self._backup(dst)
|
||||||
|
|
||||||
|
# create hierarchy
|
||||||
base = os.path.dirname(dst)
|
base = os.path.dirname(dst)
|
||||||
if not self._create_dirs(base):
|
if not self._create_dirs(base):
|
||||||
err = 'creating directory for {}'.format(dst)
|
err = 'creating directory for {}'.format(dst)
|
||||||
return False, err
|
return False, err
|
||||||
r, e = self._exec_pre_actions(actionexec)
|
|
||||||
if not r:
|
# execute pre actions
|
||||||
return False, e
|
ret, err = self._exec_pre_actions(actionexec)
|
||||||
|
if not ret:
|
||||||
|
return False, err
|
||||||
|
|
||||||
self.log.dbg('install file to \"{}\"'.format(dst))
|
self.log.dbg('install file to \"{}\"'.format(dst))
|
||||||
# re-check in case action created the file
|
# re-check in case action created the file
|
||||||
if self.safe and not overwrite and os.path.lexists(dst):
|
if self.safe and not overwrite and \
|
||||||
if not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
os.path.lexists(dst) and \
|
||||||
|
not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
||||||
self.log.warn('ignoring {}'.format(dst))
|
self.log.warn('ignoring {}'.format(dst))
|
||||||
return False, 'aborted'
|
return False, 'aborted'
|
||||||
|
|
||||||
if content:
|
# writing to file
|
||||||
# write content the file
|
return self._write_content_to_file(content, src, dst)
|
||||||
try:
|
|
||||||
with open(dst, 'wb') as f:
|
|
||||||
f.write(content)
|
|
||||||
shutil.copymode(src, dst)
|
|
||||||
except NotADirectoryError as e:
|
|
||||||
err = 'opening dest file: {}'.format(e)
|
|
||||||
return False, err
|
|
||||||
except Exception as e:
|
|
||||||
return False, str(e)
|
|
||||||
else:
|
|
||||||
# copy file
|
|
||||||
try:
|
|
||||||
shutil.copyfile(src, dst)
|
|
||||||
shutil.copymode(src, dst)
|
|
||||||
except Exception as e:
|
|
||||||
return False, str(e)
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# helpers
|
# helpers
|
||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
def _get_tmp_file_vars(self, src, dst):
|
@classmethod
|
||||||
|
def _get_tmp_file_vars(cls, src, dst):
|
||||||
tmp = {}
|
tmp = {}
|
||||||
tmp['_dotfile_sub_abs_src'] = src
|
tmp['_dotfile_sub_abs_src'] = src
|
||||||
tmp['_dotfile_sub_abs_dst'] = dst
|
tmp['_dotfile_sub_abs_dst'] = dst
|
||||||
@@ -615,10 +630,10 @@ class Installer:
|
|||||||
if content:
|
if content:
|
||||||
tmp = utils.write_to_tmpfile(content)
|
tmp = utils.write_to_tmpfile(content)
|
||||||
src = tmp
|
src = tmp
|
||||||
r = utils.fastdiff(src, dst)
|
ret = utils.fastdiff(src, dst)
|
||||||
if r:
|
if ret:
|
||||||
self.log.dbg('content differ')
|
self.log.dbg('content differ')
|
||||||
return r
|
return ret
|
||||||
|
|
||||||
def _show_diff_before_write(self, src, dst, content=None):
|
def _show_diff_before_write(self, src, dst, content=None):
|
||||||
"""
|
"""
|
||||||
@@ -691,7 +706,10 @@ class Installer:
|
|||||||
return ret, err
|
return ret, err
|
||||||
|
|
||||||
def _log_install(self, boolean, err):
|
def _log_install(self, boolean, err):
|
||||||
"""log installation process"""
|
"""
|
||||||
|
log installation process
|
||||||
|
returns success, error-if-any
|
||||||
|
"""
|
||||||
if not self.debug:
|
if not self.debug:
|
||||||
return boolean, err
|
return boolean, err
|
||||||
if boolean:
|
if boolean:
|
||||||
@@ -703,7 +721,7 @@ class Installer:
|
|||||||
self.log.dbg('install: IGNORED')
|
self.log.dbg('install: IGNORED')
|
||||||
return boolean, err
|
return boolean, err
|
||||||
|
|
||||||
def _check_paths(self, src, dst, chmod):
|
def _check_paths(self, src, dst):
|
||||||
"""
|
"""
|
||||||
check and normalize param
|
check and normalize param
|
||||||
returns <src>, <dst>, <continue>, <error>
|
returns <src>, <dst>, <continue>, <error>
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ COMMENT_END = '@@#}'
|
|||||||
|
|
||||||
|
|
||||||
class Templategen:
|
class Templategen:
|
||||||
|
"""dotfile templater"""
|
||||||
|
|
||||||
def __init__(self, base='.', variables={},
|
def __init__(self, base='.', variables=None,
|
||||||
func_file=[], filter_file=[], debug=False):
|
func_file=None, filter_file=None, debug=False):
|
||||||
"""constructor
|
"""constructor
|
||||||
@base: directory path where to search for templates
|
@base: directory path where to search for templates
|
||||||
@variables: dictionary of variables for templates
|
@variables: dictionary of variables for templates
|
||||||
@@ -69,13 +70,13 @@ class Templategen:
|
|||||||
self.log.dbg('load global functions:')
|
self.log.dbg('load global functions:')
|
||||||
self._load_funcs_to_dic(jhelpers, self.env.globals)
|
self._load_funcs_to_dic(jhelpers, self.env.globals)
|
||||||
if func_file:
|
if func_file:
|
||||||
for f in func_file:
|
for ffile in func_file:
|
||||||
self.log.dbg('load custom functions from {}'.format(f))
|
self.log.dbg('load custom functions from {}'.format(ffile))
|
||||||
self._load_path_to_dic(f, self.env.globals)
|
self._load_path_to_dic(ffile, self.env.globals)
|
||||||
if filter_file:
|
if filter_file:
|
||||||
for f in filter_file:
|
for ffile in filter_file:
|
||||||
self.log.dbg('load custom filters from {}'.format(f))
|
self.log.dbg('load custom filters from {}'.format(ffile))
|
||||||
self._load_path_to_dic(f, self.env.filters)
|
self._load_path_to_dic(ffile, self.env.filters)
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self._debug_dict('template additional variables', variables)
|
self._debug_dict('template additional variables', variables)
|
||||||
|
|
||||||
@@ -89,9 +90,9 @@ class Templategen:
|
|||||||
return ''
|
return ''
|
||||||
try:
|
try:
|
||||||
return self._handle_file(src)
|
return self._handle_file(src)
|
||||||
except UndefinedError as e:
|
except UndefinedError as exc:
|
||||||
err = 'undefined variable: {}'.format(e.message)
|
err = 'undefined variable: {}'.format(exc.message)
|
||||||
raise UndefinedException(err)
|
raise UndefinedException(err) from exc
|
||||||
|
|
||||||
def generate_string(self, string):
|
def generate_string(self, string):
|
||||||
"""
|
"""
|
||||||
@@ -103,11 +104,11 @@ class Templategen:
|
|||||||
return ''
|
return ''
|
||||||
try:
|
try:
|
||||||
return self.env.from_string(string).render(self.variables)
|
return self.env.from_string(string).render(self.variables)
|
||||||
except UndefinedError as e:
|
except UndefinedError as exc:
|
||||||
err = 'undefined variable: {}'.format(e.message)
|
err = 'undefined variable: {}'.format(exc.message)
|
||||||
raise UndefinedException(err)
|
raise UndefinedException(err) from exc
|
||||||
|
|
||||||
def add_tmp_vars(self, newvars={}):
|
def add_tmp_vars(self, newvars=None):
|
||||||
"""add vars to the globals, make sure to call restore_vars"""
|
"""add vars to the globals, make sure to call restore_vars"""
|
||||||
saved_variables = self.variables.copy()
|
saved_variables = self.variables.copy()
|
||||||
if not newvars:
|
if not newvars:
|
||||||
@@ -139,13 +140,15 @@ class Templategen:
|
|||||||
self.log.dbg('load function \"{}\"'.format(name))
|
self.log.dbg('load function \"{}\"'.format(name))
|
||||||
dic[name] = func
|
dic[name] = func
|
||||||
|
|
||||||
def _header(self, prepend=''):
|
@classmethod
|
||||||
|
def _header(cls, prepend=''):
|
||||||
"""add a comment usually in the header of a dotfile"""
|
"""add a comment usually in the header of a dotfile"""
|
||||||
return '{}{}'.format(prepend, utils.header())
|
return '{}{}'.format(prepend, utils.header())
|
||||||
|
|
||||||
def _handle_file(self, src):
|
def _handle_file(self, src):
|
||||||
"""generate the file content from template"""
|
"""generate the file content from template"""
|
||||||
try:
|
try:
|
||||||
|
# pylint: disable=C0415
|
||||||
import magic
|
import magic
|
||||||
filetype = magic.from_file(src, mime=True)
|
filetype = magic.from_file(src, mime=True)
|
||||||
self.log.dbg('using \"magic\" for filetype identification')
|
self.log.dbg('using \"magic\" for filetype identification')
|
||||||
@@ -162,7 +165,8 @@ class Templategen:
|
|||||||
return self._handle_bin_file(src)
|
return self._handle_bin_file(src)
|
||||||
return self._handle_text_file(src)
|
return self._handle_text_file(src)
|
||||||
|
|
||||||
def _is_text(self, fileoutput):
|
@classmethod
|
||||||
|
def _is_text(cls, fileoutput):
|
||||||
"""return if `file -b` output is ascii text"""
|
"""return if `file -b` output is ascii text"""
|
||||||
out = fileoutput.lower()
|
out = fileoutput.lower()
|
||||||
if out.startswith('text'):
|
if out.startswith('text'):
|
||||||
@@ -179,8 +183,8 @@ class Templategen:
|
|||||||
path = os.path.normpath(path)
|
path = os.path.normpath(path)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise TemplateNotFound(path)
|
raise TemplateNotFound(path)
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r') as file:
|
||||||
content = f.read()
|
content = file.read()
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def _handle_text_file(self, src):
|
def _handle_text_file(self, src):
|
||||||
@@ -199,18 +203,19 @@ class Templategen:
|
|||||||
# this is dirty
|
# this is dirty
|
||||||
if not src.startswith(self.base):
|
if not src.startswith(self.base):
|
||||||
src = os.path.join(self.base, src)
|
src = os.path.join(self.base, src)
|
||||||
with open(src, 'rb') as f:
|
with open(src, 'rb') as file:
|
||||||
content = f.read()
|
content = file.read()
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def _read_bad_encoded_text(self, path):
|
@classmethod
|
||||||
|
def _read_bad_encoded_text(cls, path):
|
||||||
"""decode non utf-8 data"""
|
"""decode non utf-8 data"""
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as file:
|
||||||
data = f.read()
|
data = file.read()
|
||||||
return data.decode('utf-8', 'replace')
|
return data.decode('utf-8', 'replace')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_template(path, ignore=[]):
|
def is_template(path, ignore=None):
|
||||||
"""recursively check if any file is a template within path"""
|
"""recursively check if any file is a template within path"""
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
|
|
||||||
@@ -250,10 +255,11 @@ class Templategen:
|
|||||||
markers = [BLOCK_START, VAR_START, COMMENT_START]
|
markers = [BLOCK_START, VAR_START, COMMENT_START]
|
||||||
patterns = [re.compile(marker.encode()) for marker in markers]
|
patterns = [re.compile(marker.encode()) for marker in markers]
|
||||||
try:
|
try:
|
||||||
with io.open(path, "r", encoding="utf-8") as f:
|
with io.open(path, "r", encoding="utf-8") as file:
|
||||||
m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
|
mapf = mmap.mmap(file.fileno(), 0,
|
||||||
|
access=mmap.ACCESS_READ)
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
if pattern.search(m):
|
if pattern.search(mapf):
|
||||||
return True
|
return True
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# is binary so surely no template
|
# is binary so surely no template
|
||||||
@@ -267,5 +273,5 @@ class Templategen:
|
|||||||
self.log.dbg('{}:'.format(title))
|
self.log.dbg('{}:'.format(title))
|
||||||
if not elems:
|
if not elems:
|
||||||
return
|
return
|
||||||
for k, v in elems.items():
|
for k, val in elems.items():
|
||||||
self.log.dbg(' - \"{}\": {}'.format(k, v))
|
self.log.dbg(' - \"{}\": {}'.format(k, val))
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from shutil import rmtree, which
|
|||||||
|
|
||||||
# local import
|
# local import
|
||||||
from dotdrop.logger import Logger
|
from dotdrop.logger import Logger
|
||||||
|
from dotdrop.exceptions import UnmetDependency
|
||||||
|
|
||||||
LOG = Logger()
|
LOG = Logger()
|
||||||
STAR = '*'
|
STAR = '*'
|
||||||
@@ -316,7 +317,7 @@ def dependencies_met():
|
|||||||
err = 'The tool \"{}\" was not found in the PATH!'
|
err = 'The tool \"{}\" was not found in the PATH!'
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
if not which(dep):
|
if not which(dep):
|
||||||
raise Exception(err.format(dep))
|
raise UnmetDependency(err.format(dep))
|
||||||
# check python deps
|
# check python deps
|
||||||
err = 'missing python module \"{}\"'
|
err = 'missing python module \"{}\"'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user