diff --git a/jenkinsapi/job.py b/jenkinsapi/job.py index 00df4714..fc311d0a 100644 --- a/jenkinsapi/job.py +++ b/jenkinsapi/job.py @@ -50,8 +50,14 @@ def __init__(self, url: str, name: str, jenkins_obj: "Jenkins") -> None: self._scm_prefix = "" self._scm_map = { "hudson.scm.SubversionSCM": "svn", + "jenkins.scm.impl.subversion.SubversionSCMSource": "svn", "hudson.plugins.git.GitSCM": "git", + "jenkins.plugins.git.GitSCMSource": "git", "hudson.plugins.mercurial.MercurialSCM": "hg", + "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource": "github", + "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource": "bitbucket", + "io.jenkins.plugins.gitlabbranchsource.GitLabSCMSource": "gitlab", + "org.jenkinsci.plugins.gitea.GiteaSCMSource": "gitea", "hudson.scm.NullSCM": "NullSCM", } self._scmurlmap = { @@ -528,17 +534,38 @@ def get_config(self): def load_config(self): self._config = self.get_config() + def _get_scm_from_branch_job_property(self, element_tree): + """Check element_tree for BranchJobProperty""" + scm_element = None + log.debug("Check BranchJobProperty for scm type") + multibranch_scm_prefix = "properties/org.jenkinsci.plugins.\ + workflow.multibranch.BranchJobProperty/branch/" + multibranch_path = multibranch_scm_prefix + "scm" + scm_element = element_tree.find(multibranch_path) + return scm_element + + def _get_scm_from_branch_source(self, element_tree): + """Check for scm type from BranchSource""" + source_class = None + log.debug("Check BranchSource for scm type") + branch_source_path = ".//sources//data//jenkins.branch.BranchSource" + scm_element = element_tree.find(branch_source_path) + if not scm_element: + scm_element = element_tree.find(".//sources/..") + + if scm_element: + source_class = scm_element.find(".//source") + + return source_class + def get_scm_type(self): element_tree = self._get_config_element_tree() - scm_element = element_tree.find("scm") + scm_element = element_tree.find(".//scm") + if not scm_element: + scm_element = self._get_scm_from_branch_job_property(element_tree) if not scm_element: - multibranch_scm_prefix = "properties/org.jenkinsci.plugins.\ - workflow.multibranch.BranchJobProperty/branch/" - multibranch_path = multibranch_scm_prefix + "scm" - scm_element = element_tree.find(multibranch_path) - if scm_element: - # multibranch pipeline. - self._scm_prefix = multibranch_scm_prefix + scm_element = self._get_scm_from_branch_source(element_tree) + scm_class = scm_element.get("class") if scm_element else None scm = self._scm_map.get(scm_class) if not scm: diff --git a/jenkinsapi_tests/systests/conftest.py b/jenkinsapi_tests/systests/conftest.py index a7389900..2fad409c 100644 --- a/jenkinsapi_tests/systests/conftest.py +++ b/jenkinsapi_tests/systests/conftest.py @@ -15,6 +15,18 @@ # Extra plugins required by the systests PLUGIN_DEPENDENCIES = [ + "https://updates.jenkins.io/latest/cloudbees-folder.hpi", + "https://updates.jenkins.io/latest/branch-api.hpi", + "https://updates.jenkins.io/latest/workflow-job.hpi", + "https://updates.jenkins.io/latest/workflow-cps.hpi", + "https://updates.jenkins.io/latest/workflow-multibranch.hpi", + "https://updates.jenkins.io/latest/okhttp-api.hpi", + "https://updates.jenkins.io/latest/github-api.hpi", + "https://updates.jenkins.io/latest/json-path-api.hpi", + "https://updates.jenkins.io/latest/token-macro.hpi", + "https://updates.jenkins.io/latest/github.hpi", + "https://updates.jenkins.io/latest/jjwt-api.hpi", + "https://updates.jenkins.io/latest/github-branch-source.hpi", "https://updates.jenkins.io/latest/apache-httpcomponents-client-4-api.hpi", "https://updates.jenkins.io/latest/mina-sshd-api-common.hpi", "https://updates.jenkins.io/latest/mina-sshd-api-core.hpi", diff --git a/jenkinsapi_tests/systests/job_configs.py b/jenkinsapi_tests/systests/job_configs.py index 35fa782e..d065afea 100644 --- a/jenkinsapi_tests/systests/job_configs.py +++ b/jenkinsapi_tests/systests/job_configs.py @@ -339,3 +339,170 @@ """.strip() + +PIPELINE_SCM_JOB = """ + + + + + 2 + + + https://github.com/salimfadhley/jenkinsapi.git + + + + + main + + + + +""".strip() + +MULTIBRANCH_GIT_SCM_JOB = """ + + + + + + + + + + + + + +true +-1 +-1 +false + + +false + + + + +e0a4ce06-e537-4893-9ba4-2dd4f18d7a44 +https://github.com/pycontribs/jenkinsapi + + + + +(master.*) + + + + + + + + + + + + +uv.lock + + +""".strip() + +MULTIBRANCH_GIT_BRANCH_JOB_PROPERTY = """ + + + +false + + + +bc1f7bd0-b8d0-48db-ac98-bcae92939715 + +a + + +2 + + +https://github.com/pycontribs/jenkinsapi.git + + + + +*/master + + +false + + + + + + + + + +uv.lock + + +false + +""".strip() + +MULTIBRANCH_GITHUB_SCM_JOB = """ + + + + + + + + + + + + + +true +-1 +-1 +false + + +false + + + + +e82f0840-83c9-44b3-a903-3825f776da38 +https://api.github.com +pycontribs +jenkinsapi +https://github.com/pycontribs/jenkinsapi + + +1 + + +2 + + +2 + + + + + + + + + + + + + +uv.lock + + +""" diff --git a/jenkinsapi_tests/systests/test_scm.py b/jenkinsapi_tests/systests/test_scm.py index 3558b787..d1a6841b 100644 --- a/jenkinsapi_tests/systests/test_scm.py +++ b/jenkinsapi_tests/systests/test_scm.py @@ -1,32 +1,73 @@ # ''' # System tests for `jenkinsapi.jenkins` module. # ''' -# To run unittests on python 2.6 please use unittest2 library -# try: -# import unittest2 as unittest -# except ImportError: -# import unittest -# from jenkinsapi_tests.systests.base import BaseSystemTest -# from jenkinsapi_tests.test_utils.random_strings import random_string -# from jenkinsapi_tests.systests.job_configs import SCM_GIT_JOB - -# # Maybe have a base class for all SCM test activites? -# class TestSCMGit(BaseSystemTest): -# # Maybe it makes sense to move plugin dependencies outside the code. -# # Have a config to dependencies mapping from the launcher can use -# # to install plugins. -# def test_get_revision(self): -# job_name = 'git_%s' % random_string() -# job = self.jenkins.create_job(job_name, SCM_GIT_JOB) -# ii = job.invoke() -# ii.block(until='completed') -# self.assertFalse(ii.is_running()) -# b = ii.get_build() -# try: -# self.assertIsInstance(b.get_revision(), basestring) -# except NameError: -# # Python3 -# self.assertIsInstance(b.get_revision(), str) - -# if __name__ == '__main__': -# unittest.main() +from testfixtures import compare +from time import sleep + +from jenkinsapi_tests.test_utils.random_strings import random_string +from jenkinsapi_tests.systests.job_configs import ( + SCM_GIT_JOB, + MULTIBRANCH_GIT_BRANCH_JOB_PROPERTY, + MULTIBRANCH_GIT_SCM_JOB, + MULTIBRANCH_GITHUB_SCM_JOB, +) + + +def wait_for_job_setup(jenkins, job_name): + for _ in range(5): + for _url, name in list(jenkins.get_jobs_info()): + if job_name in name: + return True + else: + sleep(10) + + +def test_get_scm_type(jenkins): + job_name = "git_%s" % random_string() + job = jenkins.create_job(job_name, SCM_GIT_JOB) + queueItem = job.invoke() + queueItem.block_until_complete() + + wait_for_job_setup(jenkins, job_name) + compare(job.get_scm_type(), "git") + jenkins.delete_job(job_name) + + +def test_get_scm_type_pipeline_scm_multibranch_BranchJobProperty( + jenkins, +): + job_name = "git_%s" % random_string() + job = jenkins.create_job(job_name, MULTIBRANCH_GIT_BRANCH_JOB_PROPERTY) + queueItem = job.invoke() + queueItem.block_until_complete() + wait_for_job_setup(jenkins, job_name) + compare(job.get_scm_type(), "git") + + +## Disabling for now, running into permissions errors +def test_get_scm_type_pipeline_scm_multibranch_BranchSource( + jenkins, +): + job_name = "git_%s" % random_string() + job = jenkins.create_multibranch_pipeline_job( + job_name, MULTIBRANCH_GIT_SCM_JOB + ) + queueItem = job.invoke() + queueItem.block_until_complete() + wait_for_job_setup(jenkins, job_name) + job.invoke(block=True, delay=20) + compare(job[0].get_scm_type(), "git") + + +def test_get_scm_type_pipeline_github_multibranch_BranchSource( + jenkins, +): + job_name = "git_%s" % random_string() + job = jenkins.create_multibranch_pipeline_job( + job_name, MULTIBRANCH_GITHUB_SCM_JOB + ) + queueItem = job.invoke() + queueItem.block_until_complete() + wait_for_job_setup(jenkins, job_name) + job.invoke(block=True, delay=20) + compare(job.get_scm_type(), "github") diff --git a/pyproject.toml b/pyproject.toml index ac6ae681..003284c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ dev = [ "mock>=5.1.0", "requests-kerberos>=0.15.0", "ruff>=0.9.6", + "testfixtures>=8.3.0", ] docs = [ "docutils>=0.20.1", diff --git a/uv.lock b/uv.lock index e3f5e304..6f13ed8d 100644 --- a/uv.lock +++ b/uv.lock @@ -584,6 +584,8 @@ dev = [ { name = "pytest-mock" }, { name = "requests-kerberos" }, { name = "ruff" }, + { name = "testfixtures", version = "8.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "testfixtures", version = "10.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tox", version = "4.30.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "tox", version = "4.32.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] @@ -613,6 +615,7 @@ dev = [ { name = "pytest-mock", specifier = ">=3.14.0" }, { name = "requests-kerberos", specifier = ">=0.15.0" }, { name = "ruff", specifier = ">=0.9.6" }, + { name = "testfixtures", specifier = ">=8.3.0" }, { name = "tox", specifier = ">=2.3.1" }, ] docs = [ @@ -1310,6 +1313,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/9a/8c11707040d13c53a2bc1dab54cb8c172e1bb00e34e9f92f64a6a41302e6/sspilib-0.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:d45eff48922c23eb63d39a7f0a51167c5284ec348e0663f1377c983bcb9eb94d", size = 495125, upload-time = "2025-09-01T00:26:19.86Z" }, ] +[[package]] +name = "testfixtures" +version = "8.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/25/d7e9d05f87e2ab84657a0dfb1f24fc295d542ac2eb221531d976ea4aa1ff/testfixtures-8.3.0.tar.gz", hash = "sha256:d4c0b84af2f267610f908009b50d6f983a4e58ade22c67bab6787b5a402d59c0", size = 137420, upload-time = "2024-06-07T18:12:27.484Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/4e/699671ba484b94bda0959b281ff59b24f728263befd13e060fa038ce3bc8/testfixtures-8.3.0-py3-none-any.whl", hash = "sha256:3d1e0e0005c4d6ac2a2ab27916704c6471047f0d2f78f2e54adf20abdacc7b10", size = 105085, upload-time = "2024-06-07T18:12:23.298Z" }, +] + +[[package]] +name = "testfixtures" +version = "10.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/53/d7439458a89dc5be4780b23af09432a8347c798ea52ba0b53fd158f86653/testfixtures-10.0.0.tar.gz", hash = "sha256:2b9829bf7f42f0ca8600250762e6725575da59af18d9a7f82aae2c97b14f0f66", size = 147464, upload-time = "2025-10-29T08:42:20.733Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/30/70da9c7cd9931f2299ab2a353cf082427ba5dbea5f34db57e6a9ef89c8a5/testfixtures-10.0.0-py3-none-any.whl", hash = "sha256:c54ed5c4cd93ad271a6add94a575a5843964ea3aaf590f4c3cb4df16a261b15f", size = 111285, upload-time = "2025-10-29T08:42:19.647Z" }, +] + [[package]] name = "tomli" version = "2.2.1"