Skip to content

Commit

Permalink
fix: ensure plugins are loaded during dispatch
Browse files Browse the repository at this point in the history
  • Loading branch information
henworth committed Feb 25, 2023
1 parent 5e52c54 commit 014a403
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 58 deletions.
20 changes: 10 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
Contributing
============
# Contributing

All kinds of contributions to Dotbot are greatly appreciated. For someone
unfamiliar with the code base, the most efficient way to contribute is usually
to submit a [feature request](#feature-requests) or [bug report](#bug-reports).
If you want to dive into the source code, you can submit a [patch](#patches) as
well, either working on your own ideas or [existing issues][issues].

Feature Requests
----------------
## Feature Requests

Do you have an idea for an awesome new feature for Dotbot? Please [submit a
feature request][issue]. It's great to hear about new ideas.

If you are inclined to do so, you're welcome to [fork][fork] Dotbot, work on
implementing the feature yourself, and submit a patch. In this case, it's
*highly recommended* that you first [open an issue][issue] describing your
_highly recommended_ that you first [open an issue][issue] describing your
enhancement to get early feedback on the new feature that you are implementing.
This will help avoid wasted efforts and ensure that your work is incorporated
into the code base.

Bug Reports
-----------
## Bug Reports

Did something go wrong with Dotbot? Sorry about that! Bug reports are greatly
appreciated!
Expand All @@ -31,15 +28,14 @@ as Dotbot version, operating system, configuration file, error messages, and
steps to reproduce the bug. The more details you can include, the easier it is
to find and fix the bug.

Patches
-------
## Patches

Want to hack on Dotbot? Awesome!

If there are [open issues][issues], you're more than welcome to work on those -
this is probably the best way to contribute to Dotbot. If you have your own
ideas, that's great too! In that case, before working on substantial changes to
the code base, it is *highly recommended* that you first [open an issue][issue]
the code base, it is _highly recommended_ that you first [open an issue][issue]
describing what you intend to work on.

**Patches are generally submitted as pull requests.** Patches are also
Expand Down Expand Up @@ -82,6 +78,10 @@ do so with the following:
docker run -it --rm -v "${PWD}:/dotbot" -w /dotbot python:3.10-alpine /bin/sh
```

If the machine you are running Docker on has SELinux in the enforcing state, you
will have to disable that on the container. This can be done by adding
`--security-opt label:disable` to the above command.

After spawning the container, follow the same instructions as above (create a
virtualenv, ..., run the tests).

Expand Down
16 changes: 15 additions & 1 deletion dotbot/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from .context import Context
from .messenger import Messenger
from .plugin import Plugin


class Dispatcher(object):
Expand Down Expand Up @@ -41,11 +42,13 @@ def dispatch(self, tasks):
) and action != "defaults":
self._log.info("Skipping action %s" % action)
continue

handled = False
if action == "defaults":
self._context.set_defaults(task[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:
Expand All @@ -62,14 +65,25 @@ def dispatch(self, tasks):
)
self._log.debug(err)
if self._exit:
# There was an execption exit
# There was an exception exit
return False

if not handled:
success = False
self._log.error("Action %s not handled" % action)
if self._exit:
# Invalid action exit
return False

if action == "plugins":
# Create a list of loaded plugin names
loaded_plugins = [plugin.__class__.__name__ for plugin in self._plugins]

# Load plugins that haven't been loaded yet
for plugin in Plugin.__subclasses__():
if plugin.__name__ not in loaded_plugins:
self._plugins.append(plugin(self._context))

return success


Expand Down
2 changes: 1 addition & 1 deletion dotbot/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .clean import Clean
from .create import Create
from .link import Link
from .shell import Shell
from .plugins import Plugins
from .shell import Shell
29 changes: 15 additions & 14 deletions dotbot/plugins/plugins.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,46 @@
import os
import glob
import dotbot
import os

from ..plugin import Plugin
from ..util import module


class Plugins(dotbot.Plugin):
'''
class Plugins(Plugin):
"""
Load plugins from a list of paths.
'''
"""

_directive = 'plugins'
_directive = "plugins"
_has_shown_override_message = False

def can_handle(self, directive):
return directive == self._directive

def handle(self, directive, data):
if directive != self._directive:
raise ValueError('plugins cannot handle directive %s' %
directive)
raise ValueError("plugins cannot handle directive %s" % directive)
return self._process_plugins(data)

def _process_plugins(self, data):
success = True
plugin_paths = []
for item in data:
self._log.lowinfo('Loading plugin from %s' % item)
self._log.lowinfo("Loading plugin from %s" % item)

plugin_path_globs = glob.glob(os.path.join(item, '*.py'))
plugin_path_globs = glob.glob(os.path.join(item, "*.py"))
if not plugin_path_globs:
success = False
self._log.warning('Failed to load plugin from %s' % item)
self._log.warning("Failed to load plugin from %s" % item)
else:
for plugin_path in plugin_path_globs:
plugin_paths.append(plugin_path)

for plugin_path in plugin_paths:
abspath = os.path.abspath(plugin_path)
dotbot.util.module.load(abspath)
module.load(abspath)

if success:
self._log.info('All commands have been executed')
self._log.info("All commands have been executed")
else:
self._log.error('Some commands were not successfully executed')
self._log.error("Some commands were not successfully executed")
return success
32 changes: 0 additions & 32 deletions test/tests/plugin-config-file.bash

This file was deleted.

19 changes: 19 additions & 0 deletions tests/dotbot_plugin_config_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Test that a plugin can be loaded by config file.
This file is copied to a location with the name "config_file.py",
and is then loaded from within the `test_cli.py` code.
"""

import os.path

import dotbot


class ConfigFile(dotbot.Plugin):
def can_handle(self, directive):
return directive == "plugin_config_file"

def handle(self, directive, data):
with open(os.path.abspath(os.path.expanduser("~/flag")), "w") as file:
file.write("config file plugin loading works")
return True
21 changes: 21 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,27 @@ def test_plugin_loading_directory(home, dotfiles, run_dotbot):
assert file.read() == "directory plugin loading works"


def test_plugin_loading_config_file(home, dotfiles, run_dotbot):
"""Verify that plugins can be loaded by config file."""

dotfiles.makedirs("plugins")
plugin_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_config_file.py"
)
shutil.copy(plugin_file, os.path.join(dotfiles.directory, "plugins", "config_file.py"))

dotfiles.write_config(
[
{"plugins": [os.path.join(dotfiles.directory, "plugins")]},
{"plugin_config_file": "~"},
]
)
run_dotbot()

with open(os.path.join(home, "flag"), "r") as file:
assert file.read() == "config file plugin loading works"


def test_disable_builtin_plugins(home, dotfiles, run_dotbot):
"""Verify that builtin plugins can be disabled."""

Expand Down

0 comments on commit 014a403

Please sign in to comment.