diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index b66f71c..1e15c23 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -8,6 +8,7 @@ entry point import os import sys import time +import fnmatch from concurrent import futures # local imports @@ -20,7 +21,7 @@ from dotdrop.comparator import Comparator from dotdrop.importer import Importer from dotdrop.utils import get_tmpdir, removepath, \ uniq_list, patch_ignores, dependencies_met, \ - adapt_workers, check_version + adapt_workers, check_version, pivot_path from dotdrop.linktypes import LinkTypes from dotdrop.exceptions import YamlException, \ UndefinedException, UnmetDependency @@ -371,6 +372,44 @@ def cmd_install(opts): return True +def _workdir_enum(opts): + workdir_files = [] + for root, dirs, files in os.walk(opts.workdir): + for file in files: + fpath = os.path.join(root, file) + workdir_files.append(fpath) + + for dotfile in opts.dotfiles: + src = os.path.join(opts.dotpath, dotfile.src) + if dotfile.link == LinkTypes.NOLINK: + # ignore not link files + continue + if not Templategen.is_template(src): + # ignore not template + continue + newpath = pivot_path(dotfile.dst, opts.workdir, + striphome=True, logger=None) + if os.path.isdir(newpath): + # recursive + pattern = '{}/*'.format(newpath) + files = workdir_files.copy() + for f in files: + if fnmatch.fnmatch(f, pattern): + workdir_files.remove(f) + # only checks children + children = [f.path for f in os.scandir(newpath)] + for c in children: + if c in workdir_files: + workdir_files.remove(c) + else: + if newpath in workdir_files: + workdir_files.remove(newpath) + for w in workdir_files: + line = '=> \"{}\" does not exist in dotdrop' + LOG.log(line.format(w)) + return len(workdir_files) + + def cmd_compare(opts, tmp): """compare dotfiles and return True if all identical""" dotfiles = opts.dotfiles @@ -416,6 +455,10 @@ def cmd_compare(opts, tmp): same = False cnt += 1 + # TODO + if _workdir_enum(opts) > 0: + same = False + LOG.log('\n{} dotfile(s) compared.'.format(cnt)) return same diff --git a/dotdrop/installer.py b/dotdrop/installer.py index fd2a69a..db05773 100644 --- a/dotdrop/installer.py +++ b/dotdrop/installer.py @@ -224,7 +224,7 @@ class Installer: self.totemp = None # install the dotfile to a temp directory - tmpdst = self._pivot_path(dst, tmpdir) + tmpdst = utils.pivot_path(dst, tmpdir, logger=self.log) ret, err = self.install(templater, src, tmpdst, LinkTypes.NOLINK, is_template=is_template, @@ -260,7 +260,8 @@ class Installer: if is_template: self.log.dbg('is a template') self.log.dbg('install to {}'.format(self.workdir)) - tmp = self._pivot_path(dst, self.workdir, striphome=True) + tmp = utils.pivot_path(dst, self.workdir, + striphome=True, logger=self.log) ret, err = self.install(templater, src, tmp, LinkTypes.NOLINK, actionexec=actionexec, @@ -326,7 +327,8 @@ class Installer: self.log.dbg('child is a template') self.log.dbg('install to {} and symlink' .format(self.workdir)) - tmp = self._pivot_path(subdst, self.workdir, striphome=True) + tmp = utils.pivot_path(subdst, self.workdir, + striphome=True, logger=self.log) ret2, err2 = self.install(templater, subsrc, tmp, LinkTypes.NOLINK, actionexec=actionexec, @@ -698,17 +700,6 @@ class Installer: self.log.log('backup {} to {}'.format(path, dst)) os.rename(path, dst) - def _pivot_path(self, path, newdir, striphome=False): - """change path to be under newdir""" - self.log.dbg('pivot new dir: \"{}\"'.format(newdir)) - self.log.dbg('strip home: {}'.format(striphome)) - if striphome: - path = utils.strip_home(path) - sub = path.lstrip(os.sep) - new = os.path.join(newdir, sub) - self.log.dbg('pivot \"{}\" to \"{}\"'.format(path, new)) - return new - def _exec_pre_actions(self, actionexec): """execute action executor""" if self.action_executed: diff --git a/dotdrop/utils.py b/dotdrop/utils.py index 67798ae..38261f6 100644 --- a/dotdrop/utils.py +++ b/dotdrop/utils.py @@ -478,3 +478,17 @@ def check_version(): if version.parse(VERSION) < version.parse(latest): msg = 'A new version of dotdrop is available ({})' LOG.warn(msg.format(latest)) + + +def pivot_path(path, newdir, striphome=False, logger=None): + """change path to be under newdir""" + if logger: + logger.dbg('pivot new dir: \"{}\"'.format(newdir)) + logger.dbg('strip home: {}'.format(striphome)) + if striphome: + path = strip_home(path) + sub = path.lstrip(os.sep) + new = os.path.join(newdir, sub) + if logger: + logger.dbg('pivot \"{}\" to \"{}\"'.format(path, new)) + return new diff --git a/tests-ng/workdir-compare.sh b/tests-ng/workdir-compare.sh new file mode 100755 index 0000000..2be1873 --- /dev/null +++ b/tests-ng/workdir-compare.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# author: deadc0de6 (https://github.com/deadc0de6) +# Copyright (c) 2021, deadc0de6 +# +# test workdir compare and warn on untracked files +# 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" +hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true + +echo "dotdrop path: ${ddpath}" +echo "pythonpath: ${PYTHONPATH}" + +# get the helpers +source ${cur}/helpers + +echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" + +################################################################ +# this is the test +################################################################ +unset DOTDROP_WORKDIR +string="blabla" + +# the dotfile source +tmp=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` + +tmpf="${tmp}/dotfiles" +tmpw="${tmp}/workdir" + +mkdir -p ${tmpf} +echo "dotfiles source (dotpath): ${tmpf}" +mkdir -p ${tmpw} +echo "workdir: ${tmpw}" + +# create the config file +cfg="${tmp}/config.yaml" +echo "config file: ${cfg}" + +# the dotfile destination +tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` +echo "dotfiles destination: ${tmpd}" + +clear_on_exit "${tmp}" +clear_on_exit "${tmpd}" + +cat > ${cfg} << _EOF +config: + backup: true + create: true + dotpath: dotfiles + workdir: ${tmpw} +dotfiles: + f_a: + dst: ${tmpd}/a + src: a + link: link + f_b: + dst: ${tmpd}/b + src: b + link: nolink + d_c: + dst: ${tmpd}/c + src: c + link: link_children +profiles: + p1: + dotfiles: + - f_a + - f_b + - d_c +_EOF +#cat ${cfg} + +# create the dotfile +echo "{{@@ profile @@}}" > ${tmpf}/a +echo "{{@@ profile @@}}" > ${tmpf}/b +mkdir -p ${tmpf}/c +echo "{{@@ profile @@}}" > ${tmpf}/c/a +echo "{{@@ profile @@}}" > ${tmpf}/c/b +mkdir ${tmpf}/c/x +echo "{{@@ profile @@}}" > ${tmpf}/c/x/a +echo "{{@@ profile @@}}" > ${tmpf}/c/x/b + +# install +cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b + +# compare (no diff) +cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V + +# add file +touch ${tmpw}/untrack + +# compare (one diff) +set +e +cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V +[ "$?" != "1" ] && echo "not found untracked file in workdir (1)" && exit 1 +set -e + +# clean +rm ${tmpw}/untrack +# add sub file +touch ${tmpw}/${tmpd}/c/x/untrack + +# compare (two diff) +set +e +cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V +[ "$?" != "1" ] && echo "not found untracked file in workdir (2)" && exit 1 +set -e + +# clean +rm ${tmpw}/${tmpd}/c/x/untrack +# add dir +mkdir ${tmpw}/d_untrack +touch ${tmpw}/d_untrack/untrack + +# compare (three diffs) +set +e +cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V +[ "$?" != "1" ] && echo "not found untracked file in workdir (3)" && exit 1 +set -e + +# clean +rm -r ${tmpw}/d_untrack +# add sub dir +mkdir ${tmpw}/${tmpd}/c/x/d_untrack +touch ${tmpw}/${tmpd}/c/x/d_untrack/untrack + +# compare +set +e +cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V +[ "$?" != "1" ] && echo "not found untracked file in workdir (4)" && exit 1 +set -e + +## CLEANING +rm -rf ${tmp} ${tmpd} + +echo "OK" +exit 0