diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..5e06ad2f3
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,8 @@
+version: 2
+updates:
+ - package-ecosystem: pip
+ directory: "/"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 10
+ target-branch: master
diff --git a/.github/workflows/jira_server_ci.yml b/.github/workflows/jira_server_ci.yml
new file mode 100644
index 000000000..400cbb1c7
--- /dev/null
+++ b/.github/workflows/jira_server_ci.yml
@@ -0,0 +1,70 @@
+name: Jira Server CI
+
+on:
+ # Trigger the workflow on push or pull request,
+ # but only for the master branch
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ test:
+ name: ${{ matrix.os }} / ${{ matrix.python-version }}
+ runs-on: ${{ matrix.os }}-latest
+ strategy:
+ matrix:
+ os: [Ubuntu]
+ python-version: [3.6, 3.7, 3.8, 3.9]
+
+ steps:
+ - uses: actions/checkout@master
+ - name: Start Jira docker instance
+ run: docker run -dit -p 2990:2990 --name jira addono/jira-software-standalone
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Get pip cache dir
+ id: pip-cache
+ run: |
+ echo "::set-output name=dir::$(pip cache dir)"
+ - name: Setup the Pip cache
+ uses: actions/cache@v2
+ with:
+ path: ${{ steps.pip-cache.outputs.dir }}
+ key: >-
+ ${{ runner.os }}-pip-${{ hashFiles('setup.cfg') }}-${{
+ hashFiles('setup.py') }}-${{ hashFiles('tox.ini') }}-${{
+ hashFiles('.pre-commit-config.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
+ ${{ runner.os }}-
+
+ - name: Install Dependencies
+ run: |
+ sudo apt-get update; sudo apt-get install gcc libkrb5-dev
+ python -m pip install --upgrade pip
+ python -m pip install --upgrade tox tox-gh-actions
+
+ - name: Lint with tox
+ run: tox -e lint
+
+ - name: Test with tox
+ run: tox
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v1.0.15
+ with:
+ file: ./coverage.xml
+ name: ${{ runner.os }}-${{ matrix.python-version }}
+
+ - name: Run tox pkg
+ run: tox -e pkg
+
+ - name: Make docs
+ run: tox -e docs
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4f59ad2b7..4bfcb0db2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,12 +1,12 @@
---
repos:
- repo: https://github.com/python/black
- rev: 19.3b0
+ rev: 21.4b2
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v2.3.0
+ rev: v3.4.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
@@ -20,21 +20,15 @@ repos:
- id: check-yaml
files: .*\.(yaml|yml)$
- repo: https://gitlab.com/pycqa/flake8
- rev: 3.7.8
+ rev: 3.9.1
hooks:
- id: flake8
- additional_dependencies:
- - flake8-black
- repo: https://github.com/adrienverge/yamllint.git
- rev: v1.17.0
+ rev: v1.26.1
hooks:
- id: yamllint
files: \.(yaml|yml)$
- - repo: https://github.com/openstack-dev/bashate.git
- rev: 0.6.0
- hooks:
- - id: bashate
- repo: https://github.com/pre-commit/mirrors-mypy.git
- rev: v0.730
+ rev: v0.812
hooks:
- id: mypy
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6e90c8aa7..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,76 +0,0 @@
----
-language: python
-dist: xenial
-# Build only commits on master and release tags for the "Build pushed branches" feature.
-# This prevents building twice on PRs originating from our repo ("Build pushed pull requests)".
-# See:
-# - https://github.com/travis-ci/travis-ci/issues/1147
-# - https://docs.travis-ci.com/user/pull-requests/#double-builds-on-pull-requests
-branches:
- only:
- - master
- - /^\d+\.\d+(\.\d+)?(-\S*)?$/
-
-cache:
- bundler: true
- pip: true
- directories:
- - $HOME/.cache/pre-commit
- - $HOME/.pre-commit
- - $HOME/.rvm
- - $HOME/Library/Caches/Homebrew
-os:
- - linux
-stages:
- - maintenance
- - test
- - deploy
-before_install:
- - pip install --upgrade tox tox-venv
- - rm -rf .tox
-notifications:
- email:
- - pycontribs@googlegroups.com
-jobs:
- include:
- - stage: maintenance
- script:
- - python -m tox -e maintenance
- if: type = cron
- - script:
- - python -m tox
- env: TOXENV="lint,docs,pkg,py37"
- python: "3.7"
- after_success:
- - bash <(curl -s https://codecov.io/bash) -e py37
- - script: python -m tox
- python: "3.6"
- env: TOXENV=py36 PYTHON='3.6'
- after_success:
- - bash <(curl -s https://codecov.io/bash) -e TOXENV
- - script: python -m tox
- python: "3.5"
- env: TOXENV=py35
- after_success:
- - bash <(curl -s https://codecov.io/bash) -e TOXENV
- - stage: deploy
- script:
- - tox -e upload
- if: tag IS present AND type != cron
- deploy:
- - provider: releases
- api_key:
- secure: YJGigSNYOzMJqs23gIZLFxiVYRqHdV4WsTZmRVosishD2QIaDlTwJma7k6Y5eMPVNdLpqo7Tq6bt7xkJAz/dcr3UO35T/Y0tiRFFW3sd6IOB6ELwSwPhSeHoyUMvZtKyDTl+9tOfeZusFZuCc+mBLQcG+S2NzEaeyrQ6n5hTT/8FGBP91FOq9l5q2gYbmACZ9MisDIjZkTHNYih36ComnZ9QHC91jHKcSuHmOfWWX3GneDVFtuPhF2vjaLQrz8IFtWGW5Sfe35yDYlVQRH+NFxzSJ2zDuT5j8cRgwXjGout78umtMsqAn+zv1Ws/MUNKMTEtONsACndMpGCkuB6Nifl/KcGj5kD7V4PO/gE0ecr830qAwJxSVB7xk6rl797nMxGbr4w2DWQ/iDdHDTlPAEzbLBMLrMRgPxzKPgg5CNxxjT1cHoBNcFPp6gaf017w4XOVUgp/zxXeCg7iGiNJj7z2t8/m9eYVNNlNRPcodN6BjSjPqkYxC3ZMVCI5KsRXbHmR0zOWbPdcRjrY/IgbiTqX09sHotHw5GThP6YTMbienC4h93cdx6MEfX656W6XMOxpC+MjWtYuV8QlfMEJFlstOnA86MVLcmbl+4A6FHuvlQMdDtP9KsKdKIf/4juGhNEFir32P1rUe8J1abmjwXmDkHVbli0SDqaFtB5gyCc=
- file_glob: true
- file:
- - dist/*
- - ChangeLog
- skip_cleanup: true
- on:
- tags: true
- repo: pycontribs/jira
- branch: master
-env:
- global:
- - secure: "pGQGM5YmHvOgaKihOyzb3k6bdqLQnZQ2OXO9QrfXlXwtop3zvZQi80Q+01l230x2psDWlwvqWTknAjAt1w463fYXPwpoSvKVCsLSSbjrf2l56nrDqnoir+n0CBy288+eIdaGEfzcxDiuULeKjlg08zrqjcjLjW0bDbBrlTXsb5U="
- - PIP_DISABLE_PIP_VERSION_CHECK=1
diff --git a/.yamllint b/.yamllint
index 028d683b9..dcb54406e 100644
--- a/.yamllint
+++ b/.yamllint
@@ -2,15 +2,15 @@
extends: default
rules:
- braces: {max-spaces-inside: 1, level: error}
- brackets: {max-spaces-inside: 1, level: error}
- colons: {max-spaces-after: -1, level: error}
- commas: {max-spaces-after: -1, level: error}
+ braces: { max-spaces-inside: 1, level: error }
+ brackets: { max-spaces-inside: 1, level: error }
+ colons: { max-spaces-after: -1, level: error }
+ commas: { max-spaces-after: -1, level: error }
comments: disable
comments-indentation: disable
document-start: disable
- empty-lines: {max: 3, level: error}
- hyphens: {level: error}
+ empty-lines: { max: 3, level: error }
+ hyphens: { level: error }
indentation:
indent-sequences: consistent
# spaces: consistent
@@ -23,9 +23,8 @@ rules:
allow-non-breakable-words: true
allow-non-breakable-inline-mappings: true
new-line-at-end-of-file: disable
- new-lines: {type: unix}
+ new-lines: disable
trailing-spaces: disable
truthy: disable
-ignore:
- .tox
+ignore: .tox
diff --git a/MANIFEST.in b/MANIFEST.in
index f7afaafa0..3c0f8bf8a 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,10 @@
include LICENSE README.rst
+
+# Exclude what is in these folders
prune tests
+prune .github
+
+# Exclude these files
+exclude package-lock.json
+recursive-exclude * *.py[co]
+recursive-exclude * __pycache__
diff --git a/Makefile b/Makefile
index b21499b7c..b19faa59e 100644
--- a/Makefile
+++ b/Makefile
@@ -72,10 +72,9 @@ dist:
$(PREFIX)python setup.py sdist bdist_wheel
prepare:
- @pyenv install -s 3.5.7
@pyenv install -s 3.6.9
@pyenv install -s 3.7.4
- @pyenv local 3.5.7 3.6.9 3.7.4
+ @pyenv local 3.6.9 3.7.4
@echo "INFO: === Preparing to run for package:$(PACKAGE_NAME) platform:$(PLATFORM) py:$(PYTHON_VERSION) dir:$(DIR) ==="
#if [ -f ${HOME}/testspace/testspace ]; then ${HOME}/testspace/testspace config url ${TESTSPACE_TOKEN}@pycontribs.testspace.com/jira/tests ; fi;
diff --git a/README.rst b/README.rst
index c1aa693cb..5bcdb02a9 100644
--- a/README.rst
+++ b/README.rst
@@ -3,41 +3,41 @@ Jira Python Library
===================
.. image:: https://img.shields.io/pypi/v/jira.svg
- :target: https://pypi.python.org/pypi/jira/
+ :target: https://pypi.python.org/pypi/jira/
.. image:: https://img.shields.io/pypi/l/jira.svg
- :target: https://pypi.python.org/pypi/jira/
+ :target: https://pypi.python.org/pypi/jira/
.. image:: https://img.shields.io/pypi/wheel/jira.svg
- :target: https://pypi.python.org/pypi/jira/
+ :target: https://pypi.python.org/pypi/jira/
.. image:: https://img.shields.io/github/issues/pycontribs/jira.svg
- :target: https://github.com/pycontribs/jira/issues
+ :target: https://github.com/pycontribs/jira/issues
.. image:: https://img.shields.io/badge/irc-%23pycontribs-blue
- :target: irc:///#pycontribs
+ :target: irc:///#pycontribs
------------
.. image:: https://readthedocs.org/projects/jira/badge/?version=master
- :target: https://jira.readthedocs.io/
+ :target: https://jira.readthedocs.io/
.. image:: https://travis-ci.com/pycontribs/jira.svg?branch=master
- :target: https://travis-ci.com/pycontribs/jira
+ :target: https://travis-ci.com/pycontribs/jira
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/python/black
:alt: Python Black Code Style
.. image:: https://codecov.io/gh/pycontribs/jira/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/pycontribs/jira
+ :target: https://codecov.io/gh/pycontribs/jira
.. image:: https://img.shields.io/bountysource/team/pycontribs/activity.svg
- :target: https://www.bountysource.com/teams/pycontribs/issues?tracker_ids=3650997
+ :target: https://www.bountysource.com/teams/pycontribs/issues?tracker_ids=3650997
.. image:: https://requires.io/github/pycontribs/jira/requirements.svg?branch=master
- :target: https://requires.io/github/pycontribs/jira/requirements/?branch=master
- :alt: Requirements Status
+ :target: https://requires.io/github/pycontribs/jira/requirements/?branch=master
+ :alt: Requirements Status
This library eases the use of the Jira REST API from Python and it has been used in production for years.
@@ -54,14 +54,14 @@ Feeling impatient? I like your style.
.. code-block:: python
- from jira import JIRA
+ from jira import JIRA
- jira = JIRA('https://jira.atlassian.com')
+ jira = JIRA('https://jira.atlassian.com')
- issue = jira.issue('JRA-9')
- print(issue.fields.project.key) # 'JRA'
- print(issue.fields.issuetype.name) # 'New Feature'
- print(issue.fields.reporter.displayName) # 'Mike Cannon-Brookes [Atlassian]'
+ issue = jira.issue('JRA-9')
+ print(issue.fields.project.key) # 'JRA'
+ print(issue.fields.issuetype.name) # 'New Feature'
+ print(issue.fields.reporter.displayName) # 'Mike Cannon-Brookes [Atlassian]'
Installation
@@ -102,18 +102,25 @@ Setup
* Fork_ repo
* Keep it sync_'ed while you are developing
* Install pyenv_
-* Install `Atlassian Jira Server`_ for testing
- - make install-sdk
-* pip install jira[test]
-* Start up Jira Server
- - atlas-run-standalone
-* Test your changes
- - make test
+* develop and test
+ * Launch docker jira server
+ - ``docker run -dit -p 2990:2990 --name jira addono/jira-software-standalone``
+ * Lint
+ - ``tox -e lint``
+ - Note: Windows users trying to run locally will need to:
+ - Comment out the ``npm`` commands in the ``lint`` environment before running the ``lint`` environment
+ - Run ``npm install`` manually
+ - Run ``cspell "**" --unique`` manually - this relies on the ``cspell.json`` to check the right files
+ * Run tests
+ - ``tox``
+ * Run tests for one env only
+ - ``tox -e py37``
+ * Build and publish with TWINE
+ - ``tox -e upload``
.. _Fork: https://help.github.com/articles/fork-a-repo/
.. _sync: https://help.github.com/articles/syncing-a-fork/
.. _pyenv: https://amaral.northwestern.edu/resources/guides/pyenv-tutorial
-.. _`Atlassian Jira Server`: https://www.atlassian.com/software/jira/download
Credits
diff --git a/cspell.json b/cspell.json
index 7bef05387..18ff1b2a8 100644
--- a/cspell.json
+++ b/cspell.json
@@ -12,6 +12,7 @@
"bspeakmon",
"capsys",
"categorised",
+ "Codecov",
"conda",
"cygwin",
"dae",
@@ -66,6 +67,7 @@
"k",
"ky",
"kzh",
+ "libkrb",
"lqqy",
"luk",
"makotemplate",
@@ -182,9 +184,16 @@
"/I18NSPHINXOPTS/"
],
"ignorePaths": [
- "docs/build",
+ "__pycache__",
+ ".eggs",
".tox",
- ".eggs"
+ "*.egg-info",
+ "*.egg",
+ "*.pyc",
+ "dist/**",
+ "docs/_build/**",
+ "node_modules/**",
+ "package-lock.json"
],
"ignoreWords": [
"AACCOUNTID",
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 1c4e9e635..a07719933 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -63,7 +63,7 @@ Issues and Feature Requests
* How to recreate the bug.
* If relevant, including the versions of your:
- * Python interpreter (3.5, etc)
+ * Python interpreter (3.6, etc)
* jira-python
* Operating System and Version (Windows 7, OS X 10.10, Ubuntu 14.04, etc.)
* IPython if using jirashell
diff --git a/docs/installation.rst b/docs/installation.rst
index bd1f1bf61..766998e02 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -28,7 +28,7 @@ Source packages are also available at PyPI:
Dependencies
============
-Python 3.5+ is required.
+Python >3.5 is required.
- :py:mod:`requests` - `python-requests `_ library handles the HTTP business. Usually, the latest version available at time of release is the minimum version required; at this writing, that version is 1.2.0, but any version >= 1.0.0 should work.
- :py:mod:`requests-oauthlib` - Used to implement OAuth. The latest version as of this writing is 0.3.3.
diff --git a/jira/client.py b/jira/client.py
index 57b2d1839..8e0f30d63 100644
--- a/jira/client.py
+++ b/jira/client.py
@@ -464,7 +464,7 @@ def __init__(
if self._options["server"].endswith("/"):
self._options["server"] = self._options["server"][:-1]
- context_path = urlparse(self._options["server"]).path
+ context_path = urlparse(self.server_url).path
if len(context_path) > 0:
self._options["context_path"] = context_path
@@ -481,9 +481,8 @@ def __init__(
self._create_kerberos_session(timeout, kerberos_options=kerberos_options)
elif auth:
self._create_cookie_auth(auth, timeout)
- validate = (
- True
- ) # always log in for cookie based auth, as we need a first request to be logged in
+ # always log in for cookie based auth, as we need a first request to be logged in
+ validate = True
else:
verify = self._options["verify"]
self._session = ResilientSession(timeout=timeout)
@@ -532,6 +531,11 @@ def __init__(
for name in f["clauseNames"]:
self._fields[name] = f["id"]
+ @property
+ def server_url(self):
+ """Return the server url"""
+ return self._options["server"]
+
def _create_cookie_auth(self, auth, timeout):
self._session = ResilientSession(timeout=timeout)
self._session.auth = JiraCookieAuth(self._session, self.session, auth)
@@ -734,7 +738,7 @@ def _get_items_from_page(self, item_type, items_key, resource):
def client_info(self):
"""Get the server this client is connected to."""
- return self._options["server"]
+ return self.server_url
# Universal resource loading
@@ -799,7 +803,7 @@ def set_application_property(self, key, value):
:param value: value to assign to the property
:type value: str
"""
- url = self._options["server"] + "/rest/api/latest/application-properties/" + key
+ url = self._get_latest_url("application-properties/" + key)
payload = {"id": key, "value": value}
return self._session.put(url, data=json.dumps(payload))
@@ -813,7 +817,7 @@ def applicationlinks(self, cached=True):
return self._applicationlinks
# url = self._options['server'] + '/rest/applinks/latest/applicationlink'
- url = self._options["server"] + "/rest/applinks/latest/listApplicationlinks"
+ url = self.server_url + "/rest/applinks/latest/listApplicationlinks"
r = self._session.get(url)
@@ -1223,7 +1227,7 @@ def add_group(self, groupname):
:return: Boolean - True if successful.
:rtype: bool
"""
- url = self._options["server"] + "/rest/api/latest/group"
+ url = self._get_latest_url("group")
# implementation based on
# https://docs.atlassian.com/jira/REST/ondemand/#d2e5173
@@ -1248,7 +1252,7 @@ def remove_group(self, groupname):
"""
# implementation based on
# https://docs.atlassian.com/jira/REST/ondemand/#d2e5173
- url = self._options["server"] + "/rest/api/latest/group"
+ url = self._get_latest_url("group")
x = {"groupname": groupname}
self._session.delete(url, params=x)
return True
@@ -1403,7 +1407,7 @@ def supports_service_desk(self):
:rtype: bool
"""
- url = self._options["server"] + "/rest/servicedeskapi/info"
+ url = self.server_url + "/rest/servicedeskapi/info"
headers = {"X-ExperimentalApi": "opt-in"}
try:
r = self._session.get(url, headers=headers)
@@ -1421,7 +1425,7 @@ def create_customer(self, email, displayName):
:rtype: Customer
"""
- url = self._options["server"] + "/rest/servicedeskapi/customer"
+ url = self.server_url + "/rest/servicedeskapi/customer"
headers = {"X-ExperimentalApi": "opt-in"}
r = self._session.post(
url,
@@ -1441,7 +1445,7 @@ def service_desks(self):
:rtype: List[ServiceDesk]
"""
- url = self._options["server"] + "/rest/servicedeskapi/servicedesk"
+ url = self.server_url + "/rest/servicedeskapi/servicedesk"
headers = {"X-ExperimentalApi": "opt-in"}
r_json = json_loads(self._session.get(url, headers=headers))
print(r_json)
@@ -1501,7 +1505,7 @@ def create_customer_request(self, fields=None, prefetch=True, **fieldargs):
elif isinstance(p, str):
data["requestTypeId"] = self.request_type_by_name(service_desk, p).id
- url = self._options["server"] + "/rest/servicedeskapi/request"
+ url = self.server_url + "/rest/servicedeskapi/request"
headers = {"X-ExperimentalApi": "opt-in"}
r = self._session.post(url, headers=headers, data=json.dumps(data))
@@ -1559,13 +1563,13 @@ def createmeta(
params["expand"] = expand
return self._get_json("issue/createmeta", params)
- def _get_user_accountid(self, user):
- """Internal method for translating an user to an accountId."""
+ def _get_user_key(self, user):
+ """Internal method for translating an user (str) to an key."""
try:
- accountId = self.search_users(user, maxResults=1)[0].accountId
+ key = self.search_users(user, maxResults=1)[0].key
except Exception as e:
raise JIRAError(e)
- return accountId
+ return key
# non-resource
@translate_resource_args
@@ -1579,13 +1583,8 @@ def assign_issue(self, issue, assignee):
:rtype: bool
"""
- url = (
- self._options["server"]
- + "/rest/api/latest/issue/"
- + str(issue)
- + "/assignee"
- )
- payload = {"accountId": self._get_user_accountid(assignee)}
+ url = self._get_latest_url("issue/{}/assignee".format(str(issue)))
+ payload = {"name": self._get_user_key(assignee)}
# 'key' and 'name' are deprecated in favor of accountId
r = self._session.put(url, data=json.dumps(payload))
raise_on_error(r)
@@ -1599,7 +1598,7 @@ def comments(self, issue):
:type issue: str
:rtype: List[Comment]
"""
- r_json = self._get_json("issue/" + str(issue) + "/comment")
+ r_json = self._get_json("issue/{}/comment".format(str(issue)))
comments = [
Comment(self._options, self._session, raw_comment_json)
@@ -1753,7 +1752,7 @@ def add_remote_link(
# check if the link comes from one of the configured application links
for x in applicationlinks:
- if x["application"]["displayUrl"] == self._options["server"]:
+ if x["application"]["displayUrl"] == self.server_url:
data["globalId"] = "appId=%s&issueId=%s" % (
x["application"]["id"],
destination.raw["id"],
@@ -1918,7 +1917,7 @@ def add_watcher(self, issue, watcher):
"""Add a user to an issue's watchers list.
:param issue: ID or key of the issue affected
- :param watcher: username of the user to add to the watchers list
+ :param watcher: key of the user to add to the watchers list
"""
url = self._get_url("issue/" + str(issue) + "/watchers")
self._session.post(url, data=json.dumps(watcher))
@@ -1928,11 +1927,12 @@ def remove_watcher(self, issue, watcher):
"""Remove a user from an issue's watch list.
:param issue: ID or key of the issue affected
- :param watcher: accountId of the user to remove from the watchers list
+ :param watcher: key of the user to remove from the watchers list
:rtype: Response
"""
url = self._get_url("issue/" + str(issue) + "/watchers")
- params = {"accountId": watcher}
+ # https://docs.atlassian.com/software/jira/docs/api/REST/8.13.6/#api/2/issue-removeWatcher
+ params = {"username": watcher}
result = self._session.delete(url, params=params)
return result
@@ -2145,7 +2145,7 @@ def issue_type_by_name(self, name):
return issue_type
def request_types(self, service_desk):
- """ Returns request types supported by a service desk instance.
+ """Returns request types supported by a service desk instance.
:param service_desk: The service desk instance.
:type service_desk: ServiceDesk
:rtype: List[RequestType]
@@ -2153,7 +2153,7 @@ def request_types(self, service_desk):
if hasattr(service_desk, "id"):
service_desk = service_desk.id
url = (
- self._options["server"]
+ self.server_url
+ "/rest/servicedeskapi/servicedesk/%s/requesttype" % service_desk
)
headers = {"X-ExperimentalApi": "opt-in"}
@@ -2585,10 +2585,12 @@ def statuses(self):
return statuses
def status(self, id):
- # type: (str) -> Status
"""Get a status Resource from the server.
:param id: ID of the status resource to get
+ :type id: str
+
+ :rtype: Status
"""
return self._find_for_resource(Status, id)
@@ -2990,7 +2992,7 @@ def session(self):
def kill_session(self):
"""Destroy the session of the current authenticated user."""
- url = self._options["server"] + "/rest/auth/latest/session"
+ url = self.server_url + "/rest/auth/latest/session"
return self._session.delete(url)
# Websudo
@@ -3002,12 +3004,12 @@ def kill_websudo(self):
:rtype: Optional[Any]
"""
if self.deploymentType != "Cloud":
- url = self._options["server"] + "/rest/auth/1/websudo"
+ url = self.server_url + "/rest/auth/1/websudo"
return self._session.delete(url)
# Utilities
def _create_http_basic_session(self, username, password, timeout=None):
- """ Creates a basic http session.
+ """Creates a basic http session.
:param username: Username for the session
:type username: str
@@ -3097,7 +3099,8 @@ def _set_avatar(self, params, url, avatar):
return self._session.put(url, params=params, data=json.dumps(data))
def _get_url(self, path, base=JIRA_BASE_URL):
- """ Returns the full url based on Jira base url and the path provided
+ """Returns the full url based on Jira base url and the path provided.
+ Using the API version specified during the __init__.
:param path: The subpath desired.
:type path: str
@@ -3112,6 +3115,23 @@ def _get_url(self, path, base=JIRA_BASE_URL):
options.update({"path": path})
return base.format(**options)
+ def _get_latest_url(self, path, base=JIRA_BASE_URL):
+ """Returns the full url based on Jira base url and the path provided.
+ Using the latest API endpoint.
+
+ :param path: The subpath desired.
+ :type path: str
+ :param base: The base url which should be prepended to the path
+ :type base: Optional[str]
+
+ :return Fully qualified URL
+ :rtype: str
+
+ """
+ options = self._options.copy()
+ options.update({"path": path, "rest_api_version": "latest"})
+ return base.format(**options)
+
def _get_json(self, path, params=None, base=JIRA_BASE_URL):
"""Get the json for a given path and params.
@@ -3195,7 +3215,7 @@ def rename_user(self, old_user, new_user):
"""
if self._version > (6, 0, 0):
- url = self._options["server"] + "/rest/api/latest/user"
+ url = self._get_latest_url("user")
payload = {"name": new_user}
params = {"username": old_user}
@@ -3220,7 +3240,7 @@ def delete_user(self, username):
"""
- url = self._options["server"] + "/rest/api/latest/user/?username=%s" % username
+ url = self._get_latest_url("user/?username=%s" % username)
r = self._session.delete(url)
if 200 <= r.status_code <= 299:
@@ -3273,7 +3293,7 @@ def deactivate_user(self, username):
logging.error("Error Deactivating %s: %s" % (username, e))
raise JIRAError("Error Deactivating %s: %s" % (username, e))
else:
- url = self._options["server"] + "/secure/admin/user/EditUser.jspa"
+ url = self.server_url + "/secure/admin/user/EditUser.jspa"
self._options["headers"][
"Content-Type"
] = "application/x-www-form-urlencoded; charset=UTF-8"
@@ -3317,7 +3337,7 @@ def reindex(self, force=False, background=True):
else:
indexingStrategy = "stoptheworld"
- url = self._options["server"] + "/secure/admin/jira/IndexReIndex.jspa"
+ url = self.server_url + "/secure/admin/jira/IndexReIndex.jspa"
r = self._session.get(url, headers=self._options["headers"])
if r.status_code == 503:
@@ -3349,11 +3369,11 @@ def reindex(self, force=False, background=True):
def backup(self, filename="backup.zip", attachments=False):
"""Will call jira export to backup as zipped xml. Returning with success does not mean that the backup process finished."""
if self.deploymentType == "Cloud":
- url = self._options["server"] + "/rest/backup/1/export/runbackup"
+ url = self.server_url + "/rest/backup/1/export/runbackup"
payload = json.dumps({"cbAttachments": attachments})
self._options["headers"]["X-Requested-With"] = "XMLHttpRequest"
else:
- url = self._options["server"] + "/secure/admin/XmlBackup.jspa"
+ url = self.server_url + "/secure/admin/XmlBackup.jspa"
payload = {"filename": filename}
try:
r = self._session.post(url, headers=self._options["headers"], data=payload)
@@ -3372,9 +3392,7 @@ def backup_progress(self):
"""
epoch_time = int(time.time() * 1000)
if self.deploymentType == "Cloud":
- url = (
- self._options["server"] + "/rest/obm/1.0/getprogress?_=%i" % epoch_time
- )
+ url = self.server_url + "/rest/obm/1.0/getprogress?_=%i" % epoch_time
else:
logging.warning("This functionality is not available in Server version")
return None
@@ -3417,7 +3435,7 @@ def backup_download(self, filename=None):
return None
remote_file = self.backup_progress()["fileName"]
local_file = filename or remote_file
- url = self._options["server"] + "/webdav/backupmanager/" + remote_file
+ url = self.server_url + "/webdav/backupmanager/" + remote_file
try:
logging.debug("Writing file to %s" % local_file)
with open(local_file, "wb") as file:
@@ -3468,7 +3486,7 @@ def delete_project(self, pid):
if hasattr(pid, "id"):
pid = pid.id
- url = self._options["server"] + "/rest/api/2/project/%s" % pid
+ url = self._get_url("project/%s" % pid)
r = self._session.delete(url)
if r.status_code == 403:
raise JIRAError("Not enough permissions to delete project")
@@ -3477,7 +3495,7 @@ def delete_project(self, pid):
return r.ok
def _gain_sudo_session(self, options, destination):
- url = self._options["server"] + "/secure/admin/WebSudoAuthenticate.jspa"
+ url = self.server_url + "/secure/admin/WebSudoAuthenticate.jspa"
if not self._session.auth:
self._session.auth = get_netrc_auth(url)
@@ -3501,7 +3519,7 @@ def _gain_sudo_session(self, options, destination):
@lru_cache(maxsize=None)
def templates(self):
- url = self._options["server"] + "/rest/project-templates/latest/templates"
+ url = self.server_url + "/rest/project-templates/latest/templates"
r = self._session.get(url)
data = json_loads(r)
@@ -3517,7 +3535,7 @@ def templates(self):
@lru_cache(maxsize=None)
def permissionschemes(self):
- url = self._options["server"] + "/rest/api/3/permissionscheme"
+ url = self._get_url("permissionscheme")
r = self._session.get(url)
data = json_loads(r)["permissionSchemes"]
@@ -3527,7 +3545,7 @@ def permissionschemes(self):
@lru_cache(maxsize=None)
def issuesecurityschemes(self):
- url = self._options["server"] + "/rest/api/3/issuesecurityschemes"
+ url = self._get_url("issuesecurityschemes")
r = self._session.get(url)
data = json_loads(r)["issueSecuritySchemes"]
@@ -3537,7 +3555,7 @@ def issuesecurityschemes(self):
@lru_cache(maxsize=None)
def projectcategories(self):
- url = self._options["server"] + "/rest/api/3/projectCategory"
+ url = self._get_url("projectCategory")
r = self._session.get(url)
data = json_loads(r)
@@ -3547,7 +3565,7 @@ def projectcategories(self):
@lru_cache(maxsize=None)
def avatars(self, entity="project"):
- url = self._options["server"] + "/rest/api/3/avatar/%s/system" % entity
+ url = self._get_url("avatar/%s/system" % entity)
r = self._session.get(url)
data = json_loads(r)["system"]
@@ -3557,7 +3575,7 @@ def avatars(self, entity="project"):
@lru_cache(maxsize=None)
def notificationschemes(self):
# TODO(ssbarnea): implement pagination support
- url = self._options["server"] + "/rest/api/3/notificationscheme"
+ url = self._get_url("notificationscheme")
r = self._session.get(url)
data = json_loads(r)
@@ -3566,7 +3584,7 @@ def notificationschemes(self):
@lru_cache(maxsize=None)
def screens(self):
# TODO(ssbarnea): implement pagination support
- url = self._options["server"] + "/rest/api/3/screens"
+ url = self._get_url("screens")
r = self._session.get(url)
data = json_loads(r)
@@ -3575,7 +3593,7 @@ def screens(self):
@lru_cache(maxsize=None)
def workflowscheme(self):
# TODO(ssbarnea): implement pagination support
- url = self._options["server"] + "/rest/api/3/workflowschemes"
+ url = self._get_url("workflowschemes")
r = self._session.get(url)
data = json_loads(r)
@@ -3584,7 +3602,7 @@ def workflowscheme(self):
@lru_cache(maxsize=None)
def workflows(self):
# TODO(ssbarnea): implement pagination support
- url = self._options["server"] + "/rest/api/3/workflow"
+ url = self._get_url("workflow")
r = self._session.get(url)
data = json_loads(r)
@@ -3592,7 +3610,7 @@ def workflows(self):
def delete_screen(self, id):
- url = self._options["server"] + "/rest/api/3/screens/%s" % id
+ url = self._get_url("screens/%s" % id)
r = self._session.delete(url)
data = json_loads(r)
@@ -3602,7 +3620,7 @@ def delete_screen(self, id):
def delete_permissionscheme(self, id):
- url = self._options["server"] + "/rest/api/3/permissionscheme/%s" % id
+ url = self._get_url("permissionscheme/%s" % id)
r = self._session.delete(url)
data = json_loads(r)
@@ -3631,7 +3649,7 @@ def create_project(
:type: str
:param name: If not specified it will use the key value.
:type name: Optional[str]
- :param assignee: accountId of the lead, if not specified it will use current user.
+ :param assignee: key of the lead, if not specified it will use current user.
:type assignee: Optional[str]
:param type: Determines the type of project should be created.
:type ptype: Optional[str]
@@ -3646,7 +3664,7 @@ def create_project(
template_key = None
if assignee is None:
- assignee = self.current_user("accountId")
+ assignee = self.current_user()
if name is None:
name = key
@@ -3682,8 +3700,12 @@ def create_project(
# https://jira.atlassian.com/browse/JRASERVER-59658
# preference list for picking a default template
if not template_name:
- template_key = "com.pyxis.greenhopper.jira:gh-simplified-basic"
+ # https://confluence.atlassian.com/jirakb/creating-projects-via-rest-api-in-jira-963651978.html
+ template_key = (
+ "com.pyxis.greenhopper.jira:basic-software-development-template"
+ )
+ # https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-projects/#api-rest-api-2-project-get
# template_keys = [
# "com.pyxis.greenhopper.jira:gh-simplified-agility-kanban",
# "com.pyxis.greenhopper.jira:gh-simplified-agility-scrum",
@@ -3743,7 +3765,8 @@ def create_project(
"key": key,
"projectTypeKey": ptype,
"projectTemplateKey": template_key,
- "leadAccountId": assignee,
+ "lead": assignee,
+ # "leadAccountId": assignee,
"assigneeType": "PROJECT_LEAD",
"description": "",
# "avatarId": 13946,
@@ -3756,7 +3779,7 @@ def create_project(
if projectCategory:
payload["categoryId"] = int(projectCategory)
- url = self._options["server"] + "/rest/api/3/project"
+ url = self._get_url("project")
r = self._session.post(url, data=json.dumps(payload))
r.raise_for_status()
@@ -3806,7 +3829,7 @@ def add_user(
fullname = username
# TODO(ssbarnea): default the directoryID to the first directory in jira instead
# of 1 which is the internal one.
- url = self._options["server"] + "/rest/api/latest/user"
+ url = self._get_latest_url("user")
# implementation based on
# https://docs.atlassian.com/jira/REST/ondemand/#d2e5173
@@ -3847,7 +3870,7 @@ def add_user_to_group(self, username, group):
:return: json response from Jira server for success or a value that evaluates as False in case of failure.
:rtype: Union[bool,Dict[str,Any]]
"""
- url = self._options["server"] + "/rest/api/latest/group/user"
+ url = self._get_latest_url("group/user")
x = {"groupname": group}
y = {"name": username}
@@ -3865,7 +3888,7 @@ def remove_user_from_group(self, username, groupname):
:param username: The user to remove from the group.
:param groupname: The group that the user will be removed from.
"""
- url = self._options["server"] + "/rest/api/latest/group/user"
+ url = self._get_latest_url("group/user")
x = {"groupname": groupname, "username": username}
self._session.delete(url, params=x)
@@ -3881,7 +3904,7 @@ def role(self):
"""
# https://developer.atlassian.com/cloud/jira/platform/rest/v3/?utm_source=%2Fcloud%2Fjira%2Fplatform%2Frest%2F&utm_medium=302#api-rest-api-3-role-get
- url = self._options["server"] + "/rest/api/latest/role"
+ url = self._get_latest_url("role")
r = self._session.get(url)
return json_loads(r)
@@ -3890,7 +3913,7 @@ def role(self):
# Experimental support for iDalko Grid, expect API to change as it's using private APIs currently
# https://support.idalko.com/browse/IGRID-1017
def get_igrid(self, issueid, customfield, schemeid):
- url = self._options["server"] + "/rest/idalko-igrid/1.0/datagrid/data"
+ url = self.server_url + "/rest/idalko-igrid/1.0/datagrid/data"
if str(customfield).isdigit():
customfield = "customfield_%s" % customfield
params = {
diff --git a/jira/exceptions.py b/jira/exceptions.py
index 6e1b4ee77..c1be44868 100644
--- a/jira/exceptions.py
+++ b/jira/exceptions.py
@@ -19,7 +19,7 @@ def __init__(
response=None,
**kwargs
):
- """ Creates a JIRAError.
+ """Creates a JIRAError.
:param status_code: Status code for the error.
:type status_code: Optional[int]
diff --git a/jira/jirashell.py b/jira/jirashell.py
index dc413581e..aa1f0221d 100644
--- a/jira/jirashell.py
+++ b/jira/jirashell.py
@@ -362,7 +362,7 @@ def main():
from IPython.frontend.terminal.embed import InteractiveShellEmbed
ip_shell = InteractiveShellEmbed(
- banner1=""
+ banner1=""
)
ip_shell("*** Jira shell active; client is in 'jira'." " Press Ctrl-D to exit.")
except Exception as e:
diff --git a/jira/resources.py b/jira/resources.py
index 6464dd449..3725e4320 100644
--- a/jira/resources.py
+++ b/jira/resources.py
@@ -235,7 +235,7 @@ def find(self, id, params=None):
self._load(url, params=params)
def _get_url(self, path):
- """ Gets the url for the specified path.
+ """Gets the url for the specified path.
:type path: str
@@ -377,7 +377,7 @@ def delete(self, params=None):
return self._session.delete(url=self.self, params=params)
def _load(self, url, headers=CaseInsensitiveDict(), params=None, path=None):
- """ Load a resource.
+ """Load a resource.
:type url: str
:type headers: CaseInsensitiveDict
@@ -1092,7 +1092,7 @@ def dict2resource(raw, top=None, options=None, session=None):
r"securitylevel/[^/]+$": SecurityLevel,
r"status/[^/]+$": Status,
r"statuscategory/[^/]+$": StatusCategory,
- r"user\?(username|accountId).+$": User,
+ r"user\?(username|key).+$": User,
r"group\?groupname.+$": Group,
r"version/[^/]+$": Version,
# GreenHopper specific resources
diff --git a/make_local_jira_user.py b/make_local_jira_user.py
new file mode 100644
index 000000000..084c2db96
--- /dev/null
+++ b/make_local_jira_user.py
@@ -0,0 +1,52 @@
+"""Attempts to create a test user,
+as the empty JIRA instance isn't provisioned with one.
+"""
+import time
+
+import requests
+
+from jira import JIRA
+from os import environ
+
+CI_JIRA_URL = environ["CI_JIRA_URL"]
+
+
+def add_user_to_jira():
+ try:
+ JIRA(
+ CI_JIRA_URL,
+ basic_auth=(environ["CI_JIRA_ADMIN"], environ["CI_JIRA_ADMIN_PASSWORD"]),
+ ).add_user(
+ environ["CI_JIRA_USER"],
+ "user@example.com",
+ password=environ["CI_JIRA_USER_PASSWORD"],
+ )
+ print("user {}".format(environ["CI_JIRA_USER"]))
+ except Exception as e:
+ if "username already exists" not in str(e):
+ raise e
+
+
+if __name__ == "__main__":
+ start_time = time.time()
+ timeout_mins = 15
+ print(
+ "waiting for instance of jira to be running, to add a user for CI system:\n timeout = {} mins".format(
+ timeout_mins
+ )
+ )
+ while True:
+ try:
+ requests.get(CI_JIRA_URL + "rest/api/2/permissions")
+ print("JIRA IS REACHABLE")
+ add_user_to_jira()
+ break
+ except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as ex:
+ print(
+ "encountered {} while waiting for the JiraServer docker".format(str(ex))
+ )
+ time.sleep(20)
+ if start_time + 60 * timeout_mins < time.time():
+ raise TimeoutError(
+ "Jira server wasn't reachable within timeout {}".format(timeout_mins)
+ )
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..6e67bf45e
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,661 @@
+{
+ "name": "python-jira",
+ "version": "0.0.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@cspell/dict-aws": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-1.0.14.tgz",
+ "integrity": "sha512-K21CfB4ZpKYwwDQiPfic2zJA/uxkbsd4IQGejEvDAhE3z8wBs6g6BwwqdVO767M9NgZqc021yAVpr79N5pWe3w=="
+ },
+ "@cspell/dict-bash": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-1.0.12.tgz",
+ "integrity": "sha512-BOMHVW/m281mqUSJkZ3oiJiUUItLd7QdzpMjm428V9yBYFwIdbds1CeatS7C6kgpI2eBE4RXmy1Hjk/lR63Jew=="
+ },
+ "@cspell/dict-companies": {
+ "version": "1.0.36",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-1.0.36.tgz",
+ "integrity": "sha512-Bk9mMJs9spzrtLxZsxBZIK6ukD9REfQYpuTBNJk/IiTViHVQ6ertHAgw1vRVtJAMxViv8dMLNtDyTpEXeaYm7w=="
+ },
+ "@cspell/dict-cpp": {
+ "version": "1.1.38",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-1.1.38.tgz",
+ "integrity": "sha512-QqVMxVNYX9XtxzflpJ/888GSyjPU5VeotltsHql1BeEPxhyV27ud9bRKDrBGzCijCK/+MvCxiMZGDpYZqHTjXw=="
+ },
+ "@cspell/dict-cryptocurrencies": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-1.0.10.tgz",
+ "integrity": "sha512-47ABvDJOkaST/rXipNMfNvneHUzASvmL6K/CbOFpYKfsd0x23Jc9k1yaOC7JAm82XSC/8a7+3Yu+Fk2jVJNnsA=="
+ },
+ "@cspell/dict-csharp": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-1.0.11.tgz",
+ "integrity": "sha512-nub+ZCiTgmT87O+swI+FIAzNwaZPWUGckJU4GN402wBq420V+F4ZFqNV7dVALJrGaWH7LvADRtJxi6cZVHJKeA=="
+ },
+ "@cspell/dict-css": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-1.0.11.tgz",
+ "integrity": "sha512-2Or5oF5ojaXYD8QbO4Z+QdaNXSp+ZyNLJdeyKfejbxLvpL5feSNB0oYtTNrweFPTAvJKQ4DJsdEXy0/s31haRg=="
+ },
+ "@cspell/dict-django": {
+ "version": "1.0.26",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-1.0.26.tgz",
+ "integrity": "sha512-mn9bd7Et1L2zuibc08GVHTiD2Go3/hdjyX5KLukXDklBkq06r+tb0OtKtf1zKodtFDTIaYekGADhNhA6AnKLkg=="
+ },
+ "@cspell/dict-dotnet": {
+ "version": "1.0.25",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-1.0.25.tgz",
+ "integrity": "sha512-3BFhdquYqqjeI8Jm1dYepZKGEg+fKFhw7UfPkVdx13C4ETo5VlsS4FAblC0pCY21pDU3QgRZOGL1Bj+KWCGp/w=="
+ },
+ "@cspell/dict-elixir": {
+ "version": "1.0.24",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-1.0.24.tgz",
+ "integrity": "sha512-pEX6GYlEx4Teusw/m+XmqoXzcHOqpcn1ZX4H33ONqR81XdPwbaKorBr1IG23Ic76IhwrFlOqs48tcnxrHYpFnA=="
+ },
+ "@cspell/dict-en-gb": {
+ "version": "1.1.28",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.28.tgz",
+ "integrity": "sha512-noOH+iv4xFpPxu1agiQgp5LhY/KA0Ir28y1xnC2QTtLvlIid7vIvgixBOz4Zi0P7lo/mPmMjQY+x7//2EKFDgQ=="
+ },
+ "@cspell/dict-en_us": {
+ "version": "1.2.40",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-1.2.40.tgz",
+ "integrity": "sha512-e8leCvGAWPWQIw0SoozgEAiMt2YM12rafOuW4aQwgTJD++vp32a9RrnVL8olBfWaA57rRWWndbMSmPTrsO9mpg=="
+ },
+ "@cspell/dict-filetypes": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-1.1.5.tgz",
+ "integrity": "sha512-yfkB37J+hL6W8qa4AknFp7u6CGECrw2ql2/y0lUKruLQYid0ApK+bH+ll+Sqgl2YS5QAOhclskc72aQHAcRJIQ=="
+ },
+ "@cspell/dict-fonts": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-1.0.14.tgz",
+ "integrity": "sha512-VhIX+FVYAnqQrOuoFEtya6+H72J82cIicz9QddgknsTqZQ3dvgp6lmVnsQXPM3EnzA8n1peTGpLDwHzT7ociLA=="
+ },
+ "@cspell/dict-fullstack": {
+ "version": "1.0.37",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-1.0.37.tgz",
+ "integrity": "sha512-ljVzUdIlBENMiyHUV06007hz2FPRt+BQmC9Jgn6iGIEQeAQp37Q6oIDmxv2lD65ScEIbysxXuaUgJ5x0j4a48A=="
+ },
+ "@cspell/dict-golang": {
+ "version": "1.1.24",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-1.1.24.tgz",
+ "integrity": "sha512-qq3Cjnx2U1jpeWAGJL1GL0ylEhUMqyaR36Xij6Y6Aq4bViCRp+HRRqk0x5/IHHbOrti45h3yy7ii1itRFo+Xkg=="
+ },
+ "@cspell/dict-haskell": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-1.0.13.tgz",
+ "integrity": "sha512-kvl8T84cnYRPpND/P3D86P6WRSqebsbk0FnMfy27zo15L5MLAb3d3MOiT1kW3vEWfQgzUD7uddX/vUiuroQ8TA=="
+ },
+ "@cspell/dict-html": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-1.1.6.tgz",
+ "integrity": "sha512-RsZXIrmsnLcUpXfyZdNg7OtO2+e4p7m/qILg03kM6vhSUMY6ryCQNPWKrHqsl8+LBKd54EgFM+O5zcgq6IIsCw=="
+ },
+ "@cspell/dict-html-symbol-entities": {
+ "version": "1.0.23",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-1.0.23.tgz",
+ "integrity": "sha512-PV0UBgcBFbBLf/m1wfkVMM8w96kvfHoiCGLWO6BR3Q9v70IXoE4ae0+T+f0CkxcEkacMqEQk/I7vuE9MzrjaNw=="
+ },
+ "@cspell/dict-java": {
+ "version": "1.0.22",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-1.0.22.tgz",
+ "integrity": "sha512-CVAJ29dx1XwwutgsMgaj5eCl1Nc7X7qFhWL2KkAdu78A/NUIaS+1I9KS0hHhdZx/wLke9dH8TR7NyPQGpGxeAw=="
+ },
+ "@cspell/dict-latex": {
+ "version": "1.0.25",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-1.0.25.tgz",
+ "integrity": "sha512-cEgg91Migqcp1SdVV7dUeMxbPDhxdNo6Fgq2eygAXQjIOFK520FFvh/qxyBvW90qdZbIRoU2AJpchyHfGuwZFA=="
+ },
+ "@cspell/dict-lorem-ipsum": {
+ "version": "1.0.22",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-1.0.22.tgz",
+ "integrity": "sha512-yqzspR+2ADeAGUxLTfZ4pXvPl7FmkENMRcGDECmddkOiuEwBCWMZdMP5fng9B0Q6j91hQ8w9CLvJKBz10TqNYg=="
+ },
+ "@cspell/dict-lua": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-1.0.16.tgz",
+ "integrity": "sha512-YiHDt8kmHJ8nSBy0tHzaxiuitYp+oJ66ffCYuFWTNB3//Y0SI4OGHU3omLsQVeXIfCeVrO4DrVvRDoCls9B5zQ=="
+ },
+ "@cspell/dict-node": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-1.0.11.tgz",
+ "integrity": "sha512-q66zAqtNmuvZGKt4stRwQPFLsbOjZGGZOZ1HEbqpOkicxvF0BWhR0Di/JBq27PDxeqQP3S5sLeogQTSNQBuTww=="
+ },
+ "@cspell/dict-npm": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-1.0.11.tgz",
+ "integrity": "sha512-mokmv9/Yk1yliDz97drWyuDWv7eKGEcFhdM43YSPK7GuMLh6i2ULOmORPFhUcjxQjPf0uySMDA2JguiQ4m5Lmg=="
+ },
+ "@cspell/dict-php": {
+ "version": "1.0.23",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-1.0.23.tgz",
+ "integrity": "sha512-rRLf/09rXDrzs0DJuNXNmFVTw2b2zLmZKNF4LIPrFHYHvdfsMvwVqxkr/SAyhF8C6zi5sW0XYC/J0S/3IE927w=="
+ },
+ "@cspell/dict-powershell": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-1.0.14.tgz",
+ "integrity": "sha512-hisOXXi5PBXB5YKtrJQIis2FIRHgSW1U0/sd4yI36lzb3ZMEvGJwdAdyhXN3IGiqRUNxMzJiXAeXfhnia4xPtQ=="
+ },
+ "@cspell/dict-python": {
+ "version": "1.0.33",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-1.0.33.tgz",
+ "integrity": "sha512-tRmE4TzHDFPs7sJ1a3XbfyFrvRHwefVz+z1wkm6tkXK9TPrCbIS+rV/T8xhj205q4lpZQ/TkNB3lT40eLB9O8A=="
+ },
+ "@cspell/dict-ruby": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-1.0.13.tgz",
+ "integrity": "sha512-YeN1acY38dgMYlEJ6iWPH+8qXB6seLKHm9BszXxaKT/IzGA9Y9XUWPGobeJFD5E/tC6HjvcqRKxEs8vnvakoLQ=="
+ },
+ "@cspell/dict-rust": {
+ "version": "1.0.22",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-1.0.22.tgz",
+ "integrity": "sha512-7WOIzv0BPiU+MssZbbMk8K+HR/g9Bcvd0+jXJC3/AKT8L6l0Mx0Tr/oF7cJ4xvCYgA84nBz3PhMZkabGSz/Nkg=="
+ },
+ "@cspell/dict-scala": {
+ "version": "1.0.21",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-1.0.21.tgz",
+ "integrity": "sha512-5V/R7PRbbminTpPS3ywgdAalI9BHzcEjEj9ug4kWYvBIGwSnS7T6QCFCiu+e9LvEGUqQC+NHgLY4zs1NaBj2vA=="
+ },
+ "@cspell/dict-software-terms": {
+ "version": "1.0.27",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-1.0.27.tgz",
+ "integrity": "sha512-O6wCGuFSnr9G9Sr62zc7/XyruRRPI0/PJ0xZj8/R+hr+vFjDaScQnkqj10gTVoLAshk1TjL5Firnzyz3ibfgdQ=="
+ },
+ "@cspell/dict-typescript": {
+ "version": "1.0.17",
+ "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-1.0.17.tgz",
+ "integrity": "sha512-CXCuXcrgAc56P3kL9I6gW6bZwTs6t3duyAtHerHg5YAYbPs6/4nXgniQgLgu8kjFHFy07XrqaaBdLU9V2DmMtQ=="
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "array-timsort": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz",
+ "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ },
+ "comment-json": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.1.0.tgz",
+ "integrity": "sha512-WEghmVYaNq9NlWbrkzQTSsya9ycLyxJxpTQfZEan6a5Jomnjw18zS3Podf8q1Zf9BvonvQd/+Z7Z39L7KKzzdQ==",
+ "requires": {
+ "array-timsort": "^1.0.3",
+ "core-util-is": "^1.0.2",
+ "esprima": "^4.0.1",
+ "has-own-prop": "^2.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "configstore": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
+ "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
+ "requires": {
+ "dot-prop": "^5.2.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^3.0.0",
+ "unique-string": "^2.0.0",
+ "write-file-atomic": "^3.0.0",
+ "xdg-basedir": "^4.0.0"
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "crypto-random-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="
+ },
+ "cspell": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/cspell/-/cspell-4.2.8.tgz",
+ "integrity": "sha512-eqan8+lCU9bSp8Tl4+SR/ccBnuPyMmp7evck/RlMdFTjLh/s+3vQ5hQyBzbzK8w2MMqL84CymW7BwIOKjpylSg==",
+ "requires": {
+ "chalk": "^4.1.0",
+ "commander": "^7.0.0",
+ "comment-json": "^4.0.6",
+ "cspell-glob": "^0.1.25",
+ "cspell-lib": "^4.3.12",
+ "fs-extra": "^9.1.0",
+ "gensequence": "^3.1.1",
+ "get-stdin": "^8.0.0",
+ "glob": "^7.1.6",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "cspell-glob": {
+ "version": "0.1.25",
+ "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-0.1.25.tgz",
+ "integrity": "sha512-/XaSHrGBpMJa+duFz3GKOWfrijrfdHT7a/XGgIcq3cymCSpOH+DPho42sl0jLI/hjM+8yv2m8aEoxRT8yVSnlg==",
+ "requires": {
+ "micromatch": "^4.0.2"
+ }
+ },
+ "cspell-io": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-4.1.7.tgz",
+ "integrity": "sha512-V0/tUu9FnIS3v+vAvDT6NNa14Nc/zUNX8+YUUOfFAiDJJTdqefmvcWjOJBIMYBf3wIk9iWLmLbMM+bNHqr7DSQ==",
+ "requires": {
+ "iconv-lite": "^0.6.2",
+ "iterable-to-stream": "^1.0.1"
+ }
+ },
+ "cspell-lib": {
+ "version": "4.3.12",
+ "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-4.3.12.tgz",
+ "integrity": "sha512-yCCb6MoW1K8Tsr/WVEQoO4dfYhH9bCsjQayccb8MlyDaNNuWJHuX+gUGHsZSXSuChSh8PrTWKXJzs13/uM977g==",
+ "requires": {
+ "@cspell/dict-aws": "^1.0.13",
+ "@cspell/dict-bash": "^1.0.11",
+ "@cspell/dict-companies": "^1.0.35",
+ "@cspell/dict-cpp": "^1.1.37",
+ "@cspell/dict-cryptocurrencies": "^1.0.10",
+ "@cspell/dict-csharp": "^1.0.10",
+ "@cspell/dict-css": "^1.0.10",
+ "@cspell/dict-django": "^1.0.25",
+ "@cspell/dict-dotnet": "^1.0.24",
+ "@cspell/dict-elixir": "^1.0.23",
+ "@cspell/dict-en-gb": "^1.1.27",
+ "@cspell/dict-en_us": "^1.2.39",
+ "@cspell/dict-filetypes": "^1.1.5",
+ "@cspell/dict-fonts": "^1.0.13",
+ "@cspell/dict-fullstack": "^1.0.36",
+ "@cspell/dict-golang": "^1.1.24",
+ "@cspell/dict-haskell": "^1.0.12",
+ "@cspell/dict-html": "^1.1.5",
+ "@cspell/dict-html-symbol-entities": "^1.0.23",
+ "@cspell/dict-java": "^1.0.22",
+ "@cspell/dict-latex": "^1.0.23",
+ "@cspell/dict-lorem-ipsum": "^1.0.22",
+ "@cspell/dict-lua": "^1.0.16",
+ "@cspell/dict-node": "^1.0.10",
+ "@cspell/dict-npm": "^1.0.10",
+ "@cspell/dict-php": "^1.0.23",
+ "@cspell/dict-powershell": "^1.0.14",
+ "@cspell/dict-python": "^1.0.32",
+ "@cspell/dict-ruby": "^1.0.12",
+ "@cspell/dict-rust": "^1.0.22",
+ "@cspell/dict-scala": "^1.0.21",
+ "@cspell/dict-software-terms": "^1.0.24",
+ "@cspell/dict-typescript": "^1.0.16",
+ "comment-json": "^4.1.0",
+ "configstore": "^5.0.1",
+ "cspell-io": "^4.1.7",
+ "cspell-trie-lib": "^4.2.8",
+ "cspell-util-bundle": "^4.1.11",
+ "fs-extra": "^9.1.0",
+ "gensequence": "^3.1.1",
+ "minimatch": "^3.0.4",
+ "resolve-from": "^5.0.0",
+ "resolve-global": "^1.0.0",
+ "vscode-uri": "^3.0.2"
+ }
+ },
+ "cspell-trie-lib": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-4.2.8.tgz",
+ "integrity": "sha512-Nt3c0gxOYXIc3/yhALDukpje1BgR6guvlUKWQO2zb0r7qRWpwUw2j2YM4dWbHQeH/3Hx5ei4Braa6cMaiJ5YBw==",
+ "requires": {
+ "gensequence": "^3.1.1"
+ }
+ },
+ "cspell-util-bundle": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/cspell-util-bundle/-/cspell-util-bundle-4.1.11.tgz",
+ "integrity": "sha512-or3OGKydZs1NwweMIgnA48k8H3F5zK4e5lonjUhpEzLYQZ2nB23decdoqZ8ogFC8pFTA40tZKDsMJ0b+65gX4Q=="
+ },
+ "dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "gensequence": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-3.1.1.tgz",
+ "integrity": "sha512-ys3h0hiteRwmY6BsvSttPmkhC0vEQHPJduANBRtH/dlDPZ0UBIb/dXy80IcckXyuQ6LKg+PloRqvGER9IS7F7g=="
+ },
+ "get-stdin": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
+ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg=="
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "global-dirs": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+ "requires": {
+ "ini": "^1.3.4"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "has-own-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz",
+ "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="
+ },
+ "iconv-lite": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+ "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+ },
+ "is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "iterable-to-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/iterable-to-stream/-/iterable-to-stream-1.0.1.tgz",
+ "integrity": "sha512-O62gD5ADMUGtJoOoM9U6LQ7i4byPXUNoHJ6mqsmkQJcom331ZJGDApWgDESWyBMEHEJRjtHozgIiTzYo9RU4UA=="
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "micromatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
+ "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+ "requires": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.2.3"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "picomatch": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
+ "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg=="
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+ },
+ "resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
+ },
+ "resolve-global": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz",
+ "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==",
+ "requires": {
+ "global-dirs": "^0.1.1"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ },
+ "signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "typedarray-to-buffer": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "unique-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
+ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+ "requires": {
+ "crypto-random-string": "^2.0.0"
+ }
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
+ },
+ "vscode-uri": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz",
+ "integrity": "sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA=="
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write-file-atomic": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+ "requires": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "xdg-basedir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
+ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q=="
+ }
+ }
+}
diff --git a/package.json b/package.json
index 070c353cd..2c3f8660c 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"url": "https://github.com/pycontribs/jira.git"
},
"dependencies": {
- "cspell": "^4.0.23",
+ "cspell": "^4.2.8",
"npm": "^6.10.0"
}
}
diff --git a/pyproject.toml b/pyproject.toml
index ba41bc7f5..3b9e229bd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,5 +5,5 @@ requires = [
"setuptools_scm_git_archive >= 1.0",
"wheel",
]
-requires-python = ">=3.5"
+requires-python = ">3.5"
build-backend = "setuptools.build_meta"
diff --git a/setup.cfg b/setup.cfg
index 545190166..4445ad8b8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,24 +1,24 @@
[metadata]
name = jira
author = Ben Speakmon
-author-email = ben.speakmon@gmail.com
+author_email = ben.speakmon@gmail.com
maintainer = Sorin Sbarnea
-maintainer-email = sorin.sbarnea@gmail.com
+maintainer_email = sorin.sbarnea@gmail.com
summary = Python library for interacting with Jira via REST APIs.
-long-description = file: README.rst
+long_description = file: README.rst
# Do not include ChangeLog in description-file due to multiple reasons:
# - Unicode chars, see https://github.com/pycontribs/jira/issues/512
# - Breaks ability to perform `python setup.py install`
-long-description-content-type = text/x-rst; charset=UTF-8
-home-page = https://github.com/pycontribs/jira
-project-urls =
+long_description_content_type = text/x-rst; charset=UTF-8
+url = https://github.com/pycontribs/jira
+project_urls =
Bug Tracker = https://github.com/pycontribs/jira/issues
Release Management = https://github.com/pycontribs/jira/projects
CI: Travis = https://travis-ci.com/pycontribs/jira
Source Code = https://github.com/pycontribs/jira.git
Documentation = https://jira.readthedocs.io/en/master/
Forum = https://community.atlassian.com/t5/tag/jira-python/tg-p?sort=recent
-requires-python = >=3.5
+requires_python = >=3.6
platforms = any
license = BSD
classifiers =
@@ -31,9 +31,10 @@ classifiers =
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
- Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
+ Programming Language :: Python :: 3.9
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Internet :: WWW/HTTP
keywords = api, atlassian, jira, rest, web
@@ -44,7 +45,7 @@ packages =
[options]
use_scm_version = True
-python_requires = >=3.5
+python_requires = >=3.6
packages = find:
include_package_data = True
zip_safe = False
@@ -86,8 +87,8 @@ test =
pytest-instafail
pytest-sugar
pytest-timeout>=1.3.1
- pytest-xdist>=1.14
- pytest>=5.0.0,<6.0 # MIT
+ pytest-xdist>=2.2
+ pytest>=6.0.0,<7.0 # MIT
PyYAML>=5.1 # MIT
requests_mock # Apache-2
requires.io # UNKNOWN!!!
@@ -146,4 +147,4 @@ filterwarnings =
ignore::pytest.PytestWarning
[mypy]
-python_version = 3.5
+python_version = 3.6
diff --git a/test.local b/test.local
deleted file mode 100755
index ebd061bbc..000000000
--- a/test.local
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-
-# Settings for using the Vagrant VM from atlassian
-# (see https://developer.atlassian.com/static/connect/docs/latest/developing/developing-locally.html)
-# a user "jira_user" with password "jira" needs to be created manually
-export CI_JIRA_URL="http://localhost:2990/jira"
-export CI_JIRA_ADMIN="admin"
-export CI_JIRA_ADMIN_PASSWORD="admin"
-export CI_JIRA_USER=jira_user
-export CI_JIRA_USER_PASSWORD=jira
-export CI_JIRA_ISSUE=Task
-
-if [ "$1" = "--tox" ] ; then
- shift
- exec tox "$@"
-else
- exec python -m pytest --cov-report xml --cov jira --pyargs jira "$@"
-fi
diff --git a/tests/test_client.py b/tests/test_client.py
index 1c49fdbc6..52408f129 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -73,24 +73,15 @@ def test_delete_inexistent_project(cl_admin):
def test_templates(cl_admin):
- templates = cl_admin.templates()
+ templates = set(cl_admin.templates())
expected_templates = set(
filter(
None,
"""
-Agility
-Basic
-Bug tracking
-Content Management
-Customer service
-Document Approval
-IT Service Desk
+Basic software development
Kanban software development
-Lead Tracking
Process management
-Procurement
Project management
-Recruitment
Scrum software development
Task management
""".split(
@@ -99,8 +90,7 @@ def test_templates(cl_admin):
)
)
- for t in expected_templates:
- assert t in templates
+ assert templates == expected_templates
def test_result_list():
diff --git a/tests/tests.py b/tests/tests.py
index 32f1d6758..d3ddcebbe 100755
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -295,7 +295,7 @@ def __init__(self):
):
break
except Exception as e:
- if "A project with that name already exists" not in e.text:
+ if "A project with that name already exists" not in str(e):
raise e
self.project_a_id = self.jira_admin.project(self.project_a).id
self.jira_admin.create_project(self.project_b, self.project_b_name)
@@ -453,7 +453,7 @@ def test_application_property(self):
clone_prefix = self.jira.application_properties(
key="jira.lf.text.headingcolour"
)
- self.assertEqual(clone_prefix["value"], "#292929")
+ self.assertEqual(clone_prefix["value"], "#172b4d")
def test_set_application_property(self):
prop = "jira.lf.favicon.hires.url"
@@ -1133,12 +1133,10 @@ def test_editmeta(self):
"comment",
"components",
"description",
- "environment",
"fixVersions",
"issuelinks",
"labels",
"summary",
- "versions",
}
for i in (self.issue_1, self.issue_2):
meta = self.jira.editmeta(i)
@@ -1304,15 +1302,15 @@ def test_votes_with_issue_obj(self):
def test_add_remove_watcher(self):
# removing it in case it exists, so we know its state
- self.jira.remove_watcher(self.issue_1, self.test_manager.user_admin.accountId)
+ self.jira.remove_watcher(self.issue_1, self.test_manager.user_admin.key)
init_watchers = self.jira.watchers(self.issue_1).watchCount
# adding a new watcher
- self.jira.add_watcher(self.issue_1, self.test_manager.user_admin.accountId)
+ self.jira.add_watcher(self.issue_1, self.test_manager.user_admin.key)
self.assertEqual(self.jira.watchers(self.issue_1).watchCount, init_watchers + 1)
# now we verify that remove does indeed remove watchers
- self.jira.remove_watcher(self.issue_1, self.test_manager.user_admin.accountId)
+ self.jira.remove_watcher(self.issue_1, self.test_manager.user_admin.key)
new_watchers = self.jira.watchers(self.issue_1).watchCount
self.assertEqual(init_watchers, new_watchers)
@@ -1598,12 +1596,7 @@ def test_project_versions(self):
self.assertEqual(test.name, name)
i = self.jira.issue(JiraTestManager().project_b_issue1)
- i.update(
- fields={
- "versions": [{"id": version.id}],
- "fixVersions": [{"id": version.id}],
- }
- )
+ i.update(fields={"fixVersions": [{"id": version.id}]})
version.delete()
def test_get_project_version_by_name(self):
@@ -1620,12 +1613,7 @@ def test_get_project_version_by_name(self):
self.assertEqual(not_found_version, None)
i = self.jira.issue(JiraTestManager().project_b_issue1)
- i.update(
- fields={
- "versions": [{"id": version.id}],
- "fixVersions": [{"id": version.id}],
- }
- )
+ i.update(fields={"fixVersions": [{"id": version.id}]})
version.delete()
def test_rename_version(self):
@@ -1647,12 +1635,7 @@ def test_rename_version(self):
self.assertEqual(not_found_version, None)
i = self.jira.issue(JiraTestManager().project_b_issue1)
- i.update(
- fields={
- "versions": [{"id": version.id}],
- "fixVersions": [{"id": version.id}],
- }
- )
+ i.update(fields={"fixVersions": [{"id": version.id}]})
version.delete()
def test_project_versions_with_project_obj(self):
@@ -2142,7 +2125,7 @@ def setUp(self):
)
def test_fetch_pages(self):
- """Tests that the JIRA._fetch_pages method works as expected. """
+ """Tests that the JIRA._fetch_pages method works as expected."""
params = {"startAt": 0}
total = 26
expected_results = []
@@ -2176,7 +2159,7 @@ def test_fetch_pages(self):
def _create_issue_result_json(issue_id, summary, key, **kwargs):
- """Returns a minimal json object for an issue. """
+ """Returns a minimal json object for an issue."""
return {
"id": "%s" % issue_id,
"summary": summary,
@@ -2186,7 +2169,7 @@ def _create_issue_result_json(issue_id, summary, key, **kwargs):
def _create_issue_search_results_json(issues, **kwargs):
- """Returns a minimal json object for Jira issue search results. """
+ """Returns a minimal json object for Jira issue search results."""
return {
"startAt": kwargs.get("start_at", 0),
"maxResults": kwargs.get("max_results", 50),
diff --git a/tox.ini b/tox.ini
index 590b5a146..9b4945947 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,17 +3,21 @@ minversion = 3.8.0
requires =
tox-pyenv
envlist =
- lint
- pkg
+ py39
py38
py37
py36
- py35
- docs
ignore_basepython_conflict = True
skip_missing_interpreters = True
skipdist = True
+[gh-actions]
+python =
+ 3.6: py36
+ 3.7: py37
+ 3.8: py38
+ 3.9: py39
+
[testenv:docs]
extras =
docs
@@ -23,10 +27,8 @@ skipdist = False
setenv =
PYTHONHTTPSVERIFY=0
commands =
- # pip install "..[docs]"
- bash -c "set | grep REQUESTS_CA_BUNDLE"
- python -m sphinx \
- -a -n -W \
+ sphinx-build \
+ -a -n -v -W --keep-going \
-b html --color \
-d "{toxworkdir}/docs_doctree" \
docs/ "{toxworkdir}/docs_out"
@@ -47,14 +49,21 @@ extras =
test
sitepackages=False
commands=
- bash -c 'find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf'
+ git clean -xdf jira tests
python -m pip check
+ python make_local_jira_user.py
python -m pytest {posargs}
setenv =
PIP_LOG={envdir}/pip.log
PIP_DISABLE_PIP_VERSION_CHECK=1
# Avoid 2020-01-01 warnings: https://github.com/pypa/pip/issues/6207
PYTHONWARNINGS=ignore:DEPRECATION::pip._internal.cli.base_command
+ CI_JIRA_URL=http://localhost:2990/jira
+ CI_JIRA_ADMIN=admin
+ CI_JIRA_ADMIN_PASSWORD=admin
+ CI_JIRA_USER=jira_user
+ CI_JIRA_USER_PASSWORD=jira
+ CI_JIRA_ISSUE=Task
passenv =
CI
CI_JIRA_*
@@ -62,44 +71,38 @@ passenv =
PIP_*
REQUESTS_CA_BUNDLE
SSL_CERT_FILE
- TRAVIS*
TWINE_*
XDG_CACHE_HOME
+ # For Windows users, getpass.get_user() needs USERNAME
+ USERNAME
envars =
PIP_DISABLE_PIP_VERSION_CHECK=1
PIP_USER=no
whitelist_externals =
- bash
- echo
- find
- grep
- rm
- xargs
+ git
+ npm
[testenv:pkg]
deps =
collective.checkdocs>=0.2
- pep517>=0.7.0
+ build>=0.3.0
pip>=19.2.3
setuptools>=41.4
twine>=2.0.0
wheel>=0.33.6
commands =
- rm -rf {toxinidir}/dist
+ git clean -xdf dist
python setup.py check -m -s
# disabled due to errors with older setuptools:
# python setup.py sdist bdist_wheel
- python -m pep517.build \
- --source \
- --binary \
- --out-dir {toxinidir}/dist/ \
- {toxinidir}
- python -m twine check {toxinidir}/dist/*
+ python -m build --wheel --sdist .
+ python -m twine check dist/*
[testenv:lint]
deps = pre-commit>=1.17.0
commands=
- bash -c "npm install && npm run spell"
+ npm install
+ npm run spell
python -m pre_commit run --color=always {posargs:--all}
extras =
skip_install = true