From 0182e314ae2fa9ae5eef41f3761c5f267e0ff0cb Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Mon, 1 Nov 2021 21:36:30 +0100 Subject: [PATCH 1/2] workdir compare --- dotdrop/dotdrop.py | 45 +++++++++- dotdrop/installer.py | 19 ++--- dotdrop/utils.py | 14 +++ tests-ng/workdir-compare.sh | 165 ++++++++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+), 15 deletions(-) create mode 100755 tests-ng/workdir-compare.sh 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 From f07c32426636d580394809c78bef0acf60b35c01 Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Mon, 1 Nov 2021 21:39:42 +0100 Subject: [PATCH 2/2] linting --- dotdrop/dotdrop.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index 1e15c23..22dc87f 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -374,7 +374,7 @@ def cmd_install(opts): def _workdir_enum(opts): workdir_files = [] - for root, dirs, files in os.walk(opts.workdir): + for root, _, files in os.walk(opts.workdir): for file in files: fpath = os.path.join(root, file) workdir_files.append(fpath) @@ -393,20 +393,20 @@ def _workdir_enum(opts): # recursive pattern = '{}/*'.format(newpath) files = workdir_files.copy() - for f in files: - if fnmatch.fnmatch(f, pattern): - workdir_files.remove(f) + for file in files: + if fnmatch.fnmatch(file, pattern): + workdir_files.remove(file) # 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) + for child in children: + if child in workdir_files: + workdir_files.remove(child) else: if newpath in workdir_files: workdir_files.remove(newpath) - for w in workdir_files: + for wfile in workdir_files: line = '=> \"{}\" does not exist in dotdrop' - LOG.log(line.format(w)) + LOG.log(line.format(wfile)) return len(workdir_files) @@ -455,8 +455,7 @@ def cmd_compare(opts, tmp): same = False cnt += 1 - # TODO - if _workdir_enum(opts) > 0: + if _workdir_enum(opts) > 0: same = False LOG.log('\n{} dotfile(s) compared.'.format(cnt))