From f18429fdf7624530fbe48e8b312ed3f433ca8c91 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Thu, 28 Apr 2011 11:38:48 +0200 Subject: [PATCH] PluginDownloader: add the @install command. --- plugins/PluginDownloader/plugin.py | 82 +++++++++++++++++++++++++++++- plugins/PluginDownloader/test.py | 36 +++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/plugins/PluginDownloader/plugin.py b/plugins/PluginDownloader/plugin.py index cd8e95d7a..e4ec3f5fe 100644 --- a/plugins/PluginDownloader/plugin.py +++ b/plugins/PluginDownloader/plugin.py @@ -28,12 +28,17 @@ ### +import os import json import urllib +import urllib2 +import tarfile +from cStringIO import StringIO import git import supybot.log as log +import supybot.conf as conf import supybot.utils as utils from supybot.commands import * import supybot.plugins as plugins @@ -56,8 +61,18 @@ class GithubRepository(GitRepository): def __init__(self, username, reponame, path='/'): self._username = username self._reponame = reponame - self._path = [x for x in path.split('/') if x != ''] + if not path.startswith('/'): + path = '/' + path + if not path.endswith('/'): + path += '/' + self._path = path + self._downloadUrl = 'https://github.com/%s/%s/tarball/master' % \ + ( + self._username, + self._reponame, + ) + _apiUrl = 'http://github.com/api/v2/json' def _query(self, type_, uri_end, args={}): @@ -74,7 +89,8 @@ class GithubRepository(GitRepository): self._reponame, ) )['branches']['master'] - treeHash = self._navigate(latestCommit, self._path) + path = [x for x in self._path.split('/') if x != ''] + treeHash = self._navigate(latestCommit, path) if treeHash is None: log.error(( 'Cannot get plugins list from repository %s/%s ' @@ -114,6 +130,41 @@ class GithubRepository(GitRepository): # Remember we pop(0)ed the path return None + def install(self, plugin): + directories = conf.supybot.directories.plugins() + directory = self._getWritableDirectoryFromList(directories) + assert directory is not None + dirname = ''.join((self._path, plugin)) + + fileObject = urllib2.urlopen(self._downloadUrl) + fileObject2 = StringIO() + fileObject2.write(fileObject.read()) + fileObject.close() + fileObject2.seek(0) + archive = tarfile.open(fileobj=fileObject2, mode='r:gz') + prefix = archive.getnames()[0] + try: + assert archive.getmember(prefix + dirname).isdir() + + for file in archive.getmembers(): + if file.name.startswith(prefix + dirname): + extractedFile = archive.extractfile(file) + newFileName = os.path.join(*file.name.split('/')[1:]) + newFileName = os.path.join(directory, newFileName) + if extractedFile is None: + os.mkdir(newFileName) + else: + open(newFileName, 'a').write(extractedFile.read()) + finally: + archive.close() + + def _getWritableDirectoryFromList(self, directories): + for directory in directories: + if os.access(directory, os.W_OK): + return directory + return None + + repositories = { 'ProgVal': GithubRepository('ProgVal', 'Supybot-plugins'), 'quantumlemur': GithubRepository( @@ -137,6 +188,11 @@ class PluginDownloader(callbacks.Plugin): global repositories if repository is None: irc.reply(_(', ').join([x for x in repositories])) + elif repository not in repositories: + irc.error(_( + 'This repository does not exist or is not known by ' + 'this bot.' + )) else: plugins = repositories[repository].getPluginList() if plugins == []: @@ -145,6 +201,28 @@ class PluginDownloader(callbacks.Plugin): irc.reply(_(', ').join([x for x in plugins])) repolist = wrap(repolist, [optional('something')]) + @internationalizeDocstring + def install(self, irc, msg, args, repository, plugin): + """ + + Downloads and installs the from the .""" + global repositories + if repository not in repositories: + irc.error(_( + 'This repository does not exist or is not known by ' + 'this bot.' + )) + else: + try: + repositories[repository].install(plugin) + irc.replySuccess() + except Exception as e: + #FIXME: more detailed error message + log.error(str(e)) + irc.error('The plugin could not be installed.') + + install = wrap(install, ['owner', 'something', 'something']) + PluginDownloader = internationalizeDocstring(PluginDownloader) Class = PluginDownloader diff --git a/plugins/PluginDownloader/test.py b/plugins/PluginDownloader/test.py index 6e49d0c20..d19493cce 100644 --- a/plugins/PluginDownloader/test.py +++ b/plugins/PluginDownloader/test.py @@ -28,14 +28,50 @@ ### +import os +import shutil + from supybot.test import * +pluginsPath = '%s/test-plugins' % os.getcwd() + class PluginDownloaderTestCase(PluginTestCase): plugins = ('PluginDownloader',) + config = {'supybot.directories.plugins': [pluginsPath]} + + def setUp(self): + PluginTestCase.setUp(self) + try: + shutil.rmtree(pluginsPath) + except: + pass + os.mkdir(pluginsPath) + + def tearDown(self): + try: + shutil.rmtree(pluginsPath) + finally: + PluginTestCase.tearDown(self) def testRepolist(self): self.assertResponse('repolist', 'quantumlemur, ProgVal') self.assertRegexp('repolist ProgVal', '(.*, )?AttackProtector(, .*)?') + def testInstallProgVal(self): + self.assertError('plugindownloader install ProgVal Listener') + self.assertNotError('plugindownloader install ProgVal AttackProtector') + self.assertError('plugindownloader install ProgVal Listener') + assert os.path.isdir(pluginsPath + '/AttackProtector/') + assert os.path.isfile(pluginsPath + '/AttackProtector/plugin.py') + assert os.path.isfile(pluginsPath + '/AttackProtector/config.py') + + def testInstallQuantumlemur(self): + self.assertError('plugindownloader install quantumlemur AttackProtector') + self.assertNotError('plugindownloader install quantumlemur Listener') + self.assertError('plugindownloader install quantumlemur AttackProtector') + assert os.path.isdir(pluginsPath + '/Listener/') + assert os.path.isfile(pluginsPath + '/Listener/plugin.py') + assert os.path.isfile(pluginsPath + '/Listener/config.py') + # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: