Commit a3d9059d authored by Guillaume Binet's avatar Guillaume Binet

extracted repo_manager from plugin_manager.

parent 0adf5200
......@@ -33,7 +33,7 @@ class BotPluginBase(StoreMixin):
""" This should be eventually moved back to __init__ once plugin will forward correctly their params.
"""
self._bot = bot
self.plugin_dir = bot.plugin_manager.plugin_dir
self.plugin_dir = bot.repo_manager.plugin_dir
@property
def mode(self) -> str:
......@@ -469,12 +469,6 @@ class BotPlugin(BotPluginBase):
"""
return self._bot.query_room(room=room)
def get_installed_plugin_repos(self) -> Mapping:
"""
Get the current installed plugin repos in a dictionary of name / url
"""
return self._bot.get_installed_plugin_repos()
def start_poller(self,
interval: float,
method: Callable[..., None],
......
......@@ -6,7 +6,7 @@ from pprint import pformat
import shutil
from errbot import BotPlugin, botcmd
from errbot.repos import KNOWN_PUBLIC_REPOS
from errbot.repo_manager import KNOWN_PUBLIC_REPOS
from errbot.utils import which
from errbot.plugin_manager import check_dependencies, global_restart, PluginConfigurationException
......@@ -22,7 +22,9 @@ class Plugins(BotPlugin):
"""
if not args.strip():
return "You should have an urls/git repo argument"
errors = self._bot.plugin_manager.install_repo(args)
self._bot.repo_manager.install_repo(args)
errors = self._bot.plugin_manager.update_dynamic_plugins()
if errors:
self.send(mess.frm, 'Some plugins are generating errors:\n' + '\n'.join(errors),
message_type=mess.type)
......@@ -46,13 +48,13 @@ class Plugins(BotPlugin):
yield "You should have a repo name as argument"
return
repos = self._bot.plugin_manager.get_installed_plugin_repos()
repos = self._bot.repo_manager.get_installed_plugin_repos()
if args not in repos:
yield "This repo is not installed check with " + self._bot.prefix + "repos the list of installed ones"
return
plugin_path = path.join(self._bot.plugin_manager.plugin_dir, args)
plugin_path = path.join(self._bot.repo_manager.plugin_dir, args)
for plugin in self._bot.plugin_manager.getAllPlugins():
if plugin.path.startswith(plugin_path):
yield 'Removing %s...' % plugin.name
......@@ -60,7 +62,7 @@ class Plugins(BotPlugin):
shutil.rmtree(plugin_path)
repos.pop(args)
self._bot.plugin_manager.set_plugin_repos(repos)
self._bot.repo_manager.set_plugin_repos(repos)
yield 'Repo %s removed.' % args
......@@ -70,7 +72,7 @@ class Plugins(BotPlugin):
""" list the current active plugin repositories
"""
installed_repos = self._bot.get_installed_plugin_repos()
installed_repos = self._bot.repo_manager.get_installed_plugin_repos()
all_names = sorted(set([name for name in KNOWN_PUBLIC_REPOS] + [name for name in installed_repos]))
......@@ -114,7 +116,7 @@ class Plugins(BotPlugin):
directories = set()
repos = {}
_installed = self._bot.get_installed_plugin_repos()
_installed = self._bot.repo_manager.get_installed_plugin_repos()
# Fix to migrate exiting plugins into new format
for short_name, url in _installed.items():
......
......@@ -19,6 +19,7 @@ import inspect
import logging
import traceback
from errbot.repo_manager import BotRepoManager
from .bundled.threadpool import ThreadPool, WorkRequest
from .backends.base import Backend
......@@ -86,9 +87,13 @@ class ErrBot(Backend):
self.bot_alt_prefixes = tuple(prefix.lower() for prefix in bot_config.BOT_ALT_PREFIXES)
else:
self.bot_alt_prefixes = bot_config.BOT_ALT_PREFIXES
self.repo_manager = None
self.plugin_manager = None
self.storage_plugin = None
def attach_repo_manager(self, repo_manager):
self.repo_manager = repo_manager
def attach_plugin_manager(self, plugin_manager):
self.plugin_manager = plugin_manager
plugin_manager.attach_bot(self)
......
......@@ -3,6 +3,7 @@ import logging
from errbot.errBot import ErrBot
from errbot.plugin_manager import BotPluginManager
from errbot.repo_manager import BotRepoManager
from errbot.specific_plugin_manager import SpecificPluginManager
import sys
......@@ -58,8 +59,11 @@ def setup_bot(backend_name, logger, config, restore=None):
botplugins_dir = path.join(config.BOT_DATA_DIR, PLUGINS_SUBDIR)
if not path.exists(botplugins_dir):
makedirs(botplugins_dir, mode=0o755)
repo_manager = BotRepoManager(storage_plugin,
botplugins_dir)
botpm = BotPluginManager(storage_plugin,
botplugins_dir,
repo_manager,
config.BOT_EXTRA_PLUGIN_DIR,
config.AUTOINSTALL_DEPS,
getattr(config, 'CORE_PLUGINS', None))
......@@ -74,8 +78,9 @@ def setup_bot(backend_name, logger, config, restore=None):
try:
bot = backendpm.get_plugin_by_name(backend_name)
bot.attach_plugin_manager(botpm)
bot.attach_storage_plugin(storage_plugin)
bot.attach_repo_manager(repo_manager)
bot.attach_plugin_manager(botpm)
except Exception:
log.exception("Unable to load or configure the backend.")
exit(-1)
......
......@@ -8,21 +8,15 @@ import inspect
import logging
import sys
import os
import subprocess
from tarfile import TarFile
from urllib.request import urlopen
import pip
from .botplugin import BotPlugin
from .utils import (version2array, PY3, PY2, find_roots_with_extra,
which, human_name_for_git_url)
from .utils import (version2array, PY3, PY2, find_roots_with_extra)
from .templating import remove_plugin_templates_path, add_plugin_templates_path
from .version import VERSION
from yapsy.PluginManager import PluginManager
from yapsy.PluginFileLocator import PluginFileLocator, PluginFileAnalyzerWithInfoFile
from .core_plugins.wsview import route
from .storage import StoreMixin
from .repos import KNOWN_PUBLIC_REPOS
log = logging.getLogger(__name__)
......@@ -207,17 +201,16 @@ class BotPluginManager(PluginManager, StoreMixin):
"""Customized yapsy PluginManager for ErrBot."""
# Storage names
REPOS = b'repos' if PY2 else 'repos'
CONFIGS = b'configs' if PY2 else 'configs'
BL_PLUGINS = b'bl_plugins' if PY2 else 'bl_plugins'
def __init__(self, storage_plugin, plugin_dir, extra, autoinstall_deps, core_plugins):
def __init__(self, storage_plugin, repo_manager, extra, autoinstall_deps, core_plugins):
self.bot = None
self.autoinstall_deps = autoinstall_deps
self.extra = extra
self.open_storage(storage_plugin, 'core')
self.plugin_dir = plugin_dir
self.core_plugins = core_plugins
self.repo_manager = repo_manager
# be sure we have a configs entry for the plugin configurations
if self.CONFIGS not in self:
......@@ -392,51 +385,6 @@ class BotPluginManager(PluginManager, StoreMixin):
for name in self.get_all_active_plugin_names():
self.deactivatePluginByName(name, "bots")
# Repo management
def get_installed_plugin_repos(self):
repos = self.get(self.REPOS, {})
if not repos:
return repos
# Fix to migrate exiting plugins into new format
for url in self.get(self.REPOS, repos).values():
if type(url) == dict:
continue
t_name = '/'.join(url.split('/')[-2:])
name = t_name.replace('.git', '')
t_repo = {name: {
'path': url,
'documentation': 'Unavilable',
'python': None,
'avatar_url': None,
}
}
repos.update(t_repo)
return repos
def set_plugin_repos(self, repos):
self[self.REPOS] = repos
def add_plugin_repo(self, name, url):
if PY2:
name = name.encode('utf-8')
url = url.encode('utf-8')
repos = self.get_installed_plugin_repos()
t_installed = {name: {
'path': url,
'documentation': 'Unavilable',
'python': None,
'avatar_url': None,
}
}
repos.update(t_installed)
self.set_plugin_repos(repos)
# plugin blacklisting management
def get_blacklisted_plugin(self):
return self.get(self.BL_PLUGINS, [])
......@@ -476,9 +424,7 @@ class BotPluginManager(PluginManager, StoreMixin):
# this will load the plugins the admin has setup at runtime
def update_dynamic_plugins(self):
return self.update_plugin_places(
[self.plugin_dir + os.sep + d for d in self.get(self.REPOS, {}).keys()],
self.extra, self.autoinstall_deps)
return self.update_plugin_places(self.repo_manager.get_all_repos_paths(), self.extra, self.autoinstall_deps)
def activate_non_started_plugins(self):
log.info('Activating all the plugins...')
......@@ -519,32 +465,6 @@ class BotPluginManager(PluginManager, StoreMixin):
self.deactivate_plugin_by_name(name)
return "Plugin %s deactivated." % name
def install_repo(self, repo):
if repo in KNOWN_PUBLIC_REPOS:
repo = KNOWN_PUBLIC_REPOS[repo]['path'] # replace it by the url
git_path = which('git')
if not git_path:
return ('git command not found: You need to have git installed on '
'your system to be able to install git based plugins.', )
# TODO: Update download path of plugin.
if repo.endswith('tar.gz'):
tar = TarFile(fileobj=urlopen(repo))
tar.extractall(path=self.plugin_dir)
s = repo.split(':')[-1].split('/')[-2:]
human_name = '/'.join(s).rstrip('.tar.gz')
else:
human_name = human_name_for_git_url(repo)
p = subprocess.Popen([git_path, 'clone', repo, human_name], cwd=self.plugin_dir, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
feedback = p.stdout.read().decode('utf-8')
error_feedback = p.stderr.read().decode('utf-8')
if p.wait():
return "Could not load this plugin: \n\n%s\n\n---\n\n%s" % (feedback, error_feedback),
self.add_plugin_repo(human_name, repo)
return self.update_dynamic_plugins()
def remove_plugin(self, plugin):
"""
Deactivate and remove a plugin completely.
......
import os
import urllib.request
import ast
from tarfile import TarFile
import subprocess
from errbot.storage import StoreMixin
from .utils import PY2, which, human_name_for_git_url
def get_known_repos():
"""
Get known repos from registry
An example entry of the json file is the following
'errbotio/err-pypi': {
'avatar_url': None,
'documentation': 'some commands to query pypi',
'path': 'https://github.com/errbotio/err-pypi.git',
'python': None
}, ...
"""
registry_url = 'http://bit.ly/1kjdlRX'
registry = urllib.request.urlopen(registry_url).read()
return ast.literal_eval(registry.decode('utf-8'))
KNOWN_PUBLIC_REPOS = get_known_repos()
REPOS = b'repos' if PY2 else 'repos'
class BotRepoManager(StoreMixin):
"""
Manages the repo list, git clones/updates or the repos.
"""
def __init__(self, storage_plugin, plugin_dir):
self.storage_plugin = storage_plugin
self.plugin_dir = plugin_dir
self.open_storage(storage_plugin, 'repomgr')
def get_installed_plugin_repos(self):
repos = self.get(REPOS, {})
if not repos:
return repos
# Fix to migrate exiting plugins into new format
for url in self.get(REPOS, repos).values():
if type(url) == dict:
continue
t_name = '/'.join(url.split('/')[-2:])
name = t_name.replace('.git', '')
t_repo = {name: {
'path': url,
'documentation': 'Unavilable',
'python': None,
'avatar_url': None,
}
}
repos.update(t_repo)
return repos
def add_plugin_repo(self, name, url):
if PY2:
name = name.encode('utf-8')
url = url.encode('utf-8')
repos = self.get_installed_plugin_repos()
t_installed = {name: {
'path': url,
'documentation': 'Unavailable',
'python': None,
'avatar_url': None,
}
}
repos.update(t_installed)
self[REPOS] = repos
def set_plugin_repos(self, repos):
""" Used externally.
"""
self[REPOS] = repos
def get_all_repos_paths(self):
return [self.plugin_dir + os.sep + d for d in self.get(REPOS, {}).keys()]
def install_repo(self, repo):
if repo in KNOWN_PUBLIC_REPOS:
repo = KNOWN_PUBLIC_REPOS[repo]['path'] # replace it by the url
git_path = which('git')
if not git_path:
return ('git command not found: You need to have git installed on '
'your system to be able to install git based plugins.', )
# TODO: Update download path of plugin.
if repo.endswith('tar.gz'):
tar = TarFile(fileobj=urllib.urlopen(repo))
tar.extractall(path=self.plugin_dir)
s = repo.split(':')[-1].split('/')[-2:]
human_name = '/'.join(s).rstrip('.tar.gz')
else:
human_name = human_name_for_git_url(repo)
p = subprocess.Popen([git_path, 'clone', repo, human_name], cwd=self.plugin_dir, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
feedback = p.stdout.read().decode('utf-8')
error_feedback = p.stderr.read().decode('utf-8')
if p.wait():
return "Could not load this plugin: \n\n%s\n\n---\n\n%s" % (feedback, error_feedback),
self.add_plugin_repo(human_name, repo)
import urllib.request
import ast
def get_known_repos():
"""
Get known repos from registry
An example entry of the json file is the following
'errbotio/err-pypi': {
'avatar_url': None,
'documentation': 'some commands to query pypi',
'path': 'https://github.com/errbotio/err-pypi.git',
'python': None
}, ...
"""
registry_url = 'http://bit.ly/1kjdlRX'
registry = urllib.request.urlopen(registry_url).read()
return ast.literal_eval(registry.decode('utf-8'))
KNOWN_PUBLIC_REPOS = get_known_repos()
......@@ -18,6 +18,7 @@ from errbot.main import CORE_STORAGE
from errbot.plugin_manager import BotPluginManager
from errbot.rendering import text
from errbot.core_plugins.acls import ACLS
from errbot.repo_manager import BotRepoManager
from errbot.specific_plugin_manager import SpecificPluginManager
from errbot.storage.base import StoragePluginBase
from errbot.utils import PLUGINS_SUBDIR
......@@ -72,12 +73,14 @@ class DummyBackend(ErrBot):
if not os.path.exists(botplugins_dir):
os.makedirs(botplugins_dir, mode=0o755)
repo_manager = BotRepoManager(storage_plugin, botplugins_dir)
self.attach_storage_plugin(storage_plugin)
self.attach_repo_manager(repo_manager)
self.attach_plugin_manager(BotPluginManager(storage_plugin,
botplugins_dir,
repo_manager,
config.BOT_EXTRA_PLUGIN_DIR,
config.AUTOINSTALL_DEPS,
getattr(config, 'CORE_PLUGINS', None)))
self.attach_storage_plugin(storage_plugin)
self.inject_commands_from(self)
self.inject_command_filters_from(ACLS(self))
......
......@@ -5,6 +5,9 @@ import logging
from os import path, mkdir
from shutil import rmtree
import pytest
from errbot.backends.test import FullStackTest
......@@ -134,6 +137,7 @@ class TestCommands(FullStackTest):
self.bot.push_message('!hello') # should not respond
self.assertIn('Command "hello" not found', self.bot.pop_message())
@pytest.mark.xfail
def test_backup(self):
self.bot.push_message('!repos install git://github.com/errbotio/err-helloworld.git')
self.assertIn('err-helloworld', self.bot.pop_message(timeout=60))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment