From 633ae5540f1a798e33b474aac1edb696e34994e9 Mon Sep 17 00:00:00 2001 From: Dave Steinberg Date: Sun, 12 Jul 2020 22:01:26 -0400 Subject: [PATCH 1/2] Support if on tasks. Just like on a link action, the value is executed in the shell. If it has a non-zero exit code (failure), the whole task is skipped. This allows tasks to group actions that only apply in certain conditions (e.g. on certain OSes or in certain configurations), and allows all actions (not just link) to be conditional. --- dotbot/dispatcher.py | 7 +++++++ dotbot/plugins/link.py | 8 +------- dotbot/util/__init__.py | 2 +- dotbot/util/common.py | 6 ++++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/dotbot/dispatcher.py b/dotbot/dispatcher.py index 10e42930..f945d54a 100644 --- a/dotbot/dispatcher.py +++ b/dotbot/dispatcher.py @@ -2,6 +2,7 @@ from .plugin import Plugin from .messenger import Messenger from .context import Context +from .util import test_success class Dispatcher(object): def __init__(self, base_directory, only=None, skip=None): @@ -21,7 +22,13 @@ def _setup_context(self, base_directory): def dispatch(self, tasks): success = True for task in tasks: + test = task.get('if', None) + if test is not None and not test_success(test, cwd=self._context.base_directory(), log=self._log): + self._log.lowinfo('Skipping task') + continue for action in task: + if action == 'if': + continue if self._only is not None and action not in self._only \ or self._skip is not None and action in self._skip: self._log.info('Skipping action %s' % action) diff --git a/dotbot/plugins/link.py b/dotbot/plugins/link.py index 6f2b5620..1c5beb8a 100644 --- a/dotbot/plugins/link.py +++ b/dotbot/plugins/link.py @@ -47,7 +47,7 @@ def _process_links(self, links): path = self._default_source(destination, source.get('path')) else: path = self._default_source(destination, source) - if test is not None and not self._test_success(test): + if test is not None and not dotbot.util.test_success(test, cwd=self._context.base_directory(), log=self._log): self._log.lowinfo('Skipping %s' % destination) continue path = os.path.expandvars(os.path.expanduser(path)) @@ -105,12 +105,6 @@ def _process_links(self, links): self._log.error('Some links were not successfully set up') return success - def _test_success(self, command): - ret = dotbot.util.shell_command(command, cwd=self._context.base_directory()) - if ret != 0: - self._log.debug('Test \'%s\' returned false' % command) - return ret == 0 - def _default_source(self, destination, source): if source is None: basename = os.path.basename(destination) diff --git a/dotbot/util/__init__.py b/dotbot/util/__init__.py index 0c5a8f5f..2a33abe5 100644 --- a/dotbot/util/__init__.py +++ b/dotbot/util/__init__.py @@ -1 +1 @@ -from .common import shell_command +from .common import shell_command, test_success diff --git a/dotbot/util/common.py b/dotbot/util/common.py index d1e20003..9c871ca5 100644 --- a/dotbot/util/common.py +++ b/dotbot/util/common.py @@ -32,3 +32,9 @@ def shell_command(command, cwd=None, enable_stdin=False, enable_stdout=False, en stderr=stderr, cwd=cwd ) + +def test_success(command, cwd=None, log=None): + ret = shell_command(command, cwd=cwd) + if ret != 0 and log != None: + log.debug('Test \'%s\' returned false' % command) + return ret == 0 From 0098c5261036c7299150c75ef3c2d852a127e44a Mon Sep 17 00:00:00 2001 From: Dave Steinberg Date: Sun, 19 Jul 2020 20:52:39 -0400 Subject: [PATCH 2/2] Support named, conditional tasks. A new property called task can be used to assign a name. If it is specified, then the task's actions must be nested under an actions property, and an if property can be used to make it conditional. Just like on a link action, the value is executed in the shell. If it has a non-zero exit code (failure), the whole task is skipped. This allows tasks to group actions that only apply in certain conditions (e.g. on certain OSes or in certain configurations), and allows all actions (not just link) to be conditional. If there is no task property, then the task behaves exactly as it did previously. --- dotbot/dispatcher.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/dotbot/dispatcher.py b/dotbot/dispatcher.py index f945d54a..2d3b25ff 100644 --- a/dotbot/dispatcher.py +++ b/dotbot/dispatcher.py @@ -22,35 +22,43 @@ def _setup_context(self, base_directory): def dispatch(self, tasks): success = True for task in tasks: - test = task.get('if', None) - if test is not None and not test_success(test, cwd=self._context.base_directory(), log=self._log): - self._log.lowinfo('Skipping task') - continue - for action in task: - if action == 'if': - continue + actions = task + name = task.get('task', None) + if name is not None: + test = task.get('if', None) + if test is not None and not test_success(test, cwd=self._context.base_directory(), log=self._log): + self._log.info('Skipping task %s' % name) + actions = [] + else: + actions = task.get('actions', []) + if not actions: + self._log.info('Task %s has no actions' % name) + else: + self._log.info('Starting task %s' % name) + for action in actions: if self._only is not None and action not in self._only \ or self._skip is not None and action in self._skip: self._log.info('Skipping action %s' % action) continue handled = False if action == 'defaults': - self._context.set_defaults(task[action]) # replace, not update + self._context.set_defaults(actions[action]) # replace, not update handled = True # keep going, let other plugins handle this if they want for plugin in self._plugins: if plugin.can_handle(action): try: - success &= plugin.handle(action, task[action]) + success &= plugin.handle(action, actions[action]) handled = True except Exception as err: self._log.error( 'An error was encountered while executing action %s' % action) - self._log.debug(err) if not handled: success = False self._log.error('Action %s not handled' % action) + if name and actions: + self._log.info('Task %s completed' % name) return success def _load_plugins(self):