1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-14 18:47:27 +00:00

adding ability to use dynamic variables in dotfile src/dst

This commit is contained in:
deadc0de6
2018-11-27 13:15:35 +01:00
parent d569201b0b
commit c45c17184e
5 changed files with 145 additions and 7 deletions

View File

@@ -85,6 +85,7 @@ why [dotdrop](https://github.com/deadc0de6/dotdrop) rocks.
* [Available variables](#available-variables) * [Available variables](#available-variables)
* [Available methods](#available-methods) * [Available methods](#available-methods)
* [Dynamic dotfile paths](#dynamic-dotfile-paths)
* [Dotdrop header](#dotdrop-header) * [Dotdrop header](#dotdrop-header)
* [Example](#example) * [Example](#example)
@@ -579,6 +580,8 @@ the following entries:
* `ignoreempty`: do not deploy template if empty (default *false*) * `ignoreempty`: do not deploy template if empty (default *false*)
* **dotfiles** entry: a list of dotfiles * **dotfiles** entry: a list of dotfiles
* `dst`: where this dotfile needs to be deployed (can use `variables` and `dynvariables`, make sure to quote).
* `src`: dotfile path within the `dotpath` (can use `variables` and `dynvariables`, make sure to quote).
* `link`: if true dotdrop will create a symlink instead of copying (default *false*). * `link`: if true dotdrop will create a symlink instead of copying (default *false*).
* `cmpignore`: list of pattern to ignore when comparing (enclose in quotes when using wildcards). * `cmpignore`: list of pattern to ignore when comparing (enclose in quotes when using wildcards).
* `actions`: list of action keys that need to be defined in the **actions** entry below. * `actions`: list of action keys that need to be defined in the **actions** entry below.
@@ -625,9 +628,7 @@ the following entries:
* **trans** entry (optional): a list of transformations (see [Use transformations](#use-transformations)) * **trans** entry (optional): a list of transformations (see [Use transformations](#use-transformations))
``` ``` <trans-key>: <command-to-execute> ```
<trans-key>: <command-to-execute>
```
* **variables** entry (optional): a list of template variables (see [Variables](#variables)) * **variables** entry (optional): a list of template variables (see [Variables](#variables))
@@ -798,6 +799,28 @@ it does exist
If you'd like a specific function to be available, either open an issue If you'd like a specific function to be available, either open an issue
or do a PR. or do a PR.
## Dynamic dotfile paths
Dotfile source (`src`) and destination (`dst`) can be dynamically constructed using
defined variables (`variables` or `dynvariables`).
For example to have a dotfile deployed on the unique firefox profile where the
profile path is dynamically found using a shell oneliner stored in a dynvariable:
```yaml
dynvariables:
mozpath: find ~/.mozilla/firefox -name '*.default'
dotfiles:
f_somefile:
dst: "{{@@ mozpath @@}}/somefile"
src: firefox/somefile
profiles:
home:
dotfiles:
- f_somefile
```
Make sure to quote the path in the config file.
## Dotdrop header ## Dotdrop header
Dotdrop is able to insert a header in the generated dotfiles. This allows Dotdrop is able to insert a header in the generated dotfiles. This allows

View File

@@ -11,6 +11,7 @@ import shlex
# local import # local import
from dotdrop.dotfile import Dotfile from dotdrop.dotfile import Dotfile
from dotdrop.templategen import Templategen
from dotdrop.logger import Logger from dotdrop.logger import Logger
from dotdrop.action import Action, Transform from dotdrop.action import Action, Transform
from dotdrop.utils import * from dotdrop.utils import *
@@ -110,6 +111,15 @@ class Cfg:
if not self._load_file(): if not self._load_file():
raise ValueError('config is not valid') raise ValueError('config is not valid')
def eval_dotfiles(self, profile, debug=False):
"""resolve dotfiles src/dst templates"""
t = Templategen(profile=profile,
variables=self.get_variables(),
debug=debug)
for d in self.get_dotfiles(profile):
d.src = t.generate_string(d.src)
d.dst = t.generate_string(d.dst)
def _load_file(self): def _load_file(self):
"""load the yaml file""" """load the yaml file"""
with open(self.cfgpath, 'r') as f: with open(self.cfgpath, 'r') as f:

View File

@@ -90,7 +90,7 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
LOG.warn(msg.format(opts['profile'])) LOG.warn(msg.format(opts['profile']))
return False return False
t = Templategen(opts['profile'], base=opts['dotpath'], t = Templategen(profile=opts['profile'], base=opts['dotpath'],
variables=opts['variables'], debug=opts['debug']) variables=opts['variables'], debug=opts['debug'])
tmpdir = None tmpdir = None
if temporary: if temporary:
@@ -158,7 +158,7 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
if len(selected) < 1: if len(selected) < 1:
return False return False
t = Templategen(opts['profile'], base=opts['dotpath'], t = Templategen(profile=opts['profile'], base=opts['dotpath'],
variables=opts['variables'], debug=opts['debug']) variables=opts['variables'], debug=opts['debug'])
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'],
@@ -439,6 +439,9 @@ def main():
LOG.dbg('config file: {}'.format(args['--cfg'])) LOG.dbg('config file: {}'.format(args['--cfg']))
LOG.dbg('opts: {}'.format(opts)) LOG.dbg('opts: {}'.format(opts))
# resolve dynamic paths
conf.eval_dotfiles(opts['profile'], debug=opts['debug'])
if ENV_NOBANNER not in os.environ \ if ENV_NOBANNER not in os.environ \
and opts['banner'] \ and opts['banner'] \
and not args['--no-banner']: and not args['--no-banner']:

View File

@@ -23,7 +23,7 @@ COMMENT_END = '@@#}'
class Templategen: class Templategen:
def __init__(self, profile, base='.', variables={}, debug=False): def __init__(self, profile='', base='.', variables={}, debug=False):
self.base = base.rstrip(os.sep) self.base = base.rstrip(os.sep)
self.debug = debug self.debug = debug
self.log = Logger() self.log = Logger()
@@ -39,7 +39,8 @@ class Templategen:
comment_end_string=COMMENT_END) comment_end_string=COMMENT_END)
# adding variables # adding variables
self.env.globals['env'] = os.environ self.env.globals['env'] = os.environ
self.env.globals['profile'] = profile if profile:
self.env.globals['profile'] = profile
self.env.globals.update(variables) self.env.globals.update(variables)
# adding header method # adding header method
self.env.globals['header'] = self._header self.env.globals['header'] = self._header
@@ -47,10 +48,17 @@ class Templategen:
self.env.globals['exists'] = jhelpers.exists self.env.globals['exists'] = jhelpers.exists
def generate(self, src): def generate(self, src):
"""render template from path"""
if not os.path.exists(src): if not os.path.exists(src):
return '' return ''
return self._handle_file(src) return self._handle_file(src)
def generate_string(self, string):
"""render template from string"""
if not string:
return ''
return self.env.from_string(string).render()
def _header(self, prepend=''): def _header(self, prepend=''):
"""add a comment usually in the header of a dotfile""" """add a comment usually in the header of a dotfile"""
return '{}{}'.format(prepend, utils.header()) return '{}{}'.format(prepend, utils.header())

94
tests-ng/dyndotfilepaths.sh Executable file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# author: deadc0de6 (https://github.com/deadc0de6)
# Copyright (c) 2017, deadc0de6
#
# test dynamic variables in dotfile src/dst
# 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
# the dotfile destination
tmpd=`mktemp -d`
# create the config file
cfg="${tmps}/config.yaml"
dst=`echo ${tmpd} | rev`
cat > ${cfg} << _EOF
config:
backup: true
create: true
dotpath: dotfiles
dynvariables:
dpath: echo ${dst} | rev
dotfiles:
f_abc:
dst: "{{@@ dpath @@}}/abc"
src: abc
profiles:
p1:
dotfiles:
- f_abc
_EOF
cat ${cfg}
# create the dotfile
echo "{{@@ dpath @@}}" > ${tmps}/dotfiles/abc
# install
cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V
#cat ${tmpd}/abc
[ ! -e ${tmpd}/abc ] && echo "abc not installed dynamically" && exit 1
grep "^${tmpd}" ${tmpd}/abc >/dev/null
#cat ${tmpd}/abc
## CLEANING
rm -rf ${tmps} ${tmpd}
echo "OK"
exit 0