Fix legacy plugin version comparison; Remove cap on setuptools version

LegacyVersion class is removed from newer setuptools package. But
support for legacy versions is added in python-jenkins 1.8.2.
Switch to that implementation.

Fix broken plugin version comparison for legacy versions.

Assume latest plugin version if no plugin version is found.

Story: 2010990
Story: 2009943
Story: 2009819
Story: 2010842
Task: 49236
Task: 44852
Task: 44396
Task: 48448

Change-Id: Id7f0be1c42357454bd9bedcdee3fefb174943d81
This commit is contained in:
Vsevolod Fedorov 2023-12-12 12:50:23 +03:00
parent e2cf69b4f4
commit 67645a46eb
21 changed files with 203 additions and 229 deletions

View File

@ -37,7 +37,6 @@ Example::
"""
import logging
import sys
import xml.etree.ElementTree as XML
import six
@ -48,7 +47,6 @@ from jenkins_jobs.errors import JenkinsJobsException
from jenkins_jobs.errors import MissingAttributeError
import jenkins_jobs.modules.base
import jenkins_jobs.modules.helpers as helpers
import pkg_resources
from jenkins_jobs.modules import hudson_model
from jenkins_jobs.modules.publishers import ssh
from jenkins_jobs.modules.publishers import cifs
@ -2885,11 +2883,9 @@ def cmake(registry, xml_parent, data):
]
helpers.convert_mapping_to_xml(cmake, data, mapping, fail_required=True)
info = registry.get_plugin_info("CMake plugin")
# Note: Assume latest version of plugin is preferred config format
version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("CMake plugin")
if version >= pkg_resources.parse_version("2.0"):
if plugin_ver >= "2.0":
mapping_20 = [
("preload-script", "preloadScript", None), # Optional parameter
("working-dir", "workingDir", ""),
@ -4701,8 +4697,7 @@ def xunit(registry, xml_parent, data):
:language: yaml
"""
info = registry.get_plugin_info("xunit")
plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("xunit")
logger = logging.getLogger(__name__)
xunit = XML.SubElement(xml_parent, "org.jenkinsci.plugins.xunit.XUnitBuilder")
@ -4744,7 +4739,7 @@ def xunit(registry, xml_parent, data):
# Generate XML for each of the supported framework types
# Note: versions 3+ are now using the 'tools' sub-element instead of 'types'
if plugin_version < pkg_resources.parse_version("3.0.0"):
if plugin_ver < "3.0.0":
types_name = "types"
else:
types_name = "tools"

View File

@ -14,7 +14,6 @@
from functools import wraps
import logging
import sys
import xml.etree.ElementTree as XML
@ -23,8 +22,6 @@ from jenkins_jobs.errors import JenkinsJobsException
from jenkins_jobs.errors import MissingAttributeError
from jenkins_jobs.modules import hudson_model
import pkg_resources
def build_trends_publisher(plugin_name, xml_element, data):
"""Helper to create various trend publishers."""
@ -515,8 +512,7 @@ def trigger_get_parameter_order(registry, plugin):
def trigger_project(tconfigs, project_def, registry, param_order=None):
info = registry.get_plugin_info("parameterized-trigger")
plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("parameterized-trigger")
logger = logging.getLogger("%s:trigger_project" % __name__)
pt_prefix = "hudson.plugins.parameterizedtrigger."
@ -554,7 +550,7 @@ def trigger_project(tconfigs, project_def, registry, param_order=None):
("fail-on-missing", "failTriggerOnMissing", False),
]
if plugin_version >= pkg_resources.parse_version("2.35.2"):
if plugin_ver >= "2.35.2":
property_file_mapping.append(
("property-multiline", "textParamValueOnNewLine", False)
)

View File

