1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-04 15:39:43 +00:00

add ability to compare with patterns in dotfile (#57)

This commit is contained in:
deadc0de6
2018-09-19 09:40:17 +02:00
parent c8ee2f7be1
commit ce53fb4339
7 changed files with 99 additions and 25 deletions

View File

@@ -268,6 +268,15 @@ $ dotdrop compare
The diffing is done by `diff` in the backend, one can provide specific
options to diff using the `-o` switch.
It is possible to add patterns to ignore when using `compare` for example
when a directory is managed by dotdrop and might contain temporary files
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`.
Make sure to quote those when using wildcards in the config file.
It is also possible to install all dotfiles for a specific profile
in a temporary directory in order to manually compare them with
the local version by using `install` and the `-t` switch.
@@ -553,6 +562,7 @@ the following entries:
* **dotfiles** entry: a list of dotfiles
* When `link` is true, dotdrop will create a symlink instead of copying (default *false*).
* `cmpignore` contains a list of pattern to ignore when comparing (enclose in quotes when using wildcards).
* `actions` contains a list of action keys that need to be defined in the **actions** entry below.
* `trans` contains a list of transformation keys that need to be defined in the **trans** entry below.
@@ -562,6 +572,8 @@ the following entries:
src: <filename-within-the-dotpath>
# Optional
link: <true|false>
cmpignore:
- "<ignore-pattern>"
actions:
- <action-key>
trans:

View File

@@ -8,6 +8,7 @@ handle the comparison of dotfiles and local deployment
import os
import shutil
import filecmp
import fnmatch
# local imports
from dotdrop.logger import Logger
@@ -16,45 +17,47 @@ import dotdrop.utils as utils
class Comparator:
def __init__(self, diffopts='', ignore=[], debug=False):
def __init__(self, diffopts='', debug=False):
self.diffopts = diffopts
self.ignore = [os.path.expanduser(i) for i in ignore]
self.debug = debug
self.log = Logger()
def compare(self, left, right):
def compare(self, left, right, ignore=[]):
"""diff left (dotdrop dotfile) and right (deployed file)"""
left = os.path.expanduser(left)
right = os.path.expanduser(right)
if self.debug:
self.log.dbg('comparing {} and {}'.format(left, right))
self.log.dbg('ignore pattern(s): {}'.format(ignore))
if not os.path.isdir(left):
return self._comp_file(left, right)
return self._comp_dir(left, right)
return self._comp_file(left, right, ignore)
return self._comp_dir(left, right, ignore)
def _comp_file(self, left, right):
def _comp_file(self, left, right, ignore):
"""compare a file"""
if left in self.ignore or right in self.ignore:
if self._ignore([left, right], ignore):
if self.debug:
self.log.dbg('ignoring diff {} and {}'.format(left, right))
return ''
return self._diff(left, right)
def _comp_dir(self, left, right):
def _comp_dir(self, left, right, ignore):
"""compare a directory"""
if left in self.ignore or right in self.ignore:
if self._ignore([left, right], ignore):
if self.debug:
self.log.dbg('ignoring diff {} and {}'.format(left, right))
return ''
if self.debug:
self.log.dbg('compare {} and {}'.format(left, right))
ret = []
comp = filecmp.dircmp(left, right, ignore=self.ignore)
comp = filecmp.dircmp(left, right)
# handle files only in deployed file
for i in comp.left_only:
if os.path.join(left, i) in self.ignore:
if self._ignore([os.path.join(left, i)], ignore):
continue
ret.append('only in left: \"{}\"\n'.format(i))
for i in comp.right_only:
if os.path.join(right, i) in self.ignore:
if self._ignore([os.path.join(right, i)], ignore):
continue
ret.append('only in right: \"{}\"\n'.format(i))
@@ -88,3 +91,15 @@ class Comparator:
rshort = os.path.basename(right)
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

View File

@@ -47,6 +47,7 @@ class Cfg:
key_dotfiles_src = 'src'
key_dotfiles_dst = 'dst'
key_dotfiles_link = 'link'
key_dotfiles_cmpignore = 'cmpignore'
key_dotfiles_actions = 'actions'
key_dotfiles_trans = 'trans'
@@ -198,10 +199,11 @@ class Cfg:
msg += ' because link is True'
self.log.warn(msg)
trans = []
ignores = v[self.key_dotfiles_cmpignore] if \
self.key_dotfiles_cmpignore in v else []
self.dotfiles[k] = Dotfile(k, dst, src,
link=link,
actions=actions,
trans=trans)
link=link, actions=actions,
trans=trans, cmpignore=ignores)
# assign dotfiles to each profile
for k, v in self.lnk_profiles.items():

View File

@@ -195,8 +195,7 @@ def compare(opts, conf, tmp, focus=None, ignore=[]):
inst = Installer(create=opts['create'], backup=opts['backup'],
dry=opts['dry'], base=opts['dotpath'],
workdir=opts['workdir'], debug=opts['debug'])
comp = Comparator(diffopts=opts['dopts'], debug=opts['debug'],
ignore=ignore)
comp = Comparator(diffopts=opts['dopts'], debug=opts['debug'])
for dotfile in selected:
if opts['debug']:
@@ -218,7 +217,8 @@ def compare(opts, conf, tmp, focus=None, ignore=[]):
if not ret:
# failed to install to tmp
continue
diff = comp.compare(insttmp, dotfile.dst)
ignores = list(set(ignore + dotfile.cmpignore))
diff = comp.compare(insttmp, dotfile.dst, ignore=ignores)
if tmpsrc:
# clean tmp transformed dotfile if any
tmpsrc = os.path.join(opts['dotpath'], tmpsrc)

View File

@@ -9,7 +9,8 @@ represents a dotfile in dotdrop
class Dotfile:
def __init__(self, key, dst, src,
actions={}, trans=[], link=False):
actions={}, trans=[],
link=False, cmpignore=[]):
# key of dotfile in the config
self.key = key
# path where to install this dotfile
@@ -22,6 +23,8 @@ class Dotfile:
self.actions = actions
# list of transformations
self.trans = trans
# pattern to ignore when comparing
self.cmpignore = cmpignore
def __str__(self):
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'

View File

@@ -73,23 +73,61 @@ touch ${tmpd}/program/b
touch ${tmpd}/config/b
# expects diff
echo "[+] comparing normal"
echo "[+] comparing normal - 2 diffs"
set +e
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose
[ "$?" = "0" ] && exit 1
set -e
# expects one diff
echo "[+] comparing with ignore"
patt="${tmpd}/config/b"
echo "[+] comparing with ignore (pattern: ${patt}) - 1 diff"
set +e
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${tmpd}/config/b
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt}
[ "$?" = "0" ] && exit 1
set -e
# expects no diff
echo "[+] comparing with ignore pattern"
patt="*b"
echo "[+] comparing with ignore (pattern: ${patt}) - 0 diff"
set +e
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=b
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt}
[ "$?" != "0" ] && exit 1
set -e
# expects one diff
patt="*/config/*b"
echo "[+] comparing with ignore (pattern: ${patt}) - 1 diff"
set +e
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt}
[ "$?" = "0" ] && exit 1
set -e
cat ${cfg}
# adding ignore in dotfile
cfg2="${basedir}/config2.yaml"
sed '/d_config:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*/config/b"' ${cfg} > ${cfg2}
cat ${cfg2}
# expects one diff
echo "[+] comparing with ignore in dotfile - 1 diff"
set +e
cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose
[ "$?" = "0" ] && exit 1
set -e
# adding ignore in dotfile
cfg2="${basedir}/config2.yaml"
sed '/d_config:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*b"' ${cfg} > ${cfg2}
sed -i '/d_program:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*b"' ${cfg2}
cat ${cfg2}
# expects no diff
patt="*b"
echo "[+] comparing with ignore in dotfile - 0 diff"
set +e
cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose
[ "$?" != "0" ] && exit 1
set -e

View File

@@ -8,9 +8,13 @@ set -ev
pycodestyle --ignore=W605 dotdrop/
pycodestyle tests/
pycodestyle scripts/
# travis
PYTHONPATH=dotdrop nosetests --with-coverage --cover-package=dotdrop
#PYTHONPATH=dotdrop nosetests -s --with-coverage --cover-package=dotdrop
# arch
#PYTHONPATH=dotdrop python3 -m nose --with-coverage --cover-package=dotdrop
# others
#PYTHONPATH=dotdrop nosetests -s --with-coverage --cover-package=dotdrop
# execute bash script tests
for scr in tests-ng/*.sh; do