1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-04 20:54:51 +00:00

add ability to patch template with update (#82)

This commit is contained in:
deadc0de6
2019-02-03 15:11:58 +01:00
parent 4acc83f91f
commit 84b2f98525
5 changed files with 154 additions and 7 deletions

View File

@@ -508,7 +508,7 @@ $ git diff
**The dotfile uses [templating](#template)**
The dotfile must be manually updated, two solutions can be used to identify the
The dotfile must be manually updated, three solutions can be used to identify the
changes to apply to the template:
* Use dotdrop's `compare` command
@@ -517,6 +517,15 @@ changes to apply to the template:
$ dotdrop compare --file=~/.vimrc
```
* Provide the switch `-P --show-patch` that will provide with an ad-hoc solution
to manually patch the template file using a temporary generated version of the template
(this isn't a bullet proof solution and might need manual checking)
```bash
./dotdrop.sh update --show-patch ~/.vimrc
[WARN] /home/user/dotfiles/vimrc uses template, update manually
[WARN] try patching with: "diff -u /tmp/dotdrop-sbx6hw0r /home/user/.vimrc | patch /home/user/dotfiles/vimrc"
```
* Install the dotfiles to a temporary directory (using the `install` command and the
`-t` switch) and compare the generated dotfile with the local one.
```bash

View File

@@ -44,7 +44,7 @@ Usage:
dotdrop import [-ldVb] [-c <path>] [-p <profile>] <path>...
dotdrop compare [-Vb] [-c <path>] [-p <profile>]
[-o <opts>] [-C <file>...] [-i <pattern>...]
dotdrop update [-fdVbk] [-c <path>] [-p <profile>]
dotdrop update [-fdVbkP] [-c <path>] [-p <profile>]
[-i <pattern>...] [<path>...]
dotdrop listfiles [-VTb] [-c <path>] [-p <profile>]
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
@@ -63,6 +63,7 @@ Options:
-T --template Only template dotfiles.
-D --showdiff Show a diff before overwriting.
-l --inv-link Invert the value of "link_by_default" when importing.
-P --show-patch Provide a one-liner to manually patch template.
-f --force Do not warn if exists.
-k --key Treat <path> as a dotfile key.
-V --verbose Be verbose.
@@ -213,12 +214,13 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
return same
def cmd_update(opts, conf, paths, iskey=False, ignore=[]):
def cmd_update(opts, conf, paths, iskey=False, ignore=[], showpatch=False):
"""update the dotfile(s) from path(s) or key(s)"""
ret = True
updater = Updater(conf, opts['dotpath'], opts['profile'],
opts['dry'], opts['safe'], iskey=iskey,
debug=opts['debug'], ignore=[])
debug=opts['debug'], ignore=[],
showpatch=showpatch)
if not iskey:
# update paths
if opts['debug']:
@@ -509,8 +511,9 @@ def main():
if opts['debug']:
LOG.dbg('running cmd: update')
iskey = args['--key']
ret = cmd_update(opts, conf, args['<path>'], iskey=iskey,
ignore=args['--ignore'])
ret = cmd_update(opts, conf, args['<path>'],
iskey=iskey, ignore=args['--ignore'],
showpatch=args['--show-patch'])
elif args['detail']:
# detail files

View File

@@ -21,7 +21,7 @@ TILD = '~'
class Updater:
def __init__(self, conf, dotpath, profile, dry, safe,
iskey=False, debug=False, ignore=[]):
iskey=False, debug=False, ignore=[], showpatch=False):
self.conf = conf
self.dotpath = dotpath
self.profile = profile
@@ -30,6 +30,7 @@ class Updater:
self.iskey = iskey
self.debug = debug
self.ignore = ignore
self.showpatch = showpatch
self.log = Logger()
def update_path(self, path):
@@ -144,6 +145,21 @@ class Updater:
self.log.warn('{} uses template, update manually'.format(path))
return True
def _show_patch(self, tpath, fpath):
"""provide a way to manually patch the template"""
content = self._resolve_template(tpath)
tmp = utils.write_to_tmpfile(content)
cmds = ['diff', '-u', tmp, fpath, '|', 'patch', tpath]
self.log.warn('try patching with: \"{}\"'.format(' '.join(cmds)))
return False
def _resolve_template(self, tpath):
"""resolve the template to a temporary file"""
variables = self.conf.get_variables(self.profile)
t = Templategen(variables=variables, base=self.dotpath,
debug=self.debug)
return t.generate(tpath)
def _handle_file(self, left, right, compare=True):
"""sync left (deployed file) and right (dotdrop dotfile)"""
if self._ignore([left, right]):
@@ -151,8 +167,11 @@ class Updater:
if self.debug:
self.log.dbg('update for file {} and {}'.format(left, right))
if self._is_template(right):
# dotfile is a template
if self.debug:
self.log.dbg('{} is a template'.format(right))
if self.showpatch:
self._show_patch(right, left)
return False
if compare and filecmp.cmp(left, right, shallow=True):
# no difference

