From 51545771e97c16926ade18adb1319dfd13b4ea0a Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 3 Jul 2022 02:27:04 -0500 Subject: [PATCH 01/10] feat: add entrypoint plugin lookup --- errbot/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/errbot/utils.py b/errbot/utils.py index f9c0e87f0..c9491fe0d 100644 --- a/errbot/utils.py +++ b/errbot/utils.py @@ -11,6 +11,7 @@ from typing import List, Tuple, Union from dulwich import porcelain +from pkg_resources import iter_entry_points log = logging.getLogger(__name__) @@ -196,6 +197,13 @@ def collect_roots(base_paths: List, file_sig: str = "*.plug") -> List: return list(collections.OrderedDict.fromkeys(result)) +def entry_point_plugins(group): + paths = [] + for entry_point in iter_entry_points(group=group, name=None): + paths.append(entry_point.dist.location) + return paths + + def global_restart() -> None: """Restart the current process.""" python = sys.executable From fd72c4d85bf424541cc45e60b97c64f8c6ba1878 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 3 Jul 2022 02:28:46 -0500 Subject: [PATCH 02/10] feat: add entrypoint plugins --- errbot/plugin_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/errbot/plugin_manager.py b/errbot/plugin_manager.py index fea1a257c..b9741ea0e 100644 --- a/errbot/plugin_manager.py +++ b/errbot/plugin_manager.py @@ -19,7 +19,7 @@ from .plugin_info import PluginInfo from .storage import StoreMixin from .templating import add_plugin_templates_path, remove_plugin_templates_path -from .utils import collect_roots, version2tuple +from .utils import collect_roots, entry_point_plugins, version2tuple from .version import VERSION PluginInstanceCallback = Callable[[str, Type[BotPlugin]], BotPlugin] @@ -334,7 +334,8 @@ def update_plugin_places(self, path_list: str) -> Dict[Path, str]: :param path_list: the path list where to search for plugins. :return: the feedback for any specific path in case of error. """ - repo_roots = (CORE_PLUGINS, self._extra_plugin_dir, path_list) + ep = entry_point_plugins(group="errbot.plugins") + repo_roots = (CORE_PLUGINS, self._extra_plugin_dir, path_list, ep) all_roots = collect_roots(repo_roots) From c02b5cb30385ddb7b3684cbcca3aba41ec5214c3 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 3 Jul 2022 02:51:43 -0500 Subject: [PATCH 03/10] feat: add entrypoint plugin for backends --- errbot/backend_plugin_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/errbot/backend_plugin_manager.py b/errbot/backend_plugin_manager.py index cf967d2dd..5a3a0bba1 100644 --- a/errbot/backend_plugin_manager.py +++ b/errbot/backend_plugin_manager.py @@ -5,7 +5,7 @@ from errbot.plugin_info import PluginInfo -from .utils import collect_roots +from .utils import collect_roots, entry_point_plugins log = logging.getLogger(__name__) @@ -44,7 +44,8 @@ def __init__( self._base_class = base_class self.plugin_info = None - all_plugins_paths = collect_roots((base_search_dir, extra_search_dirs)) + ep = entry_point_plugins(group="errbot.backend_plugins") + all_plugins_paths = collect_roots((base_search_dir, extra_search_dirs, ep)) for potential_plugin in enumerate_backend_plugins(all_plugins_paths): if potential_plugin.name == plugin_name: From 8008448aadfe7822904557adcd646136c02be609 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 3 Jul 2022 02:52:06 -0500 Subject: [PATCH 04/10] fix: ensure cli uses entrypoints to list backend plugins --- errbot/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/errbot/cli.py b/errbot/cli.py index 75c5fa8aa..b0f8585ae 100755 --- a/errbot/cli.py +++ b/errbot/cli.py @@ -27,7 +27,7 @@ from errbot.bootstrap import CORE_BACKENDS from errbot.logs import root_logger from errbot.plugin_wizard import new_plugin_wizard -from errbot.utils import collect_roots +from errbot.utils import collect_roots, entry_point_plugins from errbot.version import VERSION log = logging.getLogger(__name__) @@ -281,6 +281,8 @@ def main() -> None: extra_backend = getattr(config, "BOT_EXTRA_BACKEND_DIR", []) if isinstance(extra_backend, str): extra_backend = [extra_backend] + ep = entry_point_plugins(group="errbot.backend_plugins") + extra_backend.extend(ep) if args["list"]: from errbot.backend_plugin_manager import enumerate_backend_plugins From 92cf8db5db5057c44f6e00600e7f3f251edb5eba Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 4 Jul 2022 01:02:31 -0500 Subject: [PATCH 05/10] refactor: use importlib instead of pkg_resources --- errbot/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/errbot/utils.py b/errbot/utils.py index c9491fe0d..9bb98fa9a 100644 --- a/errbot/utils.py +++ b/errbot/utils.py @@ -7,11 +7,11 @@ import sys import time from functools import wraps +from importlib import metadata from platform import system from typing import List, Tuple, Union from dulwich import porcelain -from pkg_resources import iter_entry_points log = logging.getLogger(__name__) @@ -199,8 +199,8 @@ def collect_roots(base_paths: List, file_sig: str = "*.plug") -> List: def entry_point_plugins(group): paths = [] - for entry_point in iter_entry_points(group=group, name=None): - paths.append(entry_point.dist.location) + for entry_point in metadata.entry_points().get(group, []): + paths.append(entry_point.dist._path.parent) return paths From 5ab14d86ddc8c9573fb3a50a3f1a0cf2f16cebe2 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 4 Jul 2022 18:41:53 -0500 Subject: [PATCH 06/10] fix: ensure plugin lookup works for py37 --- errbot/utils.py | 9 +++++++-- setup.py | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/errbot/utils.py b/errbot/utils.py index 9bb98fa9a..fd7f2694b 100644 --- a/errbot/utils.py +++ b/errbot/utils.py @@ -7,7 +7,12 @@ import sys import time from functools import wraps -from importlib import metadata + +try: + from importlib.metadata import entry_points +except ImportError: + from importlib_metadata import entry_points + from platform import system from typing import List, Tuple, Union @@ -199,7 +204,7 @@ def collect_roots(base_paths: List, file_sig: str = "*.plug") -> List: def entry_point_plugins(group): paths = [] - for entry_point in metadata.entry_points().get(group, []): + for entry_point in entry_points().get(group, []): paths.append(entry_point.dist._path.parent) return paths diff --git a/setup.py b/setup.py index d63e089f4..071f15631 100755 --- a/setup.py +++ b/setup.py @@ -42,6 +42,9 @@ "deepmerge==1.0.1", ] +if py_version < (3, 8): + deps.append("importlib-metadata==4.12.0") + if py_version < (3, 9): deps.append("graphlib-backport==1.0.3") From d96f0965fa87270c785ae556fe3d014b95f8c9ad Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Mon, 4 Jul 2022 23:47:32 -0500 Subject: [PATCH 07/10] test: basic entrypoint check --- tests/plugin_management_test.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/plugin_management_test.py b/tests/plugin_management_test.py index 59f878121..c82f6df24 100644 --- a/tests/plugin_management_test.py +++ b/tests/plugin_management_test.py @@ -9,7 +9,7 @@ from errbot import plugin_manager from errbot.plugin_info import PluginInfo from errbot.plugin_manager import IncompatiblePluginException -from errbot.utils import collect_roots, find_roots +from errbot.utils import collect_roots, entry_point_plugins, find_roots CORE_PLUGINS = plugin_manager.CORE_PLUGINS @@ -143,3 +143,8 @@ def test_errbot_version_check(): plugin_manager.check_errbot_version(pi) finally: plugin_manager.VERSION = real_version + + +def test_entry_point_plugin(): + no_plugins_found = entry_point_plugins("errbot.no_plugins") + assert [] == no_plugins_found From d8e49855b96e8522c12c3f7a940ea0dfbd552e82 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sat, 9 Jul 2022 00:18:01 -0500 Subject: [PATCH 08/10] docs: refactor installing plugins section --- docs/user_guide/administration.rst | 40 ++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/docs/user_guide/administration.rst b/docs/user_guide/administration.rst index 94d5b3a34..d6343ef05 100644 --- a/docs/user_guide/administration.rst +++ b/docs/user_guide/administration.rst @@ -40,8 +40,19 @@ If you just wish to know more about a specific command you can issue:: Installing plugins ------------------ -Errbot plugins are typically published to and installed from `GitHub `_. -We periodically crawl GitHub for errbot plugin repositories and `publish the results `_ for people to browse. +Errbot plugins can be installed via these methods + +* `!repos install` bot commnand +* Cloning a `GitHub `_ repository +* Extracting a tar/zip file + + +Using a bot command +^^^^^^^^^^^^^^^^^^^ + +Plugins installed via the :code:`!repos` command are managed by errbot itself and stored inside the `BOT_DATA_DIR` you set in `config.py`. + +We periodically crawl GitHub for errbot plugin repositories and `publish the results `_ for people to browse. You can have your bot display the same list of repos by issuing:: @@ -72,6 +83,22 @@ This can be done with:: !repos update all +Cloning a repository or tar/zip install +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Using a git repository or tar/zip file to install plugins is setting up your plugins to be managed manually. + +Plugins installed from cloning a repository need to be placed inside the `BOT_EXTRA_PLUGIN_DIR` path specified in the `config.py` file. + +Assuming `BOT_EXTRA_PLUGIN_DIR` is set to `/opt/plugins`:: + + $ git clone https://github.com/errbotio/err-helloworld /opt/plugins/err-helloworld + $ tar -zxvf err-helloworld.tar.gz -C /opt/plugins/ + +.. note:: + If a repo is cloned and the git remote information is present, updating the plugin may be possible via `!repos update` + + Dependencies ^^^^^^^^^^^^ @@ -83,15 +110,6 @@ If you have installed Errbot in a virtualenv, this will run the equivalent of :c If no virtualenv is detected, the equivalent of :code:`pip install --user -r requirements.txt` is used to ensure the package(s) is/are only installed for the user running Err. -Extra plugin directory -^^^^^^^^^^^^^^^^^^^^^^ - -Plugins installed via the :code:`!repos` command are managed by errbot itself and stored inside the `BOT_DATA_DIR` you set in `config.py`. -If you want to manage your plugins manually for any reason then errbot allows you to load additional plugins from a directory you specify. -You can do so by specifying the setting `BOT_EXTRA_PLUGIN_DIR` in your `config.py` file. -See the :download:`config-template.py` file for more details. - - .. _disabling_plugins: Disabling plugins From 14cb93447ec6d323a09271661e2d70676ca540e3 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sat, 9 Jul 2022 00:57:46 -0500 Subject: [PATCH 09/10] docs: add info on using entrypoints --- docs/user_guide/administration.rst | 11 ++++++++ docs/user_guide/plugin_development/basics.rst | 27 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/docs/user_guide/administration.rst b/docs/user_guide/administration.rst index d6343ef05..0ec0520ba 100644 --- a/docs/user_guide/administration.rst +++ b/docs/user_guide/administration.rst @@ -45,6 +45,7 @@ Errbot plugins can be installed via these methods * `!repos install` bot commnand * Cloning a `GitHub `_ repository * Extracting a tar/zip file +* Using pip Using a bot command @@ -99,6 +100,16 @@ Assuming `BOT_EXTRA_PLUGIN_DIR` is set to `/opt/plugins`:: If a repo is cloned and the git remote information is present, updating the plugin may be possible via `!repos update` +Using pip for plugins +^^^^^^^^^^^^^^^^^^^^^ + +Plugins published to to pypi.org can be installed using pip.:: + + $ pip install errbot-plugin-helloworld + +As part of the packaging configuration for the plugin, it should install all necessary dependencies for the plugin to work. + + Dependencies ^^^^^^^^^^^^ diff --git a/docs/user_guide/plugin_development/basics.rst b/docs/user_guide/plugin_development/basics.rst index 697b01476..021e8609e 100644 --- a/docs/user_guide/plugin_development/basics.rst +++ b/docs/user_guide/plugin_development/basics.rst @@ -197,6 +197,33 @@ the function `invert_string()`, the `helloworld` plugin can import it and use it """Say hello to the world""" return invert_string("Hello, world!") +Packaging +--------- + +A plugin can be packaged and distributed through pypi.org. The errbot plugin system uses entrypoints in setuptools to find available plugins. + +The two entrypoint avialable are + +* `errbot.plugins` - normal plugin and flows +* `errbot.backend_plugins` - backend plugins for collaboration providers + +To get this setup, add this block of code to `setup.py`. + +.. code-block:: python + + entry_points = { + "errbot.plugins": [ + "helloworld = helloWorld:HelloWorld", + ] + } + +Optionally, you may need to include a `MANIFEST.in` to include files of the repo + +.. code-block:: python + + include *.py *.plug + + Wrapping up ----------- From e5eda8d9db3f85277ec414052449925df9057aa3 Mon Sep 17 00:00:00 2001 From: Sijis Aviles Date: Sun, 10 Jul 2022 00:35:56 -0500 Subject: [PATCH 10/10] docs: add info to CHANGES --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d5f3b4b85..34ab464a4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,10 @@ v9.9.9 (unreleased) ------------------- +features: + +- core/plugins: detect plugins using entrypoints (#1590) + fixes: - docs: add unreleased section (#1576)