diff --git a/README.md b/README.md index 767dae7..949f64b 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,49 @@ Thus when `f_vimrc` is installed, the command `vim +VundleClean! +VundleInstall +VundleInstall! +qall` will be executed. + +Sometimes, you may even want to execute some action prior deploying a dotfile. +Let's take another example with +[vim-plug](https://github.com/junegunn/vim-plug): + +```yaml +actions: + pre: + vim-plug-install: test -e ~/.vim/autoload/plug.vim || (mkdir -p ~/.vim/autoload; curl + -fLo ~/.vim/autoload/plug.vim https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim) + vim-plug: vim +PlugInstall +qall +config: + backup: true + create: true + dotpath: dotfiles +dotfiles: + f_vimrc: + dst: ~/.vimrc + src: vimrc + actions: + - vim-plug-install + - vim-plug +profiles: + home: + dotfiles: + - f_vimrc +``` + +This way, we make sure [vim-plug](https://github.com/junegunn/vim-plug) +is installed prior deploying the `~/.vimrc` dotfile. + + +Note: You can also define `post` actions like this: + +```yaml +actions: + post: + some-action: echo "Hello, World!" >/tmp/log +``` + +If you don't specify neither `post` or `pre`, actions will be executed +after dotfiles deployment (which is equivalent to `post`). + ## Use transformations Transformation actions are used to transform a dotfile before it is diff --git a/dotdrop/action.py b/dotdrop/action.py index a2e78bb..7052f22 100644 --- a/dotdrop/action.py +++ b/dotdrop/action.py @@ -11,13 +11,25 @@ import os from dotdrop.logger import Logger -class Action: +class Cmd: def __init__(self, key, action): self.key = key self.action = action self.log = Logger() + def __str__(self): + return 'key:{} -> \"{}\"'.format(self.key, self.action) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __hash__(self): + return hash(self.key) ^ hash(self.action) + + +class Action(Cmd): + def execute(self): ret = 1 self.log.sub('executing \"{}\"'.format(self.action)) @@ -27,6 +39,9 @@ class Action: self.log.warn('action interrupted') return ret == 0 + +class Transform(Cmd): + def transform(self, arg0, arg1): '''execute transformation with {0} and {1} where {0} is the file to transform and @@ -43,12 +58,3 @@ class Action: except KeyboardInterrupt: self.log.warn('action interrupted') return ret == 0 - - def __str__(self): - return 'key:{} -> \"{}\"'.format(self.key, self.action) - - def __eq__(self, other): - return self.__dict__ == other.__dict__ - - def __hash__(self): - return hash(self.key) ^ hash(self.action) diff --git a/dotdrop/config.py b/dotdrop/config.py index 13eae00..a37ff1e 100644 --- a/dotdrop/config.py +++ b/dotdrop/config.py @@ -10,7 +10,7 @@ import os # local import from dotdrop.dotfile import Dotfile from dotdrop.logger import Logger -from dotdrop.action import Action +from dotdrop.action import Action, Transform class Cfg: @@ -25,6 +25,8 @@ class Cfg: # key actions key_actions = 'actions' + key_actions_pre = 'pre' + key_actions_post = 'post' # key transformations key_trans = 'trans' @@ -101,12 +103,37 @@ class Cfg: def _parse_actions(self, actions, entries): """ parse actions specified for an element """ - res = [] + res = { + self.key_actions_pre: [], + self.key_actions_post: [], + } for entry in entries: - if entry not in actions.keys(): + action = None + if self.key_actions_pre in actions and \ + entry in actions[self.key_actions_pre]: + key = self.key_actions_pre + action = actions[self.key_actions_pre][entry] + elif self.key_actions_post in actions and \ + entry in actions[self.key_actions_post]: + key = self.key_actions_post + action = actions[self.key_actions_post][entry] + elif entry not in actions.keys(): self.log.warn('unknown action \"{}\"'.format(entry)) continue - res.append(actions[entry]) + else: + key = self.key_actions_post + action = actions[entry] + res[key].append(action) + return res + + def _parse_trans(self, trans, entries): + """ parse trans specified for an element """ + res = [] + for entry in entries: + if entry not in trans.keys(): + self.log.warn('unknown trans \"{}\"'.format(entry)) + continue + res.append(trans[entry]) return res def _complete_configs(self): @@ -124,13 +151,20 @@ class Cfg: if self.key_actions in self.content: if self.content[self.key_actions] is not None: for k, v in self.content[self.key_actions].items(): - self.actions[k] = Action(k, v) + if k in [self.key_actions_pre, self.key_actions_post]: + items = self.content[self.key_actions][k].items() + for k2, v2 in items: + if k not in self.actions: + self.actions[k] = {} + self.actions[k][k2] = Action(k2, v2) + else: + self.actions[k] = Action(k, v) # parse all transformations if self.key_trans in self.content: if self.content[self.key_trans] is not None: for k, v in self.content[self.key_trans].items(): - self.trans[k] = Action(k, v) + self.trans[k] = Transform(k, v) # parse the profiles self.profiles = self.content[self.key_profiles] @@ -158,7 +192,7 @@ class Cfg: actions = self._parse_actions(self.actions, entries) entries = v[self.key_dotfiles_trans] if \ self.key_dotfiles_trans in v else [] - trans = self._parse_actions(self.trans, entries) + trans = self._parse_trans(self.trans, entries) if len(trans) > 0 and link: msg = 'transformations disabled for \"{}\"'.format(dst) msg += ' because link is True' diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index 003780b..aa1c0f4 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -87,6 +87,10 @@ def install(opts, conf): diff=opts['installdiff'], debug=opts['debug']) installed = [] for dotfile in dotfiles: + if dotfile.actions and Cfg.key_actions_pre in dotfile.actions: + for action in dotfile.actions[Cfg.key_actions_pre]: + LOG.dbg('executing pre action {}'.format(action)) + action.execute() LOG.dbg('installing {}'.format(dotfile)) if hasattr(dotfile, 'link') and dotfile.link: r = inst.link(dotfile.src, dotfile.dst) @@ -115,11 +119,13 @@ def install(opts, conf): tmp = os.path.join(opts['dotpath'], tmp) if os.path.exists(tmp): remove(tmp) - if len(r) > 0 and len(dotfile.actions) > 0: - # execute action - for action in dotfile.actions: - LOG.dbg('executing action {}'.format(action)) - action.execute() + if len(r) > 0: + if Cfg.key_actions_post in dotfile.actions: + actions = dotfile.actions[Cfg.key_actions_post] + # execute action + for action in actions: + LOG.dbg('executing post action {}'.format(action)) + action.execute() installed.extend(r) LOG.log('\n{} dotfile(s) installed.'.format(len(installed))) return True diff --git a/dotdrop/dotfile.py b/dotdrop/dotfile.py index 85e2e69..828b75b 100644 --- a/dotdrop/dotfile.py +++ b/dotdrop/dotfile.py @@ -8,7 +8,7 @@ represents a dotfile in dotdrop class Dotfile: def __init__(self, key, dst, src, - actions=[], trans=[], link=False): + actions={}, trans=[], link=False): # key of dotfile in the config self.key = key # where to install this dotfile