mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-04 20:19:46 +00:00
Merge branch 'master' of github.com:deadc0de6/dotdrop into master
This commit is contained in:
@@ -31,6 +31,7 @@ Entry | Description | Default
|
||||
`longkey` | use long keys for dotfiles when importing (see [Import dotfiles](usage.md#import-dotfiles)) | false
|
||||
`minversion` | (*for internal use, do not modify*) provides the minimal dotdrop version to use | -
|
||||
`showdiff` | on install show a diff before asking to overwrite (see `--showdiff`) | false
|
||||
`template_dotfile_default` | disable templating on all dotfiles when set to false | true
|
||||
`upignore` | list of patterns to ignore when updating, apply to all dotfiles (enclose in quotes when using wildcards, see [ignore patterns](config.md#ignore-patterns)) | -
|
||||
`workdir` | path to the directory where templates are installed before being symlinked when using `link:link` or `link:link_children` (absolute path or relative to the config file location) | `~/.config/dotdrop`
|
||||
<s>link_by_default</s> | when importing a dotfile set `link` to that value per default | false
|
||||
@@ -48,6 +49,7 @@ Entry | Description
|
||||
`cmpignore` | list of patterns to ignore when comparing (enclose in quotes when using wildcards, see [ignore patterns](config.md#ignore-patterns))
|
||||
`ignoreempty` | if true empty template will not be deployed (defaults to value of `ignoreempty`)
|
||||
`instignore` | list of patterns to ignore when installing (enclose in quotes when using wildcards, see [ignore patterns](config.md#ignore-patterns))
|
||||
`template` | if false disable template for this dotfile (defaults to value of `template_dotfile_default`)
|
||||
`trans_read` | transformation key to apply when installing this dotfile (must be defined in the **trans_read** entry below, see [transformations](config-details.md#entry-transformations))
|
||||
`trans_write` | transformation key to apply when updating this dotfile (must be defined in the **trans_write** entry below, see [transformations](config-details.md#entry-transformations))
|
||||
`upignore` | list of patterns to ignore when updating (enclose in quotes when using wildcards, see [ignore patterns](config.md#ignore-patterns))
|
||||
@@ -69,6 +71,7 @@ Entry | Description
|
||||
- "<ignore-pattern>"
|
||||
actions:
|
||||
- <action-key>
|
||||
template: (true|false)
|
||||
trans_read: <transformation-key>
|
||||
trans_write: <transformation-key>
|
||||
```
|
||||
|
||||
@@ -13,8 +13,8 @@ Note that if the dotfile is using template directives, it will be symlinked into
|
||||
`~/.config/dotdrop` instead of directly into your *dotpath*
|
||||
(see [Templating symlinked dotfiles](#templating-symlinked-dotfiles))
|
||||
|
||||
Although the config entries `link_on_import` and `link_dotfile_default` can be set to `link_children`, it
|
||||
is not recommended since operations on a dotfile that is not a directory with the option `link_children`
|
||||
Although the config entries `link_on_import` and `link_dotfile_default` can be set to the value `link_children`,
|
||||
it is not recommended since operations on a dotfile that is not a directory with the option `link_children`
|
||||
will fail.
|
||||
|
||||
## Symlink a dotfile
|
||||
|
||||
@@ -4,6 +4,19 @@ Dotdrop leverage the power of [jinja2](https://palletsprojects.com/p/jinja/) to
|
||||
templating of dotfiles. See [jinja2 template doc](https://jinja.palletsprojects.com/en/2.11.x/templates/)
|
||||
or the below sections for more information on how to template your dotfiles.
|
||||
|
||||
## Templating or not templating
|
||||
|
||||
The dotfile config entry [template](config-format.md#dotfiles-entry)
|
||||
and the global config entry [template_dotfile_default](config-format.md#config-entry)
|
||||
allow to control if a dotfile is being process by the templating engine.
|
||||
|
||||
Obviously if the dotfile uses template directives, it needs to be templated. However if it
|
||||
is not, disabling templating will speed up its installation (since it won't have to be
|
||||
processed by the engine).
|
||||
|
||||
For dotfiles being symlinked (`link` or `link_children`), see
|
||||
[the dedicated doc](howto/symlink-dotfiles.md#templating-symlinked-dotfiles)
|
||||
|
||||
## Delimiters
|
||||
|
||||
Dotdrop uses different delimiters than
|
||||
|
||||
@@ -57,6 +57,7 @@ class CfgYaml:
|
||||
key_dotfile_link = 'link'
|
||||
key_dotfile_actions = 'actions'
|
||||
key_dotfile_noempty = 'ignoreempty'
|
||||
key_dotfile_template = 'template'
|
||||
|
||||
# profile
|
||||
key_profile_dotfiles = 'dotfiles'
|
||||
@@ -82,6 +83,7 @@ class CfgYaml:
|
||||
key_settings_noempty = Settings.key_ignoreempty
|
||||
key_settings_minversion = Settings.key_minversion
|
||||
key_imp_link = Settings.key_link_on_import
|
||||
key_settings_template = Settings.key_template_dotfile_default
|
||||
|
||||
# link values
|
||||
lnk_nolink = LinkTypes.NOLINK.name.lower()
|
||||
@@ -610,6 +612,11 @@ class CfgYaml:
|
||||
if self.key_dotfile_noempty not in v:
|
||||
val = self.settings.get(self.key_settings_noempty, False)
|
||||
v[self.key_dotfile_noempty] = val
|
||||
# apply template if undefined
|
||||
if self.key_dotfile_template not in v:
|
||||
val = self.settings.get(self.key_settings_template, True)
|
||||
v[self.key_dotfile_template] = val
|
||||
|
||||
return new
|
||||
|
||||
def _add_variables(self, new, shell=False, template=True, prio=False):
|
||||
|
||||
@@ -129,11 +129,13 @@ def cmd_install(o):
|
||||
LOG.dbg(dotfile.prt())
|
||||
if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.LINK:
|
||||
r, err = inst.link(t, dotfile.src, dotfile.dst,
|
||||
actionexec=pre_actions_exec)
|
||||
actionexec=pre_actions_exec,
|
||||
template=dotfile.template)
|
||||
elif hasattr(dotfile, 'link') and \
|
||||
dotfile.link == LinkTypes.LINK_CHILDREN:
|
||||
r, err = inst.link_children(t, dotfile.src, dotfile.dst,
|
||||
actionexec=pre_actions_exec)
|
||||
actionexec=pre_actions_exec,
|
||||
template=dotfile.template)
|
||||
else:
|
||||
src = dotfile.src
|
||||
tmp = None
|
||||
@@ -147,7 +149,8 @@ def cmd_install(o):
|
||||
r, err = inst.install(t, src, dotfile.dst,
|
||||
actionexec=pre_actions_exec,
|
||||
noempty=dotfile.noempty,
|
||||
ignore=ignores)
|
||||
ignore=ignores,
|
||||
template=dotfile.template)
|
||||
if tmp:
|
||||
tmp = os.path.join(o.dotpath, tmp)
|
||||
if os.path.exists(tmp):
|
||||
@@ -264,7 +267,8 @@ def cmd_compare(o, tmp):
|
||||
continue
|
||||
|
||||
# install dotfile to temporary dir and compare
|
||||
ret, err, insttmp = inst.install_to_temp(t, tmp, src, dotfile.dst)
|
||||
ret, err, insttmp = inst.install_to_temp(t, tmp, src, dotfile.dst,
|
||||
template=dotfile.template)
|
||||
if not ret:
|
||||
# failed to install to tmp
|
||||
line = '=> compare {}: error'
|
||||
@@ -737,9 +741,14 @@ def main():
|
||||
LOG.err('interrupted')
|
||||
ret = False
|
||||
|
||||
if o.debug:
|
||||
LOG.dbg('done executing command')
|
||||
|
||||
if ret and o.conf.save():
|
||||
LOG.log('config file updated')
|
||||
|
||||
if o.debug:
|
||||
LOG.dbg('return {}'.format(ret))
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -16,12 +16,13 @@ class Dotfile(DictParser):
|
||||
key_noempty = 'ignoreempty'
|
||||
key_trans_r = 'trans_read'
|
||||
key_trans_w = 'trans_write'
|
||||
key_template = 'template'
|
||||
|
||||
def __init__(self, key, dst, src,
|
||||
actions=[], trans_r=None, trans_w=None,
|
||||
link=LinkTypes.NOLINK, noempty=False,
|
||||
cmpignore=[], upignore=[],
|
||||
instignore=[]):
|
||||
instignore=[], template=True):
|
||||
"""
|
||||
constructor
|
||||
@key: dotfile key
|
||||
@@ -35,6 +36,7 @@ class Dotfile(DictParser):
|
||||
@upignore: patterns to ignore when updating
|
||||
@cmpignore: patterns to ignore when comparing
|
||||
@instignore: patterns to ignore when installing
|
||||
@template: template this dotfile
|
||||
"""
|
||||
self.actions = actions
|
||||
self.dst = dst
|
||||
@@ -47,6 +49,7 @@ class Dotfile(DictParser):
|
||||
self.upignore = upignore
|
||||
self.cmpignore = cmpignore
|
||||
self.instignore = instignore
|
||||
self.template = template
|
||||
|
||||
if self.link != LinkTypes.NOLINK and \
|
||||
(
|
||||
@@ -91,6 +94,7 @@ class Dotfile(DictParser):
|
||||
value['noempty'] = value.get(cls.key_noempty, False)
|
||||
value['trans_r'] = value.get(cls.key_trans_r)
|
||||
value['trans_w'] = value.get(cls.key_trans_w)
|
||||
value['template'] = value.get(cls.key_template, True)
|
||||
# remove old entries
|
||||
value.pop(cls.key_noempty, None)
|
||||
value.pop(cls.key_trans_r, None)
|
||||
@@ -104,8 +108,12 @@ class Dotfile(DictParser):
|
||||
return hash(self.dst) ^ hash(self.src) ^ hash(self.key)
|
||||
|
||||
def __str__(self):
|
||||
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
|
||||
return msg.format(self.key, self.src, self.dst, str(self.link))
|
||||
msg = 'key:\"{}\"'.format(self.key)
|
||||
msg += ', src:\"{}\"'.format(self.src)
|
||||
msg += ', dst:\"{}\"'.format(self.dst)
|
||||
msg += ', link:\"{}\"'.format(str(self.link))
|
||||
msg += ', template:{}'.format(self.template)
|
||||
return msg
|
||||
|
||||
def prt(self):
|
||||
"""extended dotfile to str"""
|
||||
@@ -114,6 +122,7 @@ class Dotfile(DictParser):
|
||||
out += '\n{}src: \"{}\"'.format(indent, self.src)
|
||||
out += '\n{}dst: \"{}\"'.format(indent, self.dst)
|
||||
out += '\n{}link: \"{}\"'.format(indent, str(self.link))
|
||||
out += '\n{}template: \"{}\"'.format(indent, str(self.template))
|
||||
|
||||
out += '\n{}pre-action:'.format(indent)
|
||||
some = self.get_pre_actions()
|
||||
|
||||
@@ -7,6 +7,7 @@ handle the installation of dotfiles
|
||||
|
||||
import os
|
||||
import errno
|
||||
import shutil
|
||||
|
||||
# local imports
|
||||
from dotdrop.logger import Logger
|
||||
@@ -65,7 +66,7 @@ class Installer:
|
||||
|
||||
def install(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[]):
|
||||
ignore=[], template=True):
|
||||
"""
|
||||
install src to dst using a template
|
||||
@templater: the templater object
|
||||
@@ -74,6 +75,7 @@ class Installer:
|
||||
@actionexec: action executor callback
|
||||
@noempty: render empty template flag
|
||||
@ignore: pattern to ignore when installing
|
||||
@template: template this dotfile
|
||||
|
||||
return
|
||||
- True, None : success
|
||||
@@ -103,22 +105,25 @@ class Installer:
|
||||
self.log.dbg('install {} to {}'.format(src, dst))
|
||||
self.log.dbg('is a directory \"{}\": {}'.format(src, isdir))
|
||||
if isdir:
|
||||
b, e = self._handle_dir(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore)
|
||||
b, e = self._install_dir(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
template=template)
|
||||
return self._log_install(b, e)
|
||||
b, e = self._handle_file(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore)
|
||||
b, e = self._install_file(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
template=template)
|
||||
return self._log_install(b, e)
|
||||
|
||||
def link(self, templater, src, dst, actionexec=None):
|
||||
def link(self, templater, src, dst, actionexec=None, template=True):
|
||||
"""
|
||||
set src as the link target of dst
|
||||
@templater: the templater
|
||||
@src: dotfile source path in dotpath
|
||||
@dst: dotfile destination path in the FS
|
||||
@actionexec: action executor callback
|
||||
@template: template this dotfile
|
||||
|
||||
return
|
||||
- True, None : success
|
||||
@@ -140,28 +145,32 @@ class Installer:
|
||||
dst = os.path.normpath(os.path.expanduser(dst))
|
||||
if self.totemp:
|
||||
# ignore actions
|
||||
b, e = self.install(templater, src, dst, actionexec=None)
|
||||
b, e = self.install(templater, src, dst, actionexec=None,
|
||||
template=template)
|
||||
return self._log_install(b, e)
|
||||
|
||||
if Templategen.is_template(src):
|
||||
if template and Templategen.is_template(src):
|
||||
if self.debug:
|
||||
self.log.dbg('dotfile is a template')
|
||||
self.log.dbg('install to {} and symlink'.format(self.workdir))
|
||||
tmp = self._pivot_path(dst, self.workdir, striphome=True)
|
||||
i, err = self.install(templater, src, tmp, actionexec=actionexec)
|
||||
i, err = self.install(templater, src, tmp, actionexec=actionexec,
|
||||
template=template)
|
||||
if not i and not os.path.exists(tmp):
|
||||
return self._log_install(i, err)
|
||||
src = tmp
|
||||
b, e = self._link(src, dst, actionexec=actionexec)
|
||||
return self._log_install(b, e)
|
||||
|
||||
def link_children(self, templater, src, dst, actionexec=None):
|
||||
def link_children(self, templater, src, dst, actionexec=None,
|
||||
template=True):
|
||||
"""
|
||||
link all dotfiles in a given directory
|
||||
link all files under a given directory
|
||||
@templater: the templater
|
||||
@src: dotfile source path in dotpath
|
||||
@dst: dotfile destination path in the FS
|
||||
@actionexec: action executor callback
|
||||
@template: template this dotfile
|
||||
|
||||
return
|
||||
- True, None: success
|
||||
@@ -221,13 +230,14 @@ class Installer:
|
||||
if self.debug:
|
||||
self.log.dbg('symlink child {} to {}'.format(src, dst))
|
||||
|
||||
if Templategen.is_template(src):
|
||||
if template and Templategen.is_template(src):
|
||||
if self.debug:
|
||||
self.log.dbg('dotfile is a template')
|
||||
self.log.dbg('install to {} and symlink'
|
||||
.format(self.workdir))
|
||||
tmp = self._pivot_path(dst, self.workdir, striphome=True)
|
||||
r, e = self.install(templater, src, tmp, actionexec=actionexec)
|
||||
r, e = self.install(templater, src, tmp, actionexec=actionexec,
|
||||
template=template)
|
||||
if not r and e and not os.path.exists(tmp):
|
||||
continue
|
||||
src = tmp
|
||||
@@ -264,7 +274,7 @@ class Installer:
|
||||
self.log.dry('would remove {} and link to {}'.format(dst, src))
|
||||
return True, None
|
||||
if self.showdiff:
|
||||
self._diff_before_write(src, dst)
|
||||
self._diff_before_write(src, dst, quiet=False)
|
||||
msg = 'Remove "{}" for link creation?'.format(dst)
|
||||
if self.safe and not self.log.ask(msg):
|
||||
err = 'ignoring "{}", link was not created'.format(dst)
|
||||
@@ -306,14 +316,16 @@ class Installer:
|
||||
tmp['_dotfile_sub_abs_dst'] = dst
|
||||
return tmp
|
||||
|
||||
def _handle_file(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[]):
|
||||
def _install_file(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[], template=True):
|
||||
"""install src to dst when is a file"""
|
||||
if self.debug:
|
||||
self.log.dbg('generate template for {}'.format(src))
|
||||
self.log.dbg('deploy file: {}'.format(src))
|
||||
self.log.dbg('ignore empty: {}'.format(noempty))
|
||||
self.log.dbg('ignore pattern: {}'.format(ignore))
|
||||
self.log.dbg('template: {}'.format(template))
|
||||
self.log.dbg('no empty: {}'.format(noempty))
|
||||
|
||||
if utils.must_ignore([src, dst], ignore, debug=self.debug):
|
||||
if self.debug:
|
||||
@@ -324,42 +336,55 @@ class Installer:
|
||||
# symlink loop
|
||||
err = 'dotfile points to itself: {}'.format(dst)
|
||||
return False, err
|
||||
saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst))
|
||||
try:
|
||||
content = templater.generate(src)
|
||||
except UndefinedException as e:
|
||||
return False, str(e)
|
||||
finally:
|
||||
templater.restore_vars(saved)
|
||||
if noempty and utils.content_empty(content):
|
||||
if self.debug:
|
||||
self.log.dbg('ignoring empty template: {}'.format(src))
|
||||
return False, None
|
||||
if content is None:
|
||||
err = 'empty template {}'.format(src)
|
||||
return False, err
|
||||
|
||||
if not os.path.exists(src):
|
||||
err = 'source dotfile does not exist: {}'.format(src)
|
||||
return False, err
|
||||
st = os.stat(src)
|
||||
ret, err = self._write(src, dst, content,
|
||||
st.st_mode, actionexec=actionexec)
|
||||
|
||||
# handle the file
|
||||
content = None
|
||||
if template:
|
||||
# template the file
|
||||
saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst))
|
||||
try:
|
||||
content = templater.generate(src)
|
||||
except UndefinedException as e:
|
||||
return False, str(e)
|
||||
finally:
|
||||
templater.restore_vars(saved)
|
||||
if noempty and utils.content_empty(content):
|
||||
if self.debug:
|
||||
self.log.dbg('ignoring empty template: {}'.format(src))
|
||||
return False, None
|
||||
if content is None:
|
||||
err = 'empty template {}'.format(src)
|
||||
return False, err
|
||||
ret, err = self._write(src, dst,
|
||||
content=content,
|
||||
actionexec=actionexec,
|
||||
template=template)
|
||||
|
||||
# build return values
|
||||
if ret < 0:
|
||||
# error
|
||||
return False, err
|
||||
if ret > 0:
|
||||
# already exists
|
||||
if self.debug:
|
||||
self.log.dbg('ignoring {}'.format(dst))
|
||||
return False, None
|
||||
if ret == 0:
|
||||
# success
|
||||
if not self.dry and not self.comparing:
|
||||
self.log.sub('copied {} to {}'.format(src, dst))
|
||||
return True, None
|
||||
# error
|
||||
err = 'installing {} to {}'.format(src, dst)
|
||||
return False, err
|
||||
|
||||
def _handle_dir(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[]):
|
||||
def _install_dir(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[], template=True):
|
||||
"""install src to dst when is a directory"""
|
||||
if self.debug:
|
||||
self.log.dbg('install dir {}'.format(src))
|
||||
@@ -374,11 +399,12 @@ class Installer:
|
||||
f = os.path.join(src, entry)
|
||||
if not os.path.isdir(f):
|
||||
# is file
|
||||
res, err = self._handle_file(templater, f,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore)
|
||||
res, err = self._install_file(templater, f,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
template=template)
|
||||
if not res and err:
|
||||
# error occured
|
||||
ret = res, err
|
||||
@@ -388,11 +414,12 @@ class Installer:
|
||||
ret = True, None
|
||||
else:
|
||||
# is directory
|
||||
res, err = self._handle_dir(templater, f,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore)
|
||||
res, err = self._install_dir(templater, f,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
template=template)
|
||||
if not res and err:
|
||||
# error occured
|
||||
ret = res, err
|
||||
@@ -403,22 +430,31 @@ class Installer:
|
||||
return ret
|
||||
|
||||
def _fake_diff(self, dst, content):
|
||||
"""fake diff by comparing file content with content"""
|
||||
"""
|
||||
fake diff by comparing file content with content
|
||||
returns True if same
|
||||
"""
|
||||
cur = ''
|
||||
with open(dst, 'br') as f:
|
||||
cur = f.read()
|
||||
return cur == content
|
||||
|
||||
def _write(self, src, dst, content, rights, actionexec=None):
|
||||
"""write content to file
|
||||
def _write(self, src, dst, content=None,
|
||||
actionexec=None, template=True):
|
||||
"""
|
||||
copy dotfile / write content to file
|
||||
return 0, None: for success,
|
||||
1, None: when already exists
|
||||
-1, err: when error"""
|
||||
-1, err: when error
|
||||
content is always empty if template is False
|
||||
and is to be ignored
|
||||
"""
|
||||
overwrite = not self.safe
|
||||
if self.dry:
|
||||
self.log.dry('would install {}'.format(dst))
|
||||
return 0, None
|
||||
if os.path.lexists(dst):
|
||||
rights = os.stat(src).st_mode
|
||||
samerights = False
|
||||
try:
|
||||
samerights = os.stat(dst).st_mode == rights
|
||||
@@ -427,15 +463,26 @@ class Installer:
|
||||
# broken symlink
|
||||
err = 'broken symlink {}'.format(dst)
|
||||
return -1, err
|
||||
if self.diff and self._fake_diff(dst, content) and samerights:
|
||||
if self.debug:
|
||||
self.log.dbg('{} is the same'.format(dst))
|
||||
return 1, None
|
||||
diff = None
|
||||
if self.diff:
|
||||
diff = self._diff_before_write(src, dst,
|
||||
content=content,
|
||||
quiet=True)
|
||||
if not diff and samerights:
|
||||
if self.debug:
|
||||
self.log.dbg('{} is the same'.format(dst))
|
||||
return 1, None
|
||||
if self.safe:
|
||||
if self.debug:
|
||||
self.log.dbg('change detected for {}'.format(dst))
|
||||
if self.showdiff:
|
||||
self._diff_before_write(src, dst, content=content)
|
||||
if diff is None:
|
||||
# get diff
|
||||
diff = self._diff_before_write(src, dst,
|
||||
content=content,
|
||||
quiet=True)
|
||||
if diff:
|
||||
self._print_diff(src, dst, diff)
|
||||
if not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
||||
self.log.warn('ignoring {}'.format(dst))
|
||||
return 1, None
|
||||
@@ -450,24 +497,39 @@ class Installer:
|
||||
if not r:
|
||||
return -1, e
|
||||
if self.debug:
|
||||
self.log.dbg('writes content to \"{}\"'.format(dst))
|
||||
self.log.dbg('install dotfile to \"{}\"'.format(dst))
|
||||
# re-check in case action created the file
|
||||
if self.safe and not overwrite and os.path.lexists(dst):
|
||||
if not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
||||
self.log.warn('ignoring {}'.format(dst))
|
||||
return 1, None
|
||||
# write the file
|
||||
try:
|
||||
with open(dst, 'wb') as f:
|
||||
f.write(content)
|
||||
except NotADirectoryError as e:
|
||||
err = 'opening dest file: {}'.format(e)
|
||||
return -1, err
|
||||
os.chmod(dst, rights)
|
||||
|
||||
if template:
|
||||
# write content the file
|
||||
try:
|
||||
with open(dst, 'wb') as f:
|
||||
f.write(content)
|
||||
shutil.copymode(src, dst)
|
||||
except NotADirectoryError as e:
|
||||
err = 'opening dest file: {}'.format(e)
|
||||
return -1, err
|
||||
except Exception as e:
|
||||
return -1, str(e)
|
||||
else:
|
||||
# copy file
|
||||
try:
|
||||
shutil.copyfile(src, dst)
|
||||
shutil.copymode(src, dst)
|
||||
except Exception as e:
|
||||
return -1, str(e)
|
||||
return 0, None
|
||||
|
||||
def _diff_before_write(self, src, dst, content=None):
|
||||
"""diff before writing when using --showdiff - not efficient"""
|
||||
def _diff_before_write(self, src, dst, content=None, quiet=False):
|
||||
"""
|
||||
diff before writing
|
||||
using a temp file if content is not None
|
||||
returns diff string ('' if same)
|
||||
"""
|
||||
tmp = None
|
||||
if content:
|
||||
tmp = utils.write_to_tmpfile(content)
|
||||
@@ -477,9 +539,12 @@ class Installer:
|
||||
if tmp:
|
||||
utils.remove(tmp, quiet=True)
|
||||
|
||||
# fake the output for readability
|
||||
if not diff:
|
||||
return
|
||||
if not quiet and diff:
|
||||
self._print_diff(src, dst, diff)
|
||||
return diff
|
||||
|
||||
def _print_diff(self, src, dst, diff):
|
||||
"""show diff to user"""
|
||||
self.log.log('diff \"{}\" VS \"{}\"'.format(dst, src))
|
||||
self.log.emph(diff)
|
||||
|
||||
@@ -530,12 +595,13 @@ class Installer:
|
||||
self.action_executed = True
|
||||
return ret, err
|
||||
|
||||
def _install_to_temp(self, templater, src, dst, tmpdir):
|
||||
def _install_to_temp(self, templater, src, dst, tmpdir, template=True):
|
||||
"""install a dotfile to a tempdir"""
|
||||
tmpdst = self._pivot_path(dst, tmpdir)
|
||||
return self.install(templater, src, tmpdst), tmpdst
|
||||
r = self.install(templater, src, tmpdst, template=template)
|
||||
return r, tmpdst
|
||||
|
||||
def install_to_temp(self, templater, tmpdir, src, dst):
|
||||
def install_to_temp(self, templater, tmpdir, src, dst, template=True):
|
||||
"""install a dotfile to a tempdir"""
|
||||
ret = False
|
||||
tmpdst = ''
|
||||
@@ -553,7 +619,8 @@ class Installer:
|
||||
if self.debug:
|
||||
self.log.dbg('tmp install {} (defined dst: {})'.format(src, dst))
|
||||
# install the dotfile to a temp directory for comparing
|
||||
r, tmpdst = self._install_to_temp(templater, src, dst, tmpdir)
|
||||
r, tmpdst = self._install_to_temp(templater, src, dst, tmpdir,
|
||||
template=template)
|
||||
ret, err = r
|
||||
if self.debug:
|
||||
self.log.dbg('tmp installed in {}'.format(tmpdst))
|
||||
|
||||
@@ -34,6 +34,7 @@ class Settings(DictParser):
|
||||
key_func_file = 'func_file'
|
||||
key_filter_file = 'filter_file'
|
||||
key_diff_command = 'diff_command'
|
||||
key_template_dotfile_default = 'template_dotfile_default'
|
||||
|
||||
# import keys
|
||||
key_import_actions = 'import_actions'
|
||||
@@ -49,7 +50,8 @@ class Settings(DictParser):
|
||||
upignore=[], cmpignore=[], instignore=[],
|
||||
workdir='~/.config/dotdrop', showdiff=False,
|
||||
minversion=None, func_file=[], filter_file=[],
|
||||
diff_command='diff -r -u {0} {1}'):
|
||||
diff_command='diff -r -u {0} {1}',
|
||||
template_dotfile_default=True):
|
||||
self.backup = backup
|
||||
self.banner = banner
|
||||
self.create = create
|
||||
@@ -72,6 +74,7 @@ class Settings(DictParser):
|
||||
self.func_file = func_file
|
||||
self.filter_file = filter_file
|
||||
self.diff_command = diff_command
|
||||
self.template_dotfile_default = template_dotfile_default
|
||||
|
||||
def _serialize_seq(self, name, dic):
|
||||
"""serialize attribute 'name' into 'dic'"""
|
||||
@@ -94,6 +97,7 @@ class Settings(DictParser):
|
||||
self.key_workdir: self.workdir,
|
||||
self.key_minversion: self.minversion,
|
||||
self.key_diff_command: self.diff_command,
|
||||
self.key_template_dotfile_default: self.template_dotfile_default,
|
||||
}
|
||||
self._serialize_seq(self.key_default_actions, dic)
|
||||
self._serialize_seq(self.key_import_actions, dic)
|
||||
|
||||
@@ -71,7 +71,7 @@ def shell(cmd, debug=False):
|
||||
|
||||
def diff(original, modified, raw=True,
|
||||
diff_cmd='', debug=False):
|
||||
"""compare two files"""
|
||||
"""compare two files, returns '' if same"""
|
||||
if not diff_cmd:
|
||||
diff_cmd = 'diff -r -u {0} {1}'
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
286
tests-ng/notemplate.sh
Executable file
286
tests-ng/notemplate.sh
Executable file
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env bash
|
||||
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||
# Copyright (c) 2020, deadc0de6
|
||||
#
|
||||
# test notemplate
|
||||
# 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"
|
||||
hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true
|
||||
|
||||
echo "dotdrop path: ${ddpath}"
|
||||
echo "pythonpath: ${PYTHONPATH}"
|
||||
|
||||
# get the helpers
|
||||
source ${cur}/helpers
|
||||
|
||||
echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)"
|
||||
|
||||
################################################################
|
||||
# this is the test
|
||||
################################################################
|
||||
|
||||
# the dotfile source
|
||||
tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
|
||||
mkdir -p ${tmps}/dotfiles
|
||||
#echo "dotfile source: ${tmps}"
|
||||
# the dotfile destination
|
||||
tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
|
||||
#echo "dotfile destination: ${tmpd}"
|
||||
|
||||
# create the config file
|
||||
cfg="${tmps}/config.yaml"
|
||||
|
||||
# globally
|
||||
cat > ${cfg} << _EOF
|
||||
config:
|
||||
backup: true
|
||||
create: true
|
||||
dotpath: dotfiles
|
||||
template_dotfile_default: false
|
||||
dotfiles:
|
||||
f_f1:
|
||||
dst: ${tmpd}/f1
|
||||
src: f1
|
||||
d_d1:
|
||||
dst: ${tmpd}/dir1
|
||||
src: dir1
|
||||
d_d2:
|
||||
dst: ${tmpd}/dir2
|
||||
src: dir2
|
||||
link: link
|
||||
d_d3:
|
||||
dst: ${tmpd}/dir3
|
||||
src: dir3
|
||||
link: link_children
|
||||
f_fl:
|
||||
dst: ${tmpd}/fl
|
||||
src: fl
|
||||
link: link
|
||||
f_fn:
|
||||
dst: ${tmpd}/fn
|
||||
src: fn
|
||||
template: true
|
||||
profiles:
|
||||
p1:
|
||||
dotfiles:
|
||||
- f_f1
|
||||
- d_d1
|
||||
- d_d2
|
||||
- d_d3
|
||||
- f_fl
|
||||
- f_fn
|
||||
_EOF
|
||||
#cat ${cfg}
|
||||
|
||||
# create the dotfile
|
||||
echo "before" > ${tmps}/dotfiles/f1
|
||||
echo "{#@@ should not be stripped @@#}" >> ${tmps}/dotfiles/f1
|
||||
echo "{{@@ header() @@}}" >> ${tmps}/dotfiles/f1
|
||||
echo "after" >> ${tmps}/dotfiles/f1
|
||||
|
||||
# create the directory
|
||||
mkdir -p ${tmps}/dotfiles/dir1/d1
|
||||
echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir1/d1/f2
|
||||
|
||||
# create the linked directory
|
||||
mkdir -p ${tmps}/dotfiles/dir2/d1
|
||||
echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir2/d1/f2
|
||||
|
||||
# create the link_children directory
|
||||
mkdir -p ${tmps}/dotfiles/dir3/{s1,s2,s3}
|
||||
echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir3/s1/f1
|
||||
echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir3/s2/f2
|
||||
|
||||
# create the linked dotfile
|
||||
echo "{{@@ header() @@}}" > ${tmps}/dotfiles/fl
|
||||
|
||||
# create the normal dotfile
|
||||
echo "before" > ${tmps}/dotfiles/fn
|
||||
echo "{#@@ should not be stripped @@#}" >> ${tmps}/dotfiles/fn
|
||||
echo "after" >> ${tmps}/dotfiles/fn
|
||||
|
||||
# install
|
||||
cd ${ddpath} | ${bin} install -f --showdiff -c ${cfg} -p p1 -V
|
||||
cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V
|
||||
|
||||
# simple file
|
||||
echo "doing globally"
|
||||
echo "* test simple file"
|
||||
[ ! -e ${tmpd}/f1 ] && echo 'not installed1' && exit 1
|
||||
grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1)
|
||||
grep 'should not be stripped' ${tmpd}/f1 || (echo "comment stripped" && exit 1)
|
||||
|
||||
# directory
|
||||
echo "* test directory"
|
||||
[ ! -d ${tmpd}/dir1 ] && echo 'not installed1' && exit 1
|
||||
[ ! -d ${tmpd}/dir1/d1 ] && echo 'not installed2' && exit 1
|
||||
[ ! -e ${tmpd}/dir1/d1/f2 ] && echo 'not installed3' && exit 1
|
||||
grep 'header' ${tmpd}/dir1/d1/f2 || (echo "header stripped" && exit 1)
|
||||
|
||||
# linked directory
|
||||
echo "* test linked directory"
|
||||
[ ! -h ${tmpd}/dir2 ] && echo 'not installed1' && exit 1
|
||||
[ ! -d ${tmpd}/dir2/d1 ] && echo 'not installed2' && exit 1
|
||||
[ ! -e ${tmpd}/dir2/d1/f2 ] && echo 'not installed3' && exit 1
|
||||
grep 'header' ${tmpd}/dir2/d1/f2 || (echo "header stripped" && exit 1)
|
||||
|
||||
# children_link directory
|
||||
echo "* test link_children directory"
|
||||
[ ! -d ${tmpd}/dir3 ] && echo 'not installed1' && exit 1
|
||||
[ ! -h ${tmpd}/dir3/s1 ] && echo 'not installed2' && exit 1
|
||||
[ ! -h ${tmpd}/dir3/s2 ] && echo 'not installed3' && exit 1
|
||||
[ ! -h ${tmpd}/dir3/s3 ] && echo 'not installed4' && exit 1
|
||||
[ ! -e ${tmpd}/dir3/s1/f1 ] && echo 'not installed5' && exit 1
|
||||
[ ! -e ${tmpd}/dir3/s2/f2 ] && echo 'not installed6' && exit 1
|
||||
grep 'header' ${tmpd}/dir3/s1/f1 || (echo "header stripped" && exit 1)
|
||||
grep 'header' ${tmpd}/dir3/s2/f2 || (echo "header stripped" && exit 1)
|
||||
|
||||
# linked file
|
||||
echo "* test linked file"
|
||||
[ ! -h ${tmpd}/fl ] && echo 'not installed' && exit 1
|
||||
grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1)
|
||||
|
||||
# normal dotfile
|
||||
echo "* normal dotfile"
|
||||
[ ! -e ${tmpd}/fn ] && echo 'not installed' && exit 1
|
||||
grep 'should not be stripped' ${tmpd}/fn && echo "no templated" && exit 1
|
||||
|
||||
# test backup done
|
||||
echo "before" > ${tmps}/dotfiles/f1
|
||||
cd ${ddpath} | ${bin} install -f --showdiff -c ${cfg} -p p1 -V
|
||||
[ ! -e ${tmpd}/f1.dotdropbak ] && echo "backup not done" && exit 1
|
||||
|
||||
# re-create the dotfile
|
||||
echo "before" > ${tmps}/dotfiles/f1
|
||||
echo "{#@@ should not be stripped @@#}" >> ${tmps}/dotfiles/f1
|
||||
echo "{{@@ header() @@}}" >> ${tmps}/dotfiles/f1
|
||||
echo "after" >> ${tmps}/dotfiles/f1
|
||||
|
||||
# through the dotfile
|
||||
cat > ${cfg} << _EOF
|
||||
config:
|
||||
backup: true
|
||||
create: true
|
||||
dotpath: dotfiles
|
||||
template_dotfile_default: true
|
||||
dotfiles:
|
||||
f_f1:
|
||||
dst: ${tmpd}/f1
|
||||
src: f1
|
||||
template: false
|
||||
d_d1:
|
||||
dst: ${tmpd}/dir1
|
||||
src: dir1
|
||||
template: false
|
||||
d_d2:
|
||||
dst: ${tmpd}/dir2
|
||||
src: dir2
|
||||
link: link
|
||||
template: false
|
||||
d_d3:
|
||||
dst: ${tmpd}/dir3
|
||||
src: dir3
|
||||
link: link_children
|
||||
template: false
|
||||
f_fl:
|
||||
dst: ${tmpd}/fl
|
||||
src: fl
|
||||
link: link
|
||||
template: false
|
||||
f_fn:
|
||||
dst: ${tmpd}/fn
|
||||
src: fn
|
||||
profiles:
|
||||
p1:
|
||||
dotfiles:
|
||||
- f_f1
|
||||
- d_d1
|
||||
- d_d2
|
||||
- d_d3
|
||||
- f_fl
|
||||
- f_fn
|
||||
_EOF
|
||||
#cat ${cfg}
|
||||
|
||||
# clean destination
|
||||
rm -rf ${tmpd}/*
|
||||
|
||||
# install
|
||||
cd ${ddpath} | ${bin} install -f --showdiff -c ${cfg} -p p1 -V
|
||||
cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V
|
||||
|
||||
# simple file
|
||||
echo "doing specifically"
|
||||
echo "* test simple file"
|
||||
[ ! -e ${tmpd}/f1 ] && echo 'not installed1' && exit 1
|
||||
grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1)
|
||||
grep 'should not be stripped' ${tmpd}/f1 || (echo "comment stripped" && exit 1)
|
||||
|
||||
# directory
|
||||
echo "* test directory"
|
||||
[ ! -d ${tmpd}/dir1 ] && echo 'not installed1' && exit 1
|
||||
[ ! -d ${tmpd}/dir1/d1 ] && echo 'not installed2' && exit 1
|
||||
[ ! -e ${tmpd}/dir1/d1/f2 ] && echo 'not installed3' && exit 1
|
||||
grep 'header' ${tmpd}/dir1/d1/f2 || (echo "header stripped" && exit 1)
|
||||
|
||||
# linked directory
|
||||
echo "* test linked directory"
|
||||
[ ! -h ${tmpd}/dir2 ] && echo 'not installed1' && exit 1
|
||||
[ ! -d ${tmpd}/dir2/d1 ] && echo 'not installed2' && exit 1
|
||||
[ ! -e ${tmpd}/dir2/d1/f2 ] && echo 'not installed3' && exit 1
|
||||
grep 'header' ${tmpd}/dir2/d1/f2 || (echo "header stripped" && exit 1)
|
||||
|
||||
# children_link directory
|
||||
echo "* test link_children directory"
|
||||
[ ! -d ${tmpd}/dir3 ] && echo 'not installed1' && exit 1
|
||||
[ ! -h ${tmpd}/dir3/s1 ] && echo 'not installed2' && exit 1
|
||||
[ ! -h ${tmpd}/dir3/s2 ] && echo 'not installed3' && exit 1
|
||||
[ ! -h ${tmpd}/dir3/s3 ] && echo 'not installed4' && exit 1
|
||||
[ ! -e ${tmpd}/dir3/s1/f1 ] && echo 'not installed5' && exit 1
|
||||
[ ! -e ${tmpd}/dir3/s2/f2 ] && echo 'not installed6' && exit 1
|
||||
grep 'header' ${tmpd}/dir3/s1/f1 || (echo "header stripped" && exit 1)
|
||||
grep 'header' ${tmpd}/dir3/s2/f2 || (echo "header stripped" && exit 1)
|
||||
|
||||
# linked file
|
||||
echo "* test linked file"
|
||||
[ ! -h ${tmpd}/fl ] && echo 'not installed' && exit 1
|
||||
grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1)
|
||||
|
||||
# normal dotfile
|
||||
echo "* normal dotfile"
|
||||
[ ! -e ${tmpd}/fn ] && echo 'not installed' && exit 1
|
||||
grep 'should not be stripped' ${tmpd}/fn && echo "no templated" && exit 1
|
||||
|
||||
|
||||
## CLEANING
|
||||
rm -rf ${tmps} ${tmpd}
|
||||
|
||||
echo "OK"
|
||||
exit 0
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
# exit on first error
|
||||
#set -e
|
||||
set -e
|
||||
|
||||
# all this crap to get current path
|
||||
rl="readlink -f"
|
||||
@@ -99,7 +99,7 @@ cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc
|
||||
|
||||
# check files haven't been updated
|
||||
grep 'b' ${dt}/a/c/acfile >/dev/null
|
||||
[ -e ${dt}/a/newfile ] && exit 1
|
||||
[ -e ${dt}/a/newfile ] && echo "should not have been updated" && exit 1
|
||||
|
||||
## CLEANING
|
||||
rm -rf ${tmps} ${tmpd}
|
||||
|
||||
Reference in New Issue
Block a user