diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 14e7ee03..cabede16 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -6,15 +6,11 @@ name: CI_TEST on: push: paths: - - 'requirements.txt' - '**.py' - '**.yml' pull_request: branches: [ "master" ] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + workflow_dispatch: # allow manual run jobs: build: @@ -23,14 +19,14 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] - token: ["stable", "latest"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + token: ["stable"] steps: - name: Harden Runner - uses: step-security/harden-runner@1f99358870fe1c846a3ccba386cc2b2246836776 # v2.2.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: - egress-policy: block + egress-policy: audit allowed-endpoints: > azure.archive.ubuntu.com:80 esm.ubuntu.com:443 @@ -39,6 +35,8 @@ jobs: ftp-nyc.osuosl.org:443 get.jenkins.io:443 github.com:443 + api.github.com:443 + int.api.stepsecurity.io:443 mirror.xmission.com:443 motd.ubuntu.com:443 packages.microsoft.com:443 @@ -46,47 +44,38 @@ jobs: pypi.org:443 updates.jenkins-ci.org:80 updates.jenkins.io:443 + mirrors.updates.jenkins.io:443 updates.jenkins.io:80 - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + - name: Install uv + uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} + enable-cache: true - - name: Get pip cache dir - id: pip-cache - run: | - echo "::set-output name=dir::$(pip cache dir)" + - name: Install python + run: uv python install - - name: Setup the Pip cache - uses: actions/cache@v3 + - name: setup java 17 + uses: actions/setup-java@v3 with: - path: ${{ steps.pip-cache.outputs.dir }} - key: >- - ${{ matrix.python-version }}-pip-${{ hashFiles('setup.cfg') }}-${{ - hashFiles('setup.py') }}-${{ hashFiles('tox.ini') }}-${{ - hashFiles('.pre-commit-config.yaml') }} - restore-keys: | - ${{ matrix.python-version }}-pip- - ${{ matrix.python-version }}- + java-version: '17' + distribution: 'temurin' - 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 -r test-requirements.txt - python -m pip install -r requirements.txt + sudo apt-get install libkrb5-dev - name: Lint with flake8 run: | - flake8 jenkinsapi/ --count --select=E9,F63,F7,F82 --ignore F821,W503,W504 --show-source --statistics - flake8 jenkinsapi/ --count --exit-zero --max-complexity=10 --max-line-length=79 --statistics + uv run flake8 jenkinsapi/ --count --select=E9,F63,F7,F82 --ignore F821,W503,W504 --show-source --statistics + uv run flake8 jenkinsapi/ --count --exit-zero --max-complexity=10 --max-line-length=79 --statistics - name: Test with pytest env: JENKINS_VERSION: ${{ matrix.token }} run: | - pytest -sv --cov=jenkinsapi --cov-report=term-missing --cov-report=xml jenkinsapi_tests + uv run pytest -sv --cov=jenkinsapi --cov-report=term-missing --cov-report=xml jenkinsapi_tests diff --git a/Makefile b/Makefile index fd547c78..b3dec4eb 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: test lint tox coverage dist test: - py.test -sv jenkinsapi_tests + pytest -sv jenkinsapi_tests lint: pycodestyle @@ -14,4 +14,4 @@ dist: python setup.py sdist bdist_wheel coverage: - py.test -sv --cov=jenkinsapi --cov-report=term-missing --cov-report=xml jenkinsapi_tests + pytest -sv --cov=jenkinsapi --cov-report=term-missing --cov-report=xml jenkinsapi_tests diff --git a/README.rst b/README.rst index b6386ce8..4380485c 100644 --- a/README.rst +++ b/README.rst @@ -4,72 +4,48 @@ jenkinsapi .. image:: https://badge.fury.io/py/jenkinsapi.png :target: http://badge.fury.io/py/jenkinsapi -.. image:: https://travis-ci.com/pycontribs/jenkinsapi.png?branch=master - :target: https://travis-ci.com/pycontribs/jenkinsapi - .. image:: https://codecov.io/gh/pycontribs/jenkinsapi/branch/master/graph/badge.svg :target: https://codecov.io/gh/pycontribs/jenkinsapi -.. image:: https://requires.io/github/pycontribs/jenkinsapi/requirements.png?branch=master - :target: https://requires.io/github/pycontribs/jenkinsapi/requirements/?branch=master - :alt: Requirements Status - About this library ------------------- Jenkins is the market leading continuous integration system, originally created by Kohsuke Kawaguchi. -Jenkins (and It's predecessor Hudson) are useful projects for automating common development tasks (e.g. unit-testing, production batches) - but they are somewhat Java-centric. Thankfully the designers have provided an excellent and complete REST interface. This library wraps up that interface as more conventional python objects in order to make many Jenkins oriented tasks easier to automate. +Jenkins (and its predecessor Hudson) are useful projects for automating common development tasks (e.g. unit-testing, production batches) - but they are somewhat Java-centric. +| Jenkinsapi makes scripting Jenkins tasks a breeze by wrapping the REST api into familiar python objects. +| Here is a list of some of the most commonly used functionality -This library allows you to automate most common Jenkins operations using Python, such as: - -* Ability to add/remove/query Jenkins jobs -* Ability to execute jobs and: +* Add, remove, and query Jenkins jobs +* Control pipeline execution * Query the results of a completed build * Block until jobs are complete or run jobs asyncronously * Get objects representing the latest builds of a job -* Work with build artifacts: +* Artifact management * Search for artifacts by simple criteria * Install artifacts to custom-specified directory structures -* Ability to search for builds by source code revision -* Ability to add/remove/query: - * Slaves (Webstart and SSH slaves) +* Search for builds by source code revision +* Create, destroy, and monitor + * Build nodes (Webstart and SSH slaves) * Views (including nested views using NestedViews Jenkins plugin) * Credentials (username/password and ssh key) -* Username/password auth support for jenkins instances with auth turned on -* Ability to script jenkins installation including plugins - -For a full documentation spec of what this library supports see: http://jenkinsapi.readthedocs.io/en/latest/index.html - -Python versions ---------------- - -The project has been tested against Python versions: - -* 2.7 - last version compatible with Python 2.7 is tagged Py2 in repository and available on PyPi as version 0.3.13 -* 3.8 - 3.11 +* Authentication support for username and password +* Manage jenkins and plugin installation -Jenkins versions ----------------- +Full library capabilities are outlined in the `Documentation `_ -Project tested on both stable (LTS) and latest Jenkins versions. Known issues ------------ * Job deletion operations fail unless Cross-Site scripting protection is disabled. - -For other issues, please refer to the support URL below. +| For other issues, please refer to the `support URL `_ Important Links --------------- - -Support and bug-reports: https://github.com/pycontribs/jenkinsapi/issues?direction=desc&sort=comments&state=open - -Project source code: github: https://github.com/pycontribs/jenkinsapi - -Project documentation: https://jenkinsapi.readthedocs.org/en/latest/ - -Releases: http://pypi.python.org/pypi/jenkinsapi +* `Support and bug-reports `_ +* `Source Code `_ +* `Documentation `_ +* `Releases `_ Installation ------------- @@ -111,20 +87,14 @@ JenkinsAPI is intended to map the objects in Jenkins (e.g. Builds, Views, Jobs) .. code-block:: python - >>> import jenkinsapi - >>> from jenkinsapi.jenkins import Jenkins - >>> J = Jenkins('http://localhost:8080') - >>> J.version - 1.542 - >>> J.keys() # Jenkins objects appear to be dict-like, mapping keys (job-names) to - ['foo', 'test_jenkinsapi'] - >>> J['test_jenkinsapi'] - - >>> J['test_jenkinsapi'].get_last_good_build() - - ... - -More examples available on Github: https://github.com/pycontribs/jenkinsapi/tree/master/examples + from jenkinsapi.jenkins import Jenkins + J = Jenkins('http://localhost:8080') + print(J.version) # 1.542 + print(J.keys()) # foo, test_jenkinsapi + print(J.get('test_jenkinsapi')) # + print(J.get('test_jenkinsapi').get_last_good_build()) # + +More examples available on `Github `_ Testing ------- @@ -141,10 +111,10 @@ missing test dependencies: .. code-block:: bash - virtualenv - source .venv/bin/active - (.venv) pip install -r requirements.txt - (.venv) python setup.py test + python -m venv ./.venv/jenkinsapi + source .venv/jenkinsapi/bin/activate + pip install -r requirements.txt + python setup.py test Development ----------- @@ -156,14 +126,26 @@ Development .. code-block:: bash - (.venv) pip install -r test-requirements.txt + pip install -r test-requirements.txt * Make your changes, write tests and check your code .. code-block:: bash - (.venv) pytest + pytest -sv + +Python versions +--------------- + +The project has been tested against Python versions: + +* 3.8 - 3.13 +* 2.7 - last version compatible with Python 2.7 is tagged Py2 in repository and available on PyPi as version 0.3.13 + +Jenkins versions +---------------- +Project tested on both stable (LTS) and latest Jenkins versions. Project Contributors -------------------- @@ -192,4 +174,4 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -.. _Java: http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html +.. _Java: https://www.oracle.com/java/technologies/downloads/#java17 diff --git a/doc/source/conf.py b/doc/source/conf.py index 0384b71b..1b1f4148 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -13,10 +13,8 @@ # serve to show the default. import logging import jenkinsapi -import pkg_resources -dist = pkg_resources.working_set.by_key[jenkinsapi.__name__] -VERSION = RELEASE = dist.version +VERSION = RELEASE = jenkinsapi.__version__ if __name__ == "__main__": logging.basicConfig() @@ -45,6 +43,7 @@ "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.viewcode", + "myst_parser", ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst new file mode 100644 index 00000000..2232a542 --- /dev/null +++ b/doc/source/contributing.rst @@ -0,0 +1,5 @@ +Contributing +============ + +```{include} ../CONTRIBUTING.md +``` diff --git a/doc/source/index.rst b/doc/source/index.rst index 6389db22..58b249c0 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -25,7 +25,7 @@ Sections artifact build using_jenkinsapi - rules_for_contributors + contributing Important Links --------------- @@ -53,14 +53,8 @@ Most users can do the following: pip install jenkinsapi -Or.. - -.. code-block:: bash - - easy_install jenkinsapi - * In Jenkins > 1.518 you will need to disable "Prevent Cross Site Request Forgery exploits". - * Remember to set the Jenkins Location in general settings - Jenkins' REST web-interface will not work if this is set incorrectly. + * Remember to set the Jenkins Location in general settings - Jenkins REST web-interface will not work if this is set incorrectly. Examples -------- diff --git a/jenkinsapi/__init__.py b/jenkinsapi/__init__.py index b4aedb0e..97957b18 100644 --- a/jenkinsapi/__init__.py +++ b/jenkinsapi/__init__.py @@ -93,9 +93,3 @@ __docformat__ = "epytext" # In case of jenkinsapi is not installed in 'develop' mode __version__ = "0.3.13" -try: - import pkg_resources - - __version__ = pkg_resources.working_set.by_key["jenkinsapi"].version -except (ImportError, KeyError): - pass diff --git a/jenkinsapi/build.py b/jenkinsapi/build.py index 4a156f73..42165273 100644 --- a/jenkinsapi/build.py +++ b/jenkinsapi/build.py @@ -530,8 +530,7 @@ def get_estimated_duration(self) -> int | None: def stop(self) -> bool: """ Stops the build execution if it's running - :return boolean True if succeded False otherwise or the build - is not running + :return: boolean True if succeeded False otherwis """ if self.is_running(): url: str = "%s/stop" % self.baseurl diff --git a/jenkinsapi/utils/jenkins_launcher.py b/jenkinsapi/utils/jenkins_launcher.py index 3769194f..9a612ecd 100644 --- a/jenkinsapi/utils/jenkins_launcher.py +++ b/jenkinsapi/utils/jenkins_launcher.py @@ -8,11 +8,10 @@ import requests import queue import threading +import tarfile import subprocess -from pkg_resources import resource_stream from urllib3 import Retry from urllib.parse import urlparse -from tarfile import TarFile from requests.adapters import HTTPAdapter @@ -136,12 +135,15 @@ def update_war(self): ) def update_config(self): - tarball = TarFile.open( - fileobj=resource_stream( - "jenkinsapi_tests.systests", "jenkins_home.tar.gz" - ) + from jenkinsapi_tests import systests + + file = os.path.join( + os.path.dirname(systests.__file__), "jenkins_home.tar.gz" ) - tarball.extractall(path=self.jenkins_home) + + with open(file, "rb") as f: + with tarfile.open(fileobj=f, mode="r:gz") as tarball: + tarball.extractall(path=self.jenkins_home) def install_plugins(self): plugin_dest_dir = os.path.join(self.jenkins_home, "plugins") diff --git a/jenkinsapi_tests/systests/conftest.py b/jenkinsapi_tests/systests/conftest.py index 1ca2960a..d5cf871f 100644 --- a/jenkinsapi_tests/systests/conftest.py +++ b/jenkinsapi_tests/systests/conftest.py @@ -16,6 +16,7 @@ "http://updates.jenkins.io/latest/" "apache-httpcomponents-client-4-api.hpi", "http://updates.jenkins.io/latest/jsch.hpi", + "http://updates.jenkins.io/latest/gson-api.hpi", "http://updates.jenkins.io/latest/trilead-api.hpi", "http://updates.jenkins.io/latest/workflow-api.hpi", "http://updates.jenkins.io/latest/display-url-api.hpi", @@ -25,7 +26,9 @@ "http://updates.jenkins.io/latest/script-security.hpi", "http://updates.jenkins.io/latest/matrix-project.hpi", "http://updates.jenkins.io/latest/credentials.hpi", + "http://updates.jenkins.io/latest/variant.hpi", "http://updates.jenkins.io/latest/ssh-credentials.hpi", + "http://updates.jenkins.io/latest/asm-api.hpi", "http://updates.jenkins.io/latest/scm-api.hpi", "http://updates.jenkins.io/latest/mailer.hpi", "http://updates.jenkins.io/latest/git.hpi", @@ -43,6 +46,7 @@ "http://updates.jenkins.io/latest/caffeine-api.hpi", "http://updates.jenkins.io/latest/script-security.hpi", "http://updates.jenkins.io/latest/checks-api.hpi", + "http://updates.jenkins.io/latest/json-api.hpi", "http://updates.jenkins.io/latest/jackson2-api.hpi", "http://updates.jenkins.io/latest/bootstrap5-api.hpi", "http://updates.jenkins.io/latest/echarts-api.hpi", diff --git a/jenkinsapi_tests/unittests/test_build.py b/jenkinsapi_tests/unittests/test_build.py index a53c439c..37fae50e 100644 --- a/jenkinsapi_tests/unittests/test_build.py +++ b/jenkinsapi_tests/unittests/test_build.py @@ -3,6 +3,7 @@ import pytz from . import configs import datetime +import warnings from typing import List from jenkinsapi.build import Build from jenkinsapi.job import Job @@ -258,7 +259,7 @@ def fake_get_data(cls, tree=None, params=None): monkeypatch.setattr(Build, "get_data", fake_get_data) with pytest.raises(requests.HTTPError) as excinfo: - with pytest.warns(None) as record: + with pytest.warns(UserWarning) as record: build.get_env_vars() assert "404" == str(excinfo.value) assert len(record) == 1 @@ -275,10 +276,9 @@ def fake_get_data(cls, tree=None, params=None): monkeypatch.setattr(Build, "get_data", fake_get_data) with pytest.raises(Exception) as excinfo: - with pytest.warns(None) as record: + with warnings.catch_warnings(): build.get_env_vars() assert "" == str(excinfo.value) - assert len(record) == 0 def test_build_get_status(build) -> None: diff --git a/pyproject.toml b/pyproject.toml index dabe7120..b3418a82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ description = "A Python API for accessing resources on a Jenkins continuous-inte readme = "README.rst" license = {text = "MIT license"} classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Information Technology", @@ -24,31 +24,27 @@ classifiers = [ "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Testing", "Topic :: Utilities", ] -requires_python = ">=2.7" +requires-python = ">=3.8" dynamic = ["version"] dependencies = [ "pytz>=2014.4", "requests>=2.3.0", - "six>=1.10.0" + "six>=1.10.0", ] -[tool.files] -packages = """ -jenkinsapi -jenkinsapi_utils -jenkinsapi_tests""" +[tool.setuptools] +packages = ["jenkinsapi", "jenkinsapi_utils", "jenkinsapi_tests"] +include-package-data = false [tool.pbr] warnerrors = "True" @@ -72,5 +68,18 @@ universal = 1 exclude = ".tox,doc/source/conf.py,build,.venv,.eggs" max-line-length = "99" -[tool.setuptools] -include-package-data = false +[dependency-groups] +dev = [ + "pytest-mock>=3.14.0", + "pytest>=8.3.4", + "pytest-cov>=4.0.0", + "pycodestyle>=2.3.1", + "astroid>=1.4.8", + "pylint>=1.7.1", + "tox>=2.3.1", + "mock>=5.1.0", + "myst-parser>=3.0.0", + "codecov>=2.1.13", + "requests-kerberos>=0.15.0", + "flake8>=5.0.0", +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index da330e69..00000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytz>=2014.4 -requests>=2.3.0 diff --git a/setup.cfg b/setup.cfg index f3746251..bebd744c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ summary = A Python API for accessing resources on a Jenkins continuous-integrati description-file = README.rst license = MIT classifier = - Development Status :: 4 - Beta + Development Status :: 5 - Production/Stable Environment :: Console Intended Audience :: Developers Intended Audience :: Information Technology @@ -16,15 +16,13 @@ classifier = Operating System :: OS Independent Operating System :: OS Independent Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Topic :: Software Development :: Testing Topic :: Utilities diff --git a/test-requirements.txt b/test-requirements.txt index 0d07d562..efc2b376 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,6 +6,7 @@ astroid>=1.4.8 pylint>=1.7.1 tox>=2.3.1 mock +myst-parser codecov requests_kerberos flake8