diff --git a/README.md b/README.md index 33e7248..68a8d13 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ that don't need to appear in the output of compare. Either use the command line switch `-i --ignore` or add an entry in the dotfile directly in the `cmpignore` entry (see [Config](#config)). -The pattern follows Unix shell-style wildcards like for example `*/path/file`. +The ignore pattern must follow Unix shell-style wildcards like for example `*/path/file`. Make sure to quote those when using wildcards in the config file. It is also possible to install all dotfiles for a specific profile @@ -474,6 +474,21 @@ $ dotdrop update ~/.vimrc $ dotdrop update --key f_vimrc ``` +It is possible to ignore files to update using unix pattern by providing those +either through the switch `-i --ignore` or as part of the dotfile under the +key `upignore` (see [Config](#config)). +The ignore pattern must follow Unix shell-style wildcards like for example `*/path/file`. +Make sure to quote those when using wildcards in the config file. +```yaml +dotfiles: + d_vim + dst: ~/.vim + src: vim + upignore: + - "*/undo-dir" + - "*/plugged" +``` + There are two cases when updating a dotfile: **The dotfile doesn't use [templating](#template)** @@ -592,6 +607,7 @@ the following entries: * `src`: dotfile path within the `dotpath` (can use `variables` and `dynvariables`, make sure to quote). * `link`: if true dotdrop will create a symlink instead of copying (default *false*). * `cmpignore`: list of pattern to ignore when comparing (enclose in quotes when using wildcards). + * `upignore`: list of pattern to ignore when updating (enclose in quotes when using wildcards). * `actions`: list of action keys that need to be defined in the **actions** entry below. * `trans`: transformation key to apply when installing this dotfile (must be defined in the **trans** entry below). * `trans_write`: transformation key to apply when updating this dotfile (must be defined in the **trans_write** entry below). @@ -606,6 +622,8 @@ the following entries: ignoreempty: cmpignore: - "" + upignore: + - "" actions: - trans: @@ -616,6 +634,8 @@ the following entries: need to be managed * `dotfiles`: the dotfiles associated to this profile * `include`: include all dotfiles from another profile (optional) + * `variables`: profile specific variables (see [Variables](#variables)) + * `dynvariables`: profile specific interpreted variables (see [Interpreted variables](#interpreted-variables)) ```yaml : @@ -627,6 +647,10 @@ the following entries: include: - - ... + variables: + : + dynvariables: + : ``` * **actions** entry (optional): a list of action (see [Use actions](#use-actions)) @@ -743,6 +767,7 @@ For example in the config file: ```yaml variables: var1: some variable content + var2: some other content ``` These can then be used in any template with @@ -750,6 +775,26 @@ These can then be used in any template with {{@@ var1 @@}} ``` +Profile variables will take precedence over globally defined variables what +means that you could do something like this: +```yaml +variables: + git_email: home@email.com +dotfiles: + f_gitconfig: + dst: ~/.gitconfig + src: gitconfig +profiles: + work: + dotfiles: + - f_gitconfig + variables: + git_email: work@email.com + private: + dotfiles: + - f_gitconfig +``` + ## Interpreted variables It is also possible to have *dynamic* variables in the sense that their @@ -768,6 +813,9 @@ These can be used as any variables in the templates {{@@ dvar1 @@}} ``` +As for variables (see [Variables](#variables)) profile dynvariables will take +precedence over globally defined dynvariables. + ## Environment variables It's possible to access environment variables inside the templates. diff --git a/dotdrop/comparator.py b/dotdrop/comparator.py index 9fa3244..1d13049 100644 --- a/dotdrop/comparator.py +++ b/dotdrop/comparator.py @@ -7,7 +7,6 @@ handle the comparison of dotfiles and local deployment import os import filecmp -import fnmatch # local imports from dotdrop.logger import Logger @@ -34,7 +33,7 @@ class Comparator: def _comp_file(self, left, right, ignore): """compare a file""" - if self._ignore([left, right], ignore): + if utils.must_ignore([left, right], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring diff {} and {}'.format(left, right)) return '' @@ -44,7 +43,7 @@ class Comparator: """compare a directory""" if not os.path.exists(right): return '' - if self._ignore([left, right], ignore): + if utils.must_ignore([left, right], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring diff {} and {}'.format(left, right)) return '' @@ -54,13 +53,15 @@ class Comparator: comp = filecmp.dircmp(left, right) # handle files only in deployed file for i in comp.left_only: - if self._ignore([os.path.join(left, i)], ignore): + if utils.must_ignore([os.path.join(left, i)], + ignore, debug=self.debug): continue - ret.append('only in left: \"{}\"\n'.format(i)) + ret.append('=> \"{}\" does not exist on local\n'.format(i)) for i in comp.right_only: - if self._ignore([os.path.join(right, i)], ignore): + if utils.must_ignore([os.path.join(right, i)], + ignore, debug=self.debug): continue - ret.append('only in right: \"{}\"\n'.format(i)) + ret.append('=> \"{}\" does not exist in dotdrop\n'.format(i)) # same left and right but different type funny = comp.common_funny @@ -90,17 +91,5 @@ class Comparator: if header: lshort = os.path.basename(left) rshort = os.path.basename(right) - diff = 'diff \"{}\":\n{}'.format(lshort, diff) + diff = '=> diff \"{}\":\n{}'.format(lshort, diff) return diff - - def _ignore(self, paths, ignore): - '''return True if any paths is ignored - not very efficient''' - if not ignore: - return False - for p in paths: - for i in ignore: - if fnmatch.fnmatch(p, i): - if self.debug: - self.log.dbg('ignore match {}'.format(p)) - return True - return False diff --git a/dotdrop/config.py b/dotdrop/config.py index b6e62ac..f75fd53 100644 --- a/dotdrop/config.py +++ b/dotdrop/config.py @@ -45,7 +45,7 @@ class Cfg: # template variables key_variables = 'variables' - # shell variable + # shell variables key_dynvariables = 'dynvariables' # dotfiles keys @@ -58,6 +58,7 @@ class Cfg: key_dotfiles_actions = 'actions' key_dotfiles_trans_r = 'trans' key_dotfiles_trans_w = 'trans_write' + key_dotfiles_upignore = 'upignore' # profiles keys key_profiles = 'profiles' @@ -112,13 +113,14 @@ class Cfg: # represents all dotfiles per profile by profile key # NOT linked inside the yaml dict (self.content) self.prodots = {} + if not self._load_file(): raise ValueError('config is not valid') def eval_dotfiles(self, profile, debug=False): - """resolve dotfiles src/dst templates""" + """resolve dotfiles src/dst templating""" t = Templategen(profile=profile, - variables=self.get_variables(), + variables=self.get_variables(profile), debug=debug) for d in self.get_dotfiles(profile): d.src = t.generate_string(d.src) @@ -275,15 +277,20 @@ class Cfg: trans_r = None trans_w = None - # parse ignore pattern - ignores = v[self.key_dotfiles_cmpignore] if \ + # parse cmpignore pattern + cmpignores = v[self.key_dotfiles_cmpignore] if \ self.key_dotfiles_cmpignore in v else [] + # parse upignore pattern + upignores = v[self.key_dotfiles_upignore] if \ + self.key_dotfiles_upignore in v else [] + # create new dotfile self.dotfiles[k] = Dotfile(k, dst, src, link=link, actions=actions, trans_r=trans_r, trans_w=trans_w, - cmpignore=ignores, noempty=noempty) + cmpignore=cmpignores, noempty=noempty, + upignore=upignores) # assign dotfiles to each profile for k, v in self.lnk_profiles.items(): @@ -604,15 +611,35 @@ class Cfg: """return all defined settings""" return self.lnk_settings.copy() - def get_variables(self): + def get_variables(self, profile): + """return the variables for this profile""" variables = {} + + # global variables if self.key_variables in self.content: variables.update(self.content[self.key_variables]) + + # global dynvariables if self.key_dynvariables in self.content: # interpret dynamic variables dynvars = self.content[self.key_dynvariables] - for key, cmd in dynvars.items(): - variables[key] = shell(cmd) + for k, v in dynvars.items(): + variables[k] = shell(v) + + if profile not in self.lnk_profiles: + return variables + + # profile variables + var = self.lnk_profiles[profile] + if self.key_variables in var.keys(): + for k, v in var[self.key_variables].items(): + variables[k] = v + + # profile dynvariables + if self.key_dynvariables in var.keys(): + for k, v in var[self.key_dynvariables].items(): + variables[k] = shell(v) + return variables def dump(self): diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index b2d9683..e2ea565 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -44,7 +44,8 @@ Usage: dotdrop import [-ldVb] [-c ] [-p ] ... dotdrop compare [-Vb] [-c ] [-p ] [-o ] [-C ...] [-i ...] - dotdrop update [-fdVbk] [-c ] [-p ] [...] + dotdrop update [-fdVbk] [-c ] [-p ] + [-i ...] [...] dotdrop listfiles [-VTb] [-c ] [-p ] dotdrop detail [-Vb] [-c ] [-p ] [...] dotdrop list [-Vb] [-c ] @@ -55,13 +56,13 @@ Options: -p --profile= Specify the profile to use [default: {}]. -c --cfg= Path to the config [default: config.yaml]. -C --file= Path of dotfile to compare. - -i --ignore= Pattern to ignore when diffing. + -i --ignore= Pattern to ignore. -o --dopts= Diff options [default: ]. -n --nodiff Do not diff when installing. -t --temp Install to a temporary directory for review. -T --template Only template dotfiles. -D --showdiff Show a diff before overwriting. - -l --link Import and link. + -l --inv-link Invert the value of "link_by_default" when importing. -f --force Do not warn if exists. -k --key Treat as a dotfile key. -V --verbose Be verbose. @@ -170,7 +171,10 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]): LOG.dbg('comparing {}'.format(dotfile)) src = dotfile.src if not os.path.lexists(os.path.expanduser(dotfile.dst)): - LOG.emph('\"{}\" does not exist on local\n'.format(dotfile.dst)) + line = '=> compare {}: \"{}\" does not exist on local' + LOG.log(line.format(dotfile.key, dotfile.dst)) + same = False + continue tmpsrc = None if dotfile.trans_r: @@ -178,12 +182,14 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]): tmpsrc = apply_trans(opts, dotfile) if not tmpsrc: # could not apply trans + same = False continue src = tmpsrc # install dotfile to temporary dir ret, insttmp = inst.install_to_temp(t, tmp, src, dotfile.dst) if not ret: # failed to install to tmp + same = False continue ignores = list(set(ignore + dotfile.cmpignore)) diff = comp.compare(insttmp, dotfile.dst, ignore=ignores) @@ -194,23 +200,24 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]): remove(tmpsrc) if diff == '': if opts['debug']: - LOG.dbg('diffing \"{}\" VS \"{}\"'.format(dotfile.key, - dotfile.dst)) + line = '=> compare {}: diffing with \"{}\"' + LOG.dbg(line.format(dotfile.key, dotfile.dst)) LOG.dbg('same file') else: - LOG.log('diffing \"{}\" VS \"{}\"'.format(dotfile.key, - dotfile.dst)) + line = '=> compare {}: diffing with \"{}\"' + LOG.log(line.format(dotfile.key, dotfile.dst)) LOG.emph(diff) same = False return same -def cmd_update(opts, conf, paths, iskey=False): +def cmd_update(opts, conf, paths, iskey=False, ignore=[]): """update the dotfile(s) from path(s) or key(s)""" ret = True updater = Updater(conf, opts['dotpath'], opts['dry'], - opts['safe'], iskey=iskey, debug=opts['debug']) + opts['safe'], iskey=iskey, + debug=opts['debug'], ignore=[]) if not iskey: # update paths if opts['debug']: @@ -434,9 +441,9 @@ def main(): opts['profile'] = args['--profile'] opts['safe'] = not args['--force'] opts['installdiff'] = not args['--nodiff'] - opts['link'] = args['--link'] + opts['link'] = args['--inv-link'] opts['debug'] = args['--verbose'] - opts['variables'] = conf.get_variables() + opts['variables'] = conf.get_variables(opts['profile']) opts['showdiff'] = opts['showdiff'] or args['--showdiff'] if opts['debug']: @@ -495,7 +502,8 @@ def main(): if opts['debug']: LOG.dbg('running cmd: update') iskey = args['--key'] - ret = cmd_update(opts, conf, args[''], iskey=iskey) + ret = cmd_update(opts, conf, args[''], iskey=iskey, + ignore=args['--ignore']) elif args['detail']: # detail files diff --git a/dotdrop/dotfile.py b/dotdrop/dotfile.py index f758de7..bf82146 100644 --- a/dotdrop/dotfile.py +++ b/dotdrop/dotfile.py @@ -12,7 +12,8 @@ class Dotfile: def __init__(self, key, dst, src, actions={}, trans_r=None, trans_w=None, - link=LinkTypes.NOLINK, cmpignore=[], noempty=False): + link=LinkTypes.NOLINK, cmpignore=[], noempty=False, + upignore=[]): # key of dotfile in the config self.key = key # path where to install this dotfile @@ -31,6 +32,8 @@ class Dotfile: self.cmpignore = cmpignore # do not deploy empty file self.noempty = noempty + # pattern to ignore when updating + self.upignore = upignore def __str__(self): msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"' diff --git a/dotdrop/templategen.py b/dotdrop/templategen.py index f911261..6b7c8e9 100644 --- a/dotdrop/templategen.py +++ b/dotdrop/templategen.py @@ -70,7 +70,7 @@ class Templategen: filetype = filetype.strip() if self.debug: self.log.dbg('\"{}\" filetype: {}'.format(src, filetype)) - istext = 'text' in filetype + istext = 'text' in filetype or 'empty' in filetype if self.debug: self.log.dbg('\"{}\" is text: {}'.format(src, istext)) if not istext: diff --git a/dotdrop/updater.py b/dotdrop/updater.py index 8a3e1a1..46a2d40 100644 --- a/dotdrop/updater.py +++ b/dotdrop/updater.py @@ -21,13 +21,14 @@ TILD = '~' class Updater: def __init__(self, conf, dotpath, dry, safe, - iskey=False, debug=False): + iskey=False, debug=False, ignore=[]): self.conf = conf self.dotpath = dotpath self.dry = dry self.safe = safe self.iskey = iskey self.debug = debug + self.ignore = ignore self.log = Logger() def update_path(self, path, profile): @@ -58,9 +59,16 @@ class Updater: """update dotfile from file pointed by path""" ret = False new_path = None + self.ignores = list(set(self.ignore + dotfile.upignore)) + if self.debug: + self.log.dbg('ignore pattern(s): {}'.format(self.ignores)) + left = os.path.expanduser(path) right = os.path.join(self.conf.abs_or_rel(self.dotpath), dotfile.src) right = os.path.expanduser(right) + + if self._ignore([left, right]): + return True if dotfile.trans_w: # apply write transformation if any new_path = self._apply_trans_w(path, dotfile) @@ -137,6 +145,8 @@ class Updater: def _handle_file(self, left, right, compare=True): """sync left (deployed file) and right (dotdrop dotfile)""" + if self._ignore([left, right]): + return True if self.debug: self.log.dbg('update for file {} and {}'.format(left, right)) if self._is_template(right): @@ -169,6 +179,8 @@ class Updater: # paths must be absolute (no tildes) left = os.path.expanduser(left) right = os.path.expanduser(right) + if self._ignore([left, right]): + return True # find the differences diff = filecmp.dircmp(left, right, ignore=None) # handle directories diff @@ -179,6 +191,8 @@ class Updater: left, right = diff.left, diff.right if self.debug: self.log.dbg('sync dir {} to {}'.format(left, right)) + if self._ignore([left, right]): + return True # create dirs that don't exist in dotdrop for toadd in diff.left_only: @@ -188,6 +202,8 @@ class Updater: continue # match to dotdrop dotpath new = os.path.join(right, toadd) + if self._ignore([exist, new]): + continue if self.dry: self.log.dry('would cp -r {} {}'.format(exist, new)) continue @@ -202,6 +218,8 @@ class Updater: if not os.path.isdir(old): # ignore files for now continue + if self._ignore([old]): + continue if self.dry: self.log.dry('would rm -r {}'.format(old)) continue @@ -219,6 +237,8 @@ class Updater: for f in fdiff: fleft = os.path.join(left, f) fright = os.path.join(right, f) + if self._ignore([fleft, fright]): + continue if self.dry: self.log.dry('would cp {} {}'.format(fleft, fright)) continue @@ -233,6 +253,8 @@ class Updater: # ignore dirs, done above continue new = os.path.join(right, toadd) + if self._ignore([exist, new]): + continue if self.dry: self.log.dry('would cp {} {}'.format(exist, new)) continue @@ -248,6 +270,8 @@ class Updater: if os.path.isdir(new): # ignore dirs, done above continue + if self._ignore([new]): + continue if self.dry: self.log.dry('would rm {}'.format(new)) continue @@ -275,3 +299,10 @@ class Updater: if self.safe and not self.log.ask(msg): return False return True + + def _ignore(self, paths): + if utils.must_ignore(paths, self.ignores, debug=self.debug): + if self.debug: + self.log.dbg('ignoring update for {}'.format(paths)) + return True + return False diff --git a/dotdrop/utils.py b/dotdrop/utils.py index e4afcad..e08af6e 100644 --- a/dotdrop/utils.py +++ b/dotdrop/utils.py @@ -12,6 +12,7 @@ import uuid import shlex import functools import operator +import fnmatch from shutil import rmtree # local import @@ -115,3 +116,16 @@ def strip_home(path): def flatten(a): """flatten list""" return functools.reduce(operator.iconcat, a, []) + + +def must_ignore(paths, ignores, debug=False): + """return true if any paths in list matches any ignore patterns""" + if not ignores: + return False + for p in paths: + for i in ignores: + if fnmatch.fnmatch(p, i): + if debug: + LOG.dbg('ignore \"{}\" match: {}'.format(i, p)) + return True + return False diff --git a/tests-ng/profile-dynvariables.sh b/tests-ng/profile-dynvariables.sh new file mode 100755 index 0000000..1940bb6 --- /dev/null +++ b/tests-ng/profile-dynvariables.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# author: deadc0de6 (https://github.com/deadc0de6) +# Copyright (c) 2017, deadc0de6 +# +# test variables per profile +# 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 dotfile source +tmps=`mktemp -d` +mkdir -p ${tmps}/dotfiles +# the dotfile destination +tmpd=`mktemp -d` +#echo "dotfile destination: ${tmpd}" + +# create a shell script +export TESTENV="this is my global testenv" +scr=`mktemp` +chmod +x ${scr} +echo -e "#!/bin/bash\necho $TESTENV\n" >> ${scr} + +export TESTENV2="this is my profile testenv" +scr2=`mktemp` +chmod +x ${scr2} +echo -e "#!/bin/bash\necho $TESTENV2\n" >> ${scr2} + +# create the config file +cfg="${tmps}/config.yaml" + +cat > ${cfg} << _EOF +config: + backup: true + create: true + dotpath: dotfiles +variables: + gvar1: "global1" + gvar2: "global2" +dynvariables: + gdvar1: head -1 /proc/meminfo + gdvar2: "echo 'this is some test' | rev | tr ' ' ','" + gdvar3: ${scr} +dotfiles: + f_abc: + dst: ${tmpd}/abc + src: abc +profiles: + p1: + variables: + gvar1: "local1" + lvar1: "local2" + dynvariables: + gdvar3: ${scr2} + pdvar1: "echo 'abc' | rev" + dotfiles: + - f_abc +_EOF +cat ${cfg} + +# create the dotfile +echo "===================" > ${tmps}/dotfiles/abc +echo "{{@@ gvar1 @@}}" >> ${tmps}/dotfiles/abc +echo "{{@@ gvar2 @@}}" >> ${tmps}/dotfiles/abc +echo "{{@@ gdvar1 @@}}" >> ${tmps}/dotfiles/abc +echo "{{@@ gdvar2 @@}}" >> ${tmps}/dotfiles/abc +echo "{{@@ gdvar3 @@}}" >> ${tmps}/dotfiles/abc +echo "{{@@ lvar1 @@}}" >> ${tmps}/dotfiles/abc +echo "{{@@ pdvar1 @@}}" >> ${tmps}/dotfiles/abc +echo "===================" >> ${tmps}/dotfiles/abc + +# install +cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V + +#cat ${tmpd}/abc + +# test variables +grep '^local1' ${tmpd}/abc >/dev/null +grep '^global2' ${tmpd}/abc >/dev/null +grep '^local2' ${tmpd}/abc >/dev/null +# test dynvariables +grep "^MemTotal" ${tmpd}/abc >/dev/null +grep '^tset,emos,si,siht' ${tmpd}/abc >/dev/null +grep "^${TESTENV2}" ${tmpd}/abc > /dev/null +grep "^cba" ${tmpd}/abc >/dev/null + +#cat ${tmpd}/abc + +## CLEANING +rm -rf ${tmps} ${tmpd} ${scr} ${scr2} + +echo "OK" +exit 0 diff --git a/tests-ng/update-ignore.sh b/tests-ng/update-ignore.sh new file mode 100755 index 0000000..077bdea --- /dev/null +++ b/tests-ng/update-ignore.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# author: deadc0de6 (https://github.com/deadc0de6) +# Copyright (c) 2017, deadc0de6 +# +# test ignore update +# 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 +################################################################ + +# dotdrop directory +tmps=`mktemp -d` +dt="${tmps}/dotfiles" +mkdir -p ${dt} +mkdir -p ${dt}/a/{b,c} +echo 'a' > ${dt}/a/b/abfile +echo 'a' > ${dt}/a/c/acfile + +# fs dotfiles +tmpd=`mktemp -d` +cp -r ${dt}/a ${tmpd}/ + +# create the config file +cfg="${tmps}/config.yaml" +cat > ${cfg} << _EOF +config: + backup: false + create: true + dotpath: dotfiles +dotfiles: + f_abc: + dst: ${tmpd}/a + src: a + upignore: + - "*/cfile" + - "*/newfile" + - "*/newdir" +profiles: + p1: + dotfiles: + - f_abc +_EOF +cat ${cfg} + +#tree ${dt} + +# edit/add files +echo "[+] edit/add files" +touch ${tmpd}/a/newfile +echo 'b' > ${tmpd}/a/c/acfile +mkdir -p ${tmpd}/a/newdir/b +touch ${tmpd}/a/newdir/b/c + +#tree ${tmpd}/a + +# update +echo "[+] update" +cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc + +#tree ${dt} + +# check files haven't been updated +grep 'b' ${dt}/a/c/acfile >/dev/null +[ -e ${dt}/a/newfile ] && exit 1 + +## CLEANING +rm -rf ${tmps} ${tmpd} + +echo "OK" +exit 0 diff --git a/tests.sh b/tests.sh index 77c7496..61f3589 100755 --- a/tests.sh +++ b/tests.sh @@ -6,6 +6,8 @@ set -ev # PEP8 tests +which pycodestyle 2>/dev/null +[ "$?" != "0" ] && echo "Install pycodestyle" && exit 1 pycodestyle --ignore=W605 dotdrop/ pycodestyle tests/ pycodestyle scripts/ diff --git a/tests/test_install.py b/tests/test_install.py index 956cc41..11a57d0 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -277,11 +277,12 @@ exec bspwm src = create_random_file(src_dir)[0] logger = MagicMock() + templater = MagicMock() installer = Installer() installer.log.err = logger # pass src file not src dir - res = installer.linkall(templater=MagicMock(), src=src, dst='/dev/null', + res = installer.linkall(templater=templater, src=src, dst='/dev/null', actions=[]) # ensure nothing performed