1
0
Fork 0
mirror of synced 2024-06-17 14:31:10 -04:00
dotbot/dotbot/dispatcher.py
Dave Steinberg 0098c52610 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.
2020-07-19 20:52:39 -04:00

70 lines
2.7 KiB
Python

import os
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):
self._log = Messenger()
self._setup_context(base_directory)
self._load_plugins()
self._only = only
self._skip = skip
def _setup_context(self, base_directory):
path = os.path.abspath(
os.path.expanduser(base_directory))
if not os.path.exists(path):
raise DispatchError('Nonexistent base directory')
self._context = Context(path)
def dispatch(self, tasks):
success = True
for task in tasks:
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(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, actions[action])
handled = True
except Exception as err:
self._log.error(
'An error was encountered while executing action %s' %
action)
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):
self._plugins = [plugin(self._context)
for plugin in Plugin.__subclasses__()]
class DispatchError(Exception):
pass