@ -74,7 +74,6 @@ Example:
# and this object is passed to the HipChat() class initialiser.
import logging
import pkg_resources
import sys
import xml.etree.ElementTree as XML
@ -137,10 +136,9 @@ class HipChat(jenkins_jobs.modules.base.Base):
logger.warning("'room' is deprecated, please use 'rooms'")
hipchat["rooms"] = [hipchat["room"]]
plugin_info = self.registry.get_plugin_info("Jenkins HipChat Plugin")
version = pkg_resources.parse_version(plugin_info.get("version", "0"))
plugin_ver = self.registry.get_plugin_version("Jenkins HipChat Plugin")
if version >= pkg_resources.parse_version("0.1.9"):
if plugin_ver >= "0.1.9":
publishers = xml_parent.find("publishers")
if publishers is None:
publishers = XML.SubElement(xml_parent, "publishers")
@ -173,7 +171,7 @@ class HipChat(jenkins_jobs.modules.base.Base):
hipchat.get("notify-start", hipchat.get("start-notify", False))
).lower()
if version >= pkg_resources.parse_version("0.1.5"):
if plugin_ver >= "0.1.5":
mapping = [
("notify-success", "notifySuccess", False),
("notify-aborted", "notifyAborted", False),
@ -191,7 +189,7 @@ class HipChat(jenkins_jobs.modules.base.Base):
publishers = XML.SubElement(xml_parent, "publishers")
hippub = XML.SubElement(publishers, "jenkins.plugins.hipchat.HipChatNotifier")
if version >= pkg_resources.parse_version("0.1.8"):
if plugin_ver >= "0.1.8":
XML.SubElement(hippub, "buildServerUrl").text = self.jenkinsUrl
XML.SubElement(hippub, "sendAs").text = self.sendAs
else:

View File

@ -82,7 +82,6 @@ CFP Example:
.. literalinclude:: /../../tests/general/fixtures/project-maven003.yaml
"""
import pkg_resources
import xml.etree.ElementTree as XML
from jenkins_jobs.errors import InvalidAttributeError
@ -106,8 +105,7 @@ class Maven(jenkins_jobs.modules.base.Base):
return xml_parent
# determine version of plugin
plugin_info = self.registry.get_plugin_info("Maven Integration plugin")
version = pkg_resources.parse_version(plugin_info.get("version", "0"))
plugin_ver = self.registry.get_plugin_version("Maven Integration plugin")
if "root-module" in data["maven"]:
root_module = XML.SubElement(xml_parent, "rootModule")
@ -160,9 +158,7 @@ class Maven(jenkins_jobs.modules.base.Base):
XML.SubElement(xml_parent, "fingerprintingDisabled").text = str(
not data["maven"].get("automatic-fingerprinting", True)
).lower()
if version > pkg_resources.parse_version(
"0"
) and version < pkg_resources.parse_version("2.0.1"):
if plugin_ver < "2.0.1":
XML.SubElement(xml_parent, "perModuleEmail").text = str(
data.get("per-module-email", True)
).lower()

View File

@ -33,7 +33,6 @@ Example::
"""
import logging
import pkg_resources
import xml.etree.ElementTree as XML
from jenkins_jobs.errors import InvalidAttributeError
@ -479,10 +478,9 @@ def inject(registry, xml_parent, data):
helpers.convert_mapping_to_xml(info, data, mapping, fail_required=False)
# determine version of plugin
plugin_info = registry.get_plugin_info("Groovy")
version = pkg_resources.parse_version(plugin_info.get("version", "0"))
plugin_ver = registry.get_plugin_version("Groovy")
if version >= pkg_resources.parse_version("2.0.0"):
if plugin_ver >= "2.0.0":
secure_groovy_script = XML.SubElement(info, "secureGroovyScript")
mapping = [
("groovy-content", "script", None),
@ -654,17 +652,16 @@ def priority_sorter(registry, xml_parent, data):
/../../tests/properties/fixtures/priority_sorter002.yaml
:language: yaml
"""
plugin_info = registry.get_plugin_info("PrioritySorter")
version = pkg_resources.parse_version(plugin_info.get("version", "0"))
plugin_ver = registry.get_plugin_version("PrioritySorter")
if version >= pkg_resources.parse_version("3.0"):
if plugin_ver >= "3.0":
priority_sorter_tag = XML.SubElement(
xml_parent,
"jenkins.advancedqueue.jobinclusion." "strategy.JobInclusionJobProperty",
)
mapping = [("use", "useJobGroup", True), ("priority", "jobGroupName", None)]
elif version >= pkg_resources.parse_version("2.0"):
elif plugin_ver >= "2.0":
priority_sorter_tag = XML.SubElement(
xml_parent, "jenkins.advancedqueue.priority." "strategy.PriorityJobProperty"
)
@ -954,10 +951,9 @@ def slack(registry, xml_parent, data):
"""
logger = logging.getLogger(__name__)
plugin_info = registry.get_plugin_info("Slack Notification Plugin")
plugin_ver = pkg_resources.parse_version(plugin_info.get("version", "0"))
plugin_ver = registry.get_plugin_version("Slack Notification Plugin")
if plugin_ver >= pkg_resources.parse_version("2.0"):
if plugin_ver >= "2.0":
logger.warning("properties section is not used with plugin version >= 2.0")
mapping = (

View File

@ -26,8 +26,6 @@ the build is complete.
"""
import logging
import pkg_resources
import sys
import xml.etree.ElementTree as XML
import six
@ -1842,8 +1840,7 @@ def xunit(registry, xml_parent, data):
:language: yaml
"""
info = registry.get_plugin_info("xunit")
plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("xunit")
logger = logging.getLogger(__name__)
xunit = XML.SubElement(xml_parent, "xunit")
@ -1885,7 +1882,7 @@ def xunit(registry, xml_parent, data):
# Generate XML for each of the supported framework types
# Note: versions 3+ are now using the 'tools' sub-element instead of 'types'
if plugin_version < pkg_resources.parse_version("3.0.0"):
if plugin_ver < "3.0.0":
types_name = "types"
else:
types_name = "tools"
@ -2475,21 +2472,20 @@ def base_email_ext(registry, xml_parent, data, ttype):
xml_parent, "hudson.plugins.emailext.plugins.trigger." + ttype
)
info = registry.get_plugin_info("email-ext")
plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("email-ext")
email = XML.SubElement(trigger, "email")
if plugin_version < pkg_resources.parse_version("2.39"):
if plugin_ver < "2.39":
XML.SubElement(email, "recipientList").text = ""
XML.SubElement(email, "subject").text = "$PROJECT_DEFAULT_SUBJECT"
XML.SubElement(email, "body").text = "$PROJECT_DEFAULT_CONTENT"
if plugin_version >= pkg_resources.parse_version("2.39"):
if plugin_ver >= "2.39":
XML.SubElement(email, "replyTo").text = "$PROJECT_DEFAULT_REPLYTO"
XML.SubElement(email, "contentType").text = "project"
if "send-to" in data:
recipient_providers = None
if plugin_version < pkg_resources.parse_version("2.39"):
if plugin_ver < "2.39":
XML.SubElement(email, "sendToDevelopers").text = str(
"developers" in data["send-to"]
).lower()
@ -2572,7 +2568,7 @@ def base_email_ext(registry, xml_parent, data, ttype):
"hudson.plugins.emailext.plugins.recipients.UpstreamComitterRecipientProvider",
).text = ""
else:
if plugin_version < pkg_resources.parse_version("2.39"):
if plugin_ver < "2.39":
XML.SubElement(email, "sendToRequester").text = "false"
XML.SubElement(email, "sendToDevelopers").text = "false"
XML.SubElement(email, "includeCulprits").text = "false"
@ -2587,7 +2583,7 @@ def base_email_ext(registry, xml_parent, data, ttype):
if ttype == "ScriptTrigger":
XML.SubElement(trigger, "triggerScript").text = data["trigger-script"]
if plugin_version >= pkg_resources.parse_version("2.39"):
if plugin_ver >= "2.39":
mappings = [
("attachments", "attachmentsPattern", ""),
("attach-build-log", "attachBuildLog", False),
@ -2687,8 +2683,7 @@ def email_ext(registry, xml_parent, data):
xml_parent, "hudson.plugins.emailext.ExtendedEmailPublisher"
)
info = registry.get_plugin_info("email-ext")
plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("email-ext")
if "recipients" in data:
XML.SubElement(emailext, "recipientList").text = data["recipients"]
@ -2754,7 +2749,7 @@ def email_ext(registry, xml_parent, data):
("reply-to", "replyTo", "$DEFAULT_REPLYTO"),
]
if plugin_version >= pkg_resources.parse_version("2.39"):
if plugin_ver >= "2.39":
mappings.append(("from", "from", ""))
helpers.convert_mapping_to_xml(emailext, data, mappings, fail_required=True)
@ -3127,13 +3122,11 @@ def groovy_postbuild(registry, xml_parent, data):
)
data = {"script": data}
# There are incompatible changes, we need to know version
info = registry.get_plugin_info("groovy-postbuild")
# Note: Assume latest version of plugin is preferred config format
version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("groovy-postbuild")
# Version specific predicates
matrix_parent_support = version >= pkg_resources.parse_version("1.9")
security_plugin_support = version >= pkg_resources.parse_version("2.0")
extra_classpath_support = version >= pkg_resources.parse_version("1.6")
matrix_parent_support = plugin_ver >= "1.9"
security_plugin_support = plugin_ver >= "2.0"
extra_classpath_support = plugin_ver >= "1.6"
root_tag = "org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildRecorder"
groovy = XML.SubElement(xml_parent, root_tag)
@ -4495,16 +4488,14 @@ def postbuildscript(registry, xml_parent, data):
xml_parent, "org.jenkinsci.plugins.postbuildscript.PostBuildScript"
)
info = registry.get_plugin_info("postbuildscript")
# Note: Assume latest version of plugin is preferred config format
version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
if version >= pkg_resources.parse_version("2.0"):
plugin_ver = registry.get_plugin_version("postbuildscript")
if plugin_ver >= "2.0":
pbs_xml = XML.SubElement(pbs_xml, "config")
mapping = [("mark-unstable-if-failed", "markBuildUnstable", False)]
helpers.convert_mapping_to_xml(pbs_xml, data, mapping, fail_required=True)
if version >= pkg_resources.parse_version("2.0"):
if plugin_ver >= "2.0":
def add_execute_on(bs_data, result_xml):
valid_values = ("matrix", "axes", "both")
@ -6756,13 +6747,11 @@ def conditional_publisher(registry, xml_parent, data):
"dont-run": evaluation_classes_pkg + ".BuildStepRunner$DontRun",
}
plugin_info = registry.get_plugin_info("Flexible Publish Plugin")
# Note: Assume latest version of plugin is preferred config format
version = pkg_resources.parse_version(plugin_info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("Flexible Publish Plugin")
# Support for MatrixAggregator was added in v0.11
# See JENKINS-14494
has_matrix_aggregator = version >= pkg_resources.parse_version("0.11")
has_matrix_aggregator = plugin_ver >= "0.11"
for cond_action in data:
cond_publisher = XML.SubElement(publishers_tag, cond_publisher_tag)
@ -6792,7 +6781,7 @@ def conditional_publisher(registry, xml_parent, data):
# XML tag changed from publisher to publisherList in v0.13
# check the plugin version to determine further operations
use_publisher_list = version >= pkg_resources.parse_version("0.13")
use_publisher_list = plugin_ver >= "0.13"
if use_publisher_list:
action_parent = XML.SubElement(cond_publisher, "publisherList")
@ -7703,11 +7692,7 @@ def slack(registry, xml_parent, data):
logger = logging.getLogger(__name__)
plugin_info = registry.get_plugin_info("Slack Notification Plugin")
# Note: Assume latest version of plugin is preferred config format
plugin_ver = pkg_resources.parse_version(
plugin_info.get("version", str(sys.maxsize))
)
plugin_ver = registry.get_plugin_version("Slack Notification Plugin")
mapping = (
("team-domain", "teamDomain", ""),
@ -7746,10 +7731,10 @@ def slack(registry, xml_parent, data):
slack = XML.SubElement(xml_parent, "jenkins.plugins.slack.SlackNotifier")
if plugin_ver >= pkg_resources.parse_version("2.0"):
if plugin_ver >= "2.0":
mapping = mapping + mapping_20
if plugin_ver < pkg_resources.parse_version("2.0"):
if plugin_ver < "2.0":
for yaml_name, _, default_value in mapping:
# All arguments that don't have a default value are mandatory for
# the plugin to work as intended.

View File

@ -30,9 +30,7 @@ Example::
"""
import logging
import pkg_resources
import re
import sys
import xml.etree.ElementTree as XML
import six
@ -198,7 +196,7 @@ def build_gerrit_triggers(xml_parent, data, plugin_ver):
("exclude-private", "excludePrivateState", False),
("exclude-wip", "excludeWipState", False),
]
if plugin_ver >= pkg_resources.parse_version("2.32.0"):
if plugin_ver >= "2.32.0":
mapping.append(
(
"commit-message-contains-regex",
@ -241,7 +239,7 @@ def build_gerrit_skip_votes(xml_parent, data, plugin_ver):
("unstable", "onUnstable"),
("notbuilt", "onNotBuilt"),
]
if plugin_ver >= pkg_resources.parse_version("2.32.0"):
if plugin_ver >= "2.32.0":
outcomes.append(("aborted", "onAborted"))
skip_vote_node = XML.SubElement(xml_parent, "skipVote")
@ -252,7 +250,7 @@ def build_gerrit_skip_votes(xml_parent, data, plugin_ver):
def build_cancellation_policy(xml_parent, data, plugin_ver):
if plugin_ver >= pkg_resources.parse_version("2.32.0"):
if plugin_ver >= "2.32.0":
options = [
("abort-new-patchsets", "abortNewPatchsets"),
("abort-manual-patchsets", "abortManualPatchsets"),
@ -272,7 +270,7 @@ def build_cancellation_policy(xml_parent, data, plugin_ver):
def build_gerrit_parameter_modes(xml_parent, data, plugin_ver):
if plugin_ver < pkg_resources.parse_version("2.18.0"):
if plugin_ver < "2.18.0":
for parameter_name in (
"commit-message",
"name-and-email",
@ -661,10 +659,7 @@ def gerrit(registry, xml_parent, data):
gerrit_handle_legacy_configuration(data)
plugin_info = registry.get_plugin_info("Gerrit Trigger")
plugin_ver = pkg_resources.parse_version(
plugin_info.get("version", str(sys.maxsize))
)
plugin_ver = registry.get_plugin_version("Gerrit Trigger")
projects = data.get("projects", [])
gtrig = XML.SubElement(
@ -797,9 +792,7 @@ def gerrit(registry, xml_parent, data):
XML.SubElement(gtrig, "triggerInformationAction").text = str(
data.get("trigger-information-action", "")
)
if (plugin_ver >= pkg_resources.parse_version("2.11.0")) and (
plugin_ver < pkg_resources.parse_version("2.14.0")
):
if plugin_ver >= "2.11.0" and plugin_ver < "2.14.0":
XML.SubElement(gtrig, "allowTriggeringUnreviewedPatches").text = str(
data.get("trigger-for-unreviewed-patches", False)
).lower()
@ -848,7 +841,7 @@ def gerrit(registry, xml_parent, data):
),
]
if plugin_ver >= pkg_resources.parse_version("2.31.0"):
if plugin_ver >= "2.31.0":
votes.append(
(
"gerrit-build-aborted-verified-value",
@ -876,7 +869,7 @@ def gerrit(registry, xml_parent, data):
("custom-url", "customUrl", ""),
("server-name", "serverName", "__ANY__"),
]
if plugin_ver >= pkg_resources.parse_version("2.31.0"):
if plugin_ver >= "2.31.0":
message_mappings.append(("aborted-message", "buildAbortedMessage", ""))
helpers.convert_mapping_to_xml(gtrig, data, message_mappings, fail_required=True)
@ -1573,14 +1566,9 @@ def gitlab_merge_request(registry, xml_parent, data):
xml_parent, "org.jenkinsci.plugins.gitlab." "GitlabBuildTrigger"
)
plugin_info = registry.get_plugin_info("Gitlab Merge Request Builder")
plugin_ver = registry.get_plugin_version("Gitlab Merge Request Builder")
# Note: Assume latest version of plugin is preferred config format
plugin_ver = pkg_resources.parse_version(
plugin_info.get("version", str(sys.maxsize))
)
if plugin_ver >= pkg_resources.parse_version("2.0.0"):
if plugin_ver >= "2.0.0":
mapping = [
("cron", "spec", None),
("project-path", "projectPath", None),
@ -1725,15 +1713,11 @@ def gitlab(registry, xml_parent, data):
xml_parent, "com.dabsquared.gitlabjenkins.GitLabPushTrigger"
)
plugin_info = registry.get_plugin_info("GitLab Plugin")
# Note: Assume latest version of plugin is preferred config format
plugin_ver = pkg_resources.parse_version(
plugin_info.get("version", str(sys.maxsize))
)
plugin_ver = registry.get_plugin_version("GitLab Plugin")
valid_merge_request = ["never", "source", "both"]
if plugin_ver >= pkg_resources.parse_version("1.1.26"):
if plugin_ver >= "1.1.26":
mapping = [
(
"trigger-open-merge-request-push",
@ -1749,7 +1733,7 @@ def gitlab(registry, xml_parent, data):
]
helpers.convert_mapping_to_xml(gitlab, data, mapping, fail_required=True)
if plugin_ver < pkg_resources.parse_version("1.2.0"):
if plugin_ver < "1.2.0":
if data.get("branch-filter-type", "") == "All":
data["branch-filter-type"] = ""
valid_filters = ["", "NameBasedFilter", "RegexBasedFilter"]

View File

@ -23,8 +23,6 @@ Wrappers can alter the way the build is run as well as the build output.
"""
import logging
import pkg_resources
import sys
import xml.etree.ElementTree as XML
from jenkins_jobs.errors import InvalidAttributeError
@ -336,12 +334,9 @@ def timeout(registry, xml_parent, data):
prefix = "hudson.plugins.build__timeout."
twrapper = XML.SubElement(xml_parent, prefix + "BuildTimeoutWrapper")
plugin_info = registry.get_plugin_info("Build Timeout")
if "version" not in plugin_info:
plugin_info = registry.get_plugin_info("Jenkins build timeout plugin")
version = plugin_info.get("version", None)
if version:
version = pkg_resources.parse_version(version)
plugin_ver = registry.get_plugin_version(
"Build Timeout", "Jenkins build timeout plugin"
)
valid_strategies = [
"absolute",
@ -353,7 +348,7 @@ def timeout(registry, xml_parent, data):
# NOTE(toabctl): if we don't know the version assume that we
# use a newer version of the plugin
if not version or version >= pkg_resources.parse_version("1.14"):
if plugin_ver >= "1.14":
strategy = data.get("type", "absolute")
if strategy not in valid_strategies:
InvalidAttributeError("type", strategy, valid_strategies)
@ -438,7 +433,7 @@ def timeout(registry, xml_parent, data):
all_actions = ["fail", "abort"]
actions = []
if version is not None and version >= pkg_resources.parse_version("1.17"):
if plugin_ver >= "1.17":
all_actions.append("abort-and-restart")
for action in all_actions:
@ -966,12 +961,9 @@ def rvm_env(registry, xml_parent, data):
ro_class = "Jenkins::Plugin::Proxies::BuildWrapper"
plugin_info = registry.get_plugin_info("RVM Plugin")
plugin_ver = pkg_resources.parse_version(
plugin_info.get("version", str(sys.maxsize))
)
plugin_ver = registry.get_plugin_version("RVM Plugin")
if plugin_ver >= pkg_resources.parse_version("0.5"):
if plugin_ver >= "0.5":
ro_class = "Jenkins::Tasks::BuildWrapperProxy"
ro = XML.SubElement(rpo, "ruby-object", {"ruby-class": ro_class, "pluginid": "rvm"})
@ -1821,8 +1813,7 @@ def pre_scm_buildstep(registry, xml_parent, data):
:language: yaml
"""
# Get plugin information to maintain backwards compatibility
info = registry.get_plugin_info("preSCMbuildstep")
version = pkg_resources.parse_version(info.get("version", "0"))
plugin_ver = registry.get_plugin_version("preSCMbuildstep")
bsp = XML.SubElement(
xml_parent, "org.jenkinsci.plugins.preSCMbuildstep." "PreSCMBuildStepsWrapper"
@ -1833,7 +1824,7 @@ def pre_scm_buildstep(registry, xml_parent, data):
for step in stepList:
for edited_node in create_builders(registry, step):
bs.append(edited_node)
if version >= pkg_resources.parse_version("0.3") and not isinstance(data, list):
if plugin_ver >= "0.3" and not isinstance(data, list):
mapping = [("failOnError", "failOnError", False)]
helpers.convert_mapping_to_xml(bsp, data, mapping, fail_required=True)
@ -2049,10 +2040,7 @@ def ssh_agent_credentials(registry, xml_parent, data):
logger = logging.getLogger(__name__)
plugin_info = registry.get_plugin_info("SSH Agent Plugin")
plugin_ver = pkg_resources.parse_version(
plugin_info.get("version", str(sys.maxsize))
)
plugin_ver = registry.get_plugin_version("SSH Agent Plugin")
entry_xml = XML.SubElement(
xml_parent, "com.cloudbees.jenkins.plugins.sshagent.SSHAgentBuildWrapper"
@ -2063,7 +2051,7 @@ def ssh_agent_credentials(registry, xml_parent, data):
user_list = list()
if "users" in data:
user_list += data["users"]
if plugin_ver >= pkg_resources.parse_version("1.5.0"):
if plugin_ver >= "1.5.0":
user_parent_entry_xml = XML.SubElement(entry_xml, "credentialIds")
xml_key = "string"
if "user" in data:
@ -2288,8 +2276,8 @@ def nodejs_installator(registry, xml_parent, data):
xml_parent, "jenkins.plugins.nodejs." "NodeJSBuildWrapper"
)
version = registry.get_plugin_info("nodejs").get("version", "0")
npm_node.set("plugin", "nodejs@" + version)
plugin_ver = registry.get_plugin_version("nodejs", default="0")
npm_node.set("plugin", "nodejs@" + plugin_ver)
mapping = [("name", "nodeJSInstallationName", None)]
helpers.convert_mapping_to_xml(npm_node, data, mapping, fail_required=True)
@ -2601,11 +2589,9 @@ def artifactory_generic(registry, xml_parent, data):
helpers.artifactory_common_details(details, data)
# Get plugin information to maintain backwards compatibility
info = registry.get_plugin_info("artifactory")
# Note: Assume latest version of plugin is preferred config format
version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
plugin_ver = registry.get_plugin_version("artifactory")
if version >= pkg_resources.parse_version("2.3.0"):
if plugin_ver >= "2.3.0":
deploy_release_repo = XML.SubElement(details, "deployReleaseRepository")
mapping = [
("key-from-text", "keyFromText", ""),

View File

@ -19,11 +19,13 @@ import inspect
import logging
import operator
import pkg_resources
import re
import sys
import types
from pkg_resources.extern.packaging.version import InvalidVersion
from six import PY2
from jenkins.plugins import PluginVersion
from jenkins_jobs.errors import JenkinsJobsException
from jenkins_jobs.expander import Expander, ParamsExpander
from jenkins_jobs.yaml_objects import BaseYamlObject
@ -50,9 +52,10 @@ class ModuleRegistry(object):
self._params_expander = ParamsExpander(jjb_config)
if plugins_list is None:
self.plugins_dict = {}
self._plugin_version = {}
else:
self.plugins_dict = self._get_plugins_info_dict(plugins_list)
# PluginVersion by short and long plugin name.
self._plugin_version = self._get_plugins_versions(plugins_list)
for entrypoint in pkg_resources.iter_entry_points(group="jenkins_jobs.modules"):
Mod = entrypoint.load()
@ -63,60 +66,38 @@ class ModuleRegistry(object):
self.modules_by_component_type[mod.component_type] = entrypoint
@staticmethod
def _get_plugins_info_dict(plugins_list):
def mutate_plugin_info(plugin_info):
"""
We perform mutations on a single member of plugin_info here, then
return a dictionary with the longName and shortName of the plugin
mapped to its plugin info dictionary.
"""
version = plugin_info.get("version", "0")
plugin_info["version"] = re.sub(
r"(.*)-(?:SNAPSHOT|BETA).*", r"\g<1>.preview", version
)
def _get_plugins_versions(plugins_list):
plugin_version = {}
if isinstance(
pkg_resources.parse_version(plugin_info["version"]),
pkg_resources.extern.packaging.version.LegacyVersion,
):
plugin_info["version"] = plugin_info["version"].replace("-", "+")
if isinstance(
pkg_resources.parse_version(plugin_info["version"]),
pkg_resources.extern.packaging.version.LegacyVersion,
):
plugin_name = plugin_info.get(
"shortName", plugin_info.get("longName", None)
)
for plugin_info in plugins_list:
short_name = plugin_info.get("shortName")
long_name = plugin_info.get("longName")
version = plugin_info["version"]
if version == None: # noqa: E711; should call PluginInfo.__eq__.
# Ensure that plugin_info always has version and it is instance of PluginVersion.
plugin_info["version"] = str(sys.maxsize)
version = plugin_info["version"]
try:
pkg_resources.parse_version(version)
except InvalidVersion:
plugin_name = short_name or long_name
if plugin_name:
logger.warning(
"Version %s for plugin %s is being treated as a LegacyVersion"
% (plugin_info["version"], plugin_name)
"Version %s for plugin %s does not conform to PEP440",
version,
plugin_name,
)
else:
logger.warning(
"Version %s is being treated as a LegacyVersion"
% plugin_info["version"]
)
logger.warning("Version %s does not conform to PEP440", version)
aliases = []
for key in ["longName", "shortName"]:
value = plugin_info.get(key, None)
if value is not None:
aliases.append(value)
if short_name:
plugin_version[short_name] = version
if long_name:
plugin_version[long_name] = version
plugin_info_dict = {}
for name in aliases:
plugin_info_dict[name] = plugin_info
return plugin_info_dict
list_of_dicts = [mutate_plugin_info(v) for v in plugins_list]
plugins_info_dict = {}
for d in list_of_dicts:
plugins_info_dict.update(d)
return plugins_info_dict
return plugin_version
@staticmethod
def _filter_kwargs(func, **kwargs):
@ -126,17 +107,21 @@ class ModuleRegistry(object):
del kwargs[name]
return kwargs
def get_plugin_info(self, plugin_name):
"""Provide information about plugins within a module's impl of Base.gen_xml.
def get_plugin_version(self, plugin_name, alt_plugin_name=None, default=None):
"""Provide plugin version to be used from a module's impl of Base.gen_xml.
The return value is a dictionary with data obtained directly from a
running Jenkins instance.
The return value is a plugin version obtained directly from a running
Jenkins instance.
This allows module authors to differentiate generated XML output based
on information such as specific plugin versions.
on it.
:arg str plugin_name: Either the shortName or longName of a plugin
as see in a query that looks like:
as seen in a query that looks like:
``http://<jenkins-hostname>/pluginManager/api/json?pretty&depth=2``
:arg str alt_plugin_name: Alternative plugin name. Used if plugin_name
is missing in plugin list.
:arg str default: Default value. Used if plugin name is missing in
plugin list.
During a 'test' run, it is possible to override JJB's query to a live
Jenkins instance by passing it a path to a file containing a YAML list
@ -151,7 +136,19 @@ class ModuleRegistry(object):
.. literalinclude:: /../../tests/cmd/fixtures/plugins-info.yaml
"""
return self.plugins_dict.get(plugin_name, {})
try:
return self._plugin_version[plugin_name]
except KeyError:
pass
if alt_plugin_name:
try:
return self._plugin_version[alt_plugin_name]
except KeyError:
pass
if default is not None:
return PluginVersion(default)
# Assume latest version of plugin is preferred config format.
return PluginVersion(str(sys.maxsize))
def registerHandler(self, category, name, method):
cat_dict = self.handlers.get(category, {})

View File

@ -1,12 +1,11 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
setuptools<=65.7.0
six>=1.9.0 # MIT
PyYAML>=3.13 # MIT
pbr>=1.8 # Apache-2.0
stevedore>=1.17.1,<2; python_version < '3.0' # Apache-2.0
stevedore>=1.17.1; python_version >= '3.0' # Apache-2.0
python-jenkins>=0.4.15
python-jenkins>=1.8.2
fasteners
Jinja2

View File

@ -6,6 +6,7 @@ from pathlib import Path
import pytest
import yaml
from jenkins.plugins import Plugin
from jenkins_jobs.alphanum import AlphanumSort
from jenkins_jobs.config import JJBConfig
from jenkins_jobs.loader import Loader
@ -72,7 +73,8 @@ def input(scenario, jjb_config):
def plugins_info(scenario):
if not scenario.plugins_info_path.exists():
return None
return yaml.safe_load(scenario.plugins_info_path.read_text())
plugin_dict_list = yaml.safe_load(scenario.plugins_info_path.read_text())
return [Plugin(**plugin_dict) for plugin_dict in plugin_dict_list]
@pytest.fixture

View File

@ -0,0 +1,3 @@
- longName: 'Jenkins HipChat Plugin'
shortName: 'hipchat'
version: "0.1"

View File

@ -155,6 +155,7 @@ echo &quot;Doing somethin cool with zsh&quot;
</info>
</EnvInjectBuilder>
</buildSteps>
<failOnError>false</failOnError>
</org.jenkinsci.plugins.preSCMbuildstep.PreSCMBuildStepsWrapper>
<com.michelin.cio.hudson.plugins.copytoslave.CopyToSlaveBuildWrapper>
<includes>file1,file2*.txt</includes>

View File

@ -1,9 +1,10 @@
import pkg_resources
import sys
from collections import namedtuple
from operator import attrgetter
import pytest
from jenkins.plugins import Plugin, PluginVersion
from jenkins_jobs.config import JJBConfig
from jenkins_jobs.registry import ModuleRegistry
@ -35,6 +36,18 @@ scenarios = [
Scenario("s17", v1="1.0.1-1.v1", op="__lt__", v2="1.0.2"),
Scenario("s18", v1="1.0.2-1.v1", op="__gt__", v2="1.0.1"),
Scenario("s19", v1="1.0.2-1.v1", op="__gt__", v2="1.0.1-2"),
# 'Groovy' plugin in 'inject' property.
Scenario("s20", v1="453.vcdb_a_c5c99890", op="__ge__", v2="2.0.0"),
# 'postbuildscript' plugin in 'postbuildscript' publisher.
Scenario("s21", v1="3.2.0-460.va_fda_0fa_26720", op="__ge__", v2="2.0"),
# Same, from story: 2009943.
Scenario("s22", v1="3.1.0-375.v3db_cd92485e1", op="__ge__", v2="2.0"),
# 'Slack Notification Plugin' in 'slack' publisher, from story: 2009819.
Scenario("s23", v1="602.v0da_f7458945d", op="__ge__", v2="2.0"),
# 'preSCMbuildstep' plugin in 'pre_scm_buildstep' wrapper.
Scenario("s24", v1="44.v6ef4fd97f56e", op="__ge__", v2="0.3"),
# 'SSH Agent Plugin' plugin in 'ssh_agent_credentials' wrapper.
Scenario("s25", v1="295.v9ca_a_1c7cc3a_a_", op="__ge__", v2="1.5.0"),
]
@ -66,65 +79,69 @@ def registry(config, scenario):
"version": scenario.v1,
},
]
return ModuleRegistry(config, plugin_info)
return ModuleRegistry(config, [Plugin(**d) for d in plugin_info])
def test_get_plugin_info_dict(registry):
def test_get_plugin_version_by_short_name(scenario, registry):
"""
The goal of this test is to validate that the plugin_info returned by
ModuleRegistry.get_plugin_info is a dictionary whose key 'shortName' is
the same value as the string argument passed to
ModuleRegistry.get_plugin_info.
Plugin version should be available by it's short name
"""
plugin_name = "JankyPlugin1"
plugin_info = registry.get_plugin_info(plugin_name)
version = registry.get_plugin_version(plugin_name)
assert isinstance(plugin_info, dict)
assert plugin_info["shortName"] == plugin_name
assert isinstance(version, PluginVersion)
assert version == scenario.v1
def test_get_plugin_info_dict_using_longName(registry):
def test_get_plugin_version_by_long_name(scenario, registry):
"""
The goal of this test is to validate that the plugin_info returned by
ModuleRegistry.get_plugin_info is a dictionary whose key 'longName' is
the same value as the string argument passed to
ModuleRegistry.get_plugin_info.
Plugin version should be available by it's long name
"""
plugin_name = "Blah Blah Blah Plugin"
plugin_info = registry.get_plugin_info(plugin_name)
plugin_name = "Not A Real Plugin"
version = registry.get_plugin_version(plugin_name)
assert isinstance(plugin_info, dict)
assert plugin_info["longName"] == plugin_name
assert isinstance(version, PluginVersion)
assert version == scenario.v1
def test_get_plugin_info_dict_no_plugin(registry):
def test_get_plugin_version_by_alternative_name(scenario, registry):
version = registry.get_plugin_version("Non-existent name", "Not A Real Plugin")
assert version == scenario.v1
def test_get_plugin_version_default_value(registry):
version = registry.get_plugin_version("Non-existent name", default="1.2.3")
assert isinstance(version, PluginVersion)
assert version == "1.2.3"
def test_get_plugin_version_for_missing_plugin(registry):
"""
The goal of this test case is to validate the behavior of
ModuleRegistry.get_plugin_info when the given plugin cannot be found in
ModuleRegistry.get_plugin_version when the given plugin cannot be found in
ModuleRegistry's internal representation of the plugins_info.
"""
plugin_name = "PluginDoesNotExist"
plugin_info = registry.get_plugin_info(plugin_name)
version = registry.get_plugin_version(plugin_name)
assert isinstance(plugin_info, dict)
assert plugin_info == {}
assert isinstance(version, PluginVersion)
assert version == str(sys.maxsize)
def test_get_plugin_info_dict_no_version(registry):
def test_get_plugin_version_for_missing_version(registry):
"""
The goal of this test case is to validate the behavior of
ModuleRegistry.get_plugin_info when the given plugin shortName returns
ModuleRegistry.get_plugin_version when the given plugin shortName returns
plugin_info dict that has no version string. In a sane world where
plugin frameworks like Jenkins' are sane this should never happen, but
I am including this test and the corresponding default behavior
because, well, it's Jenkins.
"""
plugin_name = "HerpDerpPlugin"
plugin_info = registry.get_plugin_info(plugin_name)
version = registry.get_plugin_version(plugin_name)
assert isinstance(plugin_info, dict)
assert plugin_info["shortName"] == plugin_name
assert plugin_info["version"] == "0"
assert isinstance(version, PluginVersion)
assert version == str(sys.maxsize)
def test_plugin_version_comparison(registry, scenario):
@ -134,13 +151,12 @@ def test_plugin_version_comparison(registry, scenario):
where 'op' is the equality operator defined for the scenario.
"""
plugin_name = "JankyPlugin1"
plugin_info = registry.get_plugin_info(plugin_name)
v1 = plugin_info.get("version")
v1 = registry.get_plugin_version(plugin_name)
op = getattr(pkg_resources.parse_version(v1), scenario.op)
test = op(pkg_resources.parse_version(scenario.v2))
op = getattr(v1, scenario.op)
test = op(scenario.v2)
assert test, (
f"Unexpectedly found {v1} {scenario.v2} {scenario.op} == False"
f"Unexpectedly found {v1} {scenario.op} {scenario.v2} == False"
" when comparing versions!"
)

View File

@ -153,6 +153,7 @@ echo &quot;Doing somethin cool with zsh&quot;
</info>
</EnvInjectBuilder>
</buildSteps>
<failOnError>false</failOnError>
</org.jenkinsci.plugins.preSCMbuildstep.PreSCMBuildStepsWrapper>
<com.michelin.cio.hudson.plugins.copytoslave.CopyToSlaveBuildWrapper>
<includes>file1,file2*.txt</includes>

View File

@ -14,6 +14,9 @@
<EnvInjectJobProperty>
<info>
<loadFilesFromMaster>false</loadFilesFromMaster>
<secureGroovyScript>
<sandbox>false</sandbox>
</secureGroovyScript>
</info>
<on>true</on>
<keepJenkinsSystemVariables>true</keepJenkinsSystemVariables>

View File

@ -11,6 +11,9 @@
<EnvInjectJobProperty>
<info>
<loadFilesFromMaster>false</loadFilesFromMaster>
<secureGroovyScript>
<sandbox>false</sandbox>
</secureGroovyScript>
</info>
<on>true</on>
<keepJenkinsSystemVariables>true</keepJenkinsSystemVariables>
@ -55,6 +58,7 @@ echo &quot;Doing somethin cool with zsh&quot;
</info>
</EnvInjectBuilder>
</buildSteps>
<failOnError>false</failOnError>
</org.jenkinsci.plugins.preSCMbuildstep.PreSCMBuildStepsWrapper>
<com.michelin.cio.hudson.plugins.copytoslave.CopyToSlaveBuildWrapper>
<includes>file1,file2*.txt</includes>

View File

@ -20,6 +20,7 @@ echo &quot;Unicode! ☃&quot;
</command>
</hudson.tasks.Shell>
</buildSteps>
<failOnError>false</failOnError>
</org.jenkinsci.plugins.preSCMbuildstep.PreSCMBuildStepsWrapper>
</buildWrappers>
</project>

View File

@ -11,6 +11,9 @@
<EnvInjectJobProperty>
<info>
<loadFilesFromMaster>false</loadFilesFromMaster>
<secureGroovyScript>
<sandbox>false</sandbox>
</secureGroovyScript>
</info>
<on>true</on>
<keepJenkinsSystemVariables>true</keepJenkinsSystemVariables>
@ -55,6 +58,7 @@ echo &quot;Doing somethin cool with zsh&quot;
</info>
</EnvInjectBuilder>
</buildSteps>
<failOnError>false</failOnError>
</org.jenkinsci.plugins.preSCMbuildstep.PreSCMBuildStepsWrapper>
<com.michelin.cio.hudson.plugins.copytoslave.CopyToSlaveBuildWrapper>
<includes>file1,file2*.txt</includes>

View File

@ -13,6 +13,9 @@
<propertiesContent>FILE_LIST=/path/to/file1,/path/to/file2,/path/to/file3,/path/to/file4,/path/to/file5,/path/to/file6,/path/to/file7,/path/to/file8,/path/to/file9,/path/to/file10,/path/to/file11,/path/to/file12,/path/to/file13,/path/to/file14,/path/to/file15,/path/to/file16,/path/to/file17,/path/to/file18,/path/to/file19,/path/to/file20
</propertiesContent>
<loadFilesFromMaster>false</loadFilesFromMaster>
<secureGroovyScript>
<sandbox>false</sandbox>
</secureGroovyScript>
</info>
<on>true</on>
<keepJenkinsSystemVariables>true</keepJenkinsSystemVariables>
@ -48,6 +51,9 @@ echo &quot;${INPUT_DATA}&quot;
<propertiesContent>FILE_LIST=/another/different/path/to/file1,/another/different/path/to/file2,/another/different/path/to/file3,/another/different/path/to/file4,/another/different/path/to/file5,/another/different/path/to/file6,/another/different/path/to/file7,/another/different/path/to/file8,/another/different/path/to/file9,/another/different/path/to/file10,/another/different/path/to/file11,/another/different/path/to/file12,/another/different/path/to/file13,/another/different/path/to/file14,/another/different/path/to/file15,/another/different/path/to/file16,/another/different/path/to/file17,/another/different/path/to/file18,/another/different/path/to/file19,/another/different/path/to/file20
</propertiesContent>
<loadFilesFromMaster>false</loadFilesFromMaster>
<secureGroovyScript>
<sandbox>false</sandbox>
</secureGroovyScript>
</info>
<on>true</on>
<keepJenkinsSystemVariables>true</keepJenkinsSystemVariables>

View File

@ -20,6 +20,7 @@ echo &quot;Unicode! ☃&quot;
</command>
</hudson.tasks.Shell>
</buildSteps>
<failOnError>false</failOnError>
</org.jenkinsci.plugins.preSCMbuildstep.PreSCMBuildStepsWrapper>
</buildWrappers>
</project>