1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-10 06:19:17 +00:00

- add ability to use "write transformation"

- only allow a single transformation per dotfile
- refactoring and bug fixes
This commit is contained in:
deadc0de6
2018-12-06 13:19:57 +01:00
parent 1d53602769
commit 37db5cd68c
9 changed files with 262 additions and 112 deletions

View File

@@ -403,13 +403,19 @@ when xinitrc is installed.
## Use transformations ## Use transformations
Transformations are used to transform a dotfile before it is There are two types of transformations available:
installed. These are executed before the dotfile is installed to transform the source.
Transformation commands have two arguments: * **read transformations** ([Config](#config) key *trans*): used to transform dotfiles before they are installed
(used for commands `install` and `compare`). They have two arguments:
* **{0}** will be replaced with the dotfile to process * **{0}** will be replaced with the dotfile to process
* **{1}** will be replaced with a temporary file to store the result of the transformation * **{1}** will be replaced with a temporary file to store the result of the transformation
* **write transformations** ([Config](#config) key *trans_write**): used to transform files before updating a dotfile
(used for command `update`). They have two arguments
* **{0}** will be replaced with the file path to update the dotfile with
* **{1}** will be replaced with a temporary file to store the result of the transformation
A typical use-case for transformations is when the dotfile needs to be A typical use-case for transformations is when the dotfile needs to be
stored encrypted. stored encrypted.
@@ -420,42 +426,19 @@ dotfiles:
f_secret: f_secret:
dst: ~/.secret dst: ~/.secret
src: secret src: secret
trans: trans: gpg
- gpg
trans: trans:
gpg: gpg2 -q --for-your-eyes-only --no-tty -d {0} > {1} gpg: gpg2 -q --for-your-eyes-only --no-tty -d {0} > {1}
``` ```
The above config allows to store the dotfile `~/.secret` encrypted in the *dotfiles* The above config allows to store the dotfile `~/.secret` encrypted in the *dotpath*
directory and uses gpg to decrypt it when `install` is run. directory and uses gpg to decrypt it when `install` is run.
Here's how to deploy the above solution: See the wiki page for a walkthrough on how to deploy this solution as well
as more information on transformations:
* import the clear dotfile (what creates the correct entries in the config file) [wiki transformation page](https://github.com/deadc0de6/dotdrop/wiki/transformations).
```bash
$ dotdrop import ~/.secret
```
* encrypt the original dotfile
```bash
$ <some-gpg-command> ~/.secret
```
* overwrite the dotfile with the encrypted version
```bash
$ cp <encrypted-version-of-secret> dotfiles/secret
```
* edit the config file and add the transformation to the dotfile
(as shown in the example above)
* commit and push the changes
Note that transformations cannot be used if the dotfiles is to be linked (`link: true`). Note that transformations cannot be used if the dotfiles is to be linked (`link: true`).
Also `compare` won't work on dotfiles using transformations.
## Update dotdrop ## Update dotdrop
@@ -482,17 +465,17 @@ $ sudo pip3 install dotdrop --upgrade
Dotfiles managed by dotdrop can be updated using the `update` command. When updating, only Dotfiles managed by dotdrop can be updated using the `update` command. When updating, only
dotfiles that have differences with the stored version are updated. dotfiles that have differences with the stored version are updated.
A confirmation is requested from the user before any overwrite/update unless the A confirmation is requested from the user before any overwrite/update unless the
`--force` switch is used. `-f --force` switch is used.
Either provide the path of the file containing the new version of the dotfile or Either provide the path of the file containing the new version of the dotfile or
provide the dotfile key to update (as found in the config file) along with the `--key` switch. provide the dotfile key to update (as found in the config file) along with the `-k --key` switch.
When using the `--key` switch and no key is provided, all dotfiles for that profile are updated. When using the `-k --key` switch and no key is provided, all dotfiles for that profile are updated.
```bash ```bash
# update by path # update by path
$ dotdrop update ~/.vimrc $ dotdrop update ~/.vimrc
# update by key # update by key with the --key switch
$ dotdrop update f_vimrc $ dotdrop update --key f_vimrc
``` ```
There are two cases when updating a dotfile: There are two cases when updating a dotfile:
@@ -585,7 +568,8 @@ the following entries:
* `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.
* `trans`: list of transformation keys that need to be defined in the **trans** entry below. * `trans`: transformation key to apply when installing this dotfile (must be defined in the **trans** entry below).
* `trans_write`: transformation key to apply when updating this dotfile (must be defined in the **trans_write** entry below).
* `ignoreempty`: if true empty template will not be deployed (defaults to the value of `ignoreempty` above) * `ignoreempty`: if true empty template will not be deployed (defaults to the value of `ignoreempty` above)
```yaml ```yaml
@@ -599,8 +583,8 @@ the following entries:
- "<ignore-pattern>" - "<ignore-pattern>"
actions: actions:
- <action-key> - <action-key>
trans: trans: <transformation-key>
- <transformation-key> trans_write: <transformation-key>
``` ```
* **profiles** entry: a list of profiles with the different dotfiles that * **profiles** entry: a list of profiles with the different dotfiles that
@@ -632,6 +616,12 @@ the following entries:
<trans-key>: <command-to-execute> <trans-key>: <command-to-execute>
``` ```
* **trans_write** entry (optional): a list of write transformations (see [Use transformations](#use-transformations))
```
<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))
``` ```

View File

@@ -60,15 +60,15 @@ class Transform(Cmd):
"""execute transformation with {0} and {1} """execute transformation with {0} and {1}
where {0} is the file to transform and where {0} is the file to transform and
{1} is the result file""" {1} is the result file"""
if os.path.exists(arg1):
msg = 'transformation destination exists: {}'
self.log.warn(msg.format(arg1))
return False
ret = 1 ret = 1
cmd = self.action.format(arg0, arg1) cmd = self.action.format(arg0, arg1)
if os.path.exists(arg1):
msg = 'transformation \"{}\": destination exists: {}'
self.log.warn(msg.format(cmd, arg1))
return False
self.log.sub('transforming with \"{}\"'.format(cmd)) self.log.sub('transforming with \"{}\"'.format(cmd))
try: try:
ret = subprocess.call(cmd, shell=True) ret = subprocess.call(cmd, shell=True)
except KeyboardInterrupt: except KeyboardInterrupt:
self.log.warn('action interrupted') self.log.warn('transformation interrupted')
return ret == 0 return ret == 0

View File

@@ -42,7 +42,8 @@ class Cfg:
key_actions_post = 'post' key_actions_post = 'post'
# transformations keys # transformations keys
key_trans = 'trans' key_trans_r = 'trans'
key_trans_w = 'trans_write'
# template variables # template variables
key_variables = 'variables' key_variables = 'variables'
@@ -57,7 +58,8 @@ class Cfg:
key_dotfiles_noempty = 'ignoreempty' key_dotfiles_noempty = 'ignoreempty'
key_dotfiles_cmpignore = 'cmpignore' key_dotfiles_cmpignore = 'cmpignore'
key_dotfiles_actions = 'actions' key_dotfiles_actions = 'actions'
key_dotfiles_trans = 'trans' key_dotfiles_trans_r = 'trans'
key_dotfiles_trans_w = 'trans_write'
# profiles keys # profiles keys
key_profiles = 'profiles' key_profiles = 'profiles'
@@ -101,9 +103,13 @@ class Cfg:
# NOT linked inside the yaml dict (self.content) # NOT linked inside the yaml dict (self.content)
self.actions = {} self.actions = {}
# dict of all transformation objects by trans key # dict of all read transformation objects by trans key
# NOT linked inside the yaml dict (self.content) # NOT linked inside the yaml dict (self.content)
self.trans = {} self.trans_r = {}
# dict of all write transformation objects by trans key
# NOT linked inside the yaml dict (self.content)
self.trans_w = {}
# represents all dotfiles per profile by profile key # represents all dotfiles per profile by profile key
# NOT linked inside the yaml dict (self.content) # NOT linked inside the yaml dict (self.content)
@@ -174,11 +180,17 @@ class Cfg:
self.actions[self.key_actions_post] = {} self.actions[self.key_actions_post] = {}
self.actions[self.key_actions_post][k] = Action(k, v) self.actions[self.key_actions_post][k] = Action(k, v)
# parse all transformations # parse read transformations
if self.key_trans in self.content: if self.key_trans_r in self.content:
if self.content[self.key_trans] is not None: if self.content[self.key_trans_r] is not None:
for k, v in self.content[self.key_trans].items(): for k, v in self.content[self.key_trans_r].items():
self.trans[k] = Transform(k, v) self.trans_r[k] = Transform(k, v)
# parse write transformations
if self.key_trans_w in self.content:
if self.content[self.key_trans_w] is not None:
for k, v in self.content[self.key_trans_w].items():
self.trans_w[k] = Transform(k, v)
# parse the profiles # parse the profiles
self.lnk_profiles = self.content[self.key_profiles] self.lnk_profiles = self.content[self.key_profiles]
@@ -213,20 +225,60 @@ class Cfg:
itsactions = v[self.key_dotfiles_actions] if \ itsactions = v[self.key_dotfiles_actions] if \
self.key_dotfiles_actions in v else [] self.key_dotfiles_actions in v else []
actions = self._parse_actions(itsactions) actions = self._parse_actions(itsactions)
itstrans = v[self.key_dotfiles_trans] if \
self.key_dotfiles_trans in v else [] # parse read transformation
trans = self._parse_trans(itstrans) itstrans_r = v[self.key_dotfiles_trans_r] if \
if len(trans) > 0 and link: self.key_dotfiles_trans_r in v else None
trans_r = None
if itstrans_r:
if type(itstrans_r) is list:
msg = 'One transformation allowed per dotfile'
msg += ', error on dotfile \"{}\"'
self.log.err(msg.format(k))
msg = 'Please modify your config file to: \"trans: {}\"'
self.log.err(msg.format(itstrans_r[0]))
return False
trans_r = self._parse_trans(itstrans_r, read=True)
if not trans_r:
msg = 'unknown trans \"{}\" for \"{}\"'
self.log.err(msg.format(itstrans_r, k))
return False
# parse write transformation
itstrans_w = v[self.key_dotfiles_trans_w] if \
self.key_dotfiles_trans_w in v else None
trans_w = None
if itstrans_w:
if type(itstrans_w) is list:
msg = 'One write transformation allowed per dotfile'
msg += ', error on dotfile \"{}\"'
self.log.err(msg.format(k))
msg = 'Please modify your config file: \"trans_write: {}\"'
self.log.err(msg.format(itstrans_w[0]))
return False
trans_w = self._parse_trans(itstrans_w, read=False)
if not trans_w:
msg = 'unknown trans_write \"{}\" for \"{}\"'
self.log.err(msg.format(itstrans_w, k))
return False
# disable transformation when link is true
if link and (trans_r or trans_w):
msg = 'transformations disabled for \"{}\"'.format(dst) msg = 'transformations disabled for \"{}\"'.format(dst)
msg += ' because link is True' msg += ' because link is True'
self.log.warn(msg) self.log.warn(msg)
trans = [] trans_r = None
trans_w = None
# parse ignore pattern
ignores = v[self.key_dotfiles_cmpignore] if \ ignores = v[self.key_dotfiles_cmpignore] if \
self.key_dotfiles_cmpignore in v else [] self.key_dotfiles_cmpignore in v else []
# create new dotfile
self.dotfiles[k] = Dotfile(k, dst, src, self.dotfiles[k] = Dotfile(k, dst, src,
link=link, actions=actions, link=link, actions=actions,
trans=trans, cmpignore=ignores, trans_r=trans_r, trans_w=trans_w,
noempty=noempty) cmpignore=ignores, noempty=noempty)
# 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():
@@ -315,16 +367,14 @@ class Cfg:
res[key].append(action) res[key].append(action)
return res return res
def _parse_trans(self, entries): def _parse_trans(self, trans, read=True):
"""parse transformations specified for an element """parse transformation key specified for a dotfile"""
where entries are the ones defined for this dotfile""" transformations = self.trans_r
res = [] if not read:
for entry in entries: transformations = self.trans_w
if entry not in self.trans.keys(): if trans not in transformations.keys():
self.log.warn('unknown trans \"{}\"'.format(entry)) return None
continue return transformations[trans]
res.append(self.trans[entry])
return res
def _complete_settings(self): def _complete_settings(self):
"""set settings defaults if not present""" """set settings defaults if not present"""

View File

@@ -113,7 +113,7 @@ def cmd_install(opts, conf, temporary=False, keys=[]):
else: else:
src = dotfile.src src = dotfile.src
tmp = None tmp = None
if dotfile.trans: if dotfile.trans_r:
tmp = apply_trans(opts, dotfile) tmp = apply_trans(opts, dotfile)
if not tmp: if not tmp:
continue continue
@@ -173,7 +173,7 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
LOG.emph('\"{}\" does not exist on local\n'.format(dotfile.dst)) LOG.emph('\"{}\" does not exist on local\n'.format(dotfile.dst))
tmpsrc = None tmpsrc = None
if dotfile.trans: if dotfile.trans_r:
# apply transformation # apply transformation
tmpsrc = apply_trans(opts, dotfile) tmpsrc = apply_trans(opts, dotfile)
if not tmpsrc: if not tmpsrc:
@@ -387,22 +387,18 @@ def _select(selections, dotfiles):
def apply_trans(opts, dotfile): def apply_trans(opts, dotfile):
"""apply the transformation to the dotfile """apply the read transformation to the dotfile
return None if fails and new source if succeed""" return None if fails and new source if succeed"""
src = dotfile.src src = dotfile.src
new_src = '{}.{}'.format(src, TRANS_SUFFIX) new_src = '{}.{}'.format(src, TRANS_SUFFIX)
err = False trans = dotfile.trans_r
for trans in dotfile.trans: if opts['debug']:
if opts['debug']: LOG.dbg('executing transformation {}'.format(trans))
LOG.dbg('executing transformation {}'.format(trans)) s = os.path.join(opts['dotpath'], src)
s = os.path.join(opts['dotpath'], src) temp = os.path.join(opts['dotpath'], new_src)
temp = os.path.join(opts['dotpath'], new_src) if not trans.transform(s, temp):
if not trans.transform(s, temp): msg = 'transformation \"{}\" failed for {}'
msg = 'transformation \"{}\" failed for {}' LOG.err(msg.format(trans.key, dotfile.key))
LOG.err(msg.format(trans.key, dotfile.key))
err = True
break
if err:
if new_src and os.path.exists(new_src): if new_src and os.path.exists(new_src):
remove(new_src) remove(new_src)
return None return None
@@ -422,7 +418,7 @@ def main():
try: try:
conf = Cfg(os.path.expanduser(args['--cfg'])) conf = Cfg(os.path.expanduser(args['--cfg']))
except ValueError as e: except ValueError as e:
LOG.err('error: {}'.format(str(e))) LOG.err('Config format error: {}'.format(str(e)))
return False return False
opts = conf.get_settings() opts = conf.get_settings()

View File

@@ -9,9 +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=[], actions={}, trans_r=None, trans_w=None,
link=False, cmpignore=[], link=False, cmpignore=[], noempty=False):
noempty=False):
# 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,8 +21,10 @@ class Dotfile:
self.link = link self.link = link
# list of actions # list of actions
self.actions = actions self.actions = actions
# list of transformations # read transformation
self.trans = trans self.trans_r = trans_r
# write transformation
self.trans_w = trans_w
# pattern to ignore when comparing # pattern to ignore when comparing
self.cmpignore = cmpignore self.cmpignore = cmpignore
# do not deploy empty file # do not deploy empty file

View File

@@ -56,12 +56,39 @@ class Updater:
def _update(self, path, dotfile): def _update(self, path, dotfile):
"""update dotfile from file pointed by path""" """update dotfile from file pointed by path"""
ret = False
new_path = None
left = os.path.expanduser(path) left = os.path.expanduser(path)
right = os.path.join(self.conf.abs_dotpath(self.dotpath), dotfile.src) right = os.path.join(self.conf.abs_dotpath(self.dotpath), dotfile.src)
right = os.path.expanduser(right) right = os.path.expanduser(right)
if os.path.isdir(path): if dotfile.trans_w:
return self._handle_dir(left, right) # apply write transformation if any
return self._handle_file(left, right) new_path = self._apply_trans_w(path, dotfile)
if not new_path:
return False
left = new_path
if os.path.isdir(left):
ret = self._handle_dir(left, right)
else:
ret = self._handle_file(left, right)
# clean temporary files
if new_path and os.path.exists(new_path):
utils.remove(new_path)
return ret
def _apply_trans_w(self, path, dotfile):
"""apply write transformation to dotfile"""
trans = dotfile.trans_w
if self.debug:
self.log.dbg('executing write transformation {}'.format(trans))
tmp = utils.get_unique_tmp_name()
if not trans.transform(path, tmp):
msg = 'transformation \"{}\" failed for {}'
self.log.err(msg.format(trans.key, dotfile.key))
if os.path.exists(tmp):
utils.remove(tmp)
return None
return tmp
def _normalize(self, path): def _normalize(self, path):
"""normalize the path to match dotfile""" """normalize the path to match dotfile"""

View File

@@ -8,6 +8,7 @@ utilities
import subprocess import subprocess
import tempfile import tempfile
import os import os
import uuid
import shlex import shlex
from shutil import rmtree from shutil import rmtree
@@ -61,6 +62,12 @@ def get_tmpfile():
return path return path
def get_unique_tmp_name():
"""get a unique file name (not created)"""
unique = str(uuid.uuid4())
return os.path.join(tempfile.gettempdir(), unique)
def remove(path): def remove(path):
"""remove a file/directory/symlink""" """remove a file/directory/symlink"""
if not os.path.lexists(path): if not os.path.lexists(path):

View File

@@ -9,6 +9,7 @@
# exit on first error # exit on first error
set -e set -e
#set -v
# all this crap to get current path # all this crap to get current path
rl="readlink -f" rl="readlink -f"
@@ -59,10 +60,16 @@ cfg="${tmps}/config.yaml"
# token # token
token="test-base64" token="test-base64"
tokend="compressed archive"
touched="touched"
cat > ${cfg} << _EOF cat > ${cfg} << _EOF
trans: trans:
base64: cat {0} | base64 -d > {1} base64: cat {0} | base64 -d > {1}
uncompress: mkdir -p {1} && tar -xf {0} -C {1}
trans_write:
base64: cat {0} | base64 > {1}
compress: tar -cf {1} -C {0} .
config: config:
backup: true backup: true
create: true create: true
@@ -74,43 +81,117 @@ dotfiles:
f_abc: f_abc:
dst: ${tmpd}/abc dst: ${tmpd}/abc
src: abc src: abc
trans: trans: base64
- base64 trans_write: base64
d_ghi:
dst: ${tmpd}/ghi
src: ghi
trans: uncompress
trans_write: compress
profiles: profiles:
p1: p1:
dotfiles: dotfiles:
- f_abc - f_abc
- f_def - f_def
- d_ghi
_EOF _EOF
cat ${cfg} cat ${cfg}
# create the dotfile # create the base64 dotfile
tmpf=`mktemp` tmpf=`mktemp`
echo ${token} > ${tmpf} echo ${token} > ${tmpf}
cat ${tmpf} | base64 > ${tmps}/dotfiles/abc cat ${tmpf} | base64 > ${tmps}/dotfiles/abc
rm -f ${tmpf} rm -f ${tmpf}
# create the canary dotfile
echo 'marker' > ${tmps}/dotfiles/def echo 'marker' > ${tmps}/dotfiles/def
# install # create the compressed dotfile
cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b tmpx=`mktemp -d`
mkdir -p ${tmpx}/{a,b,c}
mkdir -p ${tmpx}/a/{dir1,dir2}
# ambiguous redirect ??
#echo ${tokend} > ${tmpd}/{a,b,c}/somefile
echo ${tokend} > ${tmpx}/a/somefile
echo ${tokend} > ${tmpx}/b/somefile
echo ${tokend} > ${tmpx}/c/somefile
echo ${tokend} > ${tmpx}/a/dir1/otherfile
tar -cf ${tmps}/dotfiles/ghi -C ${tmpx} .
rm -rf ${tmpx}
tar -tf ${tmps}/dotfiles/ghi
# checks ###########################
# test install and compare
###########################
# install
cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V
# check canary dotfile
[ ! -e ${tmpd}/def ] && exit 1
# check base64 dotfile
[ ! -e ${tmpd}/abc ] && exit 1 [ ! -e ${tmpd}/abc ] && exit 1
content=`cat ${tmpd}/abc` content=`cat ${tmpd}/abc`
[ "${content}" != "${token}" ] && exit 1 [ "${content}" != "${token}" ] && exit 1
# check directory dotfile
[ ! -e ${tmpd}/ghi/a/dir1/otherfile ] && exit 1
content=`cat ${tmpd}/ghi/a/somefile`
[ "${content}" != "${tokend}" ] && exit 1
content=`cat ${tmpd}/ghi/a/dir1/otherfile`
[ "${content}" != "${tokend}" ] && exit 1
# compare # compare
cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V
[ "$?" != "0" ] && exit 1 [ "$?" != "0" ] && exit 1
# change file # change base64 deployed file
echo 'touched' >> ${tmpd}/abc echo ${touched} > ${tmpd}/abc
set +e set +e
cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V
[ "$?" != "1" ] && exit 1 [ "$?" != "1" ] && exit 1
set -e set -e
# change uncompressed deployed dotfile
echo ${touched} > ${tmpd}/ghi/a/somefile
set +e
cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V
[ "$?" != "1" ] && exit 1
set -e
###########################
# test update
###########################
# update single file
cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V f_abc
[ "$?" != "0" ] && exit 1
# test updated file
[ ! -e ${tmps}/dotfiles/abc ] && exit 1
content=`cat ${tmps}/dotfiles/abc`
bcontent=`echo ${touched} | base64`
[ "${content}" != "${bcontent}" ] && exit 1
# update directory
echo ${touched} > ${tmpd}/ghi/b/newfile
rm -r ${tmpd}/ghi/c
cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V d_ghi
[ "$?" != "0" ] && exit 1
# test updated directory
tar -tf ${tmps}/dotfiles/ghi | grep './b/newfile'
tar -tf ${tmps}/dotfiles/ghi | grep './a/dir1/otherfile'
tmpy=`mktemp -d`
tar -xf ${tmps}/dotfiles/ghi -C ${tmpy}
content=`cat ${tmpy}/a/somefile`
[ "${content}" != "${touched}" ] && exit 1
# check canary dotfile
[ ! -e ${tmps}/dotfiles/def ] && exit 1
## CLEANING ## CLEANING
rm -rf ${tmps} ${tmpd} rm -rf ${tmps} ${tmpd}

View File

@@ -55,10 +55,8 @@ exec bspwm
f.write(' actions:\n') f.write(' actions:\n')
for action in d.actions: for action in d.actions:
f.write(' - {}\n'.format(action.key)) f.write(' - {}\n'.format(action.key))
if len(d.trans) > 0: if d.trans_r:
f.write(' trans:\n') f.write(' trans: {}\n'.format(d.trans_r.key))
for action in d.trans:
f.write(' - {}\n'.format(action.key))
f.write('profiles:\n') f.write('profiles:\n')
f.write(' {}:\n'.format(profile)) f.write(' {}:\n'.format(profile))
for d in dotfiles: for d in dotfiles:
@@ -154,7 +152,7 @@ exec bspwm
tr = Action('testtrans', cmd) tr = Action('testtrans', cmd)
f9, c9 = create_random_file(tmp, content=trans1) f9, c9 = create_random_file(tmp, content=trans1)
dst9 = os.path.join(dst, get_string(6)) dst9 = os.path.join(dst, get_string(6))
d9 = Dotfile(get_string(6), dst9, os.path.basename(f9), trans=[tr]) d9 = Dotfile(get_string(6), dst9, os.path.basename(f9), trans_r=tr)
# to test template # to test template
f10, _ = create_random_file(tmp, content='{{@@ profile @@}}') f10, _ = create_random_file(tmp, content='{{@@ profile @@}}')