From 44a26a261d4a23805e61c7862fa172aaa746e455 Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Wed, 1 May 2019 15:55:51 +0200 Subject: [PATCH] adding default_actions and dotfile src/dst paths to template these (for #125) --- dotdrop/action.py | 9 ++- dotdrop/config.py | 40 ++++++++-- dotdrop/dotdrop.py | 68 +++++++++++++--- dotdrop/dotfile.py | 7 ++ dotdrop/installer.py | 93 ++++++++++++---------- dotdrop/options.py | 3 +- dotdrop/templategen.py | 28 ++++++- tests-ng/actions-default.sh | 150 +++++++++++++++++++++++++++++++++++ tests-ng/actions-template.sh | 142 +++++++++++++++++++++++++++++++++ tests/test_install.py | 12 +-- 10 files changed, 480 insertions(+), 72 deletions(-) create mode 100755 tests-ng/actions-default.sh create mode 100755 tests-ng/actions-template.sh diff --git a/dotdrop/action.py b/dotdrop/action.py index 6b22280..e383d0a 100644 --- a/dotdrop/action.py +++ b/dotdrop/action.py @@ -68,13 +68,16 @@ class Action(Cmd): def __repr__(self): return 'action({})'.format(self.__str__()) - def execute(self): + def execute(self, templater=None, newvars={}): """execute the action in the shell""" ret = 1 + action = self.action + if templater: + action = templater.generate_string(self.action, tmpvars=newvars) try: - cmd = self.action.format(*self.args) + cmd = action.format(*self.args) except IndexError: - err = 'bad action: \"{}\"'.format(self.action) + err = 'bad action: \"{}\"'.format(action) err += ' with \"{}\"'.format(self.args) self.log.warn(err) return False diff --git a/dotdrop/config.py b/dotdrop/config.py index 1d79797..155349e 100644 --- a/dotdrop/config.py +++ b/dotdrop/config.py @@ -40,6 +40,7 @@ class Cfg: key_workdir = 'workdir' key_cmpignore = 'cmpignore' key_upignore = 'upignore' + key_defactions = 'default_actions' # import keys key_import_vars = 'import_variables' @@ -148,14 +149,22 @@ class Cfg: self.prodots = {} # represents all variables from external files + # NOT linked inside the yaml dict (self.content) self.ext_variables = {} self.ext_dynvariables = {} # cmpignore patterns + # NOT linked inside the yaml dict (self.content) self.cmpignores = [] + # upignore patterns + # NOT linked inside the yaml dict (self.content) self.upignores = [] + # default actions + # NOT linked inside the yaml dict (self.content) + self.defactions = {} + if not self._load_config(profile=profile): raise ValueError('config is not valid') @@ -171,13 +180,16 @@ class Cfg: d.src = t.generate_string(d.src) d.dst = t.generate_string(d.dst) # pre actions + var = d.get_vars() if self.key_actions_pre in d.actions: for action in d.actions[self.key_actions_pre]: - action.action = t.generate_string(action.action) + action.action = t.generate_string(action.action, + tmpvars=var) # post actions if self.key_actions_post in d.actions: for action in d.actions[self.key_actions_post]: - action.action = t.generate_string(action.action) + action.action = t.generate_string(action.action, + tmpvars=var) return dotfiles def _load_config(self, profile=None): @@ -263,11 +275,13 @@ class Cfg: # load global upignore if self.key_upignore in self.lnk_settings: - self.upignores = self.lnk_settings[self.key_upignore] or [] + key = self.key_upignore + self.upignores = self.lnk_settings[key].copy() or [] # load global cmpignore if self.key_cmpignore in self.lnk_settings: - self.cmpignores = self.lnk_settings[self.key_cmpignore] or [] + key = self.key_cmpignore + self.cmpignores = self.lnk_settings[key].copy() or [] # parse external actions try: @@ -324,6 +338,17 @@ class Cfg: except KeyError: pass + # load default actions + try: + dactions = self.lnk_settings[self.key_defactions].copy() or [] + self.defactions = self._parse_actions_list(dactions, + profile=profile) + except KeyError: + self.defactions = { + self.key_actions_pre: [], + self.key_actions_post: [], + } + # parse read transformations # If read transformations are None, replaces them with empty dict try: @@ -385,7 +410,7 @@ class Cfg: # parse actions itsactions = v.get(self.key_dotfiles_actions, []) - actions = self._parse_actions(itsactions, profile=profile) + actions = self._parse_actions_list(itsactions, profile=profile) if self.debug: self.log.dbg('action for {}'.format(k)) for t in [self.key_actions_pre, self.key_actions_post]: @@ -722,7 +747,7 @@ class Cfg: dotfiles.extend(self.prodots[other]) return True, dotfiles - def _parse_actions(self, entries, profile=None): + def _parse_actions_list(self, entries, profile=None): """parse actions specified for an element where entries are the ones defined for this dotfile""" res = { @@ -1052,6 +1077,9 @@ class Cfg: settings[key] = self._string_to_linktype(settings[key]) key = self.key_dotfile_link settings[key] = self._string_to_linktype(settings[key]) + # patch defactions + key = self.key_defactions + settings[key] = self.defactions return settings def get_variables(self, profile, debug=False): diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index a82e125..d8b1dca 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -27,6 +27,48 @@ TRANS_SUFFIX = 'trans' ########################################################### +def action_executor(o, dotfile, actions, defactions, templater, post=False): + """closure for action execution""" + def execute(): + """ + execute actions and return + True, None if ok + False, errstring if issue + """ + s = 'pre' if not post else 'post' + + # execute default actions + for action in defactions: + if o.dry: + LOG.dry('would execute def-{}-action: {}'.format(s, + action)) + continue + if o.debug: + LOG.dbg('executing def-{}-action {}'.format(s, action)) + newvars = dotfile.get_vars() + ret = action.execute(templater=templater, newvars=newvars) + if not ret: + err = 'def-{}-action \"{}\" failed'.format(s, action.key) + LOG.err(err) + return False, err + + # execute actions + for action in actions: + if o.dry: + LOG.dry('would execute {}-action: {}'.format(s, action)) + continue + if o.debug: + LOG.dbg('executing {}-action {}'.format(s, action)) + newvars = dotfile.get_vars() + ret = action.execute(templater=templater, newvars=newvars) + if not ret: + err = '{}-action \"{}\" failed'.format(s, action.key) + LOG.err(err) + return False, err + return True, None + return execute + + def cmd_install(o): """install dotfiles for this profile""" dotfiles = o.dotfiles @@ -57,14 +99,19 @@ def cmd_install(o): and Cfg.key_actions_pre in dotfile.actions: for action in dotfile.actions[Cfg.key_actions_pre]: preactions.append(action) + defactions = o.install_default_actions[Cfg.key_actions_pre] + pre_actions_exec = action_executor(o, dotfile, preactions, + defactions, t, post=False) + if o.debug: LOG.dbg('installing {}'.format(dotfile)) if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.LINK: - r = inst.link(t, dotfile.src, dotfile.dst, actions=preactions) + r = inst.link(t, dotfile.src, dotfile.dst, + actionexec=pre_actions_exec) elif hasattr(dotfile, 'link') and \ dotfile.link == LinkTypes.LINK_CHILDREN: r = inst.link_children(t, dotfile.src, dotfile.dst, - actions=preactions) + actionexec=pre_actions_exec) else: src = dotfile.src tmp = None @@ -73,7 +120,8 @@ def cmd_install(o): if not tmp: continue src = tmp - r, err = inst.install(t, src, dotfile.dst, actions=preactions, + r, err = inst.install(t, src, dotfile.dst, + actionexec=pre_actions_exec, noempty=dotfile.noempty) if tmp: tmp = os.path.join(o.dotpath, tmp) @@ -82,15 +130,11 @@ def cmd_install(o): if r: if not o.install_temporary and \ Cfg.key_actions_post in dotfile.actions: - actions = dotfile.actions[Cfg.key_actions_post] - # execute post action - for action in actions: - if o.dry: - LOG.dry('would execute action: {}'.format(action)) - else: - if o.debug: - LOG.dbg('executing post action {}'.format(action)) - action.execute() + defactions = o.install_default_actions[Cfg.key_actions_post] + postactions = dotfile.actions[Cfg.key_actions_post] + post_actions_exec = action_executor(o, dotfile, postactions, + defactions, t, post=True) + post_actions_exec() installed += 1 elif not r and err: LOG.err('installing \"{}\" failed: {}'.format(dotfile.key, err)) diff --git a/dotdrop/dotfile.py b/dotdrop/dotfile.py index c7e5944..ce1cac3 100644 --- a/dotdrop/dotfile.py +++ b/dotdrop/dotfile.py @@ -40,6 +40,13 @@ class Dotfile: self.noempty = noempty self.upignore = upignore + def get_vars(self): + """return this dotfile templating vars""" + _vars = {} + _vars['_dotfile_src'] = self.src + _vars['_dotfile_dst'] = self.dst + return _vars + def __str__(self): msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"' return msg.format(self.key, self.src, self.dst, self.link.name.lower()) diff --git a/dotdrop/installer.py b/dotdrop/installer.py index 574c1e4..912dcc8 100644 --- a/dotdrop/installer.py +++ b/dotdrop/installer.py @@ -49,13 +49,19 @@ class Installer: self.action_executed = False self.log = Logger() - def install(self, templater, src, dst, actions=[], noempty=False): + def install(self, templater, src, dst, actionexec=None, noempty=False): """ install src to dst using a template + @templater: the templater object + @src: dotfile source path in dotpath + @dst: dotfile destination path in the FS + @actionexec: action executor callback + @noempty: render empty template flag + return - - True, None: success - - False, error_msg: error - - False, None, ignored + - True, None : success + - False, error_msg : error + - False, None : ignored """ if self.debug: self.log.dbg('install {} to {}'.format(src, dst)) @@ -76,18 +82,24 @@ class Installer: self.log.dbg('install {} to {}'.format(src, dst)) self.log.dbg('is \"{}\" a directory: {}'.format(src, isdir)) if isdir: - return self._handle_dir(templater, src, dst, actions=actions, + return self._handle_dir(templater, src, dst, + actionexec=actionexec, noempty=noempty) return self._handle_file(templater, src, dst, - actions=actions, noempty=noempty) + actionexec=actionexec, noempty=noempty) - def link(self, templater, src, dst, actions=[]): + def link(self, templater, src, dst, actionexec=None): """ set src as the link target of dst + @templater: the templater + @src: dotfile source path in dotpath + @dst: dotfile destination path in the FS + @actionexec: action executor callback + return - - True, None: success - - False, error_msg: error - - False, None, ignored + - True, None : success + - False, error_msg : error + - False, None : ignored """ if self.debug: self.log.dbg('link {} to {}'.format(src, dst)) @@ -100,22 +112,27 @@ class Installer: dst = os.path.normpath(os.path.expanduser(dst)) if self.totemp: # ignore actions - return self.install(templater, src, dst, actions=[]) + return self.install(templater, src, dst, actionexec=None) if Templategen.is_template(src): if self.debug: self.log.dbg('dotfile is a template') self.log.dbg('install to {} and symlink'.format(self.workdir)) tmp = self._pivot_path(dst, self.workdir, striphome=True) - i, err = self.install(templater, src, tmp, actions=actions) + i, err = self.install(templater, src, tmp, actionexec=actionexec) if not i and not os.path.exists(tmp): return i, err src = tmp - return self._link(src, dst, actions=actions) + return self._link(src, dst, actionexec=actionexec) - def link_children(self, templater, src, dst, actions=[]): + def link_children(self, templater, src, dst, actionexec=None): """ link all dotfiles in a given directory + @templater: the templater + @src: dotfile source path in dotpath + @dst: dotfile destination path in the FS + @actionexec: action executor callback + return - True, None: success - False, error_msg: error @@ -175,21 +192,21 @@ class Installer: self.log.dbg('install to {} and symlink' .format(self.workdir)) tmp = self._pivot_path(dst, self.workdir, striphome=True) - r, e = self.install(templater, src, tmp, actions=actions) + r, e = self.install(templater, src, tmp, actionexec=actionexec) if not r and e and not os.path.exists(tmp): continue src = tmp - result = self._link(src, dst, actions) + result = self._link(src, dst, actionexec=actionexec) - # Empty actions if dotfile installed - # This prevents from running actions multiple times + # void actionexec if dotfile installed + # to prevent from running actions multiple times if len(result): - actions = [] + actionexec = None return True, None - def _link(self, src, dst, actions=[]): + def _link(self, src, dst, actionexec=None): """set src as a link target of dst""" overwrite = not self.safe if os.path.lexists(dst): @@ -216,7 +233,7 @@ class Installer: if not self._create_dirs(base): err = 'creating directory for {}'.format(dst) return False, err - r, e = self._exec_pre_actions(actions) + r, e = self._exec_pre_actions(actionexec) if not r: return False, e # re-check in case action created the file @@ -234,7 +251,8 @@ class Installer: self.log.sub('linked {} to {}'.format(dst, src)) return True, None - def _handle_file(self, templater, src, dst, actions=[], noempty=False): + def _handle_file(self, templater, src, dst, + actionexec=None, noempty=False): """install src to dst when is a file""" if self.debug: self.log.dbg('generate template for {}'.format(src)) @@ -254,7 +272,8 @@ class Installer: err = 'source dotfile does not exist: {}'.format(src) return False, err st = os.stat(src) - ret, err = self._write(src, dst, content, st.st_mode, actions=actions) + ret, err = self._write(src, dst, content, + st.st_mode, actionexec=actionexec) if ret < 0: return False, err if ret > 0: @@ -268,7 +287,7 @@ class Installer: err = 'installing {} to {}'.format(src, dst) return False, err - def _handle_dir(self, templater, src, dst, actions=[], noempty=False): + def _handle_dir(self, templater, src, dst, actionexec=None, noempty=False): """install src to dst when is a directory""" if self.debug: self.log.dbg('install dir {}'.format(src)) @@ -285,7 +304,7 @@ class Installer: # is file res, err = self._handle_file(templater, f, os.path.join(dst, entry), - actions=actions, + actionexec=actionexec, noempty=noempty) if not res and err: # error occured @@ -298,7 +317,7 @@ class Installer: # is directory res, err = self._handle_dir(templater, f, os.path.join(dst, entry), - actions=actions, + actionexec=actionexec, noempty=noempty) if not res and err: # error occured @@ -316,7 +335,7 @@ class Installer: cur = f.read() return cur == content - def _write(self, src, dst, content, rights, actions=[]): + def _write(self, src, dst, content, rights, actionexec=None): """write content to file return 0, None: for success, 1, None: when already exists @@ -353,7 +372,7 @@ class Installer: if not self._create_dirs(base): err = 'creating directory for {}'.format(dst) return -1, err - r, e = self._exec_pre_actions(actions) + r, e = self._exec_pre_actions(actionexec) if not r: return -1, e if self.debug: @@ -422,21 +441,15 @@ class Installer: self.log.dbg('pivot \"{}\" to \"{}\"'.format(path, new)) return new - def _exec_pre_actions(self, actions): - """execute pre-actions if any""" + def _exec_pre_actions(self, actionexec): + """execute action executor""" if self.action_executed: return True, None - for action in actions: - if self.dry: - self.log.dry('would execute action: {}'.format(action)) - else: - if self.debug: - self.log.dbg('executing pre action {}'.format(action)) - if not action.execute(): - err = 'pre-action \"{}\" failed'.format(action.key) - return False, err + if not actionexec: + return True, None + ret, err = actionexec() self.action_executed = True - return True, None + return ret, err def _install_to_temp(self, templater, src, dst, tmpdir): """install a dotfile to a tempdir""" diff --git a/dotdrop/options.py b/dotdrop/options.py index 6e5d95f..ae30cc6 100644 --- a/dotdrop/options.py +++ b/dotdrop/options.py @@ -182,7 +182,7 @@ class Options(AttrMonitor): def _read_config(self, profile=None): """read the config file""" self.conf = Cfg(self.confpath, profile=profile, debug=self.debug) - # transform the configs in attribute + # transform the config settings to self attribute for k, v in self.conf.get_settings().items(): if self.debug: self.log.dbg('setting: {}={}'.format(k, v)) @@ -223,6 +223,7 @@ class Options(AttrMonitor): self.install_diff = not self.args['--nodiff'] self.install_showdiff = self.showdiff or self.args['--showdiff'] self.install_backup_suffix = BACKUP_SUFFIX + self.install_default_actions = self.default_actions # "compare" specifics self.compare_dopts = self.args['--dopts'] self.compare_focus = self.args['--file'] diff --git a/dotdrop/templategen.py b/dotdrop/templategen.py index 53b0bbc..d5b7a1f 100644 --- a/dotdrop/templategen.py +++ b/dotdrop/templategen.py @@ -51,17 +51,37 @@ class Templategen: self.env.globals['exists'] = jhelpers.exists self.env.globals['exists_in_path'] = jhelpers.exists_in_path - def generate(self, src): + def generate(self, src, tmpvars={}): """render template from path""" if not os.path.exists(src): return '' - return self._handle_file(src) + saved = self._patch_globals(tmpvars) + ret = self._handle_file(src) + self._restore_globals(saved) + return ret - def generate_string(self, string): + def generate_string(self, string, tmpvars={}): """render template from string""" if not string: return '' - return self.env.from_string(string).render() + saved = self._patch_globals(tmpvars) + if self.debug: + self.log.dbg('new vars: {}'.format(tmpvars)) + ret = self.env.from_string(string).render() + self._restore_globals(saved) + return ret + + def _patch_globals(self, newvars={}): + """add vars to the globals, make sure to call _restore_globals""" + saved_globals = self.env.globals.copy() + if not newvars: + return saved_globals + self.env.globals.update(newvars) + return saved_globals + + def _restore_globals(self, saved_globals): + """restore globals from _patch_globals""" + self.env.globals = saved_globals.copy() def update_variables(self, variables): """update variables""" diff --git a/tests-ng/actions-default.sh b/tests-ng/actions-default.sh new file mode 100755 index 0000000..b2cd891 --- /dev/null +++ b/tests-ng/actions-default.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# author: deadc0de6 (https://github.com/deadc0de6) +# Copyright (c) 2017, deadc0de6 +# +# test default action execution +# returns 1 in case of error +# + +# exit on first error +set -e + +# all this crap to get current path +rl="readlink -f" +if ! ${rl} "${0}" >/dev/null 2>&1; then + rl="realpath" + + if ! hash ${rl}; then + echo "\"${rl}\" not found !" && exit 1 + fi +fi +cur=$(dirname "$(${rl} "${0}")") + +#hash dotdrop >/dev/null 2>&1 +#[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 + +#echo "called with ${1}" + +# dotdrop path can be pass as argument +ddpath="${cur}/../" +[ "${1}" != "" ] && ddpath="${1}" +[ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 + +export PYTHONPATH="${ddpath}:${PYTHONPATH}" +bin="python3 -m dotdrop.dotdrop" + +echo "dotdrop path: ${ddpath}" +echo "pythonpath: ${PYTHONPATH}" + +# get the helpers +source ${cur}/helpers + +echo -e "\e[96m\e[1m==> RUNNING $(basename $BASH_SOURCE) <==\e[0m" + +################################################################ +# this is the test +################################################################ + +# the action temp +tmpa=`mktemp -d --suffix='-dotdrop-tests'` +# the dotfile source +tmps=`mktemp -d --suffix='-dotdrop-tests'` +mkdir -p ${tmps}/dotfiles +# the dotfile destination +tmpd=`mktemp -d --suffix='-dotdrop-tests'` + +# create the config file +cfg="${tmps}/config.yaml" + +cat > ${cfg} << _EOF +actions: + pre: + failpre: "false" + preaction: echo 'pre' > ${tmpa}/pre + preaction1: echo 'preinside' > ${tmpa}/preinside + post: + failpost: "false" + postaction: echo 'post' > ${tmpa}/post + postaction1: echo 'postinside' > ${tmpa}/postinside + nakedaction: echo 'naked' > ${tmpa}/naked + nakedaction1: echo 'nakedinside' > ${tmpa}/nakedinside +config: + backup: true + create: true + dotpath: dotfiles + default_actions: + - preaction + - postaction + - nakedaction +dotfiles: + f_abc: + dst: ${tmpd}/abc + src: abc + actions: + - preaction1 + - nakedaction1 + - postaction1 +profiles: + p1: + dotfiles: + - f_abc +_EOF +#cat ${cfg} + +# create the dotfile +echo 'test' > ${tmps}/dotfiles/abc + +# install +cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V + +# checks pre action +[ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 +[ ! -e ${tmpa}/preinside ] && echo 'pre action not executed' && exit 1 +grep pre ${tmpa}/pre >/dev/null +grep preinside ${tmpa}/preinside >/dev/null +# checks post action +[ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 +[ ! -e ${tmpa}/postinside ] && echo 'post action not executed' && exit 1 +grep post ${tmpa}/post >/dev/null +grep postinside ${tmpa}/postinside >/dev/null +# checks naked action +[ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 +[ ! -e ${tmpa}/nakedinside ] && echo 'naked action not executed' && exit 1 +grep naked ${tmpa}/naked >/dev/null +grep nakedinside ${tmpa}/nakedinside >/dev/null + +# clear +rm -f ${tmpa}/naked* ${tmpa}/pre* ${tmpa}/post* ${tmpd}/abc + +cat > ${cfg} << _EOF +actions: + pre: + failpre: "false" +config: + backup: true + create: true + dotpath: dotfiles + default_actions: + - failpre +dotfiles: + f_abc: + dst: ${tmpd}/abc + src: abc +profiles: + p1: + dotfiles: + - f_abc +_EOF + +# ensure failing actions make the installation fail +# install +set +e +cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V +set -e +[ -e ${tmpd}/abc ] && exit 1 + +## CLEANING +rm -rf ${tmps} ${tmpd} ${tmpa} + +echo "OK" +exit 0 diff --git a/tests-ng/actions-template.sh b/tests-ng/actions-template.sh new file mode 100755 index 0000000..e651ce0 --- /dev/null +++ b/tests-ng/actions-template.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# author: deadc0de6 (https://github.com/deadc0de6) +# Copyright (c) 2017, deadc0de6 +# +# test action template execution +# returns 1 in case of error +# + +# exit on first error +set -e + +# all this crap to get current path +rl="readlink -f" +if ! ${rl} "${0}" >/dev/null 2>&1; then + rl="realpath" + + if ! hash ${rl}; then + echo "\"${rl}\" not found !" && exit 1 + fi +fi +cur=$(dirname "$(${rl} "${0}")") + +#hash dotdrop >/dev/null 2>&1 +#[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 + +#echo "called with ${1}" + +# dotdrop path can be pass as argument +ddpath="${cur}/../" +[ "${1}" != "" ] && ddpath="${1}" +[ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 + +export PYTHONPATH="${ddpath}:${PYTHONPATH}" +bin="python3 -m dotdrop.dotdrop" + +echo "dotdrop path: ${ddpath}" +echo "pythonpath: ${PYTHONPATH}" + +# get the helpers +source ${cur}/helpers + +echo -e "\e[96m\e[1m==> RUNNING $(basename $BASH_SOURCE) <==\e[0m" + +################################################################ +# this is the test +################################################################ + +# the action temp +tmpa=`mktemp -d --suffix='-dotdrop-tests'` +# the dotfile source +tmps=`mktemp -d --suffix='-dotdrop-tests'` +mkdir -p ${tmps}/dotfiles +# the dotfile destination +tmpd=`mktemp -d --suffix='-dotdrop-tests'` + +# create the config file +cfg="${tmps}/config.yaml" + +cat > ${cfg} << _EOF +actions: + pre: + preaction: "echo {{@@ _dotfile_src @@}} > ${tmpa}/pre" + post: + postaction: "echo {{@@ _dotfile_src @@}} > ${tmpa}/post" + nakedaction: "echo {{@@ _dotfile_src @@}} > ${tmpa}/naked" +config: + backup: true + create: true + dotpath: dotfiles + default_actions: + - preaction + - postaction + - nakedaction +dotfiles: + f_abc: + dst: ${tmpd}/abc + src: abc +profiles: + p1: + dotfiles: + - f_abc +_EOF +#cat ${cfg} + +# create the dotfile +echo 'test' > ${tmps}/dotfiles/abc + +# install +cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V + +# checks action +[ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 +[ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 +[ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 +grep abc ${tmpa}/pre >/dev/null +grep abc ${tmpa}/post >/dev/null +grep abc ${tmpa}/naked >/dev/null + +# clear +rm -f ${tmpa}/naked* ${tmpa}/pre* ${tmpa}/post* ${tmpd}/abc + +cat > ${cfg} << _EOF +actions: + pre: + preaction: "echo {{@@ _dotfile_dst @@}} > ${tmpa}/pre" + post: + postaction: "echo {{@@ _dotfile_dst @@}} > ${tmpa}/post" + nakedaction: "echo {{@@ _dotfile_dst @@}} > ${tmpa}/naked" +config: + backup: true + create: true + dotpath: dotfiles +dotfiles: + f_abc: + dst: ${tmpd}/abc + src: abc + actions: + - preaction + - nakedaction + - postaction +profiles: + p1: + dotfiles: + - f_abc +_EOF + +# install +cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V + +# checks action +[ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 +[ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 +[ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 +grep "${tmpd}/abc" ${tmpa}/pre >/dev/null +grep "${tmpd}/abc" ${tmpa}/post >/dev/null +grep "${tmpd}/abc" ${tmpa}/naked >/dev/null + +## CLEANING +rm -rf ${tmps} ${tmpd} ${tmpa} + +echo "OK" +exit 0 diff --git a/tests/test_install.py b/tests/test_install.py index 44a9967..b4cd587 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -350,7 +350,7 @@ exec bspwm installer = Installer() installer.link_children(templater=MagicMock(), src=src_dir, - dst=dst_dir, actions=[]) + dst=dst_dir, actionexec=None) # Ensure all destination files point to source for src in srcs: @@ -366,7 +366,7 @@ exec bspwm installer.log.err = logger res, err = installer.link_children(templater=MagicMock(), src=src, - dst='/dev/null', actions=[]) + dst='/dev/null', actionexec=None) self.assertFalse(res) e = 'source dotfile does not exist: {}'.format(src) @@ -388,7 +388,7 @@ exec bspwm # pass src file not src dir res, err = installer.link_children(templater=templater, src=src, - dst='/dev/null', actions=[]) + dst='/dev/null', actionexec=None) # ensure nothing performed self.assertFalse(res) @@ -411,7 +411,7 @@ exec bspwm installer = Installer() installer.link_children(templater=MagicMock(), src=src_dir, - dst=dst_dir, actions=[]) + dst=dst_dir, actionexec=None) # ensure dst dir created self.assertTrue(os.path.exists(dst_dir)) @@ -443,7 +443,7 @@ exec bspwm installer.log.ask = ask installer.link_children(templater=MagicMock(), src=src_dir, dst=dst, - actions=[]) + actionexec=None) # ensure destination now a directory self.assertTrue(os.path.isdir(dst)) @@ -477,7 +477,7 @@ exec bspwm mocked_templategen.is_template.return_value = True installer.link_children(templater=templater, src=src_dir, dst=dst_dir, - actions=[]) + actionexec=None) for src in srcs: dst = os.path.join(dst_dir, os.path.basename(src))