From 0ba9879b921d20bac6446500a4c71293b89e6348 Mon Sep 17 00:00:00 2001 From: Sergey Shishov Date: Sun, 28 Feb 2016 02:52:28 +0300 Subject: [PATCH 1/4] Switching to nose-json module --- circle.yml | 2 +- requirements-dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 1129112..d84ec1b 100644 --- a/circle.yml +++ b/circle.yml @@ -25,7 +25,7 @@ dependencies: test: override: - - TEST_ARGS='--with-xunit --with-json-extended' make lint test + - TEST_ARGS='--with-xunit --with-json' make lint test post: # - coveralls diff --git a/requirements-dev.txt b/requirements-dev.txt index eb76aeb..1eaa397 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ flake8-import-order==0.5.3 flake8-pep257==1.0.3 https://github.com/zheller/flake8-quotes/tarball/aef86c4f8388e790332757e5921047ad53160a75#egg=flake8-quotes nose==1.3.7 -nosetests-json-extended==0.1.0 +nose-json==0.2.4 pep257==0.6.0 pep8==1.6.2 pep8-naming==0.3.3 From a5f0da6ee285533b8a0e7208b30272aeef4ba4e5 Mon Sep 17 00:00:00 2001 From: Sergey Shishov Date: Sun, 28 Feb 2016 09:55:10 +0300 Subject: [PATCH 2/4] Add nose-json package --- .gitignore | 4 ++ Makefile | 8 ++- circle.yml | 3 +- nosetests.xml | 1 - requirements-dev.txt | 1 + src/authentication/tests.py | 3 +- src/django_and_angular/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../commands/report_failed_tests.py | 68 +++++++++++++++++++ src/django_and_angular/settings/defaults.py | 1 + 10 files changed, 84 insertions(+), 5 deletions(-) delete mode 100644 nosetests.xml create mode 100644 src/django_and_angular/management/__init__.py create mode 100644 src/django_and_angular/management/commands/__init__.py create mode 100644 src/django_and_angular/management/commands/report_failed_tests.py diff --git a/.gitignore b/.gitignore index b809fa0..8207902 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ staticfiles/ # npm npm-debug.log +# nose +nosetests.json +nosetests.xml + diff --git a/Makefile b/Makefile index a2b7ef9..c788a40 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: test fasttest run lint pep8 eslint manage +.PHONY: test fasttest run lint pep8 eslint manage report_failed_tests # Project settings LEVEL ?= development @@ -33,6 +33,8 @@ SERVER_PORT ?= 8008 # Other settings DJANGO_SERVER ?= runserver DJANGO_SHELL ?= shell_plus +REPORT_FAILED_TEST ?= report_failed_tests +REPORT_FAILED_TEST_BRANCHES ?= develop,master,test1 # Setup bootstrapper & Gunicorn args has_bootstrapper = $(shell python -m bootstrapper --version 2>&1 | grep -v "No module") @@ -115,3 +117,7 @@ devserver: clean # Production Server server: clean pep8 LEVEL=$(LEVEL) PYTHONPATH=$(PROJECT) $(GUNICORN) -b $(SERVER_HOST):$(SERVER_PORT) -w $(GUNICORN_WORKERS) -n $(GUNICORN_NAME) -t 60 --graceful-timeout 60 $(gunicorn_args) $(GUNICORN_ARGS) $(PROJECT).wsgi:application + +# Reporting of failed cases: +report_failed_tests: + COMMAND="$(REPORT_FAILED_TEST) --branches $(REPORT_FAILED_TEST_BRANCHES) $(COMMAND_ARGS)" $(MAKE) manage diff --git a/circle.yml b/circle.yml index d84ec1b..f2d311e 100644 --- a/circle.yml +++ b/circle.yml @@ -25,13 +25,14 @@ dependencies: test: override: - - TEST_ARGS='--with-xunit --with-json' make lint test + - TEST_ARGS='--with-xunit --with-json --json-file="./nosetests.json"' make lint test post: # - coveralls - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - "[ -r nosetests.xml ] && mv nosetests.xml $CIRCLE_TEST_REPORTS/junit/ || :" - "[ -r nosetests.json ] && mv nosetests.json $CIRCLE_TEST_REPORTS/junit/ || :" + - COMMAND_ARGS='--target-branch develop' make report_failed_tests # # Override /etc/hosts # hosts: diff --git a/nosetests.xml b/nosetests.xml deleted file mode 100644 index c33cfd0..0000000 --- a/nosetests.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 1eaa397..f2a33de 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,6 +4,7 @@ django-nose==1.4.2 flake8==2.3.0 flake8-import-order==0.5.3 flake8-pep257==1.0.3 +GitPython==1.0.2 https://github.com/zheller/flake8-quotes/tarball/aef86c4f8388e790332757e5921047ad53160a75#egg=flake8-quotes nose==1.3.7 nose-json==0.2.4 diff --git a/src/authentication/tests.py b/src/authentication/tests.py index f9d30b0..437c6fe 100644 --- a/src/authentication/tests.py +++ b/src/authentication/tests.py @@ -7,5 +7,4 @@ def test_success(self): pass def test_failure(self): - # self.fail('Failed test') - pass + self.fail('Failed test') diff --git a/src/django_and_angular/management/__init__.py b/src/django_and_angular/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/django_and_angular/management/commands/__init__.py b/src/django_and_angular/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/django_and_angular/management/commands/report_failed_tests.py b/src/django_and_angular/management/commands/report_failed_tests.py new file mode 100644 index 0000000..6d17d6c --- /dev/null +++ b/src/django_and_angular/management/commands/report_failed_tests.py @@ -0,0 +1,68 @@ +import re +from git import Repo + +from optparse import make_option + +from xml.etree import cElementTree as ElementTree + +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + + help = 'Creates JIRA issues for every failed case for specified branch' + result_file = 'nosetests.xml' + failure_row_re = re.compile(r'\s*File\s"(.*)",\sline\s(.*),.*') + + option_list = BaseCommand.option_list + ( + make_option( + '-b', + '--branches', + action='store', + dest='branches', + help='Affected branches', + ), + make_option( + '-t', + '--target-branch', + action='store', + dest='target_branch', + help='Target branch', + ) + ) + + @staticmethod + def parse_test_path(path): + path, classname = path.rsplit('.', 1) + path = path.replace('.', '/') + return path, classname + + def handle(self, *args, **options): + repo = Repo() + try: + branches = [repo.heads[x] for x in options['branches'].split(',')] + target = repo.heads[options['target_branch']] + except IndexError as e: + return 'Cannot find branch with error "{0}"'.format(e) + if target != repo.head.ref: + return 'Current branch "{0}" does not match provided CircleCI branch "{1}"'.format( + repo.head.ref, target + ) + elif target not in branches: + return 'Skipping check for branch "{0}"'.format(repo.head.ref) + + try: + root = ElementTree.parse(self.result_file).getroot() + if root.attrib['errors']: + for testcase in root: + for failure in testcase: + print('Failure for {}'. format(testcase.attrib['name'])) + path, classname = self.parse_test_path(testcase.attrib['classname']) + print path, classname + for (file_path, line_number) in re.findall(self.failure_row_re, failure.text): + if path in file_path: + print repo.git.blame('HEAD', file_path) + else: + return 'No errors' + except IOError: + return 'File "{0}" does not exist'.format(self.result_file) diff --git a/src/django_and_angular/settings/defaults.py b/src/django_and_angular/settings/defaults.py index e9115e0..7be8898 100644 --- a/src/django_and_angular/settings/defaults.py +++ b/src/django_and_angular/settings/defaults.py @@ -41,6 +41,7 @@ 'compressor', 'authentication', 'posts', + 'django_and_angular', ) MIDDLEWARE_CLASSES = ( From d16846c5c08a471e39c8a3d723c9776a3ecd4a81 Mon Sep 17 00:00:00 2001 From: Sergey Shishov Date: Thu, 3 Mar 2016 23:54:59 +0300 Subject: [PATCH 3/4] Finishing initial implementation of JIRA integration --- Makefile | 11 +- circle.yml | 5 +- requirements-dev.txt | 4 +- .../commands/report_failed_tests.py | 203 +++++++++++++++--- 4 files changed, 182 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index c788a40..7922f14 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,14 @@ SERVER_PORT ?= 8008 DJANGO_SERVER ?= runserver DJANGO_SHELL ?= shell_plus REPORT_FAILED_TEST ?= report_failed_tests -REPORT_FAILED_TEST_BRANCHES ?= develop,master,test1 +REPORT_FAILED_TEST_BRANCHES ?= develop,master + +# JIRA Settings +JIRA_ISSUE_TYPE ?= Bug +JIRA_PROJECT_KEY ?= EZHOME +JIRA_SERVER ?= https://ezhome-test.atlassian.net +JIRA_USERNAME ?= jira_bot +JIRA_PASSWORD ?= P@ssw0rd # Setup bootstrapper & Gunicorn args has_bootstrapper = $(shell python -m bootstrapper --version 2>&1 | grep -v "No module") @@ -120,4 +127,4 @@ server: clean pep8 # Reporting of failed cases: report_failed_tests: - COMMAND="$(REPORT_FAILED_TEST) --branches $(REPORT_FAILED_TEST_BRANCHES) $(COMMAND_ARGS)" $(MAKE) manage + COMMAND="$(REPORT_FAILED_TEST) --branches $(REPORT_FAILED_TEST_BRANCHES) --issue-type $(JIRA_ISSUE_TYPE) --project-key $(JIRA_PROJECT_KEY) --jira-server $(JIRA_SERVER) --jira-username $(JIRA_USERNAME) --jira-password $(JIRA_PASSWORD) $(COMMAND_ARGS)" $(MAKE) manage diff --git a/circle.yml b/circle.yml index f2d311e..9ea8006 100644 --- a/circle.yml +++ b/circle.yml @@ -25,14 +25,13 @@ dependencies: test: override: - - TEST_ARGS='--with-xunit --with-json --json-file="./nosetests.json"' make lint test + - TEST_ARGS='--with-xunit' make lint test post: # - coveralls - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - "[ -r nosetests.xml ] && mv nosetests.xml $CIRCLE_TEST_REPORTS/junit/ || :" - - "[ -r nosetests.json ] && mv nosetests.json $CIRCLE_TEST_REPORTS/junit/ || :" - - COMMAND_ARGS='--target-branch develop' make report_failed_tests + - COMMAND_ARGS='--target-branch ${CIRCLE_BRANCH} --test-results ${CIRCLE_TEST_REPORTS}/junit/nosetests.xml' make report_failed_tests # # Override /etc/hosts # hosts: diff --git a/requirements-dev.txt b/requirements-dev.txt index f2a33de..45bd270 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,10 +4,10 @@ django-nose==1.4.2 flake8==2.3.0 flake8-import-order==0.5.3 flake8-pep257==1.0.3 -GitPython==1.0.2 https://github.com/zheller/flake8-quotes/tarball/aef86c4f8388e790332757e5921047ad53160a75#egg=flake8-quotes +GitPython==1.0.2 # For Jira integration +jira==1.0.3 # For Jira integreation nose==1.3.7 -nose-json==0.2.4 pep257==0.6.0 pep8==1.6.2 pep8-naming==0.3.3 diff --git a/src/django_and_angular/management/commands/report_failed_tests.py b/src/django_and_angular/management/commands/report_failed_tests.py index 6d17d6c..29c6420 100644 --- a/src/django_and_angular/management/commands/report_failed_tests.py +++ b/src/django_and_angular/management/commands/report_failed_tests.py @@ -1,68 +1,203 @@ import re -from git import Repo - from optparse import make_option - from xml.etree import cElementTree as ElementTree from django.core.management.base import BaseCommand +from git import Repo +from jira import JIRA +from jira.exceptions import JIRAError + + +FAILURE_ROW_RE = re.compile(r'\s*File\s"(.*)",\sline\s(.*),.*') class Command(BaseCommand): help = 'Creates JIRA issues for every failed case for specified branch' - result_file = 'nosetests.xml' - failure_row_re = re.compile(r'\s*File\s"(.*)",\sline\s(.*),.*') option_list = BaseCommand.option_list + ( make_option( - '-b', '--branches', action='store', dest='branches', help='Affected branches', ), make_option( - '-t', '--target-branch', action='store', dest='target_branch', help='Target branch', - ) + ), + make_option( + '--issue-type', + action='store', + dest='issue_type', + default='Bug', + help='Issue type', + ), + make_option( + '--project-key', + action='store', + dest='project_key', + default='EZHOME', + help='Project key', + ), + make_option( + '--jira-server', + action='store', + dest='jira_server', + help='JIRA server', + ), + make_option( + '--jira-username', + action='store', + dest='jira_username', + help='Username for JIRA account', + ), + make_option( + '--jira-password', + action='store', + dest='jira_password', + help='Password for JIRA account', + ), + make_option( + '--test-results', + action='store', + dest='test_results', + default='nosetests.xml', + help='Location of test results', + ), ) - @staticmethod - def parse_test_path(path): - path, classname = path.rsplit('.', 1) - path = path.replace('.', '/') - return path, classname - def handle(self, *args, **options): - repo = Repo() + self.issue_type = options['issue_type'] + self.project_key = options['project_key'] + self.jira_server = options['jira_server'] + self.jira_username = options['jira_username'] + self.jira_password = options['jira_password'] + test_results = options['test_results'] + + self.repo = Repo() + branches = [] try: - branches = [repo.heads[x] for x in options['branches'].split(',')] - target = repo.heads[options['target_branch']] - except IndexError as e: - return 'Cannot find branch with error "{0}"'.format(e) - if target != repo.head.ref: - return 'Current branch "{0}" does not match provided CircleCI branch "{1}"'.format( - repo.head.ref, target + for branch in options['branches'].split(','): + branches.append(self.repo.heads[branch]) + except IndexError: + return 'Cannot find branch "{0}"'.format(branch) + try: + self.target_branch = self.repo.heads[options['target_branch']] + except IndexError: + return 'Cannot find branch "{0}"'.format(options['target_branch']) + if self.target_branch != self.repo.head.ref: + return ( + 'Current branch "{0}" does not match ' + 'provided CircleCI branch "{1}"' + .format(self.repo.head.ref, self.target_branch) ) - elif target not in branches: - return 'Skipping check for branch "{0}"'.format(repo.head.ref) + elif self.target_branch not in branches: + return 'Skipping check for branch "{0}"'.format(self.repo.head.ref) try: - root = ElementTree.parse(self.result_file).getroot() + root = ElementTree.parse(test_results).getroot() if root.attrib['errors']: + results = [] for testcase in root: - for failure in testcase: - print('Failure for {}'. format(testcase.attrib['name'])) - path, classname = self.parse_test_path(testcase.attrib['classname']) - print path, classname - for (file_path, line_number) in re.findall(self.failure_row_re, failure.text): - if path in file_path: - print repo.git.blame('HEAD', file_path) + if testcase: + results.append(self.handle_testcase(testcase)) + return '\n'.join(results) else: - return 'No errors' + return 'No errors in tests' except IOError: - return 'File "{0}" does not exist'.format(self.result_file) + return 'File "{0}" does not exist'.format(test_results) + + @staticmethod + def parse_test_path(path): + path, classname = path.rsplit('.', 1) + path = path.replace('.', '/') + return path, classname + + def handle_testcase(self, testcase): + path, classname = self.parse_test_path( + testcase.attrib['classname'] + ) + for (file_path, line_number) in re.findall( + FAILURE_ROW_RE, testcase[0].text + ): + if path in file_path: + # Finding the line of testcase definition + authors = {} + commit, line = self.repo.blame( + '-L/def {}/'.format(testcase.attrib['name']), file_path + )[0] + if commit.author not in authors: + authors['function'] = commit.author + # Finding the line of failure + commit, line = self.repo.blame( + '-L{0},{0}'.format(line_number), file_path + )[0] + if commit.author not in authors: + authors['failure'] = commit.author + return self.handle_jira( + path=path, + authors=authors, + classname=classname, + testcase=testcase, + ) + + def handle_jira(self, path, authors, classname, testcase): + try: + jira = JIRA( + server=self.jira_server, + basic_auth=( + self.jira_username, + self.jira_password, + ) + ) + summary = ( + 'Fail: {path}:{classname}.{testcase}, ' + 'branch: {branch}'.format( + path=path, + classname=classname, + testcase=testcase.attrib['name'], + branch=self.target_branch, + ) + ) + open_issues = jira.search_issues( + 'summary ~ "{summary}" AND ' + 'resolution=unresolved'.format( + summary=summary + ), + maxResults=1 + ) + if open_issues: + # Update priority + issue = open_issues[0] + new_priority = '1' + if int(issue.fields.priority.id) > 1: + new_priority = str(int(issue.fields.priority.id) - 1) + issue.update(priority={'id': new_priority}) + return ( + 'Priority of issue "{issue}" ' + 'has been set to "{priority}"'.format( + issue=issue, priority=jira.priority(new_priority) + ) + ) + else: + # Create issue + assignee = jira.search_users( + user=authors['function'].email, + maxResults=1 + ) + issue_dict = dict( + project={'key': self.project_key}, + summary=summary, + issuetype={'name': self.issue_type}, + priority={'id': jira.priorities()[-1].id}, + description='Description here', + ) + if assignee: + issue_dict['assignee'] = {'name': assignee[0].name} + new_issue = jira.create_issue(fields=issue_dict) + return 'New issue "{0}" has been created'.format(new_issue) + except JIRAError as e: + return 'JIRA ERROR: {}'.format(e.text) From 6faff12091693a12dbd1b6786960b25db6dfcaef Mon Sep 17 00:00:00 2001 From: Sergey Shishov Date: Sat, 12 Mar 2016 01:04:52 +0400 Subject: [PATCH 4/4] test after refactor --- Makefile | 13 ++----- circle.yml | 11 +++++- requirements-dev.txt | 9 +++-- requirements.txt | 2 +- .../commands/report_failed_tests.py | 35 +++++++++++++++---- 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 7922f14..f61b13e 100644 --- a/Makefile +++ b/Makefile @@ -33,15 +33,6 @@ SERVER_PORT ?= 8008 # Other settings DJANGO_SERVER ?= runserver DJANGO_SHELL ?= shell_plus -REPORT_FAILED_TEST ?= report_failed_tests -REPORT_FAILED_TEST_BRANCHES ?= develop,master - -# JIRA Settings -JIRA_ISSUE_TYPE ?= Bug -JIRA_PROJECT_KEY ?= EZHOME -JIRA_SERVER ?= https://ezhome-test.atlassian.net -JIRA_USERNAME ?= jira_bot -JIRA_PASSWORD ?= P@ssw0rd # Setup bootstrapper & Gunicorn args has_bootstrapper = $(shell python -m bootstrapper --version 2>&1 | grep -v "No module") @@ -126,5 +117,5 @@ server: clean pep8 LEVEL=$(LEVEL) PYTHONPATH=$(PROJECT) $(GUNICORN) -b $(SERVER_HOST):$(SERVER_PORT) -w $(GUNICORN_WORKERS) -n $(GUNICORN_NAME) -t 60 --graceful-timeout 60 $(gunicorn_args) $(GUNICORN_ARGS) $(PROJECT).wsgi:application # Reporting of failed cases: -report_failed_tests: - COMMAND="$(REPORT_FAILED_TEST) --branches $(REPORT_FAILED_TEST_BRANCHES) --issue-type $(JIRA_ISSUE_TYPE) --project-key $(JIRA_PROJECT_KEY) --jira-server $(JIRA_SERVER) --jira-username $(JIRA_USERNAME) --jira-password $(JIRA_PASSWORD) $(COMMAND_ARGS)" $(MAKE) manage +jira_check_tests: + ezh-jira-test-checker run_check $(COMMAND_ARGS) diff --git a/circle.yml b/circle.yml index 9ea8006..08f5794 100644 --- a/circle.yml +++ b/circle.yml @@ -5,6 +5,15 @@ machine: DATABASE_URL: postgres://ubuntu:@127.0.0.1:5432/circle_test DJANGO_SETTINGS_MODULE: django_and_angular.settings.development LEVEL: development + + # JIRA integration + REPORT_FAILED_TEST_BRANCHES: develop,master,test1 + JIRA_ISSUE_TYPE: Bug + JIRA_PROJECT_KEY: EZHOME + JIRA_SERVER: https://ezhome-test.atlassian.net + JIRA_USERNAME: jira_bot + JIRA_PASSWORD: P@ssw0rd + JIRA_DEFAULT_ASSIGNEE: sshishov.sshishov@yandex.ru timezone: America/Los_Angeles node: @@ -31,7 +40,7 @@ test: # - coveralls - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - "[ -r nosetests.xml ] && mv nosetests.xml $CIRCLE_TEST_REPORTS/junit/ || :" - - COMMAND_ARGS='--target-branch ${CIRCLE_BRANCH} --test-results ${CIRCLE_TEST_REPORTS}/junit/nosetests.xml' make report_failed_tests + - "COMMAND_ARGS='--target-branch ${CIRCLE_BRANCH} --test-results ${CIRCLE_TEST_REPORTS}/junit/nosetests.xml --branches $(REPORT_FAILED_TEST_BRANCHES) --issue-type $(JIRA_ISSUE_TYPE) --project-key $(JIRA_PROJECT_KEY) --jira-server $(JIRA_SERVER) --jira-username $(JIRA_USERNAME) --jira-password $(JIRA_PASSWORD) --jira-default-assignee $(JIRA_DEFAULT_ASSIGNEE)' make jira_check_tests || :" # # Override /etc/hosts # hosts: diff --git a/requirements-dev.txt b/requirements-dev.txt index 45bd270..db08fdb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,12 +1,15 @@ +--extra-index-url=http://ezhome-bot:q8zgdmot3@pypi.ezhome.io/simple/ + coverage==4.0 coveralls==1.0 django-nose==1.4.2 +# ezhinfra==0.2.? +# temporary solution for being able to check the PR +-e git+git@github.com:ezhome/ezh-infrastructure-tools.git@d910af7fdbe2c04e1c258e718e5026992c1ac29d#egg=ezhinfra flake8==2.3.0 flake8-import-order==0.5.3 flake8-pep257==1.0.3 -https://github.com/zheller/flake8-quotes/tarball/aef86c4f8388e790332757e5921047ad53160a75#egg=flake8-quotes -GitPython==1.0.2 # For Jira integration -jira==1.0.3 # For Jira integreation +flake8-quotes==0.2.4 nose==1.3.7 pep257==0.6.0 pep8==1.6.2 diff --git a/requirements.txt b/requirements.txt index db1dd86..450ee67 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,6 @@ drf-nested-routers==0.9.0 gunicorn==19.1.1 psycopg2==2.6.1 raven==5.10.2 -six==1.8.0 +six==1.9.0 static3==0.5.1 wsgiref==0.1.2 diff --git a/src/django_and_angular/management/commands/report_failed_tests.py b/src/django_and_angular/management/commands/report_failed_tests.py index 29c6420..86c4b52 100644 --- a/src/django_and_angular/management/commands/report_failed_tests.py +++ b/src/django_and_angular/management/commands/report_failed_tests.py @@ -67,6 +67,13 @@ class Command(BaseCommand): default='nosetests.xml', help='Location of test results', ), + make_option( + '--jira-default-assignee', + action='store', + dest='jira_default_assignee', + default='', + help='Default assignee for new user', + ) ) def handle(self, *args, **options): @@ -75,6 +82,7 @@ def handle(self, *args, **options): self.jira_server = options['jira_server'] self.jira_username = options['jira_username'] self.jira_password = options['jira_password'] + self.jira_default_assignee = options['jira_default_assignee'] test_results = options['test_results'] self.repo = Repo() @@ -103,8 +111,13 @@ def handle(self, *args, **options): results = [] for testcase in root: if testcase: - results.append(self.handle_testcase(testcase)) - return '\n'.join(results) + result = self.handle_testcase(testcase) + if result is not None: + results.append(result) + if results: + return '\n'.join(results) + else: + return 'No errors in tests' else: return 'No errors in tests' except IOError: @@ -184,10 +197,20 @@ def handle_jira(self, path, authors, classname, testcase): ) else: # Create issue - assignee = jira.search_users( + assignees = [] + assignees.extend(jira.search_users( user=authors['function'].email, maxResults=1 - ) + )) + assignees.extend(jira.search_users( + user=authors['failure'].email, + maxResults=1 + )) + if self.jira_default_assignee: + assignees.extend(jira.search_users( + user=self.jira_default_assignee, + maxResults=1 + )) issue_dict = dict( project={'key': self.project_key}, summary=summary, @@ -195,8 +218,8 @@ def handle_jira(self, path, authors, classname, testcase): priority={'id': jira.priorities()[-1].id}, description='Description here', ) - if assignee: - issue_dict['assignee'] = {'name': assignee[0].name} + if assignees: + issue_dict['assignee'] = {'name': assignees[0].name} new_issue = jira.create_issue(fields=issue_dict) return 'New issue "{0}" has been created'.format(new_issue) except JIRAError as e: