Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CI broken tests #799

Merged
merged 5 commits into from
May 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ cache:
os:
- linux
stages:
- phase1
- phase2
- maintenance
- test
- deploy
before_install:
# begin: workaround to enable support for py37:
Expand All @@ -42,8 +42,11 @@ notifications:
- [email protected]
jobs:
include:
- stage: phase1
- stage: maintenance
script:
- python -m tox -e maintenance
if: type = cron
- script:
# package building added here purely to fail-fast if is broken
- python setup.py sdist bdist_wheel
- python -m tox
Expand All @@ -52,37 +55,31 @@ jobs:
language: nodejs
node_js:
- "8"
- stage: phase1
script: python -m tox
- script: python -m tox
python: "2.7"
env: TOXENV=docs
- stage: phase1
script: python -m tox
- script: python -m tox
python: "2.7"
env: TOXENV=py27
after_success:
- bash <(curl -s https://codecov.io/bash) -e TOX_ENV
- requires.io update-site -t ac3bbcca32ae03237a6aae2b02eb9411045489bb -r
- stage: phase2
script: python -m tox
- script: python -m tox
python: "3.4"
env: TOXENV=py34
after_success:
- bash <(curl -s https://codecov.io/bash) -e TOX_ENV
- stage: phase2
script: python -m tox
- script: python -m tox
python: "3.5"
env: TOXENV=py35
after_success:
- bash <(curl -s https://codecov.io/bash) -e TOX_ENV
- stage: phase2
script: python -m tox
- script: python -m tox
python: "3.6"
env: TOXENV=py36 PYTHON='3.6' PYENV_VERSION='system'
after_success:
- bash <(curl -s https://codecov.io/bash) -e TOX_ENV
- stage: phase2
script: python -m pip install -q tox-travis && python -m tox
- script: python -m pip install -q tox-travis && python -m tox
python: "3.7"
env: TOXENV=py37 PYTHON='3.7'
after_success:
Expand All @@ -95,7 +92,7 @@ jobs:
script:
- python setup.py sdist bdist_wheel
- python -m twine upload dist/*
if: tag IS present
if: tag IS present AND type != cron
deploy:
- provider: releases
api_key:
Expand Down
2 changes: 2 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@
".eggs"
],
"ignoreWords": [
"AACCOUNTID",
"GDPR",
"I18NSPHINXOPTS",
"hdost",
"βρέθηκε"
Expand Down
4 changes: 2 additions & 2 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ clean:
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html/index.html"
@echo "Build finished. The HTML pages are in file://$(BUILDDIR)/html/index.html"

dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
@echo "Build finished. The HTML pages are in file://$(BUILDDIR)/dirhtml."

singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
Expand Down
4 changes: 2 additions & 2 deletions docs/requirements-rtd.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pbr>=3.0.0
sphinx>=1.7.1
sphinx_rtd_theme
Sphinx>=1.8.5
sphinx_rtd_theme>=0.4.3
28 changes: 28 additions & 0 deletions examples/maintenance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This script will cleanup your jira instance by removing all projects and
# it is used to clean the CI/CD Jira server used for testing.
#
from __future__ import unicode_literals
import os
from jira import Role, Issue, JIRA, JIRAError, Project # noqa


CI_JIRA_URL = os.environ['CI_JIRA_URL']
CI_JIRA_ADMIN = os.environ['CI_JIRA_ADMIN']
CI_JIRA_ADMIN_PASSWORD = os.environ['CI_JIRA_ADMIN_PASSWORD']

j = JIRA(CI_JIRA_URL,
basic_auth=(CI_JIRA_ADMIN, CI_JIRA_ADMIN_PASSWORD),
logging=True,
validate=True,
async_=True,
async_workers=20)

for p in j.projects():
print(p)
try:
j.delete_project(p)
except Exception as e:
print(e)
147 changes: 62 additions & 85 deletions jira/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import logging
import os
import re
import tempfile
try: # Python 2.7+
from logging import NullHandler
except ImportError:
Expand Down Expand Up @@ -3323,23 +3322,28 @@ def backup_download(self, filename=None):
return None

def current_user(self):
"""Returns the username of the current user.
"""Returns the username or account-id of the current user. For anonymous
users it will return a value that evaluates as False.

:rtype: str
"""
if not hasattr(self, '_serverInfo') or 'username' not in self._serverInfo:
if not hasattr(self, '_myself'):

url = self._get_url('serverInfo')
url = self._get_url('myself')
r = self._session.get(url, headers=self._options['headers'])

r_json = json_loads(r)
if 'x-ausername' in r.headers:
r_json['username'] = r.headers['x-ausername']
else:
r_json['username'] = None
self._serverInfo = r_json
self._myself = r_json
print(r_json, r.headers)
# if 'X-AACCOUNTID' in r.headers:
# r_json['username'] = r.headers['X-AACCOUNTID']
# elif 'x-ausername' in r.headers:
# r_json['username'] = r.headers['x-ausername']
# else:
# r_json['username'] = None
# del r_json['self'] # this isn't really an addressable resource
return self._serverInfo['username']
print(self._myself)
return self._myself['name']

def delete_project(self, pid):
"""Delete project from Jira.
Expand All @@ -3355,38 +3359,13 @@ def delete_project(self, pid):
if hasattr(pid, 'id'):
pid = pid.id

# Check if pid is a number - then we assume that it is
# projectID
try:
str(int(pid)) == pid
except Exception as e:
# pid looks like a slug, lets verify that
r_json = self._get_json('project')
for e in r_json:
if e['key'] == pid or e['name'] == pid:
pid = e['id']
break
else:
# pid is not a Project
# not a projectID and not a slug - we raise error here
raise ValueError('Parameter pid="%s" is not a Project, '
'projectID or slug' % pid)

uri = '/rest/api/2/project/%s' % pid
url = self._options['server'] + uri
try:
r = self._session.delete(
url, headers={'Content-Type': 'application/json'}
)
except JIRAError as je:
if '403' in str(je):
raise JIRAError('Not enough permissions to delete project')
if '404' in str(je):
raise JIRAError('Project not found in Jira')
raise je

if r.status_code == 204:
return True
url = self._options['server'] + '/rest/api/2/project/%s' % pid
r = self._session.delete(url)
if r.status_code == 403:
raise JIRAError('Not enough permissions to delete project')
if r.status_code == 404:
raise JIRAError('Project not found in Jira')
return r.ok

def _gain_sudo_session(self, options, destination):
url = self._options['server'] + '/secure/admin/WebSudoAuthenticate.jspa'
Expand Down Expand Up @@ -3414,16 +3393,14 @@ def templates(self):
data = json_loads(r)

templates = {}
# if 'projectTemplates' in data:
# template_ = data['projectTemplates']
# el
if 'projectTemplatesGroupedByType' in data:
for group in data['projectTemplatesGroupedByType']:
for t in group['projectTemplates']:
templates[t['name']] = t
# pprint(templates.keys())
return templates

def create_project(self, key, name=None, assignee=None, type="Software", template_name=None):
def create_project(self, key, name=None, assignee=None, type="software", template_name=None):
"""Create a project with the specified parameters.

:param key: Mandatory. Must match JIRA project key requirements, usually only 2-10 uppercase characters.
Expand All @@ -3442,60 +3419,60 @@ def create_project(self, key, name=None, assignee=None, type="Software", templat
:rtype: Union[bool,int]

"""
template_key = None

if assignee is None:
assignee = self.current_user()
if name is None:
name = key

possible_templates = ['Basic', 'JIRA Classic', 'JIRA Default Schemes', 'Basic software development']
# preference list for picking a default template
possible_templates = [
'Scrum software development', # have Bug
'Agility', # cannot set summary
'Bug tracking',
'JIRA Classic',
'JIRA Default Schemes',
'Basic software development',
'Project management',
'Kanban software development',
'Task management',

'Basic', # does not have Bug
'Content Management',
'Customer service',
'Document Approval',
'IT Service Desk',
'Lead Tracking',
'Process management',
'Procurement',
'Recruitment',
]

if template_name is not None:
possible_templates = [template_name]

# https://confluence.atlassian.com/jirakb/creating-a-project-via-rest-based-on-jira-default-schemes-744325852.html
templates = self.templates()
# TODO(ssbarnea): find a better logic to pick a default fallback template
template_key = list(templates.values())[0]['projectTemplateModuleCompleteKey']
for template_name, template_dic in templates.items():
if template_name in possible_templates:
template_key = template_dic['projectTemplateModuleCompleteKey']
break
if not template_name:
template_name = next(t for t in possible_templates if t in templates)

template_key = templates[template_name]['projectTemplateModuleCompleteKey']
project_type_key = templates[template_name]['projectTypeKey']

# https://confluence.atlassian.com/jirakb/creating-a-project-via-rest-based-on-jira-default-schemes-744325852.html
# see https://confluence.atlassian.com/jirakb/creating-projects-via-rest-api-in-jira-963651978.html
payload = {'name': name,
'key': key,
'keyEdited': 'false',
# 'projectTemplate': 'com.atlassian.jira-core-project-templates:jira-issuetracking',
# 'permissionScheme': '',
'projectTemplateWebItemKey': template_key,
'projectTemplateModuleKey': template_key,
'projectTypeKey': project_type_key,
'projectTemplateKey': template_key,
'lead': assignee,
# 'assigneeType': '2',
'assigneeType': 'PROJECT_LEAD',
}

if self._version[0] > 6:
# JIRA versions before 7 will throw an error if we specify type parameter
payload['type'] = type

headers = CaseInsensitiveDict(
{'Content-Type': 'application/x-www-form-urlencoded'})
url = self._options['server'] + \
'/rest/project-templates/latest/templates'

r = self._session.post(url, data=payload, headers=headers)

if r.status_code == 200:
r_json = json_loads(r)
return r_json

f = tempfile.NamedTemporaryFile(
suffix='.html', prefix='python-jira-error-create-project-', delete=False)
f.write(r.text)
'/rest/api/2/project'

if self.logging:
logging.error(
"Unexpected result while running create project. Server response saved in %s for further investigation [HTTP response=%s]." % (
f.name, r.status_code))
return False
r = self._session.post(url, data=json.dumps(payload))
r.raise_for_status()
r_json = json_loads(r)
return r_json

def add_user(self,
username,
Expand Down
2 changes: 1 addition & 1 deletion jira/resilientsession.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def __verb(self, verb, url, retry_data=None, **kwargs):
try:
method = getattr(super(ResilientSession, self), verb.lower())
response = method(url, timeout=self.timeout, **kwargs)
if response.status_code == 200:
if response.status_code >= 200 and response.status_code <= 299:
return response
except ConnectionError as e:
logging.warning(
Expand Down
1 change: 1 addition & 0 deletions jira/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ def __init__(self, options, session, raw=None):
self._parse_raw(raw)

def update(self, fields=None, async_=None, jira=None, body='', visibility=None):
"""Update a comment"""
data = {}
if body:
data['body'] = body
Expand Down
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ pep8ignore = E501 E265 E127 E901 E128 E402
pep8maxlinelength = 1024

# pytest-timeout
timeout = 60
timeout = 80
# delete_project on jira cloud takes >70s
Loading