diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04e018c --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..dba3f45 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include setup.py README.md MANIFEST.in LICENSE +recursive-include sentry_taiga * +global-exclude *~ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d148cf0 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Sentry Taiga + +A plugin for [Sentry](http://getsentry.com) whish allows issue creation in your [Taiga](https://taiga.io) peojects from errors. + +## Install + +Install the package via `pip` + +``` +pip install sentry-taiga +``` + +or `easy_install` + +``` +easy_install sentry-taiga +``` + +And restart Sentry + +## Configure + +Once enabled you can configure your settings for the project. You can also include default tags you wish to apply to issues created. + +I would recommend you create a specific user for Taiga to use with only `Reporter` priviledges to your projects. + +Bugs & Issues +------------- + +If you find something that doesn't work please create an issue or even better fix it and submit a pull request! + +Dependencies +------------ + +* [python-taiga](https://pypi.python.org/pypi/python-taiga) + +License +------- + +MIT License. See [LICENSE](https://github.com/rochsystems/sentry-taiga/blob/master/LICENSE) file. + +Based on [sentry-gitlab](https://pypi.python.org/pypi/python-taiga) from Pancentric Ltd. + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file diff --git a/sentry_taiga/__init__.py b/sentry_taiga/__init__.py new file mode 100644 index 0000000..d29488d --- /dev/null +++ b/sentry_taiga/__init__.py @@ -0,0 +1,5 @@ +try: + VERSION = __import__('pkg_resources') \ + .get_distribution(__name__).version +except Exception, e: + VERSION = 'unknown' \ No newline at end of file diff --git a/sentry_taiga/plugin.py b/sentry_taiga/plugin.py new file mode 100644 index 0000000..723a199 --- /dev/null +++ b/sentry_taiga/plugin.py @@ -0,0 +1,136 @@ +""" +sentry_taiga.plugin +~~~~~~~~~~~~~~~~~~~~ + +:copyright: (c) 2015 by RochSystems. +:license: MIT, see LICENSE for more details. +""" + +from django import forms +from sentry.plugins.bases.issue import IssuePlugin +from django.utils.translation import ugettext_lazy as _ + +from taiga import TaigaAPI + +import sentry_taiga + +class TaigaOptionsForm(forms.Form): + taiga_url = forms.CharField( + label=_('Taiga URL'), + widget=forms.TextInput(attrs={'placeholder': + 'e.g. https://taiga.example.com'}), + help_text=_('Enter the URL for your Taiga server'), + required=True) + + taiga_username = forms.CharField( + label=_('Taiga User Name'), + widget=forms.TextInput(attrs={'placeholder': 'e.g. user@example.com'}), + help_text=_('Enter your Taiga User name'), + required=True) + + taiga_password = forms.CharField( + label=_('Taiga Password'), + widget=forms.PasswordInput(attrs={'placeholder': 'e.g. your password'}), + help_text=_('Enter your Taiga User password'), + required=True) + + taiga_project = forms.CharField( + label=_('Taiga Project Slug'), + widget=forms.TextInput(attrs={'placeholder': 'e.g. project-slug'}), + help_text=_('Enter your project slug.'), + required=True) + + taiga_labels = forms.CharField( + label=_('Issue Labels'), + widget=forms.TextInput(attrs={'placeholder': 'e.g. high, bug'}), + help_text=_('Enter comma separated labels you ' + 'want to auto assign to issues.'), + required=False) + + +class TaigaPlugin(IssuePlugin): + author = 'RochSystems LLC' + author_url = 'http://rochsystems.com/' + version = sentry_taiga.VERSION + description = "Integrate Taiga issues by linking a repository to a project" + resource_links = [ + ('Bug Tracker', 'https://github.com/rochsystems/sentry-taiga/issues'), + ('Source', 'https://github.com/rochsystems/sentry-taiga'), + ] + + slug = 'taiga' + title = _('Taiga') + conf_title = title + conf_key = 'taiga' + project_conf_form = TaigaOptionsForm + + def is_configured(self, request, project, **kwargs): + return bool(self.get_option('taiga_project', project)) + + def get_new_issue_title(self, **kwargs): + return _('Create Taiga Issue') + + def create_issue(self, request, group, form_data, **kwargs): + + url = self.get_option('taiga_url', group.project) + username = self.get_option('taiga_username', group.project) + password = self.get_option('taiga_password', group.project) + project_slug = self.get_option('taiga_project', group.project) + labels = self.get_option('taiga_labels', group.project) + + tg = TaigaAPI(host=url) + + try: + tg.auth(username=username, password=password) + except Exception as e: + raise forms.ValidationError(_('Error Communicating ' + 'with Taiga: %s') % (e,)) + + projects = tg.projects.list() + + project = projects.get(slug=project_slug) + if project is None: + raise forms.ValidationError(_('No project found in Taiga with slug %s') % + (project_slug,)) + + if not project.is_issues_activated: + raise forms.ValidationError(_('Project %s has issues disabled.') % + (project_slug,)) + + default_priority = project.default_priority + default_issue_status = project.default_issue_status + default_issue_type = project.default_issue_type + default_severity = project.default_severity + + if default_priority is None: + raise forms.ValidationError(_('Project %s has no default priority. ' + 'Set the default priority in Taiga') % (project.name,)) + if default_issue_status is None: + raise forms.ValidationError(_('Project %s has no default status. ' + 'Set the default issue status in Taiga') % (project.name,)) + if default_issue_type is None: + raise forms.ValidationError(_('Project %s has no default type. ' + 'Set the default issue type in Taiga') % (project.name,)) + if default_severity is None: + raise forms.ValidationError(_('Project %s has no default severity. ' + 'Set the default severity in Taiga') % (project.name,)) + + data = {'subject': form_data['title'], + 'priority': default_priority, 'status': default_issue_status, + 'issue_type': default_issue_type, 'severity': default_severity, + 'description': form_data['description'], + 'tags': map(lambda x:x.strip(), labels.split(","))} + + issue = project.add_issue(**data) + + return issue.ref + + + def get_issue_label(self, group, issue_id, **kwargs): + return 'TG-%s' % issue_id + + def get_issue_url(self, group, issue_id, **kwargs): + url = self.get_option('taiga_url', group.project) + slug = self.get_option('taiga_project', group.project) + + return '%s/project/%s/issue/%s' % (url, slug, issue_id) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..5e40900 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[wheel] +universal = 1 diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..f17bc21 --- /dev/null +++ b/setup.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +""" +sentry-taiga +============= + +An extension for Sentry which integrates with Taiga. Specifically, it allows +you to easily create issues from events within Sentry. + +:copyright: (c) 2015 RochSystems LLC, see AUTHORS for more details. +:license: MIT, see LICENSE for more details. +""" +from setuptools import setup, find_packages + + +tests_require = [ + 'nose', +] + +install_requires = [ + 'sentry>=5.0.0', + 'python-taiga==0.2.0', +] + +setup( + name='sentry-taiga', + version=open('VERSION').read().strip(), + author='Jordi Llonch', + author_email='jordi.llonch@rochsystems.com', + url='http://github.com/rochsystems/sentry-taiga', + description='A Sentry extension which integrates with Taiga.', + long_description=__doc__, + license='BSD', + package_dir={'': 'sentry_taiga'}, + packages=find_packages('sentry_taiga'), + zip_safe=False, + install_requires=install_requires, + tests_require=tests_require, + extras_require={'test': tests_require}, + test_suite='runtests.runtests', + include_package_data=True, + entry_points={ + 'sentry.apps': [ + 'taiga = sentry_taiga', + ], + 'sentry.plugins': [ + 'taiga = sentry_taiga.plugin:TaigaPlugin' + ], + }, + classifiers=[ + 'Framework :: Django', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Operating System :: OS Independent', + 'Topic :: Software Development' + ], +)