View File

@@ -41,6 +41,14 @@ def run(cmd, raw=True, debug=False, checkerr=False):
return ret == 0, lines
def write_to_tmpfile(content):
"""write some content to a tmp file"""
path = get_tmpfile()
with open(path, 'wb') as f:
f.write(content)
return path
def shell(cmd):
"""run a command in the shell (expects a string)"""
return subprocess.getoutput(cmd)

108
tests-ng/update-templates.sh Executable file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env bash
# author: deadc0de6 (https://github.com/deadc0de6)
# Copyright (c) 2017, deadc0de6
#
# test update of templates
# 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"
echo "dotdrop path: ${ddpath}"
echo "pythonpath: ${PYTHONPATH}"
# get the helpers
source ${cur}/helpers
echo -e "\e[96m\e[1m==> RUNNING $(basename $BASH_SOURCE) <==\e[0m"
################################################################
# this is the test
################################################################
# the dotfile source
tmps=`mktemp -d`
mkdir -p ${tmps}/dotfiles
echo "dotfiles source (dotpath): ${tmps}"
# the dotfile destination
tmpd=`mktemp -d`
echo "dotfiles destination: ${tmpd}"
# the workdir
tmpw=`mktemp -d`
echo "workdir: ${tmpw}"
# create the config file
cfg="${tmps}/config.yaml"
cat > ${cfg} << _EOF
config:
backup: true
create: true
dotpath: dotfiles
workdir: ${tmpw}
dotfiles:
f_abc:
dst: ${tmpd}/abc
src: abc
profiles:
p1:
dotfiles:
- f_abc
_EOF
cat ${cfg}
# create the dotfile
echo "head" > ${tmps}/dotfiles/abc
echo '{%@@ if profile == "p1" @@%}' >> ${tmps}/dotfiles/abc
echo "is p1" >> ${tmps}/dotfiles/abc
echo '{%@@ else @@%}' >> ${tmps}/dotfiles/abc
echo "is not p1" >> ${tmps}/dotfiles/abc
echo '{%@@ endif @@%}' >> ${tmps}/dotfiles/abc
echo "tail" >> ${tmps}/dotfiles/abc
# create the installed dotfile
echo "head" > ${tmpd}/abc
echo "is p1" >> ${tmpd}/abc
echo "tail" >> ${tmpd}/abc
# update
cat ${tmps}/dotfiles/abc
set +e
patch=`cd ${ddpath} | ${bin} update -P -p p1 -k f_abc --cfg ${cfg} 2>&1 | grep 'try patching with' | sed 's/"//g'`
set -e
patch=`echo ${patch} | sed 's/^.*: //g'`
echo "patching with: ${patch}"
eval ${patch}
cat ${tmps}/dotfiles/abc
## CLEANING
rm -rf ${tmps} ${tmpd} ${tmpw}
echo "OK"
exit 0