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:
11
README.md
11
README.md
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
108
tests-ng/update-templates.sh
Executable 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
|
||||
Reference in New Issue
Block a user