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:
12
README.md
12
README.md
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:\"{}\"'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
6
tests.sh
6
tests.sh
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user