mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-11 07:19:16 +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
|
The diffing is done by `diff` in the backend, one can provide specific
|
||||||
options to diff using the `-o` switch.
|
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
|
It is also possible to install all dotfiles for a specific profile
|
||||||
in a temporary directory in order to manually compare them with
|
in a temporary directory in order to manually compare them with
|
||||||
the local version by using `install` and the `-t` switch.
|
the local version by using `install` and the `-t` switch.
|
||||||
@@ -553,6 +562,7 @@ the following entries:
|
|||||||
|
|
||||||
* **dotfiles** entry: a list of dotfiles
|
* **dotfiles** entry: a list of dotfiles
|
||||||
* When `link` is true, dotdrop will create a symlink instead of copying (default *false*).
|
* 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.
|
* `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.
|
* `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>
|
src: <filename-within-the-dotpath>
|
||||||
# Optional
|
# Optional
|
||||||
link: <true|false>
|
link: <true|false>
|
||||||
|
cmpignore:
|
||||||
|
- "<ignore-pattern>"
|
||||||
actions:
|
actions:
|
||||||
- <action-key>
|
- <action-key>
|
||||||
trans:
|
trans:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ handle the comparison of dotfiles and local deployment
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import filecmp
|
import filecmp
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from dotdrop.logger import Logger
|
from dotdrop.logger import Logger
|
||||||
@@ -16,45 +17,47 @@ import dotdrop.utils as utils
|
|||||||
|
|
||||||
class Comparator:
|
class Comparator:
|
||||||
|
|
||||||
def __init__(self, diffopts='', ignore=[], debug=False):
|
def __init__(self, diffopts='', debug=False):
|
||||||
self.diffopts = diffopts
|
self.diffopts = diffopts
|
||||||
self.ignore = [os.path.expanduser(i) for i in ignore]
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.log = Logger()
|
self.log = Logger()
|
||||||
|
|
||||||
def compare(self, left, right):
|
def compare(self, left, right, ignore=[]):
|
||||||
"""diff left (dotdrop dotfile) and right (deployed file)"""
|
"""diff left (dotdrop dotfile) and right (deployed file)"""
|
||||||
left = os.path.expanduser(left)
|
left = os.path.expanduser(left)
|
||||||
right = os.path.expanduser(right)
|
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):
|
if not os.path.isdir(left):
|
||||||
return self._comp_file(left, right)
|
return self._comp_file(left, right, ignore)
|
||||||
return self._comp_dir(left, right)
|
return self._comp_dir(left, right, ignore)
|
||||||
|
|
||||||
def _comp_file(self, left, right):
|
def _comp_file(self, left, right, ignore):
|
||||||
"""compare a file"""
|
"""compare a file"""
|
||||||
if left in self.ignore or right in self.ignore:
|
if self._ignore([left, right], ignore):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
||||||
return ''
|
return ''
|
||||||
return self._diff(left, right)
|
return self._diff(left, right)
|
||||||
|
|
||||||
def _comp_dir(self, left, right):
|
def _comp_dir(self, left, right, ignore):
|
||||||
"""compare a directory"""
|
"""compare a directory"""
|
||||||
if left in self.ignore or right in self.ignore:
|
if self._ignore([left, right], ignore):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
||||||
return ''
|
return ''
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('compare {} and {}'.format(left, right))
|
self.log.dbg('compare {} and {}'.format(left, right))
|
||||||
ret = []
|
ret = []
|
||||||
comp = filecmp.dircmp(left, right, ignore=self.ignore)
|
comp = filecmp.dircmp(left, right)
|
||||||
# handle files only in deployed file
|
# handle files only in deployed file
|
||||||
for i in comp.left_only:
|
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
|
continue
|
||||||
ret.append('only in left: \"{}\"\n'.format(i))
|
ret.append('only in left: \"{}\"\n'.format(i))
|
||||||
for i in comp.right_only:
|
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
|
continue
|
||||||
ret.append('only in right: \"{}\"\n'.format(i))
|
ret.append('only in right: \"{}\"\n'.format(i))
|
||||||
|
|
||||||
@@ -88,3 +91,15 @@ class Comparator:
|
|||||||
rshort = os.path.basename(right)
|
rshort = os.path.basename(right)
|
||||||
diff = 'diff \"{}\":\n{}'.format(lshort, diff)
|
diff = 'diff \"{}\":\n{}'.format(lshort, diff)
|
||||||
return 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_src = 'src'
|
||||||
key_dotfiles_dst = 'dst'
|
key_dotfiles_dst = 'dst'
|
||||||
key_dotfiles_link = 'link'
|
key_dotfiles_link = 'link'
|
||||||
|
key_dotfiles_cmpignore = 'cmpignore'
|
||||||
key_dotfiles_actions = 'actions'
|
key_dotfiles_actions = 'actions'
|
||||||
key_dotfiles_trans = 'trans'
|
key_dotfiles_trans = 'trans'
|
||||||
|
|
||||||
@@ -198,10 +199,11 @@ class Cfg:
|
|||||||
msg += ' because link is True'
|
msg += ' because link is True'
|
||||||
self.log.warn(msg)
|
self.log.warn(msg)
|
||||||
trans = []
|
trans = []
|
||||||
|
ignores = v[self.key_dotfiles_cmpignore] if \
|
||||||
|
self.key_dotfiles_cmpignore in v else []
|
||||||
self.dotfiles[k] = Dotfile(k, dst, src,
|
self.dotfiles[k] = Dotfile(k, dst, src,
|
||||||
link=link,
|
link=link, actions=actions,
|
||||||
actions=actions,
|
trans=trans, cmpignore=ignores)
|
||||||
trans=trans)
|
|
||||||
|
|
||||||
# assign dotfiles to each profile
|
# assign dotfiles to each profile
|
||||||
for k, v in self.lnk_profiles.items():
|
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'],
|
inst = Installer(create=opts['create'], backup=opts['backup'],
|
||||||
dry=opts['dry'], base=opts['dotpath'],
|
dry=opts['dry'], base=opts['dotpath'],
|
||||||
workdir=opts['workdir'], debug=opts['debug'])
|
workdir=opts['workdir'], debug=opts['debug'])
|
||||||
comp = Comparator(diffopts=opts['dopts'], debug=opts['debug'],
|
comp = Comparator(diffopts=opts['dopts'], debug=opts['debug'])
|
||||||
ignore=ignore)
|
|
||||||
|
|
||||||
for dotfile in selected:
|
for dotfile in selected:
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
@@ -218,7 +217,8 @@ def compare(opts, conf, tmp, focus=None, ignore=[]):
|
|||||||
if not ret:
|
if not ret:
|
||||||
# failed to install to tmp
|
# failed to install to tmp
|
||||||
continue
|
continue
|
||||||
diff = comp.compare(insttmp, dotfile.dst)
|
ignores = list(set(ignore + dotfile.cmpignore))
|
||||||
|
diff = comp.compare(insttmp, dotfile.dst, ignore=ignores)
|
||||||
if tmpsrc:
|
if tmpsrc:
|
||||||
# clean tmp transformed dotfile if any
|
# clean tmp transformed dotfile if any
|
||||||
tmpsrc = os.path.join(opts['dotpath'], tmpsrc)
|
tmpsrc = os.path.join(opts['dotpath'], tmpsrc)
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ represents a dotfile in dotdrop
|
|||||||
class Dotfile:
|
class Dotfile:
|
||||||
|
|
||||||
def __init__(self, key, dst, src,
|
def __init__(self, key, dst, src,
|
||||||
actions={}, trans=[], link=False):
|
actions={}, trans=[],
|
||||||
|
link=False, cmpignore=[]):
|
||||||
# key of dotfile in the config
|
# key of dotfile in the config
|
||||||
self.key = key
|
self.key = key
|
||||||
# path where to install this dotfile
|
# path where to install this dotfile
|
||||||
@@ -22,6 +23,8 @@ class Dotfile:
|
|||||||
self.actions = actions
|
self.actions = actions
|
||||||
# list of transformations
|
# list of transformations
|
||||||
self.trans = trans
|
self.trans = trans
|
||||||
|
# pattern to ignore when comparing
|
||||||
|
self.cmpignore = cmpignore
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
|
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
|
||||||
|
|||||||
@@ -73,23 +73,61 @@ touch ${tmpd}/program/b
|
|||||||
touch ${tmpd}/config/b
|
touch ${tmpd}/config/b
|
||||||
|
|
||||||
# expects diff
|
# expects diff
|
||||||
echo "[+] comparing normal"
|
echo "[+] comparing normal - 2 diffs"
|
||||||
set +e
|
set +e
|
||||||
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose
|
cd ${ddpath} | ${bin} compare -c ${cfg} --verbose
|
||||||
[ "$?" = "0" ] && exit 1
|
[ "$?" = "0" ] && exit 1
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# expects one diff
|
# expects one diff
|
||||||
echo "[+] comparing with ignore"
|
patt="${tmpd}/config/b"
|
||||||
|
echo "[+] comparing with ignore (pattern: ${patt}) - 1 diff"
|
||||||
set +e
|
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
|
[ "$?" = "0" ] && exit 1
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# expects no diff
|
# expects no diff
|
||||||
echo "[+] comparing with ignore pattern"
|
patt="*b"
|
||||||
|
echo "[+] comparing with ignore (pattern: ${patt}) - 0 diff"
|
||||||
set +e
|
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
|
[ "$?" != "0" ] && exit 1
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|||||||
6
tests.sh
6
tests.sh
@@ -8,9 +8,13 @@ set -ev
|
|||||||
pycodestyle --ignore=W605 dotdrop/
|
pycodestyle --ignore=W605 dotdrop/
|
||||||
pycodestyle tests/
|
pycodestyle tests/
|
||||||
pycodestyle scripts/
|
pycodestyle scripts/
|
||||||
|
|
||||||
|
# travis
|
||||||
PYTHONPATH=dotdrop nosetests --with-coverage --cover-package=dotdrop
|
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
|
#PYTHONPATH=dotdrop python3 -m nose --with-coverage --cover-package=dotdrop
|
||||||
|
# others
|
||||||
|
#PYTHONPATH=dotdrop nosetests -s --with-coverage --cover-package=dotdrop
|
||||||
|
|
||||||
# execute bash script tests
|
# execute bash script tests
|
||||||
for scr in tests-ng/*.sh; do
|
for scr in tests-ng/*.sh; do
|
||||||
|
|||||||
Reference in New Issue
Block a user