1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-04 19:09:44 +00:00

Merge pull request #334 from deadc0de6/compare-workdir

Compare workdir
This commit is contained in:
deadc0de
2021-11-11 10:22:56 +01:00
committed by GitHub
7 changed files with 237 additions and 16 deletions

View File

@@ -16,6 +16,7 @@ Entry | Description | Default
`check_version` | Check if a new version of dotdrop is available on github | false
`chmod_on_import` | Always add a chmod entry on newly imported dotfiles (see `--preserve-mode`) | false
`clear_workdir` | On `install` clear the `workdir` before installing dotfiles (see `--workdir-clear`) | false
`compare_workdir` | On `compare` notify on files in `workdir` not tracked by dotdrop | false
`cmpignore` | List of patterns to ignore when comparing, applied to all dotfiles (enclose in quotes when using wildcards; see [ignore patterns](config.md#ignore-patterns)) | -
`create` | Create a directory hierarchy when installing dotfiles if it doesn't exist | true
`default_actions` | List of action keys to execute for all installed dotfiles (See [actions](config-details.md#actions-entry)) | -

View File

@@ -112,6 +112,9 @@ To ignore specific patterns, see [the ignore patterns](config.md#ignore-patterns
To completely ignore all files not present in `dotpath` see [Ignore missing](#ignore-missing).
If you want to get notified on files present in the `workdir` but not tracked
by dotdrop see the [compare_workdir](config-format.md).
For more options, see the usage with `dotdrop --help`.
## List profiles

View File

@@ -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
@@ -379,6 +380,44 @@ def cmd_install(opts):
return True
def _workdir_enum(opts):
workdir_files = []
for root, _, 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 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 child in children:
if child in workdir_files:
workdir_files.remove(child)
else:
if newpath in workdir_files:
workdir_files.remove(newpath)
for wfile in workdir_files:
line = '=> \"{}\" does not exist in dotdrop'
LOG.log(line.format(wfile))
return len(workdir_files)
def cmd_compare(opts, tmp):
"""compare dotfiles and return True if all identical"""
dotfiles = opts.dotfiles
@@ -424,6 +463,9 @@ def cmd_compare(opts, tmp):
same = False
cnt += 1
if opts.compare_workdir and _workdir_enum(opts) > 0:
same = False
LOG.log('\n{} dotfile(s) compared.'.format(cnt))
return same

View File

@@ -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:

View File

@@ -47,6 +47,7 @@ class Settings(DictParser):
key_chmod_on_import = 'chmod_on_import'
key_check_version = 'check_version'
key_clear_workdir = 'clear_workdir'
key_compare_workdir = 'compare_workdir'
# import keys
key_import_actions = 'import_actions'
@@ -67,7 +68,8 @@ class Settings(DictParser):
template_dotfile_default=True,
ignore_missing_in_dotdrop=False,
force_chmod=False, chmod_on_import=False,
check_version=False, clear_workdir=False):
check_version=False, clear_workdir=False,
compare_workdir=False):
self.backup = backup
self.banner = banner
self.create = create
@@ -99,6 +101,7 @@ class Settings(DictParser):
self.chmod_on_import = chmod_on_import
self.check_version = check_version
self.clear_workdir = clear_workdir
self.compare_workdir = compare_workdir
def _serialize_seq(self, name, dic):
"""serialize attribute 'name' into 'dic'"""
@@ -127,6 +130,7 @@ class Settings(DictParser):
self.key_chmod_on_import: self.chmod_on_import,
self.key_check_version: self.check_version,
self.key_clear_workdir: self.clear_workdir,
self.key_compare_workdir: self.compare_workdir,
}
self._serialize_seq(self.key_default_actions, dic)
self._serialize_seq(self.key_import_actions, dic)

View File

@@ -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

166
tests-ng/workdir-compare.sh Executable file
View File

@@ -0,0 +1,166 @@
#!/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}
compare_workdir: true
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