1# Copyright 2012 Hewlett-Packard Development Company, L.P.
2# Copyright 2012 Varnish Software AS
3# Copyright 2013-2014 Antoine "hashar" Musso
4# Copyright 2013-2014 Wikimedia Foundation Inc.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18
19"""
20Publishers define actions that the Jenkins job should perform after
21the build is complete.
22
23**Component**: publishers
24  :Macro: publisher
25  :Entry Point: jenkins_jobs.publishers
26"""
27
28import logging
29import pkg_resources
30import sys
31import xml.etree.ElementTree as XML
32
33import six
34
35from jenkins_jobs.errors import InvalidAttributeError
36from jenkins_jobs.errors import JenkinsJobsException
37from jenkins_jobs.errors import MissingAttributeError
38import jenkins_jobs.modules.base
39from jenkins_jobs.modules import hudson_model
40import jenkins_jobs.modules.helpers as helpers
41
42logger = logging.getLogger(__name__)
43
44
45def influx_db(registry, xml_parent, data):
46    """yaml: influx-db
47    Requires the Jenkins :jenkins-plugins: `Influx DB
48    <influxdb>`.
49    """
50
51    influx_db = XML.SubElement(
52        xml_parent,
53        "jenkinsci.plugins.influxdb.InfluxDbPublisher",
54        {"plugin": "influx-db"},
55    )
56
57    mapping = [
58        ("selected-target", "selectedTarget", ""),
59        ("custom-project-name", "customProjectName", ""),
60        ("custom-prefix", "customPrefix", ""),
61        ("jenkins-env-parameter-field", "jenkinsEnvParameterField", ""),
62        ("jenkins-env-parameter-tag", "jenkinsEnvParameterTag", ""),
63    ]
64
65    helpers.convert_mapping_to_xml(influx_db, data, mapping, fail_required=True)
66
67
68def allure(registry, xml_parent, data):
69    """yaml: allure
70
71    Publish Allure report for the build. Requires the Jenkins
72    :jenkins-plugins:`Allure Plugin <allure-jenkins-plugin>`.
73
74    :arg str jdk: String identifier for a JDK installation in Jenkins
75    :arg str commandline: String identifier for a Allure-commandline tool
76        installation
77    :arg str report-build-policy: String identifier for a report build
78        policy enum. Possible values: 'ALWAYS', 'UNSTABLE', 'UNSUCCESSFUL'.
79        (By default is 'ALWAYS')
80    :arg bool include-properties: Flag to include specified properties
81    :arg list results-paths: List of results directories
82    :arg list properties: List of key:value property pairs
83
84    Minimal Example:
85
86        .. literalinclude::
87           /../../tests/publishers/fixtures/allure-minimal.yaml
88           :language: yaml
89
90    Full Example:
91
92        .. literalinclude:: /../../tests/publishers/fixtures/allure-full.yaml
93           :language: yaml
94
95    """
96    publisher_class = "ru.yandex.qatools.allure.jenkins.AllureReportPublisher"
97    property_class = "ru.yandex.qatools.allure.jenkins.config.PropertyConfig"
98    results_class = "ru.yandex.qatools.allure.jenkins.config.ResultsConfig"
99
100    allure_publisher = XML.SubElement(xml_parent, publisher_class)
101    allure_publisher.set("plugin", "allure-jenkins-plugin")
102    config = XML.SubElement(allure_publisher, "config")
103
104    results = XML.SubElement(config, "results")
105    if "results-paths" in data:
106        for results_path in data["results-paths"]:
107            entry = XML.SubElement(results, results_class)
108            path = XML.SubElement(entry, "path")
109            path.text = results_path["path"]
110
111    properties = XML.SubElement(config, "properties")
112    if "properties" in data:
113        property_mapping = [("key", "key", None), ("value", "value", None)]
114        for prop in data["properties"]:
115            entry = XML.SubElement(properties, property_class)
116            helpers.convert_mapping_to_xml(
117                entry, prop, property_mapping, fail_required=True
118            )
119    else:
120        properties.set("class", "empty-list")
121
122    mapping = [
123        ("jdk", "jdk", ""),
124        ("commandline", "commandline", ""),
125        (
126            "report-build-policy",
127            "reportBuildPolicy",
128            "ALWAYS",
129            ["ALWAYS", "UNSTABLE", "UNSUCCESSFUL"],
130        ),
131        ("include-properties", "includeProperties", False),
132    ]
133
134    helpers.convert_mapping_to_xml(config, data, mapping, fail_required=True)
135
136
137def archive(registry, xml_parent, data):
138    """yaml: archive
139    Archive build artifacts
140
141    :arg str artifacts: path specifier for artifacts to archive
142    :arg str excludes: path specifier for artifacts to exclude (optional)
143    :arg bool latest-only: only keep the artifacts from the latest
144        successful build
145    :arg bool allow-empty:  pass the build if no artifacts are
146        found (default false)
147    :arg bool only-if-success: archive artifacts only if build is successful
148        (default false)
149    :arg bool fingerprint: fingerprint all archived artifacts (default false)
150    :arg bool default-excludes: This option allows you to enable or disable the
151        default Ant exclusions. (default true)
152    :arg bool case-sensitive: Treat include and exclude patterns as case
153        sensitive. (default true)
154
155    Example:
156
157        .. literalinclude::  /../../tests/publishers/fixtures/archive001.yaml
158           :language: yaml
159    """
160    archiver = XML.SubElement(xml_parent, "hudson.tasks.ArtifactArchiver")
161    mapping = [
162        ("artifacts", "artifacts", None),
163        ("allow-empty", "allowEmptyArchive", False),
164        ("only-if-success", "onlyIfSuccessful", False),
165        ("fingerprint", "fingerprint", False),
166        ("default-excludes", "defaultExcludes", True),
167        ("case-sensitive", "caseSensitive", True),
168        ("latest-only", "latestOnly", False),
169    ]
170
171    if "excludes" in data:
172        mapping.append(("excludes", "excludes", None))
173    helpers.convert_mapping_to_xml(archiver, data, mapping, fail_required=True)
174
175
176def blame_upstream(registry, xml_parent, data):
177    """yaml: blame-upstream
178    Notify upstream committers when build fails
179
180    Requires the Jenkins :jenkins-github:`Blame Upstream Committers Plugin
181    <blame-upstream-commiters-plugin>`.
182
183    Example:
184
185        .. literalinclude::  /../../tests/publishers/fixtures/blame001.yaml
186           :language: yaml
187    """
188
189    XML.SubElement(
190        xml_parent,
191        "hudson.plugins.blame__upstream__commiters." "BlameUpstreamCommitersPublisher",
192    )
193
194
195def jclouds(registry, xml_parent, data):
196    """yaml: jclouds
197    JClouds Cloud Storage Settings provides a way to store artifacts on
198    JClouds supported storage providers. Requires the Jenkins
199    :jenkins-plugins:`JClouds Plugin <jclouds-jenkins>`.
200
201    JClouds Cloud Storage Settings must be configured for the Jenkins instance.
202
203    :arg str profile: preconfigured storage profile (required)
204    :arg str files: files to upload (regex) (required)
205    :arg str basedir: the source file path (relative to workspace, Optional)
206    :arg str container: the destination container name (required)
207    :arg bool hierarchy: keep hierarchy (default false)
208
209    Example:
210
211        .. literalinclude::  /../../tests/publishers/fixtures/jclouds001.yaml
212
213    """
214
215    deployer = XML.SubElement(
216        xml_parent, "jenkins.plugins.jclouds.blobstore." "BlobStorePublisher"
217    )
218    entries = XML.SubElement(deployer, "entries")
219    deployer_entry = XML.SubElement(
220        entries, "jenkins.plugins.jclouds.blobstore." "BlobStoreEntry"
221    )
222    deployer_mapping = [("profile", "profileName", None)]
223    helpers.convert_mapping_to_xml(deployer, data, deployer_mapping, fail_required=True)
224    try:
225        XML.SubElement(deployer_entry, "container").text = data["container"]
226        XML.SubElement(deployer_entry, "sourceFile").text = data["files"]
227    except KeyError as e:
228        raise JenkinsJobsException("blobstore requires '%s' to be set" % e.args[0])
229    deployer_entry_mapping = [
230        ("hierarchy", "keepHierarchy", False),
231        ("basedir", "path", ""),
232    ]
233    helpers.convert_mapping_to_xml(
234        deployer_entry, data, deployer_entry_mapping, fail_required=True
235    )
236
237
238def javadoc(registry, xml_parent, data):
239    """yaml: javadoc
240    Publish Javadoc
241    Requires the Jenkins :jenkins-plugins:`Javadoc Plugin <javadoc>`.
242
243    :arg str directory: Directory relative to the root of the workspace,
244      such as 'myproject/build/javadoc' (optional)
245    :arg bool keep-all-successful: When true, it will retain Javadoc for each
246      successful build. This allows you to browse Javadoc for older builds,
247      at the expense of additional disk space requirement. If false, it will
248      only keep the latest Javadoc, so older Javadoc will be overwritten as
249      new builds succeed. (default false)
250
251    Example:
252
253        .. literalinclude::  /../../tests/publishers/fixtures/javadoc001.yaml
254           :language: yaml
255    """
256
257    root = XML.SubElement(xml_parent, "hudson.tasks.JavadocArchiver")
258
259    mapping = [
260        ("directory", "javadocDir", None),
261        ("keep-all-successful", "keepAll", False),
262    ]
263    helpers.convert_mapping_to_xml(root, data, mapping, fail_required=False)
264
265
266def jdepend(registry, xml_parent, data):
267    """yaml: jdepend
268    Publish jdepend report
269    Requires the :jenkins-plugins:`JDepend Plugin <jdepend>`.
270
271    :arg str file: path to jdepend file (required)
272
273    Example:
274
275        .. literalinclude::  /../../tests/publishers/fixtures/jdepend001.yaml
276           :language: yaml
277    """
278    jdepend = XML.SubElement(xml_parent, "hudson.plugins.jdepend.JDependRecorder")
279    mapping = [("file", "configuredJDependFile", None)]
280    helpers.convert_mapping_to_xml(jdepend, data, mapping, fail_required=True)
281
282
283def hue_light(registry, xml_parent, data):
284    """yaml: hue-light
285    This plugin shows the state of your builds using the awesome Philips hue
286    lights.
287
288    Requires the Jenkins :jenkins-plugins:`hue-light Plugin
289    <hue-light>`.
290
291    :arg int light-id: ID of light. Define multiple lights by a comma as a
292        separator (required)
293    :arg str pre-build: Colour of building state (default 'blue')
294    :arg str good-build: Colour of successful state (default 'green')
295    :arg str unstable-build: Colour of unstable state (default 'yellow')
296    :arg str bad-build: Colour of unsuccessful state (default 'red')
297
298    Full Example:
299
300        .. literalinclude::
301           /../../tests/publishers/fixtures/hue-light-full.yaml
302           :language: yaml
303
304    Minimal Example:
305
306        .. literalinclude::
307           /../../tests/publishers/fixtures/hue-light-minimal.yaml
308           :language: yaml
309    """
310
311    hue_light = XML.SubElement(
312        xml_parent, "org.jenkinsci.plugins.hue__light.LightNotifier"
313    )
314    hue_light.set("plugin", "hue-light")
315    lightId = XML.SubElement(hue_light, "lightId")
316
317    id_mapping = [("light-id", "string", None)]
318    helpers.convert_mapping_to_xml(lightId, data, id_mapping, fail_required=True)
319
320    build_mapping = [
321        ("pre-build", "preBuild", "blue"),
322        ("good-build", "goodBuild", "green"),
323        ("unstable-build", "unstableBuild", "yellow"),
324        ("bad-build", "badBuild", "red"),
325    ]
326    helpers.convert_mapping_to_xml(hue_light, data, build_mapping, fail_required=True)
327
328
329def campfire(registry, xml_parent, data):
330    """yaml: campfire
331    Send build notifications to Campfire rooms.
332    Requires the Jenkins :jenkins-plugins:`Campfire Plugin <campfire>`.
333
334    Campfire notifications global default values must be configured for
335    the Jenkins instance. Default values will be used if no specific
336    values are specified for each job, so all config params are optional.
337
338    :arg str subdomain: override the default campfire subdomain
339    :arg str token: override the default API token
340    :arg bool ssl: override the default 'use SSL'
341    :arg str room: override the default room name
342
343    Example:
344
345        .. literalinclude::  /../../tests/publishers/fixtures/campfire001.yaml
346           :language: yaml
347    """
348
349    root = XML.SubElement(xml_parent, "hudson.plugins.campfire." "CampfireNotifier")
350    campfire = XML.SubElement(root, "campfire")
351
352    mapping = [
353        ("subdomain", "subdomain", None),
354        ("token", "token", None),
355        ("ssl", "ssl", None),
356    ]
357    helpers.convert_mapping_to_xml(campfire, data, mapping, fail_required=False)
358
359    if "room" in data:
360        room = XML.SubElement(root, "room")
361        mapping = [("room", "name", None)]
362        helpers.convert_mapping_to_xml(room, data, mapping, fail_required=True)
363
364        XML.SubElement(room, 'campfire reference="../../campfire"')
365
366
367def mqtt(registry, xml_parent, data):
368    """yaml: mqtt
369    This plugin lets you send build notifications to a MQTT message queue.
370    Requires the :jenkins-plugins:`MQTT Notification Plugin
371    <mqtt-notification-plugin>`.
372
373    :arg str broker-url: the broker URL, as protocol://address:port (required)
374    :arg str credentials-id: credentials to use to connect to the broker
375        (optional)
376    :arg str topic: the message topic (default "jenkins/$PROJECT_URL")
377    :arg str message: the message itself (default "$BUILD_RESULT")
378    :arg str qos: one of AT_MOST_ONCE, AT_LEAST_ONCE, or EXACTLY_ONCE
379        (default AT_MOST_ONCE)
380    :arg bool retain-message: whether to resend message or not when a new
381        client connects (default false)
382
383    Minimal Example:
384
385        .. literalinclude:: /../../tests/publishers/fixtures/mqtt-minimal.yaml
386           :language: yaml
387
388    Full Example:
389
390        .. literalinclude:: /../../tests/publishers/fixtures/mqtt-full.yaml
391           :language: yaml
392    """
393
394    mqtt = XML.SubElement(xml_parent, "jenkins.plugins.mqttnotification.MqttNotifier")
395    mqtt.set("plugin", "mqtt-notification-plugin")
396    mqtt_mapping = [("broker-url", "brokerUrl", None)]
397    helpers.convert_mapping_to_xml(mqtt, data, mqtt_mapping, fail_required=True)
398    mqtt_mapping = [
399        ("credentials-id", "credentialsId", None),
400        ("topic", "topic", "jenkins/$PROJECT_URL"),
401        ("message", "message", "$BUILD_RESULT"),
402        (
403            "qos",
404            "qos",
405            "AT_MOST_ONCE",
406            {"AT_MOST_ONCE": "0", "AT_LEAST_ONCE": "1", "EXACTLY_ONCE": "2"},
407        ),
408        ("retain-message", "retainMessage", False),
409    ]
410    helpers.convert_mapping_to_xml(mqtt, data, mqtt_mapping, fail_required=False)
411
412
413def codecover(registry, xml_parent, data):
414    """yaml: codecover
415    This plugin allows you to capture code coverage report from CodeCover.
416    Jenkins will generate the trend report of coverage.
417    Requires the Jenkins :jenkins-plugins:`CodeCover Plugin <codecover>`.
418
419    :arg str include: Specify the path to the CodeCover HTML report file,
420        relative to the workspace root (default '')
421    :arg int min-statement: Minimum statement threshold (default 0)
422    :arg int max-statement: Maximum statement threshold (default 90)
423    :arg int min-branch: Minimum branch threshold (default 0)
424    :arg int max-branch: Maximum branch threshold (default 80)
425    :arg int min-loop: Minimum loop threshold (default 0)
426    :arg int max-loop: Maximum loop threshold (default 50)
427    :arg int min-condition: Minimum condition threshold (default 0)
428    :arg int max-condition: Maximum conditon threshold (default 50)
429
430    Minimal Example:
431
432        .. literalinclude::
433           /../../tests/publishers/fixtures/codecover-minimal.yaml
434           :language: yaml
435
436    Full Example:
437
438        .. literalinclude::
439           /../../tests/publishers/fixtures/codecover-full.yaml
440           :language: yaml
441    """
442
443    codecover = XML.SubElement(
444        xml_parent, "hudson.plugins.codecover.CodeCoverPublisher"
445    )
446    codecover.set("plugin", "codecover")
447
448    XML.SubElement(codecover, "includes").text = str(data.get("include", ""))
449
450    health_report = XML.SubElement(codecover, "healthReports")
451    mapping = [
452        ("min-statement", "minStatement", 0),
453        ("max-statement", "maxStatement", 90),
454        ("min-branch", "minBranch", 0),
455        ("max-branch", "maxBranch", 80),
456        ("min-loop", "minLoop", 0),
457        ("max-loop", "maxLoop", 50),
458        ("min-condition", "minCondition", 0),
459        ("max-condition", "maxCondition", 50),
460    ]
461    helpers.convert_mapping_to_xml(health_report, data, mapping, fail_required=True)
462
463
464def emotional_jenkins(registry, xml_parent, data):
465    """yaml: emotional-jenkins
466    Emotional Jenkins. This funny plugin changes the expression of Mr. Jenkins
467    in the background when your builds fail.
468
469    Requires the Jenkins :jenkins-plugins:`Emotional Jenkins Plugin
470    <emotional-jenkins-plugin>`.
471
472    Example:
473
474        .. literalinclude::
475           /../../tests/publishers/fixtures/emotional-jenkins.yaml
476           :language: yaml
477    """
478    XML.SubElement(
479        xml_parent,
480        "org.jenkinsci.plugins.emotional__jenkins." "EmotionalJenkinsPublisher",
481    )
482
483
484def trigger_parameterized_builds(registry, xml_parent, data):
485    """yaml: trigger-parameterized-builds
486    Trigger parameterized builds of other jobs.
487    Requires the Jenkins :jenkins-plugins:`Parameterized Trigger Plugin
488    <parameterized-trigger>`.
489
490    Use of the `node-label-name` or `node-label` parameters
491    requires the Jenkins :jenkins-plugins:`NodeLabel Parameter Plugin
492    <nodelabelparameter>`.
493    Note: 'node-parameters' overrides the Node that the triggered
494    project is tied to.
495
496    :arg list project: list the jobs to trigger, will generate comma-separated
497      string containing the named jobs.
498    :arg str predefined-parameters: parameters to pass to the other
499      job (optional)
500    :arg bool current-parameters: Whether to include the parameters passed
501      to the current build to the triggered job (optional)
502    :arg bool node-parameters: Use the same Node for the triggered builds
503      that was used for this build. (optional)
504    :arg bool svn-revision: Pass svn revision to the triggered job (optional)
505    :arg bool include-upstream: Include/pass through Upstream SVN Revisons.
506        Only valid when 'svn-revision' is true. (default false)
507    :arg dict git-revision: Passes git revision to the triggered job
508        (optional).
509
510        * **combine-queued-commits** (bool): Whether to combine queued git
511          hashes or not (default false)
512
513    :arg bool combine-queued-commits: Combine Queued git hashes. Only valid
514        when 'git-revision' is true. (default false)
515
516        .. deprecated:: 1.5.0 Please use `combine-queued-commits` under the
517            `git-revision` argument instead.
518
519    :arg dict boolean-parameters: Pass boolean parameters to the downstream
520        jobs. Specify the name and boolean value mapping of the parameters.
521        (optional)
522    :arg str condition: when to trigger the other job. Can be: 'SUCCESS',
523      'UNSTABLE', 'FAILED_OR_BETTER', 'UNSTABLE_OR_BETTER',
524      'UNSTABLE_OR_WORSE', 'FAILED', 'ALWAYS'. (default 'ALWAYS')
525    :arg str property-file: Use properties from file (optional)
526    :arg bool fail-on-missing: Blocks the triggering of the downstream jobs
527        if any of the property files are not found in the workspace.
528        Only valid when 'property-file' is specified.
529        (default 'False')
530    :arg bool property-multiline: When enabled properties containing
531        newline character(s) are propagated as TextParameterValue which is
532        a specialized StringParameterValue commonly used for handling
533        multi-line strings in Jenkins. When disabled (default)
534        all properties are propagated as StringParameterValue. (default
535        'False') (>=2.35.2)
536    :arg bool trigger-from-child-projects: Trigger build from child projects.
537        Used for matrix projects. (default 'False')
538    :arg bool use-matrix-child-files: Use files in workspaces of child
539        builds (default 'False')
540    :arg str matrix-child-combination-filter: A Groovy expression to filter
541        the child builds to look in for files
542    :arg bool only-exact-matrix-child-runs: Use only child builds triggered
543        exactly by the parent.
544    :arg str file-encoding: Encoding of contents of the files. If not
545        specified, default encoding of the platform is used. Only valid when
546        'property-file' is specified. (optional)
547    :arg bool trigger-with-no-params: Trigger a build even when there are
548      currently no parameters defined (default 'False')
549    :arg str restrict-matrix-project: Filter that restricts the subset
550        of the combinations that the downstream project will run (optional)
551    :arg str node-label-name: Specify the Name for the NodeLabel parameter.
552      (optional)
553    :arg str node-label: Specify the Node for the NodeLabel parameter.
554      (optional)
555
556    Example:
557
558        .. literalinclude::
559            /../../tests/publishers/fixtures/trigger_parameterized_builds001.yaml
560           :language: yaml
561        .. literalinclude::
562            /../../tests/publishers/fixtures/trigger_parameterized_builds003.yaml
563           :language: yaml
564    """
565    pt_prefix = "hudson.plugins.parameterizedtrigger."
566    tbuilder = XML.SubElement(xml_parent, pt_prefix + "BuildTrigger")
567    configs = XML.SubElement(tbuilder, "configs")
568
569    param_order = helpers.trigger_get_parameter_order(
570        registry, "trigger-parameterized-builds"
571    )
572
573    for project_def in data:
574        tconfig = XML.SubElement(configs, pt_prefix + "BuildTriggerConfig")
575        tconfigs = XML.SubElement(tconfig, "configs")
576
577        helpers.trigger_project(tconfigs, project_def, registry, param_order)
578
579        if not list(tconfigs):
580            # no child parameter tags added
581            tconfigs.set("class", "java.util.Collections$EmptyList")
582
583        projects = XML.SubElement(tconfig, "projects")
584
585        if isinstance(project_def["project"], list):
586            projects.text = ",".join(project_def["project"])
587        else:
588            projects.text = project_def["project"]
589
590        condition = XML.SubElement(tconfig, "condition")
591        condition.text = project_def.get("condition", "ALWAYS")
592        mapping = [
593            ("trigger-from-child-projects", "triggerFromChildProjects", False),
594            ("trigger-with-no-params", "triggerWithNoParameters", False),
595        ]
596        helpers.convert_mapping_to_xml(
597            tconfig, project_def, mapping, fail_required=False
598        )
599
600
601def trigger(registry, xml_parent, data):
602    """yaml: trigger
603    Trigger non-parametrised builds of other jobs.
604
605    :arg str project: name of the job to trigger
606    :arg str threshold: when to trigger the other job (default 'SUCCESS'),
607      alternatives: SUCCESS, UNSTABLE, FAILURE
608
609    Example:
610
611    .. literalinclude:: /../../tests/publishers/fixtures/trigger_success.yaml
612       :language: yaml
613    """
614    tconfig = XML.SubElement(xml_parent, "hudson.tasks.BuildTrigger")
615    childProjects = XML.SubElement(tconfig, "childProjects")
616    childProjects.text = data["project"]
617
618    threshold = data.get("threshold", "SUCCESS")
619    supported_thresholds = ["SUCCESS", "UNSTABLE", "FAILURE"]
620    helpers.trigger_threshold(
621        tconfig, "threshold", threshold, supported_thresholds=supported_thresholds
622    )
623
624
625def clone_workspace(registry, xml_parent, data):
626    """yaml: clone-workspace
627    Archive the workspace from builds of one project and reuse them as the SCM
628    source for another project.
629    Requires the Jenkins :jenkins-plugins:`Clone Workspace SCM Plugin
630    <clone-workspace-scm>`.
631
632    :arg str workspace-glob: Files to include in cloned workspace (default '')
633    :arg str workspace-exclude-glob: Files to exclude from cloned workspace
634    :arg str criteria: Criteria for build to be archived.  Can be 'any',
635        'not failed', or 'successful'. (default 'any')
636    :arg str archive-method: Choose the method to use for archiving the
637        workspace.  Can be 'tar' or 'zip'.  (default 'tar')
638    :arg bool override-default-excludes: Override default ant excludes.
639        (default false)
640
641    Minimal example:
642
643    .. literalinclude::
644        /../../tests/publishers/fixtures/clone-workspace001.yaml
645       :language: yaml
646
647    Full example:
648
649    .. literalinclude::
650        /../../tests/publishers/fixtures/clone-workspace002.yaml
651       :language: yaml
652    """
653
654    cloneworkspace = XML.SubElement(
655        xml_parent, "hudson.plugins.cloneworkspace.CloneWorkspacePublisher"
656    )
657    cloneworkspace.set("plugin", "clone-workspace-scm")
658
659    criteria_valid_types = ["Any", "Not Failed", "Successful"]
660    archive_valid_types = ["TAR", "ZIP"]
661
662    mappings = [
663        ("workspace-glob", "workspaceGlob", ""),
664        ("override-default-excludes", "overrideDefaultExcludes", False),
665        ("criteria", "criteria", "Any", criteria_valid_types),
666        ("archive-method", "archiveMethod", "TAR", archive_valid_types),
667    ]
668    helpers.convert_mapping_to_xml(cloneworkspace, data, mappings, fail_required=True)
669
670    mappings = [("workspace-exclude-glob", "workspaceExcludeGlob", "")]
671    helpers.convert_mapping_to_xml(cloneworkspace, data, mappings, fail_required=False)
672
673
674def cloud_foundry(parser, xml_parent, data):
675    """yaml: cloudfoundry
676    Pushes a project to Cloud Foundry or a CF-based platform (e.g. Stackato) at
677    the end of a build. Requires the Jenkins :jenkins-plugins:`Cloud Foundry
678    Plugin <cloudfoundry>`.
679
680    :arg str target: The API endpoint of the platform you want to push to.
681        This is the URL you use to access the platform, possibly with ".api"
682        added. (required)
683    :arg str organization: An org is a development account that an individual
684        or multiple collaborators can own and use (required)
685    :arg str space: Provide users with access to a shared location for
686        application development, deployment, and maintenance (required)
687    :arg str credentials-id: credentials-id of the user (required)
688    :arg bool self-signed: Allow self-signed SSL certificates from the target
689        (default false)
690    :arg bool reset-app: Delete app before pushing app's configurations
691        (default false)
692    :arg int plugin-timeout: The time in seconds before the Cloud Foundry
693        plugin stops fetching logs and marks the build a failure (default 120)
694    :arg list create-services: Create services automatically (default '')
695
696        :create-services:
697            * **name** ('str') -- Service name (default '')
698            * **type** ('str') -- Service type (default '')
699            * **plan** ('str') -- Service plan (default '')
700            * **reset-service** ('bool') -- Delete the service before creating
701                the new one (default false)
702    :arg str value: Select to read configuration from manifest file or to enter
703        configuration in Jenkins (default 'manifestFile')
704    :arg str manifest-file: Path to manifest file (default 'manifest.yml')
705    :arg str app-name: The application's name. Default to Jenkins build name.
706        (default '')
707    :arg int memory: The application's memory usage in MB (default 512)
708    :arg str host-name: The hostname of the URI to access your application.
709        Default to app-name (default '')
710    :arg int instances: Number of instances of your application on creation
711        (default 1)
712    :arg int manifest-timeout: The time in seconds before the health-manager
713        gives up on starting the application (default 60)
714    :arg bool no-route: No URI path will be created to access the application
715        (default false)
716    :arg str app-path: Path to application (default '')
717    :arg build-pack: If your application requires a custom buildpack, you can
718        use this to specify its URL or name (default '')
719    :arg str stack: If your application requires a custom stack, you can use
720        this to specify its name. (default '')
721    :arg str command: Set a custom start command for your application
722        (default '')
723    :arg str domain: The domain of the URI to access your application
724        (default '')
725    :arg list environment-variables: Inject environment variables
726
727        :environment-variables:
728            * **key** ('str') -- Environment variable key (default '')
729            * **value** ('str') -- Environment variable value (default '')
730    :arg list services-names: Name of service instances
731
732        :services-names:
733            * **name** ('str') -- Name of the service instance (default '')
734
735    Minimal example:
736
737    .. literalinclude::
738       /../../tests/publishers/fixtures/cloudfoundry-minimal.yaml
739       :language: yaml
740
741    Full example:
742
743    .. literalinclude:: /../../tests/publishers/fixtures/cloudfoundry-full.yaml
744       :language: yaml
745    """
746    cloud_foundry = XML.SubElement(
747        xml_parent, "com.hpe.cloudfoundryjenkins.CloudFoundryPushPublisher"
748    )
749    cloud_foundry.set("plugin", "cloudfoundry")
750
751    mapping = [
752        ("target", "target", None),
753        ("organization", "organization", None),
754        ("space", "cloudSpace", None),
755        ("credentials-id", "credentialsId", None),
756        ("self-signed", "selfSigned", False),
757        ("reset-app", "resetIfExists", False),
758        ("timeout", "pluginTimeout", 120),
759    ]
760    helpers.convert_mapping_to_xml(cloud_foundry, data, mapping, fail_required=True)
761    XML.SubElement(cloud_foundry, "appURIs").text = ""
762
763    create_services = XML.SubElement(cloud_foundry, "servicesToCreate")
764    create_services_mapping = [
765        ("name", "name", ""),
766        ("type", "type", ""),
767        ("plan", "plan", ""),
768        ("reset-service", "resetService", ""),
769    ]
770    for service in data.get("create-services", ""):
771        create_services_sub = XML.SubElement(
772            create_services,
773            "com.hpe.cloudfoundryjenkins.CloudFoundryPushPublisher_-Service",
774        )
775        helpers.convert_mapping_to_xml(
776            create_services_sub, service, create_services_mapping, fail_required=True
777        )
778
779    manifest = XML.SubElement(cloud_foundry, "manifestChoice")
780    valid_values = ["manifestFile", "jenkinsConfig"]
781    manifest_mapping = [
782        ("value", "value", "manifestFile", valid_values),
783        ("manifest-file", "manifestFile", "manifest.yml"),
784        ("app-name", "appName", ""),
785        ("memory", "memory", 512),
786        ("host-name", "hostname", ""),
787        ("instances", "instances", 1),
788        ("manifest-timeout", "timeout", 60),
789        ("no-route", "noRoute", False),
790        ("app-path", "appPath", ""),
791        ("build-pack", "buildpack", ""),
792        ("stack", "stack", ""),
793        ("command", "command", ""),
794        ("domain", "domain", ""),
795    ]
796    helpers.convert_mapping_to_xml(manifest, data, manifest_mapping, fail_required=True)
797
798    if "environment-variables" in data:
799        env_vars = XML.SubElement(manifest, "envVars")
800        env_vars_mapping = [("key", "key", ""), ("value", "value", "")]
801        for var in data["environment-variables"]:
802            env_vars_sub = XML.SubElement(
803                env_vars,
804                "com.hpe.cloudfoundryjenkins.CloudFoundryPushPublisher_-"
805                "EnvironmentVariable",
806            )
807            helpers.convert_mapping_to_xml(
808                env_vars_sub, var, env_vars_mapping, fail_required=True
809            )
810
811    if "services-names" in data:
812        services_names = XML.SubElement(manifest, "servicesNames")
813        service_name_mapping = [("name", "name", "")]
814        for name in data["services-names"]:
815            services_names_sub = XML.SubElement(
816                services_names,
817                "com.hpe.cloudfoundryjenkins.CloudFoundryPushPublisher_-" "ServiceName",
818            )
819            helpers.convert_mapping_to_xml(
820                services_names_sub, name, service_name_mapping, fail_required=True
821            )
822
823
824def cloverphp(registry, xml_parent, data):
825    """yaml: cloverphp
826    Capture code coverage reports from PHPUnit
827    Requires the Jenkins :jenkins-plugins:`Clover PHP Plugin <cloverphp>`.
828
829    Your job definition should pass to PHPUnit the --coverage-clover option
830    pointing to a file in the workspace (ex: clover-coverage.xml). The filename
831    has to be filled in the `xml-location` field.
832
833    :arg str xml-location: Path to the coverage XML file generated by PHPUnit
834        using --coverage-clover. Relative to workspace. (required)
835    :arg dict html: When existent, whether the plugin should generate a HTML
836        report.  Note that PHPUnit already provide a HTML report via its
837        --cover-html option which can be set in your builder (optional):
838
839        * **dir** (str): Directory where HTML report will be generated relative
840                         to workspace. (required in `html` dict).
841        * **archive** (bool): Whether to archive HTML reports (default true).
842
843    :arg list metric-targets: List of metric targets to reach, must be one of
844        **healthy**, **unhealthy** and **failing**. Each metric target can
845        takes two parameters:
846
847        * **method**  Target for method coverage
848        * **statement** Target for statements coverage
849
850        Whenever a metric target is not filled in, the Jenkins plugin can fill
851        in defaults for you (as of v0.3.3 of the plugin the healthy target will
852        have method: 70 and statement: 80 if both are left empty). Jenkins Job
853        Builder will mimic that feature to ensure clean configuration diff.
854
855    Minimal example:
856
857    .. literalinclude:: /../../tests/publishers/fixtures/cloverphp001.yaml
858       :language: yaml
859
860    Full example:
861
862    .. literalinclude:: /../../tests/publishers/fixtures/cloverphp002.yaml
863       :language: yaml
864    """
865    cloverphp = XML.SubElement(
866        xml_parent, "org.jenkinsci.plugins.cloverphp.CloverPHPPublisher"
867    )
868    cloverphp.set("plugin", "cloverphp")
869
870    # The plugin requires clover XML file to parse
871    if "xml-location" not in data:
872        raise JenkinsJobsException("xml-location must be set")
873
874    # Whether HTML publishing has been checked
875    html_publish = False
876    # By default, disableArchiving = false. Note that we use
877    # reversed logic.
878    html_archive = True
879
880    if "html" in data:
881        html_publish = True
882        html_dir = data["html"].get("dir", None)
883        html_archive = data["html"].get("archive", html_archive)
884        if html_dir is None:
885            # No point in going further, the plugin would not work
886            raise JenkinsJobsException("htmldir is required in a html block")
887
888    XML.SubElement(cloverphp, "publishHtmlReport").text = str(html_publish).lower()
889    if html_publish:
890        XML.SubElement(cloverphp, "reportDir").text = html_dir
891    XML.SubElement(cloverphp, "xmlLocation").text = data.get("xml-location")
892    XML.SubElement(cloverphp, "disableArchiving").text = str(not html_archive).lower()
893
894    # Handle targets
895
896    # Plugin v0.3.3 will fill defaults for us whenever healthy targets are both
897    # blanks.
898    default_metrics = {"healthy": {"method": 70, "statement": 80}}
899    allowed_metrics = ["healthy", "unhealthy", "failing"]
900
901    metrics = data.get("metric-targets", [])
902    # list of dicts to dict
903    metrics = dict(kv for m in metrics for kv in m.items())
904
905    # Populate defaults whenever nothing has been filled by user.
906    for default in default_metrics.keys():
907        if metrics.get(default, None) is None:
908            metrics[default] = default_metrics[default]
909
910    # The plugin would at least define empty targets so make sure
911    # we output them all in the XML regardless of what the user
912    # has or has not entered.
913    for target in allowed_metrics:
914        cur_target = XML.SubElement(cloverphp, target + "Target")
915
916        for t_type in ["method", "statement"]:
917            val = metrics.get(target, {}).get(t_type)
918            if val is None or type(val) != int:
919                continue
920            if val < 0 or val > 100:
921                raise JenkinsJobsException(
922                    "Publisher cloverphp metric target %s:%s = %s "
923                    "is not in valid range 0-100." % (target, t_type, val)
924                )
925            XML.SubElement(cur_target, t_type + "Coverage").text = str(val)
926
927
928def coverage(registry, xml_parent, data):
929    """yaml: coverage
930    WARNING: The coverage function is deprecated. Instead, use the
931    cobertura function to generate a cobertura coverage report.
932    Requires the Jenkins :jenkins-plugins:`Cobertura Coverage Plugin
933    <cobertura>`.
934
935    Example:
936
937    .. literalinclude:: /../../tests/publishers/fixtures/coverage001.yaml
938       :language: yaml
939    """
940    logger = logging.getLogger(__name__)
941    logger.warning("Coverage function is deprecated. Switch to cobertura.")
942
943    cobertura = XML.SubElement(
944        xml_parent, "hudson.plugins.cobertura.CoberturaPublisher"
945    )
946    XML.SubElement(cobertura, "coberturaReportFile").text = "**/coverage.xml"
947    XML.SubElement(cobertura, "onlyStable").text = "false"
948    healthy = XML.SubElement(cobertura, "healthyTarget")
949    targets = XML.SubElement(
950        healthy,
951        "targets",
952        {
953            "class": "enum-map",
954            "enum-type": "hudson.plugins.cobertura.targets.CoverageMetric",
955        },
956    )
957    entry = XML.SubElement(targets, "entry")
958    XML.SubElement(
959        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
960    ).text = "CONDITIONAL"
961    XML.SubElement(entry, "int").text = "70"
962    entry = XML.SubElement(targets, "entry")
963    XML.SubElement(
964        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
965    ).text = "LINE"
966    XML.SubElement(entry, "int").text = "80"
967    entry = XML.SubElement(targets, "entry")
968    XML.SubElement(
969        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
970    ).text = "METHOD"
971    XML.SubElement(entry, "int").text = "80"
972    unhealthy = XML.SubElement(cobertura, "unhealthyTarget")
973    targets = XML.SubElement(
974        unhealthy,
975        "targets",
976        {
977            "class": "enum-map",
978            "enum-type": "hudson.plugins.cobertura.targets.CoverageMetric",
979        },
980    )
981    entry = XML.SubElement(targets, "entry")
982    XML.SubElement(
983        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
984    ).text = "CONDITIONAL"
985    XML.SubElement(entry, "int").text = "0"
986    entry = XML.SubElement(targets, "entry")
987    XML.SubElement(
988        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
989    ).text = "LINE"
990    XML.SubElement(entry, "int").text = "0"
991    entry = XML.SubElement(targets, "entry")
992    XML.SubElement(
993        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
994    ).text = "METHOD"
995    XML.SubElement(entry, "int").text = "0"
996    failing = XML.SubElement(cobertura, "failingTarget")
997    targets = XML.SubElement(
998        failing,
999        "targets",
1000        {
1001            "class": "enum-map",
1002            "enum-type": "hudson.plugins.cobertura.targets.CoverageMetric",
1003        },
1004    )
1005    entry = XML.SubElement(targets, "entry")
1006    XML.SubElement(
1007        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
1008    ).text = "CONDITIONAL"
1009    XML.SubElement(entry, "int").text = "0"
1010    entry = XML.SubElement(targets, "entry")
1011    XML.SubElement(
1012        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
1013    ).text = "LINE"
1014    XML.SubElement(entry, "int").text = "0"
1015    entry = XML.SubElement(targets, "entry")
1016    XML.SubElement(
1017        entry, "hudson.plugins.cobertura.targets.CoverageMetric"
1018    ).text = "METHOD"
1019    XML.SubElement(entry, "int").text = "0"
1020    XML.SubElement(cobertura, "sourceEncoding").text = "ASCII"
1021
1022
1023def cobertura(registry, xml_parent, data):
1024    """yaml: cobertura
1025    Generate a cobertura coverage report.
1026    Requires the Jenkins :jenkins-plugins:`Cobertura Coverage Plugin
1027    <cobertura>`.
1028
1029    :arg str report-file: This is a file name pattern that can be used
1030        to locate the cobertura xml report files (optional)
1031    :arg bool only-stable: Include only stable builds (default false)
1032    :arg bool fail-no-reports: fail builds if no coverage reports are found
1033        (default false)
1034    :arg bool fail-unhealthy: Unhealthy projects will be failed (default false)
1035    :arg bool fail-unstable: Unstable projects will be failed (default false)
1036    :arg bool health-auto-update: Auto update threshold for health on
1037        successful build (default false)
1038    :arg bool stability-auto-update: Auto update threshold for stability on
1039        successful build (default false)
1040    :arg bool zoom-coverage-chart: Zoom the coverage chart and crop area below
1041        the minimum and above the maximum coverage of the past reports
1042        (default false)
1043    :arg str source-encoding: Override the source encoding (default ASCII)
1044    :arg dict targets:
1045
1046           :targets: (packages, files, classes, method, line, conditional)
1047
1048                * **healthy** (`int`): Healthy threshold (default 0)
1049                * **unhealthy** (`int`): Unhealthy threshold (default 0)
1050                * **failing** (`int`): Failing threshold (default 0)
1051
1052    Example:
1053
1054    .. literalinclude:: /../../tests/publishers/fixtures/cobertura001.yaml
1055       :language: yaml
1056    """
1057    cobertura = XML.SubElement(
1058        xml_parent, "hudson.plugins.cobertura.CoberturaPublisher"
1059    )
1060    mapping = [
1061        ("report-file", "coberturaReportFile", "**/coverage.xml"),
1062        ("only-stable", "onlyStable", False),
1063        ("fail-unhealthy", "failUnhealthy", False),
1064        ("fail-unstable", "failUnstable", False),
1065        ("health-auto-update", "autoUpdateHealth", False),
1066        ("stability-auto-update", "autoUpdateStability", False),
1067        ("zoom-coverage-chart", "zoomCoverageChart", False),
1068        ("fail-no-reports", "failNoReports", False),
1069    ]
1070    helpers.convert_mapping_to_xml(cobertura, data, mapping, fail_required=True)
1071
1072    healthy = XML.SubElement(cobertura, "healthyTarget")
1073    targets = XML.SubElement(
1074        healthy,
1075        "targets",
1076        {
1077            "class": "enum-map",
1078            "enum-type": "hudson.plugins.cobertura.targets.CoverageMetric",
1079        },
1080    )
1081    for item in data["targets"]:
1082        item_name = next(iter(item.keys()))
1083        item_values = item.get(item_name, 0)
1084        entry = XML.SubElement(targets, "entry")
1085        XML.SubElement(
1086            entry, "hudson.plugins.cobertura.targets." "CoverageMetric"
1087        ).text = str(item_name).upper()
1088        XML.SubElement(entry, "int").text = str(item_values.get("healthy", 0))
1089    unhealthy = XML.SubElement(cobertura, "unhealthyTarget")
1090    targets = XML.SubElement(
1091        unhealthy,
1092        "targets",
1093        {
1094            "class": "enum-map",
1095            "enum-type": "hudson.plugins.cobertura.targets.CoverageMetric",
1096        },
1097    )
1098    for item in data["targets"]:
1099        item_name = next(iter(item.keys()))
1100        item_values = item.get(item_name, 0)
1101        entry = XML.SubElement(targets, "entry")
1102        XML.SubElement(
1103            entry, "hudson.plugins.cobertura.targets." "CoverageMetric"
1104        ).text = str(item_name).upper()
1105        XML.SubElement(entry, "int").text = str(item_values.get("unhealthy", 0))
1106    failing = XML.SubElement(cobertura, "failingTarget")
1107    targets = XML.SubElement(
1108        failing,
1109        "targets",
1110        {
1111            "class": "enum-map",
1112            "enum-type": "hudson.plugins.cobertura.targets.CoverageMetric",
1113        },
1114    )
1115    for item in data["targets"]:
1116        item_name = next(iter(item.keys()))
1117        item_values = item.get(item_name, 0)
1118        entry = XML.SubElement(targets, "entry")
1119        XML.SubElement(
1120            entry, "hudson.plugins.cobertura.targets." "CoverageMetric"
1121        ).text = str(item_name).upper()
1122        XML.SubElement(entry, "int").text = str(item_values.get("failing", 0))
1123    XML.SubElement(cobertura, "sourceEncoding").text = data.get(
1124        "source-encoding", "ASCII"
1125    )
1126
1127
1128def jacoco(registry, xml_parent, data):
1129    """yaml: jacoco
1130    Generate a JaCoCo coverage report.
1131    Requires the Jenkins :jenkins-plugins:`JaCoCo Plugin <jacoco>`.
1132
1133    :arg str exec-pattern: This is a file name pattern that can be used to
1134        locate the jacoco report files (default ``**/**.exec``)
1135    :arg str class-pattern: This is a file name pattern that can be used
1136        to locate class files (default ``**/classes``)
1137    :arg str source-pattern: This is a file name pattern that can be used
1138        to locate source files (default ``**/src/main/java``)
1139    :arg str source-inclusion-pattern: This is a file name pattern that can
1140        be used to include certain source files (default ``**/*.java``)
1141    :arg bool update-build-status: Update the build according to the results
1142        (default false)
1143    :arg str inclusion-pattern: This is a file name pattern that can be used
1144        to include certain class files (default '')
1145    :arg str exclusion-pattern: This is a file name pattern that can be used
1146        to exclude certain class files (default '')
1147    :arg dict targets:
1148
1149           :targets: (instruction, branch, complexity, line, method, class)
1150
1151                * **healthy** (`int`): Healthy threshold (default 0)
1152                * **unhealthy** (`int`): Unhealthy threshold (default 0)
1153
1154    Minimal Example:
1155
1156    .. literalinclude:: /../../tests/publishers/fixtures/jacoco-minimal.yaml
1157       :language: yaml
1158
1159    Full Example:
1160
1161    .. literalinclude:: /../../tests/publishers/fixtures/jacoco-full.yaml
1162       :language: yaml
1163    """
1164
1165    jacoco = XML.SubElement(xml_parent, "hudson.plugins.jacoco.JacocoPublisher")
1166    jacoco.set("plugin", "jacoco")
1167
1168    mappings = [
1169        ("exec-pattern", "execPattern", "**/**.exec"),
1170        ("class-pattern", "classPattern", "**/classes"),
1171        ("source-pattern", "sourcePattern", "**/src/main/java"),
1172        ("source-inclusion-pattern", "sourceInclusionPattern", "**/*.java"),
1173        ("update-build-status", "changeBuildStatus", False),
1174        ("inclusion-pattern", "inclusionPattern", ""),
1175        ("exclusion-pattern", "exclusionPattern", ""),
1176    ]
1177    helpers.convert_mapping_to_xml(jacoco, data, mappings, fail_required=True)
1178
1179    itemsList = ["instruction", "branch", "complexity", "line", "method", "class"]
1180
1181    if "targets" in data:
1182        for item in data["targets"]:
1183            item_name = next(iter(item.keys()))
1184            if item_name not in itemsList:
1185                raise InvalidAttributeError("targets", item_name, itemsList)
1186
1187            item_values = item[item_name]
1188            if item_values:
1189                XML.SubElement(
1190                    jacoco, "maximum" + item_name.capitalize() + "Coverage"
1191                ).text = str(item_values.get("healthy", 0))
1192                XML.SubElement(
1193                    jacoco, "minimum" + item_name.capitalize() + "Coverage"
1194                ).text = str(item_values.get("unhealthy", 0))
1195            else:
1196                raise MissingAttributeError(
1197                    ["healthy", "unhealthy"], "publishers.jacoco.targets." + item_name
1198                )
1199
1200
1201def ftp(registry, xml_parent, data):
1202    """yaml: ftp
1203    Upload files via FTP.
1204    Requires the Jenkins :jenkins-plugins:`Publish over FTP Plugin
1205    <publish-over-ftp>`.
1206
1207    :arg str site: name of the ftp site (required)
1208    :arg str target: destination directory (required)
1209    :arg bool target-is-date-format: whether target is a date format. If true,
1210      raw text should be quoted (default false)
1211    :arg bool clean-remote: should the remote directory be deleted before
1212      transferring files (default false)
1213    :arg str source: source path specifier (required)
1214    :arg str excludes: excluded file pattern (optional)
1215    :arg str remove-prefix: prefix to remove from uploaded file paths
1216      (optional)
1217    :arg bool fail-on-error: fail the build if an error occurs (default false).
1218    :arg bool flatten: only create files on the server, don't create
1219      directories (default false).
1220    :arg bool verbose: adds lots of detail useful for debug to the console
1221      but generally should be left off (default false)
1222    :arg int retries: the number of times to retry this server in the event of
1223      failure (optional)
1224    :arg int retry-delay: the time to wait, in milliseconds, before attempting
1225      another transfer (default 10000)
1226
1227    Minimal Example:
1228
1229        .. literalinclude:: /../../tests/publishers/fixtures/ftp-minimal.yaml
1230           :language: yaml
1231
1232    Full Example:
1233
1234        .. literalinclude::  /../../tests/publishers/fixtures/ftp-full.yaml
1235           :language: yaml
1236
1237    """
1238    console_prefix = "FTP: "
1239    plugin_tag = "jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin"
1240    publisher_tag = "jenkins.plugins.publish__over__ftp.BapFtpPublisher"
1241    transfer_tag = "jenkins.plugins.publish__over__ftp.BapFtpTransfer"
1242    retry_tag = "jenkins.plugins.publish_over_ftp.BapFtpRetry"
1243    plugin_reference_tag = "jenkins.plugins.publish_over_ftp." "BapFtpPublisherPlugin"
1244    (_, transfer_node) = base_publish_over(
1245        xml_parent,
1246        data,
1247        console_prefix,
1248        plugin_tag,
1249        publisher_tag,
1250        transfer_tag,
1251        retry_tag,
1252        plugin_reference_tag,
1253    )
1254    mapping = [("", "asciiMode", "false")]
1255    helpers.convert_mapping_to_xml(transfer_node, data, mapping, fail_required=True)
1256
1257
1258def ftp_publisher(registry, xml_parent, data):
1259    """yaml: ftp-publisher
1260    This plugin can be used to upload project artifacts and whole directories
1261    to an ftp server.
1262    Requires the Jenkins :jenkins-plugins:`FTP-Publisher Plugin
1263    <ftppublisher>`.
1264
1265    :arg list uploads: List of files to upload
1266
1267        :uploads:
1268            * **file-path** ('str') -- Destination folder. It will be created
1269                if doesn't exists. Created relative to ftp root directory.
1270                (default '')
1271            * **source-file** ('str') -- Source files which will be uploaded
1272                (default '')
1273    :arg str site-name: Name of FTP server to upload to (required)
1274    :arg bool use-timestamps: Use timestamps in the FTP directory path (default
1275        false)
1276    :arg bool flatten-files: Flatten files on the FTP host (default false)
1277    :arg bool skip-publishing: Skip publishing (default false)
1278
1279    Minimal Example:
1280
1281    .. literalinclude::
1282       /../../tests/publishers/fixtures/ftp-publisher-minimal.yaml
1283       :language: yaml
1284
1285    Full Example:
1286
1287    .. literalinclude::
1288       /../../tests/publishers/fixtures/ftp-publisher-full.yaml
1289       :language: yaml
1290    """
1291    ftp = XML.SubElement(xml_parent, "com.zanox.hudson.plugins.FTPPublisher")
1292    ftp.set("plugin", "ftppublisher")
1293
1294    entries = XML.SubElement(ftp, "entries")
1295    if "uploads" in data:
1296        upload_mapping = [
1297            ("file-path", "filePath", ""),
1298            ("source-file", "sourceFile", ""),
1299        ]
1300        for upload in data["uploads"]:
1301            entry = XML.SubElement(entries, "com.zanox.hudson.plugins.Entry")
1302            helpers.convert_mapping_to_xml(
1303                entry, upload, upload_mapping, fail_required=True
1304            )
1305
1306    mapping = [
1307        ("site-name", "siteName", None),
1308        ("use-timestamps", "useTimestamps", False),
1309        ("flatten-files", "flatten", False),
1310        ("skip-publishing", "skip", False),
1311    ]
1312    helpers.convert_mapping_to_xml(ftp, data, mapping, fail_required=True)
1313
1314
1315def opsgenie(registry, xml_parent, data):
1316    """yaml: opsgenie
1317    OpsGenie notification on build completion,
1318    Requires the :jenkins-plugins:`OpsGenie Notifier Plugin <opsgenie>`.
1319
1320    :arg bool enable-sending-alerts: Send alerts to opsgenie. (default false)
1321    :arg bool notify-build-start: Send a notification when the build starts. (default false)
1322    :arg str api-key: This token is used to verify requests between OpsGenie and Jenkins. You can copy this key from your OpsGenie-Jenkins Integration page. (default '')
1323    :arg str tags: Comma-separated list of tags you want to add on alert. (default '')
1324    :arg str teams: Comma-separated list of teams that get notified from alert. (default '')
1325    :arg str priority: Set the priority of the alert that's going to be created at OpsGenie, if job's build fails. (default 'P3')
1326    :arg str build-starts-alerts-priority: Set the priority of the build started alert that's going to be created at OpsGenie. (default 'P3')
1327    :arg str api-url: Api url that collects the webhook. (default '')
1328
1329    Minimal example:
1330
1331    .. literalinclude::
1332       /../../tests/publishers/fixtures/opsgenie-minimal.yaml
1333       :language: yaml
1334
1335    Full Example:
1336
1337    .. literalinclude::
1338       /../../tests/publishers/fixtures/opsgenie-full.yaml
1339       :language: yaml
1340    """
1341
1342    mapping = [
1343        ("priority", "alertPriority", "P3"),
1344        ("build-starts-alerts-priority", "notifyBuildStartPriority", "P3"),
1345        ("enable-sending-alerts", "enable", "false"),
1346        ("notify-build-start", "notifyBuildStart", "false"),
1347        ("api-key", "apiKey", ""),
1348        ("api-url", "apiUrl", ""),
1349        ("tags", "tags", ""),
1350        ("teams", "teams", ""),
1351    ]
1352
1353    opsgenie_notifier = XML.SubElement(
1354        xml_parent,
1355        "com.opsgenie.integration.jenkins.OpsGenieNotifier",
1356        {"plugin": "opsgenie"},
1357    )
1358
1359    helpers.convert_mapping_to_xml(opsgenie_notifier, data, mapping, fail_required=True)
1360
1361
1362def rocket(registry, xml_parent, data):
1363    """yaml: rocket
1364    RocketChat notification on build completion,
1365    Requires the :jenkins-plugins:`RocketChat Notifier Plugin
1366    <rocketchatnotifier>`.
1367
1368    :arg str channel: Comma separated list of rooms (e.g. #project)
1369        or persons (e.g. @john)
1370    :arg bool abort: Notify abort (default false)
1371    :arg bool start: Notify start (default false)
1372    :arg bool success: Notify success (default false)
1373    :arg bool failure: Notify failure (default false)
1374    :arg bool repeated-failure: Notify repeated failure (default false)
1375    :arg bool back-to-normal: Notify back to normal (default false)
1376    :arg bool not-built: Notify if not built (default false)
1377    :arg bool unstable: Notify if unstable (default false)
1378    :arg str webhook-token: Webhook token for posting messages.
1379        Overrides global authentication data and channel
1380    :arg str commit-info: What commit information to include into
1381        notification message.
1382
1383        :commit-info values:
1384            * **none**
1385            * **authors**
1386            * **authors-and-titles**
1387
1388    :arg str custom-message: Custom text message (default '')
1389    :arg bool trust-ssl: Trust RocketChat server certificate, if checked,
1390        the SSL certificate will not be checked (default false)
1391    :arg str build-server: Build Server URL
1392    :arg list attachments: Optional list of attachments
1393
1394       :attachments:
1395            * **title** (`str`) Attachment title (required)
1396            * **title-link** (`str`)
1397            * **title-link-download** (`bool`)
1398            * **color** (`str`)
1399            * **text** (`str`)
1400            * **collapsed** (`bool`)
1401            * **message-link** (`str`)
1402            * **author-name** (`str`)
1403            * **author-link** (`str`)
1404            * **author-icon** (`str`)
1405            * **thumb** (`str`) Thumb URL
1406            * **image** (`str`) Image URL
1407            * **audio** (`str`) Audio URL
1408            * **video** (`str`) Video URL
1409
1410    Minimal example using defaults:
1411
1412    .. literalinclude:: /../../tests/publishers/fixtures/rocket001.yaml
1413       :language: yaml
1414
1415    Full example:
1416
1417    .. literalinclude:: /../../tests/publishers/fixtures/rocket002.yaml
1418       :language: yaml
1419    """
1420    rocketchat = XML.SubElement(
1421        xml_parent, "jenkins.plugins.rocketchatnotifier.RocketChatNotifier"
1422    )
1423    rocketchat.set("plugin", "rocketchatnotifier")
1424    required_mapping = [
1425        ("channel", "channel", ""),
1426        ("start", "startNotification", False),
1427        ("success", "notifySuccess", False),
1428        ("abort", "notifyAborted", False),
1429        ("not-built", "notifyNotBuilt", False),
1430        ("unstable", "notifyUnstable", False),
1431        ("failure", "notifyFailure", False),
1432        ("back-to-normal", "notifyBackToNormal", False),
1433        ("repeated-failure", "notifyRepeatedFailure", False),
1434        ("include-test-summary", "includeTestSummary", False),
1435        ("include-test-log", "includeTestLog", False),
1436        ("include-custom-message", "includeCustomMessage", False),
1437        (
1438            "commit-info",
1439            "commitInfoChoice",
1440            "none",
1441            {
1442                "none": "NONE",
1443                "authors": "AUTHORS",
1444                "authors-and-titles": "AUTHORS_AND_TITLES",
1445            },
1446        ),
1447        ("raw-message", "rawMessage", False),
1448        ("webhook-token", "webhookToken", ""),
1449        ("webhook-token-credential-id", "webhookTokenCredentialId", ""),
1450    ]
1451    optional_mapping = [
1452        ("trust-ssl", "trustSSL", None),
1453        ("build-server", "buildServerUrl", None),
1454    ]
1455
1456    helpers.convert_mapping_to_xml(
1457        rocketchat, data, optional_mapping, fail_required=False
1458    )
1459    helpers.convert_mapping_to_xml(
1460        rocketchat, data, required_mapping, fail_required=True
1461    )
1462
1463    attach_required_mapping = [("title", "title", None)]
1464    attach_optional_mapping = [
1465        ("title-link", "titleLink", None),
1466        ("title-link-download", "titleLinkDownload", None),
1467        ("color", "color", None),
1468        ("text", "text", None),
1469        ("collapsed", "collapsed", None),  # false | true
1470        ("message-link", "messageLink", None),
1471        ("author-name", "authorName", None),
1472        ("author-link", "authorLink", None),
1473        ("author-icon", "authorIcon", None),
1474        ("thumb", "thumbUrl", None),
1475        ("image", "imageUrl", None),
1476        ("audio", "audioUrl", None),
1477        ("video", "videoUrl", None),
1478    ]
1479    attach_list = data.get("attachments", None)
1480
1481    attachments = XML.SubElement(rocketchat, "attachments")
1482    if attach_list is not None:
1483        for attach_data in attach_list:
1484            item = XML.SubElement(
1485                attachments,
1486                "jenkins.plugins.rocketchatnotifier.model.MessageAttachment",
1487            )
1488            helpers.convert_mapping_to_xml(
1489                item, attach_data, attach_required_mapping, fail_required=True
1490            )
1491            helpers.convert_mapping_to_xml(
1492                item, attach_data, attach_optional_mapping, fail_required=False
1493            )
1494
1495    XML.SubElement(rocketchat, "customMessage").text = data.get("custom-message", "")
1496
1497
1498def hp_alm(registry, xml_parent, data):
1499    """yaml: hp-alm
1500    Publish test results to HP-ALM.
1501
1502    Requires the Jenkins :jenkins-plugins:`Micro Focus Application Automation
1503    Tools <hp-application-automation-tools-plugin>`.
1504
1505    :arg str server-name: The name of the ALM Server. (required)
1506    :arg str credentials-id: credentials-id of the user (default '')
1507    :arg str domaine: The Domain of the project to be used. (required)
1508    :arg str client-type: Client type is required for some ALM above 12.60
1509        in authentication.(default '')
1510    :arg str project: The project to be used. (required)
1511    :arg str testing-framework: The testing framework that is used when
1512        generate the testing result file. (default Junit)
1513    :arg str testing-tool: The testing tool that is used when generate
1514        the testing result file. (default '')
1515    :arg str folder: The path of the test folder that will contain
1516        the uploaded test.
1517        The path doesn't include the Root test folder (Subject).
1518        For example, sampletestfolder/subfolder means, the tests will be
1519        uploaded to test folder named 'subfolder', which is under
1520        the test folder named 'sampletestfolder', and 'sampletestfolder'
1521        is under the root test folder 'Subject'. (required)
1522    :arg str set-folder: The path of the testset folder that will contain
1523        the uploaded testset. The path doesn't include the Root testset folder.
1524        For example, sampletestsetfolder/subfolder means, the testsets will be
1525        uploaded to testset folder named 'subfolder', which is under
1526        the testset folder named 'sampletestsetfolder',
1527        and 'sampletestsetfolder' is under the root testset folder 'Root'.
1528        (required)
1529    :arg str testing-result-file: The condition to find the testing
1530        result file, start from the root path of the job.
1531        For example, ``**/junitResult.xml`` to find testing result file
1532        for Junit Plugin, ``**/testng-results.xml`` to find
1533        testing result file for TestNG plugin. (required)
1534    :arg str jenkins-server-url: The HTTP URL of the Jenkins Server,
1535        form example, http://jenkins.example.org:8080 . (optional)
1536
1537    Minimal example using defaults:
1538
1539    .. literalinclude::  /../../tests/publishers/fixtures/hp-alm001.yaml
1540       :language: yaml
1541
1542    Full example:
1543
1544    .. literalinclude::  /../../tests/publishers/fixtures/hp-alm002.yaml
1545       :language: yaml
1546    """
1547    alm_uploader = XML.SubElement(
1548        xml_parent,
1549        "com.microfocus.application.automation."
1550        "tools.results.TestResultToALMUploader",
1551    )
1552    alm_uploader.set("plugin", "hp-application-automation-tools-plugin")
1553    mapping = [
1554        ("server-name", "almServerName", None),
1555        ("credentials-id", "credentialsId", ""),
1556        ("domain", "almDomain", None),
1557        ("project", "almProject", None),
1558        ("client-type", "clientType", ""),
1559        ("testing-framework", "testingFramework", "JUnit"),
1560        ("testing-tool", "testingTool", ""),
1561        ("folder", "almTestFolder", None),
1562        ("set-folder", "almTestSetFolder", None),
1563        ("testing-result-file", "testingResultFile", None),
1564        ("jenkins-server-url", "jenkinsServerUrl", ""),
1565    ]
1566    helpers.convert_mapping_to_xml(alm_uploader, data, mapping, fail_required=True)
1567
1568
1569def junit(registry, xml_parent, data):
1570    """yaml: junit
1571    Publish JUnit test results. Requires the Jenkins :jenkins-plugins:`JUnit
1572    Plugin <junit>`.
1573
1574    :arg str results: results filename (required)
1575    :arg bool keep-long-stdio: Retain long standard output/error in test
1576        results (default true).
1577    :arg float health-scale-factor: Amplification factor to apply to test
1578        failures when computing the test result contribution to the build
1579        health score. (default 1.0)
1580    :arg bool allow-empty-results: Do not fail the build on empty test results
1581        (default false)
1582    :arg bool skip-publishing-checks: Do not publish issues to SCM provider
1583        platforms (default false)
1584    :arg bool skip-marking-build-unstable: Do not mark build as unstable on
1585        test failure (default false)
1586    :arg bool test-stability: Add historical information about test
1587        results stability (default false).
1588        Requires the Jenkins :jenkins-plugins:`Test stability Plugin
1589        <test-stability>`.
1590    :arg bool claim-build: Allow claiming of failed tests (default false)
1591        Requires the Jenkins :jenkins-plugins:`Claim Plugin <claim>`.
1592    :arg bool measurement-plots: Create measurement plots (default false)
1593        Requires the Jenkins :jenkins-github:`Measurement Plots Plugin
1594        <measurement-plots-plugin>`.
1595    :arg bool flaky-test-reports: Publish flaky test reports (default false).
1596        Requires the Jenkins :jenkins-plugins:`Flaky Test Handler Plugin
1597        <flaky-test-handler>`.
1598    :arg bool junit-attachments: Publish test attachments (default false).
1599        Requires the Jenkins :jenkins-plugins:`JUnit Attachments Plugin
1600        <junit-attachments>`.
1601
1602
1603    Minimal example using defaults:
1604
1605    .. literalinclude::  /../../tests/publishers/fixtures/junit001.yaml
1606       :language: yaml
1607
1608    Full example:
1609
1610    .. literalinclude::  /../../tests/publishers/fixtures/junit002.yaml
1611       :language: yaml
1612    """
1613    junitresult = XML.SubElement(xml_parent, "hudson.tasks.junit.JUnitResultArchiver")
1614    junitresult.set("plugin", "junit")
1615    mapping = [
1616        ("results", "testResults", None),
1617        ("keep-long-stdio", "keepLongStdio", True),
1618        ("health-scale-factor", "healthScaleFactor", "1.0"),
1619        ("allow-empty-results", "allowEmptyResults", False),
1620        ("skip-publishing-checks", "skipPublishingChecks", False),
1621        ("skip-marking-build-unstable", "skipMarkingBuildUnstable", False),
1622    ]
1623    helpers.convert_mapping_to_xml(junitresult, data, mapping, fail_required=True)
1624
1625    datapublisher = XML.SubElement(junitresult, "testDataPublishers")
1626    if str(data.get("test-stability", False)).lower() == "true":
1627        XML.SubElement(
1628            datapublisher,
1629            "de.esailors.jenkins.teststability" ".StabilityTestDataPublisher",
1630        )
1631    if str(data.get("claim-build", False)).lower() == "true":
1632        XML.SubElement(datapublisher, "hudson.plugins.claim.ClaimTestDataPublisher")
1633    if str(data.get("measurement-plots", False)).lower() == "true":
1634        XML.SubElement(
1635            datapublisher, "hudson.plugins.measurement__plots.TestDataPublisher"
1636        )
1637    if str(data.get("flaky-test-reports", False)).lower() == "true":
1638        XML.SubElement(
1639            datapublisher,
1640            "com.google.jenkins.flakyTestHandler.plugin" ".JUnitFlakyTestDataPublisher",
1641        )
1642    if str(data.get("junit-attachments", False)).lower() == "true":
1643        XML.SubElement(
1644            datapublisher, "hudson.plugins.junitattachments.AttachmentPublisher"
1645        )
1646
1647
1648def cucumber_reports(registry, xml_parent, data):
1649    """yaml: cucumber-reports
1650    This plugin creates pretty cucumber-jvm html reports on jenkins.
1651
1652    Requires the Jenkins :jenkins-plugins:`cucumber reports
1653    <cucumber-reports>`.
1654
1655    :arg str json-reports-path: The path relative to the workspace of
1656        the json reports generated by cucumber-jvm e.g. target - leave
1657        empty to scan the whole workspace (default '')
1658    :arg str file-include-pattern: Include pattern (default '')
1659    :arg str file-exclude-pattern: Exclude pattern (default '')
1660    :arg str plugin-url-path: The path to the jenkins user content url
1661        e.g. :samp:`http://host:port[/jenkins/]plugin` - leave empty if jenkins
1662        url root is host:port (default '')
1663    :arg bool skipped-fails: Skipped steps to cause the build to fail
1664        (default false)
1665    :arg bool pending-fails: Pending steps to cause the build to fail
1666        (default false)
1667    :arg bool undefined-fails: Undefined steps to cause the build to fail
1668        (default false)
1669    :arg bool missing-fails: Missing steps to cause the build to fail
1670        (default false)
1671    :arg bool no-flash-charts: Use javascript charts instead of flash charts
1672        (default false)
1673    :arg bool ignore-failed-tests: Entire build to fail when these tests fail
1674        (default false)
1675    :arg bool parallel-testing: Run same test in parallel for multiple devices
1676        (default false)
1677    :arg int failed-steps-number: Maximum number of failed steps above which
1678        build result is changed (default 0)
1679    :arg int skipped-steps-number: Maximum number of skipped steps above which
1680        build result is changed (default 0)
1681    :arg int pending-steps-number: Maximum number of pending steps above which
1682        build result is changed (default 0)
1683    :arg int undefined-steps-number: Maximum number of undefined steps above
1684        which build result is changed (default 0)
1685    :arg int failed-scenarios-number: Maximum number of failed scenarios above
1686        which build result is changed (default 0)
1687    :arg int failed-features-number: Maximum number of failed features above
1688        which build result is changed (default 0)
1689    :arg list build-status: Build result to which the build should be set
1690        when the report becomes failed or unstable (default '')
1691    :arg int trends-limit: Number of past reports that should be presented.
1692        Zero means unlimited number of builds (default 0)
1693    :arg list sorting-method: Result sorting order (default 'NATURAL')
1694
1695    Full example:
1696
1697    .. literalinclude::
1698       /../../tests/publishers/fixtures/cucumber-reports-full.yaml
1699       :language: yaml
1700
1701    Minimal Example:
1702
1703    .. literalinclude::
1704       /../../tests/publishers/fixtures/cucumber-reports-minimal.yaml
1705       :language: yaml
1706    """
1707    cucumber_reports = XML.SubElement(
1708        xml_parent, "net.masterthought.jenkins." "CucumberReportPublisher"
1709    )
1710    cucumber_reports.set("plugin", "cucumber-reports")
1711
1712    valid_build_status = ["", "UNSTABLE", "FAILURE"]
1713    valid_sorting_method = ["NATURAL", "ALPHABETICAL"]
1714    mappings = [
1715        ("json-reports-path", "jsonReportDirectory", ""),
1716        ("plugin-url-path", "pluginUrlPath", ""),
1717        ("file-include-pattern", "fileIncludePattern", ""),
1718        ("file-exclude-pattern", "fileExcludePattern", ""),
1719        ("skipped-fails", "skippedFails", False),
1720        ("pending-fails", "pendingFails", False),
1721        ("undefined-fails", "undefinedFails", False),
1722        ("missing-fails", "missingFails", False),
1723        ("no-flash-charts", "noFlashCharts", False),
1724        ("ignore-failed-tests", "ignoreFailedTests", False),
1725        ("parallel-testing", "parallelTesting", False),
1726        ("failed-steps-number", "failedStepsNumber", 0),
1727        ("skipped-steps-number", "skippedStepsNumber", 0),
1728        ("pending-steps-number", "pendingStepsNumber", 0),
1729        ("undefined-steps-number", "undefinedStepsNumber", 0),
1730        ("failed-scenarios-number", "failedScenariosNumber", 0),
1731        ("failed-features-number", "failedFeaturesNumber", 0),
1732        ("build-status", "buildStatus", "", valid_build_status),
1733        ("trends-limit", "trendsLimit", 0),
1734        ("sorting-method", "sortingMethod", "NATURAL", valid_sorting_method),
1735    ]
1736    helpers.convert_mapping_to_xml(cucumber_reports, data, mappings, fail_required=True)
1737
1738    if "sorting-values" in data:
1739        format_dict = {
1740            "classifications": "net.masterthought.jenkins"
1741            ".CucumberReportPublisher_-Classification"
1742        }
1743        classifications_tag = XML.SubElement(cucumber_reports, "classifications")
1744        for values in data["sorting-values"]:
1745            for value, params in values.items():
1746                cucumber_report_publisher = XML.SubElement(
1747                    classifications_tag, format_dict.get("classifications")
1748                )
1749                XML.SubElement(cucumber_report_publisher, "key").text = params.get(
1750                    "key"
1751                )
1752                XML.SubElement(cucumber_report_publisher, "value").text = params.get(
1753                    "value"
1754                )
1755
1756
1757def cucumber_testresult(registry, xml_parent, data):
1758    """yaml: cucumber-testresult
1759    Publish cucumber test results.
1760    Requires the Jenkins :jenkins-plugins:`Cucumber testresult
1761    <cucumber-testresult-plugin>`.
1762
1763    :arg str results: Results filename (required)
1764    :arg bool ignore-bad-steps: Ignore not existed step results (default false)
1765
1766    Minimal example:
1767
1768    .. literalinclude::
1769       /../../tests/publishers/fixtures/cucumber-testresult-minimal.yaml
1770       :language: yaml
1771
1772    Full Example:
1773
1774    .. literalinclude::
1775       /../../tests/publishers/fixtures/cucumber-testresult-full.yaml
1776       :language: yaml
1777    """
1778    cucumber_result = XML.SubElement(
1779        xml_parent,
1780        "org.jenkinsci.plugins.cucumber."
1781        "jsontestsupport."
1782        "CucumberTestResultArchiver",
1783    )
1784    cucumber_result.set("plugin", "cucumber-testresult-plugin")
1785
1786    mappings = [
1787        ("results", "testResults", None),
1788        ("ignore-bad-steps", "ignoreBadSteps", False),
1789    ]
1790    helpers.convert_mapping_to_xml(cucumber_result, data, mappings, fail_required=True)
1791
1792
1793def xunit(registry, xml_parent, data):
1794    """yaml: xunit
1795    Publish tests results. Requires the Jenkins :jenkins-plugins:`xUnit Plugin
1796    <xunit>`.
1797
1798    :arg str thresholdmode: Whether thresholds represents an absolute number
1799        of tests or a percentage. Either 'number' or 'percent'. (default
1800        'number')
1801    :arg list thresholds: Thresholds for both 'failed' and 'skipped' tests.
1802
1803        :threshold (`dict`): Threshold values to set, where missing, xUnit
1804            should default to an internal value of 0. Each test threshold
1805            should contain the following:
1806
1807            * **unstable** (`int`)
1808            * **unstablenew** (`int`)
1809            * **failure** (`int`)
1810            * **failurenew** (`int`)
1811
1812    :arg int test-time-margin: Give the report time margin value in ms, before
1813        to fail if not new unless the option **requireupdate** is set for the
1814        configured framework. (default 3000)
1815    :arg list types: Frameworks to configure, and options. Supports the
1816        following: ``aunit``, ``boosttest``, ``checktype``, ``cpptest``,
1817        ``cppunit``, ``ctest``, ``dotnettest``, ``embunit``, ``fpcunit``,
1818        ``gtest``, ``junit``, ``mstest``, ``nunit``, ``phpunit``, ``tusar``,
1819        ``unittest``, and ``valgrind``.
1820
1821        The 'custom' type is not supported.
1822
1823        :type (`dict`): each type can be configured using the following:
1824
1825            * **pattern** (`str`): An Ant pattern to look for Junit result
1826              files, relative to the workspace root (default '')
1827            * **requireupdate** (`bool`): fail the build whenever fresh tests
1828              results have not been found (default true).
1829            * **deleteoutput** (`bool`): delete temporary JUnit files
1830              (default true).
1831            * **skip-if-no-test-files** (`bool`): Skip parsing this xUnit type
1832              report if there are no test reports files (default false).
1833            * **stoponerror** (`bool`): Fail the build whenever an error occur
1834              during a result file processing (default true).
1835
1836    Example:
1837
1838    .. literalinclude::  /../../tests/publishers/fixtures/xunit001.yaml
1839       :language: yaml
1840
1841    """
1842    info = registry.get_plugin_info("xunit")
1843    plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
1844
1845    logger = logging.getLogger(__name__)
1846    xunit = XML.SubElement(xml_parent, "xunit")
1847    xunit.set("plugin", "xunit")
1848
1849    # Map our internal types to the XML element names used by Jenkins plugin
1850    types_to_plugin_types = {
1851        "aunit": "AUnitJunitHudsonTestType",
1852        "boosttest": "BoostTestJunitHudsonTestType",
1853        "checktype": "CheckType",
1854        "cpptest": "CppTestJunitHudsonTestType",
1855        "cppunit": "CppUnitJunitHudsonTestType",
1856        "ctest": "CTestType",
1857        "dotnettest": "XUnitDotNetTestType",  # since plugin v1.93
1858        "embunit": "EmbUnitType",  # since plugin v1.84
1859        "fpcunit": "FPCUnitJunitHudsonTestType",
1860        "gtest": "GoogleTestType",
1861        "junit": "JUnitType",
1862        "mstest": "MSTestJunitHudsonTestType",
1863        "nunit": "NUnitJunitHudsonTestType",
1864        "phpunit": "PHPUnitJunitHudsonTestType",
1865        "tusar": "TUSARJunitHudsonTestType",
1866        "unittest": "UnitTestJunitHudsonTestType",
1867        "valgrind": "ValgrindJunitHudsonTestType",
1868        # FIXME should implement the 'custom' type
1869    }
1870    implemented_types = types_to_plugin_types.keys()  # shortcut
1871
1872    # Unit framework we are going to generate xml for
1873    supported_types = []
1874
1875    for configured_type in data["types"]:
1876        type_name = next(iter(configured_type.keys()))
1877        if type_name not in implemented_types:
1878            logger.warning("Requested xUnit type '%s' is not yet supported", type_name)
1879        else:
1880            # Append for generation
1881            supported_types.append(configured_type)
1882
1883    # Generate XML for each of the supported framework types
1884    # Note: versions 3+ are now using the 'tools' sub-element instead of 'types'
1885    if plugin_version < pkg_resources.parse_version("3.0.0"):
1886        types_name = "types"
1887    else:
1888        types_name = "tools"
1889
1890    xmltypes = XML.SubElement(xunit, types_name)
1891    for supported_type in supported_types:
1892        framework_name = next(iter(supported_type.keys()))
1893        xmlframework = XML.SubElement(xmltypes, types_to_plugin_types[framework_name])
1894
1895        mappings = [
1896            ("pattern", "pattern", ""),
1897            ("requireupdate", "failIfNotNew", True),
1898            ("deleteoutput", "deleteOutputFiles", True),
1899            ("skip-if-no-test-files", "skipNoTestFiles", False),
1900            ("stoponerror", "stopProcessingIfError", True),
1901        ]
1902        helpers.convert_mapping_to_xml(
1903            xmlframework, supported_type[framework_name], mappings, fail_required=True
1904        )
1905
1906    xmlthresholds = XML.SubElement(xunit, "thresholds")
1907    for t in data.get("thresholds", []):
1908        if not ("failed" in t or "skipped" in t):
1909            logger.warning("Unrecognized threshold, should be 'failed' or 'skipped'")
1910            continue
1911        elname = (
1912            "org.jenkinsci.plugins.xunit.threshold.%sThreshold"
1913            % next(iter(t.keys())).title()
1914        )
1915        el = XML.SubElement(xmlthresholds, elname)
1916        for threshold_name, threshold_value in next(iter(t.values())).items():
1917            # Normalize and craft the element name for this threshold
1918            elname = "%sThreshold" % threshold_name.lower().replace("new", "New")
1919            XML.SubElement(el, elname).text = str(threshold_value)
1920
1921    # Whether to use percent of exact number of tests.
1922    # Thresholdmode is either:
1923    # - 1 : absolute (number of tests), default.
1924    # - 2 : relative (percentage of tests)
1925    thresholdmode = "1"
1926    if "percent" == data.get("thresholdmode", "number"):
1927        thresholdmode = "2"
1928    XML.SubElement(xunit, "thresholdMode").text = thresholdmode
1929
1930    extra_config = XML.SubElement(xunit, "extraConfiguration")
1931    XML.SubElement(extra_config, "testTimeMargin").text = str(
1932        data.get("test-time-margin", "3000")
1933    )
1934
1935
1936def _violations_add_entry(xml_parent, name, data):
1937    vmin = data.get("min", 10)
1938    vmax = data.get("max", 999)
1939    vunstable = data.get("unstable", 999)
1940    pattern = data.get("pattern", None)
1941
1942    entry = XML.SubElement(xml_parent, "entry")
1943    mapping = [("", "string", name)]
1944    helpers.convert_mapping_to_xml(entry, data, mapping, fail_required=True)
1945
1946    tconfig = XML.SubElement(entry, "hudson.plugins.violations.TypeConfig")
1947    mapping = [
1948        ("", "type", name),
1949        ("", "min", str(vmin)),
1950        ("", "max", str(vmax)),
1951        ("", "unstable", str(vunstable)),
1952        ("", "usePattern", "false"),
1953    ]
1954    helpers.convert_mapping_to_xml(tconfig, data, mapping, fail_required=True)
1955
1956    if pattern:
1957        XML.SubElement(tconfig, "pattern").text = pattern
1958    else:
1959        XML.SubElement(tconfig, "pattern")
1960
1961
1962def violations(registry, xml_parent, data):
1963    """yaml: violations
1964    Publish code style violations.
1965    Requires the Jenkins :jenkins-plugins:`Violations Plugin <violations>`.
1966
1967    The violations component accepts any number of dictionaries keyed
1968    by the name of the violations system.  The dictionary has the
1969    following values:
1970
1971    :arg int min: sunny threshold
1972    :arg int max: stormy threshold
1973    :arg int unstable: unstable threshold
1974    :arg str pattern: report filename pattern
1975
1976    Any system without a dictionary provided will use default values.
1977
1978    Valid systems are:
1979
1980      checkstyle, codenarc, cpd, cpplint, csslint, findbugs, fxcop,
1981      gendarme, jcreport, jslint, pep8, perlcritic, pmd, pylint,
1982      simian, stylecop
1983
1984    Example:
1985
1986    .. literalinclude::  /../../tests/publishers/fixtures/violations001.yaml
1987       :language: yaml
1988    """
1989    violations = XML.SubElement(
1990        xml_parent, "hudson.plugins.violations." "ViolationsPublisher"
1991    )
1992    config = XML.SubElement(violations, "config")
1993    suppressions = XML.SubElement(config, "suppressions", {"class": "tree-set"})
1994    XML.SubElement(suppressions, "no-comparator")
1995    configs = XML.SubElement(config, "typeConfigs")
1996    XML.SubElement(configs, "no-comparator")
1997
1998    for name in [
1999        "checkstyle",
2000        "codenarc",
2001        "cpd",
2002        "cpplint",
2003        "csslint",
2004        "findbugs",
2005        "fxcop",
2006        "gendarme",
2007        "jcreport",
2008        "jslint",
2009        "pep8",
2010        "perlcritic",
2011        "pmd",
2012        "pylint",
2013        "simian",
2014        "stylecop",
2015    ]:
2016        _violations_add_entry(configs, name, data.get(name, {}))
2017    mapping = [
2018        ("", "limit", "100"),
2019        ("", "sourcePathPattern", ""),
2020        ("", "fauxProjectPath", ""),
2021        ("", "encoding", "default"),
2022    ]
2023    helpers.convert_mapping_to_xml(config, data, mapping, fail_required=True)
2024
2025
2026def findbugs(registry, xml_parent, data):
2027    r"""yaml: findbugs
2028    FindBugs reporting for builds
2029
2030    Requires the Jenkins FindBugs Plugin
2031    (https://github.com/jenkinsci/findbugs-plugin).
2032
2033    :arg str pattern: specifies the generated raw FindBugs XML report files,
2034        such as \*\*/findbugs.xml or \*\*/findbugsXml.xml. (default '')
2035    :arg bool rank-priority: Use rank as priority (default false)
2036    :arg str include-files: Comma separated list of files to include.
2037        (default '')
2038    :arg str exclude-files: Comma separated list of files to exclude.
2039        (default '')
2040    :arg bool can-run-on-failed: Weather or not to run plug-in on failed builds
2041        (default false)
2042    :arg bool should-detect-modules: Determines if Ant or Maven modules should
2043        be detected for all files that contain warnings. (default false)
2044    :arg int healthy: Sunny threshold (default '')
2045    :arg int unhealthy: Stormy threshold (default '')
2046    :arg str health-threshold: Threshold priority for health status
2047        ('low', 'normal' or 'high', defaulted to 'low')
2048    :arg bool dont-compute-new: If set to false, computes new warnings based on
2049        the reference build (default true)
2050    :arg bool use-delta-values: Use delta for new warnings. (default false)
2051    :arg bool use-previous-build-as-reference:  If set then the number of new
2052        warnings will always be calculated based on the previous build.
2053        Otherwise the reference build. (default false)
2054    :arg bool use-stable-build-as-reference: The number of new warnings will be
2055        calculated based on the last stable build, allowing reverts of unstable
2056        builds where the number of warnings was decreased. (default false)
2057    :arg dict thresholds:
2058        :thresholds:
2059            * **unstable** (`dict`)
2060                :unstable: * **total-all** (`int`)
2061                           * **total-high** (`int`)
2062                           * **total-normal** (`int`)
2063                           * **total-low** (`int`)
2064                           * **new-all** (`int`)
2065                           * **new-high** (`int`)
2066                           * **new-normal** (`int`)
2067                           * **new-low** (`int`)
2068
2069            * **failed** (`dict`)
2070                :failed: * **total-all** (`int`)
2071                         * **total-high** (`int`)
2072                         * **total-normal** (`int`)
2073                         * **total-low** (`int`)
2074                         * **new-all** (`int`)
2075                         * **new-high** (`int`)
2076                         * **new-normal** (`int`)
2077                         * **new-low** (`int`)
2078
2079    Minimal Example:
2080
2081    .. literalinclude:: /../../tests/publishers/fixtures/findbugs-minimal.yaml
2082
2083    Full Example:
2084
2085    .. literalinclude:: /../../tests/publishers/fixtures/findbugs-full.yaml
2086    """
2087    findbugs = XML.SubElement(xml_parent, "hudson.plugins.findbugs.FindBugsPublisher")
2088    findbugs.set("plugin", "findbugs")
2089
2090    helpers.findbugs_settings(findbugs, data)
2091    helpers.build_trends_publisher("[FINDBUGS] ", findbugs, data)
2092
2093
2094def checkstyle(registry, xml_parent, data):
2095    """yaml: checkstyle
2096    Publish trend reports with Checkstyle.
2097
2098    Requires the Jenkins Checkstyle Plugin
2099    (https://github.com/jenkinsci/checkstyle-plugin).
2100
2101    The checkstyle component accepts a dictionary with the
2102    following values:
2103
2104    :arg str pattern: Report filename pattern (default '')
2105    :arg bool can-run-on-failed: Also runs for failed builds, instead of just
2106        stable or unstable builds (default false)
2107    :arg bool should-detect-modules: Determines if Ant or Maven modules should
2108        be detected for all files that contain warnings (default false)
2109    :arg int healthy: Sunny threshold (default '')
2110    :arg int unhealthy: Stormy threshold (default '')
2111    :arg str health-threshold: Threshold priority for health status
2112        ('low', 'normal' or 'high') (default 'low')
2113    :arg dict thresholds: Mark build as failed or unstable if the number of
2114        errors exceeds a threshold. (optional)
2115
2116        :thresholds:
2117            * **unstable** (`dict`)
2118                :unstable: * **total-all** (`int`)
2119                           * **total-high** (`int`)
2120                           * **total-normal** (`int`)
2121                           * **total-low** (`int`)
2122                           * **new-all** (`int`)
2123                           * **new-high** (`int`)
2124                           * **new-normal** (`int`)
2125                           * **new-low** (`int`)
2126
2127            * **failed** (`dict`)
2128                :failed: * **total-all** (`int`)
2129                         * **total-high** (`int`)
2130                         * **total-normal** (`int`)
2131                         * **total-low** (`int`)
2132                         * **new-all** (`int`)
2133                         * **new-high** (`int`)
2134                         * **new-normal** (`int`)
2135                         * **new-low** (`int`)
2136    :arg str default-encoding: Encoding for parsing or showing files
2137        (default '')
2138    :arg bool do-not-resolve-relative-paths: (default false)
2139    :arg bool dont-compute-new: If set to false, computes new warnings based on
2140        the reference build (default true)
2141    :arg bool use-previous-build-as-reference: determines whether to always
2142        use the previous build as the reference build (default false)
2143    :arg bool use-stable-build-as-reference: The number of new warnings will be
2144        calculated based on the last stable build, allowing reverts of unstable
2145        builds where the number of warnings was decreased. (default false)
2146    :arg bool use-delta-values: If set then the number of new warnings is
2147        calculated by subtracting the total number of warnings of the current
2148        build from the reference build. (default false)
2149
2150    Example:
2151
2152    .. literalinclude::  /../../tests/publishers/fixtures/checkstyle004.yaml
2153       :language: yaml
2154
2155    Full example:
2156
2157    .. literalinclude::  /../../tests/publishers/fixtures/checkstyle006.yaml
2158       :language: yaml
2159    """
2160
2161    def convert_settings(lookup, data):
2162        """Helper to convert settings from one key to another."""
2163
2164        for old_key in list(data.keys()):
2165            if old_key in lookup:
2166                data.setdefault(lookup[old_key], data[old_key])
2167                del data[old_key]
2168
2169    checkstyle = XML.SubElement(
2170        xml_parent, "hudson.plugins.checkstyle." "CheckStylePublisher"
2171    )
2172    checkstyle.set("plugin", "checkstyle")
2173
2174    # Convert old style yaml to new style
2175    convert_settings(
2176        {
2177            "unHealthy": "unhealthy",
2178            "healthThreshold": "health-threshold",
2179            "defaultEncoding": "default-encoding",
2180            "canRunOnFailed": "can-run-on-failed",
2181            "shouldDetectModules": "should-detect-modules",
2182        },
2183        data,
2184    )
2185
2186    threshold_data = data.get("thresholds", {})
2187    for threshold in ["unstable", "failed"]:
2188        convert_settings(
2189            {
2190                "totalAll": "total-all",
2191                "totalHigh": "total-high",
2192                "totalNormal": "total-normal",
2193                "totalLow": "total-low",
2194            },
2195            threshold_data.get(threshold, {}),
2196        )
2197
2198    helpers.build_trends_publisher("[CHECKSTYLE] ", checkstyle, data)
2199
2200
2201def scp(registry, xml_parent, data):
2202    """yaml: scp
2203    Upload files via SCP
2204    Requires the Jenkins :jenkins-plugins:`SCP Plugin <scp>`.
2205
2206    When writing a publisher macro, it is important to keep in mind that
2207    Jenkins uses Ant's `SCP Task
2208    <https://ant.apache.org/manual/Tasks/scp.html>`_ via the Jenkins
2209    :jenkins-plugins:`SCP Plugin <scp>` which relies on `FileSet
2210    <https://ant.apache.org/manual/Types/fileset.html>`_
2211    and `DirSet <https://ant.apache.org/manual/Types/dirset.html>`_ patterns.
2212    The relevant piece of documentation is excerpted below:
2213
2214        Source points to files which will be uploaded. You can use ant
2215        includes syntax, eg. ``folder/dist/*.jar``. Path is constructed from
2216        workspace root. Note that you cannot point files outside the workspace
2217        directory.  For example providing: ``../myfile.txt`` won't work...
2218        Destination points to destination folder on remote site. It will be
2219        created if doesn't exists and relative to root repository path. You
2220        can define multiple blocks of source/destination pairs.
2221
2222    This means that absolute paths, e.g., ``/var/log/**`` will not work and
2223    will fail to compile. All paths need to be relative to the directory that
2224    the publisher runs and the paths have to be contained inside of that
2225    directory. The relative working directory is usually::
2226
2227        /home/jenkins/workspace/${JOB_NAME}
2228
2229    :arg str site: name of the scp site (required)
2230    :arg str target: destination directory (required)
2231    :arg str source: source path specifier (default '')
2232    :arg bool keep-hierarchy: keep the file hierarchy when uploading
2233      (default false)
2234    :arg bool copy-after-failure: copy files even if the job fails
2235      (default false)
2236    :arg bool copy-console: copy the console log (default false); if
2237      specified, omit 'source'
2238
2239    Example:
2240
2241    .. literalinclude:: /../../tests/publishers/fixtures/scp001.yaml
2242       :language: yaml
2243    """
2244    scp = XML.SubElement(
2245        xml_parent, "be.certipost.hudson.plugin.SCPRepositoryPublisher"
2246    )
2247    scp.set("plugin", "scp")
2248
2249    mappings = [("site", "siteName", None)]
2250    helpers.convert_mapping_to_xml(scp, data, mappings, fail_required=True)
2251
2252    entries = XML.SubElement(scp, "entries")
2253    for entry in data["files"]:
2254        entry_e = XML.SubElement(entries, "be.certipost.hudson.plugin.Entry")
2255        mappings = [
2256            ("target", "filePath", None),
2257            ("source", "sourceFile", ""),
2258            ("keep-hierarchy", "keepHierarchy", False),
2259            ("copy-console", "copyConsoleLog", False),
2260            ("copy-after-failure", "copyAfterFailure", False),
2261        ]
2262        helpers.convert_mapping_to_xml(entry_e, entry, mappings, fail_required=True)
2263
2264
2265def ssh(registry, xml_parent, data):
2266    """yaml: ssh
2267    Upload files via SCP.
2268    Requires the Jenkins :jenkins-plugins:`Publish over SSH Plugin
2269    <publish-over-ssh>`.
2270
2271    :arg str site: name of the ssh site
2272    :arg str target: destination directory
2273    :arg bool target-is-date-format: whether target is a date format. If true,
2274      raw text should be quoted (default false)
2275    :arg bool clean-remote: should the remote directory be deleted before
2276      transferring files (default false)
2277    :arg str source: source path specifier
2278    :arg str command: a command to execute on the remote server (optional)
2279    :arg int timeout: timeout in milliseconds for the Exec command (optional)
2280    :arg bool use-pty: run the exec command in pseudo TTY (default false)
2281    :arg str excludes: excluded file pattern (optional)
2282    :arg str remove-prefix: prefix to remove from uploaded file paths
2283      (optional)
2284    :arg bool fail-on-error: fail the build if an error occurs (default false).
2285    :arg bool always-publish-from-master: transfer the files through the master
2286      before being sent to the remote server (defaults false)
2287    :arg bool flatten: only create files on the server, don't create
2288      directories (default false).
2289    :arg bool verbose: adds lots of detail useful for debug to the console
2290      but generally should be left off (default false)
2291    :arg int retries: the number of times to retry this server in the event of
2292      failure (optional)
2293    :arg int retry-delay: the time to wait, in milliseconds, before attempting
2294      another transfer (default 10000)
2295
2296    Minimal Example:
2297
2298        .. literalinclude:: /../../tests/publishers/fixtures/ssh-minimal.yaml
2299           :language: yaml
2300
2301    Full Example:
2302
2303        .. literalinclude::  /../../tests/publishers/fixtures/ssh-full.yaml
2304           :language: yaml
2305    """
2306    console_prefix = "SSH: "
2307    tag_prefix = "jenkins.plugins.publish"
2308    publisher_tag = "%s__over__ssh.BapSshPublisher" % tag_prefix
2309    transfer_tag = "%s__over__ssh.BapSshTransfer" % tag_prefix
2310    retry_tag = "%s_over_ssh.BapSshRetry" % tag_prefix
2311    reference_tag = "%s_over_ssh.BapSshPublisherPlugin" % tag_prefix
2312
2313    if xml_parent.tag == "publishers":
2314        plugin_tag = "%s__over__ssh.BapSshPublisherPlugin" % tag_prefix
2315        is_builder = False
2316    else:
2317        plugin_tag = "%s__over__ssh.BapSshBuilderPlugin" % tag_prefix
2318        is_builder = True
2319
2320    base_publish_over(
2321        xml_parent,
2322        data,
2323        console_prefix,
2324        plugin_tag,
2325        publisher_tag,
2326        transfer_tag,
2327        retry_tag,
2328        reference_tag,
2329        is_builder,
2330    )
2331
2332
2333def pipeline(registry, xml_parent, data):
2334    """yaml: pipeline
2335    Specify a downstream project in a pipeline.
2336    Requires the Jenkins :jenkins-plugins:`Build Pipeline Plugin
2337    <build-pipeline-plugin>`.
2338
2339    Use of the `node-label-name` or `node-label` parameters
2340    requires the Jenkins :jenkins-plugins:`NodeLabel Parameter Plugin
2341    <nodelabelparameter>`.
2342    Note: 'node-parameters' overrides the Node that the triggered
2343    project is tied to.
2344
2345    :arg list projects: list the jobs to trigger, will generate comma-separated
2346        string containing the named jobs.
2347    :arg str predefined-parameters: parameters to pass to the other
2348      job (optional)
2349    :arg bool current-parameters: Whether to include the parameters passed
2350      to the current build to the triggered job (optional)
2351    :arg bool node-parameters: Use the same Node for the triggered builds
2352        that was used for this build. (optional)
2353    :arg bool svn-revision: Pass svn revision to the triggered job (optional)
2354    :arg bool include-upstream: Include/pass through Upstream SVN Revisons.
2355        Only valid when 'svn-revision' is true. (default false)
2356    :arg dict git-revision: Passes git revision to the triggered job
2357        (optional).
2358
2359        * **combine-queued-commits** (bool): Whether to combine queued git
2360          hashes or not (default false)
2361
2362    :arg dict boolean-parameters: Pass boolean parameters to the downstream
2363        jobs. Specify the name and boolean value mapping of the parameters.
2364        (optional)
2365    :arg str property-file: Use properties from file (optional)
2366    :arg bool fail-on-missing: Blocks the triggering of the downstream jobs
2367        if any of the property files are not found in the workspace.
2368        Only valid when 'property-file' is specified.
2369        (default false)
2370    :arg str file-encoding: Encoding of contents of the files. If not
2371        specified, default encoding of the platform is used. Only valid when
2372        'property-file' is specified. (optional)
2373    :arg str restrict-matrix-project: Filter that restricts the subset
2374        of the combinations that the downstream project will run (optional)
2375
2376    Example:
2377
2378    .. literalinclude:: /../../tests/publishers/fixtures/pipeline002.yaml
2379       :language: yaml
2380
2381    .. literalinclude:: /../../tests/publishers/fixtures/pipeline003.yaml
2382       :language: yaml
2383
2384
2385    You can build pipeline jobs that are re-usable in different pipelines by
2386    using a :ref:`job-template` to define the pipeline jobs,
2387    and variable substitution to specify the name of
2388    the downstream job in the pipeline.
2389    Job-specific substitutions are useful here (see :ref:`project`).
2390
2391    See 'samples/pipeline.yaml' for an example pipeline implementation.
2392    """
2393    logger = logging.getLogger("%s:pipeline" % __name__)
2394    param_order = helpers.trigger_get_parameter_order(registry, "pipeline")
2395
2396    if "project" in data:
2397        logger.warning(
2398            "Using 'project' for pipeline definition is deprecated. Please "
2399            "update your job definition to use 'projects' with a list format."
2400        )
2401
2402    projects = ",".join(data.get("projects", [data.get("project", "")]))
2403    if projects != "":
2404
2405        pippub = XML.SubElement(
2406            xml_parent,
2407            "au.com.centrumsystems.hudson.plugin."
2408            "buildpipeline.trigger.BuildPipelineTrigger",
2409        )
2410
2411        configs = XML.SubElement(pippub, "configs")
2412
2413        helpers.trigger_project(configs, data, registry, param_order)
2414
2415        XML.SubElement(pippub, "downstreamProjectNames").text = projects
2416
2417
2418def email(registry, xml_parent, data):
2419    """yaml: email
2420    Email notifications on build failure.
2421    Requires the Jenkins :jenkins-plugins:`Mailer Plugin <mailer>`.
2422
2423    :arg str recipients: Space separated list of recipient email addresses
2424        (required)
2425    :arg bool notify-every-unstable-build: Send an email for every
2426        unstable build (default true)
2427    :arg bool send-to-individuals: Send an email to the individual
2428        who broke the build (default false)
2429
2430    Example:
2431
2432    .. literalinclude::
2433       /../../tests/publishers/fixtures/email-minimal.yaml
2434       :language: yaml
2435
2436    .. literalinclude::  /../../tests/publishers/fixtures/email-full.yaml
2437       :language: yaml
2438    """
2439
2440    # TODO: raise exception if this is applied to a maven job
2441    mailer = XML.SubElement(xml_parent, "hudson.tasks.Mailer")
2442    mailer.set("plugin", "mailer")
2443    mapping = [("recipients", "recipients", None)]
2444    helpers.convert_mapping_to_xml(mailer, data, mapping, fail_required=True)
2445
2446    # Note the logic reversal (included here to match the GUI
2447    if data.get("notify-every-unstable-build", True):
2448        XML.SubElement(mailer, "dontNotifyEveryUnstableBuild").text = "false"
2449    else:
2450        XML.SubElement(mailer, "dontNotifyEveryUnstableBuild").text = "true"
2451    XML.SubElement(mailer, "sendToIndividuals").text = str(
2452        data.get("send-to-individuals", False)
2453    ).lower()
2454
2455
2456def claim_build(registry, xml_parent, data):
2457    """yaml: claim-build
2458    Claim build failures
2459    Requires the Jenkins :jenkins-plugins:`Claim Plugin <claim>`.
2460
2461    Example:
2462
2463    .. literalinclude::  /../../tests/publishers/fixtures/claim-build001.yaml
2464       :language: yaml
2465    """
2466
2467    XML.SubElement(xml_parent, "hudson.plugins.claim.ClaimPublisher")
2468
2469
2470def base_email_ext(registry, xml_parent, data, ttype):
2471    trigger = XML.SubElement(
2472        xml_parent, "hudson.plugins.emailext.plugins.trigger." + ttype
2473    )
2474
2475    info = registry.get_plugin_info("email-ext")
2476    plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
2477
2478    email = XML.SubElement(trigger, "email")
2479
2480    if plugin_version < pkg_resources.parse_version("2.39"):
2481        XML.SubElement(email, "recipientList").text = ""
2482    XML.SubElement(email, "subject").text = "$PROJECT_DEFAULT_SUBJECT"
2483    XML.SubElement(email, "body").text = "$PROJECT_DEFAULT_CONTENT"
2484    if plugin_version >= pkg_resources.parse_version("2.39"):
2485        XML.SubElement(email, "replyTo").text = "$PROJECT_DEFAULT_REPLYTO"
2486        XML.SubElement(email, "contentType").text = "project"
2487    if "send-to" in data:
2488        recipient_providers = None
2489        if plugin_version < pkg_resources.parse_version("2.39"):
2490            XML.SubElement(email, "sendToDevelopers").text = str(
2491                "developers" in data["send-to"]
2492            ).lower()
2493            XML.SubElement(email, "sendToRequester").text = str(
2494                "requester" in data["send-to"]
2495            ).lower()
2496            XML.SubElement(email, "includeCulprits").text = str(
2497                "culprits" in data["send-to"]
2498            ).lower()
2499            XML.SubElement(email, "sendToRecipientList").text = str(
2500                "recipients" in data["send-to"]
2501            ).lower()
2502        else:
2503            for recipient in data["send-to"]:
2504                if "developers" == recipient:
2505                    if recipient_providers is None:
2506                        recipient_providers = XML.SubElement(
2507                            email, "recipientProviders"
2508                        )
2509                    XML.SubElement(
2510                        recipient_providers,
2511                        "hudson.plugins.emailext.plugins.recipients.DevelopersRecipientProvider",
2512                    ).text = ""
2513                elif "requester" == recipient:
2514                    if recipient_providers is None:
2515                        recipient_providers = XML.SubElement(
2516                            email, "recipientProviders"
2517                        )
2518                    XML.SubElement(
2519                        recipient_providers,
2520                        "hudson.plugins.emailext.plugins.recipients.RequesterRecipientProvider",
2521                    ).text = ""
2522                elif "culprits" == recipient:
2523                    if recipient_providers is None:
2524                        recipient_providers = XML.SubElement(
2525                            email, "recipientProviders"
2526                        )
2527                    XML.SubElement(
2528                        recipient_providers,
2529                        "hudson.plugins.emailext.plugins.recipients.CulpritsRecipientProvider",
2530                    ).text = ""
2531                elif "recipients" == recipient:
2532                    if recipient_providers is None:
2533                        recipient_providers = XML.SubElement(
2534                            email, "recipientProviders"
2535                        )
2536                    XML.SubElement(
2537                        recipient_providers,
2538                        "hudson.plugins.emailext.plugins.recipients.ListRecipientProvider",
2539                    ).text = ""
2540                elif "failing-test-suspects-recipients" == recipient:
2541                    if recipient_providers is None:
2542                        recipient_providers = XML.SubElement(
2543                            email, "recipientProviders"
2544                        )
2545                    XML.SubElement(
2546                        recipient_providers,
2547                        "hudson.plugins.emailext.plugins.recipients.FailingTestSuspectsRecipientProvider",
2548                    ).text = ""
2549                elif "first-failing-build-suspects-recipients" == recipient:
2550                    if recipient_providers is None:
2551                        recipient_providers = XML.SubElement(
2552                            email, "recipientProviders"
2553                        )
2554                    XML.SubElement(
2555                        recipient_providers,
2556                        "hudson.plugins.emailext.plugins.recipients.FirstFailingBuildSuspectsRecipientProvider",
2557                    ).text = ""
2558            # `failureCount` is deprecated and has no effect
2559            # on email, but the element has been created
2560            # in order to match the XML generated via UI.
2561            failure_count_supporters = ["FirstFailureTrigger", "SecondFailureTrigger"]
2562            if ttype in failure_count_supporters:
2563                XML.SubElement(trigger, "failureCount").text = "0"
2564            if "upstream-committers" in data["send-to"]:
2565                if recipient_providers is None:
2566                    recipient_providers = XML.SubElement(email, "recipientProviders")
2567                XML.SubElement(
2568                    recipient_providers,
2569                    "hudson.plugins.emailext.plugins.recipients.UpstreamComitterRecipientProvider",
2570                ).text = ""
2571    else:
2572        if plugin_version < pkg_resources.parse_version("2.39"):
2573            XML.SubElement(email, "sendToRequester").text = "false"
2574            XML.SubElement(email, "sendToDevelopers").text = "false"
2575            XML.SubElement(email, "includeCulprits").text = "false"
2576            XML.SubElement(email, "sendToRecipientList").text = "true"
2577        else:
2578            recipient_providers = XML.SubElement(email, "recipientProviders")
2579            XML.SubElement(
2580                recipient_providers,
2581                "hudson.plugins.emailext.plugins.recipients.ListRecipientProvider",
2582            ).text = ""
2583
2584    if ttype == "ScriptTrigger":
2585        XML.SubElement(trigger, "triggerScript").text = data["trigger-script"]
2586
2587    if plugin_version >= pkg_resources.parse_version("2.39"):
2588        mappings = [
2589            ("attachments", "attachmentsPattern", ""),
2590            ("attach-build-log", "attachBuildLog", False),
2591            ("compress-log", "compressBuildLog", False),
2592        ]
2593
2594        helpers.convert_mapping_to_xml(email, data, mappings, fail_required=True)
2595
2596
2597def email_ext(registry, xml_parent, data):
2598    """yaml: email-ext
2599    Extend Jenkin's built in email notification
2600    Requires the Jenkins :jenkins-plugins:`Email-ext Plugin
2601    <email-ext>`.
2602
2603    :arg bool disable-publisher: Disable the publisher, while maintaining the
2604        settings. The usage model for this is when you want to test things out
2605        in the build, not send out e-mails during the testing. A message will
2606        be printed to the build log saying that the publisher is disabled.
2607        (default false)
2608    :arg str recipients: Comma separated list of recipient email addresses
2609        (default '$DEFAULT_RECIPIENTS')
2610    :arg str reply-to: Comma separated list of email addresses that should be
2611        in the Reply-To header for this project (default '$DEFAULT_REPLYTO')
2612    :arg str from: Email address that should be
2613        in the From header for this project (default '')
2614    :arg str content-type: The content type of the emails sent. If not set, the
2615        Jenkins plugin uses the value set on the main configuration page.
2616        Possible values: 'html', 'text', 'both-html-text' or 'default'
2617        (default 'default')
2618    :arg str subject: Subject for the email, can include variables like
2619        ${BUILD_NUMBER} or even groovy or javascript code
2620        (default '$DEFAULT_SUBJECT')
2621    :arg str body: Content for the body of the email, can include variables
2622        like ${BUILD_NUMBER}, but the real magic is using groovy or
2623        javascript to hook into the Jenkins API itself
2624        (default '$DEFAULT_CONTENT')
2625    :arg bool attach-build-log: Include build log in the email (default false)
2626    :arg bool compress-log: Compress build log in the email (default false)
2627    :arg str attachments: pattern of files to include as attachment
2628         (default '')
2629    :arg bool always: Send an email for every result (default false)
2630    :arg bool unstable: Send an email for an unstable result (default false)
2631    :arg bool first-failure: Send an email for just the first failure
2632        (default false)
2633    :arg bool first-unstable: Send an email for just the first unstable build
2634        (default false)
2635    :arg bool not-built: Send an email if not built (default false)
2636    :arg bool aborted: Send an email if the build is aborted (default false)
2637    :arg bool regression: Send an email if there is a regression
2638        (default false)
2639    :arg bool failure: Send an email if the build fails (default true)
2640    :arg bool second-failure: Send an email for the second failure
2641        (default false)
2642    :arg bool improvement: Send an email if the build improves (default false)
2643    :arg bool still-failing: Send an email if the build is still failing
2644        (default false)
2645    :arg bool success: Send an email for a successful build (default false)
2646    :arg bool fixed: Send an email if the build is fixed (default false)
2647    :arg bool fixed-unhealthy: Send an email if the build status
2648        changes from "Failure" or "Unstable" to "Success". Intermediate
2649        "Aborted" builds are ignored. (default false)
2650    :arg bool still-unstable: Send an email if the build is still unstable
2651        (default false)
2652    :arg bool pre-build: Send an email before the build (default false)
2653    :arg str trigger-script: A Groovy script used to determine if an email
2654        should be sent.
2655    :arg str presend-script: A Groovy script executed prior sending the mail.
2656        (default '')
2657    :arg str postsend-script: A Goovy script executed after sending the email.
2658        (default '')
2659    :arg bool save-output: Save email content to workspace (default false)
2660    :arg str matrix-trigger: If using matrix projects, when to trigger
2661
2662        :matrix-trigger values:
2663            * **both**
2664            * **only-parent**
2665            * **only-configurations**
2666    :arg list send-to: list of recipients from the predefined groups
2667
2668        :send-to values:
2669            * **developers** (disabled by default)
2670            * **requester** (disabled by default)
2671            * **culprits** (disabled by default)
2672            * **recipients** (enabled by default)
2673            * **upstream-committers** (>=2.39) (disabled by default)
2674            * **failing-test-suspects-recipients** (>=2.39) (disabled by default)
2675            * **first-failing-build-suspects-recipients** (>=2.39) (disabled by default)
2676
2677    Example:
2678
2679    .. literalinclude:: /../../tests/publishers/fixtures/email-ext001.yaml
2680       :language: yaml
2681    """
2682
2683    emailext = XML.SubElement(
2684        xml_parent, "hudson.plugins.emailext.ExtendedEmailPublisher"
2685    )
2686
2687    info = registry.get_plugin_info("email-ext")
2688    plugin_version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
2689
2690    if "recipients" in data:
2691        XML.SubElement(emailext, "recipientList").text = data["recipients"]
2692    else:
2693        XML.SubElement(emailext, "recipientList").text = "$DEFAULT_RECIPIENTS"
2694    ctrigger = XML.SubElement(emailext, "configuredTriggers")
2695    if data.get("always", False):
2696        base_email_ext(registry, ctrigger, data, "AlwaysTrigger")
2697    if data.get("unstable", False):
2698        base_email_ext(registry, ctrigger, data, "UnstableTrigger")
2699    if data.get("first-failure", False):
2700        base_email_ext(registry, ctrigger, data, "FirstFailureTrigger")
2701    if data.get("first-unstable", False):
2702        base_email_ext(registry, ctrigger, data, "FirstUnstableTrigger")
2703    if data.get("not-built", False):
2704        base_email_ext(registry, ctrigger, data, "NotBuiltTrigger")
2705    if data.get("aborted", False):
2706        base_email_ext(registry, ctrigger, data, "AbortedTrigger")
2707    if data.get("regression", False):
2708        base_email_ext(registry, ctrigger, data, "RegressionTrigger")
2709    if data.get("failure", True):
2710        base_email_ext(registry, ctrigger, data, "FailureTrigger")
2711    if data.get("second-failure", False):
2712        base_email_ext(registry, ctrigger, data, "SecondFailureTrigger")
2713    if data.get("improvement", False):
2714        base_email_ext(registry, ctrigger, data, "ImprovementTrigger")
2715    if data.get("still-failing", False):
2716        base_email_ext(registry, ctrigger, data, "StillFailingTrigger")
2717    if data.get("success", False):
2718        base_email_ext(registry, ctrigger, data, "SuccessTrigger")
2719    if data.get("fixed", False):
2720        base_email_ext(registry, ctrigger, data, "FixedTrigger")
2721    if data.get("fixed-unhealthy", False):
2722        base_email_ext(registry, ctrigger, data, "FixedUnhealthyTrigger")
2723    if data.get("still-unstable", False):
2724        base_email_ext(registry, ctrigger, data, "StillUnstableTrigger")
2725    if data.get("pre-build", False):
2726        base_email_ext(registry, ctrigger, data, "PreBuildTrigger")
2727    if data.get("trigger-script", False):
2728        base_email_ext(registry, ctrigger, data, "ScriptTrigger")
2729
2730    content_type_mime = {
2731        "text": "text/plain",
2732        "html": "text/html",
2733        "default": "default",
2734        "both-html-text": "both",
2735    }
2736    ctype = data.get("content-type", "default")
2737    if ctype not in content_type_mime:
2738        raise InvalidAttributeError(ctype, ctype, content_type_mime.keys())
2739    XML.SubElement(emailext, "contentType").text = content_type_mime[ctype]
2740
2741    mappings = [
2742        ("subject", "defaultSubject", "$DEFAULT_SUBJECT"),
2743        ("body", "defaultContent", "$DEFAULT_CONTENT"),
2744        ("attachments", "attachmentsPattern", ""),
2745        ("presend-script", "presendScript", ""),
2746        ("postsend-script", "postsendScript", ""),
2747        ("attach-build-log", "attachBuildLog", False),
2748        ("compress-log", "compressBuildLog", False),
2749        ("save-output", "saveOutput", False),
2750        ("disable-publisher", "disabled", False),
2751        ("reply-to", "replyTo", "$DEFAULT_REPLYTO"),
2752    ]
2753
2754    if plugin_version >= pkg_resources.parse_version("2.39"):
2755        mappings.append(("from", "from", ""))
2756
2757    helpers.convert_mapping_to_xml(emailext, data, mappings, fail_required=True)
2758
2759    matrix_dict = {
2760        "both": "BOTH",
2761        "only-configurations": "ONLY_CONFIGURATIONS",
2762        "only-parent": "ONLY_PARENT",
2763    }
2764    matrix_trigger = data.get("matrix-trigger", None)
2765    # If none defined, then do not create entry
2766    if matrix_trigger is not None:
2767        if matrix_trigger not in matrix_dict:
2768            raise InvalidAttributeError(
2769                matrix_trigger, matrix_trigger, matrix_dict.keys()
2770            )
2771        XML.SubElement(emailext, "matrixTriggerMode").text = matrix_dict.get(
2772            matrix_trigger
2773        )
2774
2775
2776def fingerprint(registry, xml_parent, data):
2777    """yaml: fingerprint
2778    Fingerprint files to track them across builds. Requires the
2779    Jenkins :jenkins-plugins:`Fingerprint Plugin <create-fingerprint>`.
2780
2781    :arg str files: files to fingerprint, follows the @includes of Ant fileset
2782        (default '')
2783    :arg bool record-artifacts: fingerprint all archived artifacts
2784        (default false)
2785
2786    Example:
2787
2788    .. literalinclude::  /../../tests/publishers/fixtures/fingerprint001.yaml
2789       :language: yaml
2790    """
2791    finger = XML.SubElement(xml_parent, "hudson.tasks.Fingerprinter")
2792    mappings = [
2793        ("files", "targets", ""),
2794        ("record-artifacts", "recordBuildArtifacts", False),
2795    ]
2796    helpers.convert_mapping_to_xml(finger, data, mappings, fail_required=True)
2797
2798
2799def aggregate_tests(registry, xml_parent, data):
2800    """yaml: aggregate-tests
2801    Aggregate downstream test results
2802
2803    :arg bool include-failed-builds: whether to include failed builds
2804        (default false)
2805
2806    Example:
2807
2808    .. literalinclude::
2809        /../../tests/publishers/fixtures/aggregate-tests001.yaml
2810       :language: yaml
2811    """
2812    agg = XML.SubElement(xml_parent, "hudson.tasks.test.AggregatedTestResultPublisher")
2813    mapping = [("include-failed-builds", "includeFailedBuilds", False)]
2814    helpers.convert_mapping_to_xml(agg, data, mapping, fail_required=True)
2815
2816
2817def aggregate_flow_tests(registry, xml_parent, data):
2818    """yaml: aggregate-flow-tests
2819    Aggregate downstream test results in a Build Flow job.
2820
2821    Requires the Jenkins `Build Flow Test Aggregator Plugin
2822    <https://github.com/zeroturnaround/build-flow-test-aggregator>`_.
2823
2824    :arg bool show-test-results-trend: whether to show test results
2825        trend graph (default true)
2826
2827    Example:
2828
2829    .. literalinclude::
2830        /../../tests/publishers/fixtures/aggregate-flow-tests002.yaml
2831       :language: yaml
2832
2833    """
2834    agg_flow = XML.SubElement(
2835        xml_parent,
2836        "org.zeroturnaround.jenkins." "flowbuildtestaggregator.FlowTestAggregator",
2837    )
2838    mapping = [("show-test-results-trend", "showTestResultTrend", True)]
2839    helpers.convert_mapping_to_xml(agg_flow, data, mapping, fail_required=True)
2840
2841
2842def cppcheck(registry, xml_parent, data):
2843    """yaml: cppcheck
2844    Cppcheck result publisher
2845    Requires the Jenkins :jenkins-plugins:`Cppcheck Plugin <cppcheck>`.
2846
2847    :arg str pattern: File pattern for cppcheck xml report (required)
2848    :arg bool ignoreblankfiles: Ignore blank files (default false)
2849    :arg bool allow-no-report: Do not fail the build if the Cppcheck report
2850        is not found (default false)
2851    :arg dict thresholds:
2852        :thresholds: Configure the build status and health. A build is
2853            considered as unstable or failure if the new or total number
2854            of issues exceeds the specified thresholds. The build health
2855            is also determined by thresholds. If the actual number of issues
2856            is between the provided thresholds, then the build health is
2857            interpolated.
2858
2859        * **unstable** (`str`): Total number unstable threshold (default '')
2860        * **new-unstable** (`str`): New number unstable threshold (default '')
2861        * **failure** (`str`): Total number failure threshold (default '')
2862        * **new-failure** (`str`): New number failure threshold (default '')
2863        * **healthy** (`str`): Healthy threshold (default '')
2864        * **unhealthy** (`str`): Unhealthy threshold (default '')
2865
2866    :arg dict severity:
2867        :severity: Determines which severity of issues should be considered
2868            when evaluating the build status and health, default all true
2869
2870        * **error** (`bool`): Severity error (default true)
2871        * **warning** (`bool`): Severity warning (default true)
2872        * **style** (`bool`): Severity style (default true)
2873        * **performance** (`bool`): Severity performance (default true)
2874        * **information** (`bool`): Severity information (default true)
2875        * **nocategory** (`bool`): Severity nocategory (default true)
2876        * **portability** (`bool`): Severity portability (default true)
2877
2878    :arg dict graph:
2879        :graph: Graph configuration
2880
2881        * **xysize** (`array`): Chart width and height (default [500, 200])
2882        * **num-builds-in-graph** (`int`): Builds number in graph (default 0)
2883
2884    :arg dict display
2885        :display: which errors to display, default only sum
2886
2887        * **sum** (`bool`): Display sum of all issues (default true)
2888        * **error** (`bool`): Display errors (default false)
2889        * **warning** (`bool`): Display warnings (default false)
2890        * **style** (`bool`): Display style (default false)
2891        * **performance** (`bool`): Display performance (default false)
2892        * **information** (`bool`): Display information (default false)
2893        * **nocategory** (`bool`): Display no category (default false)
2894        * **portability** (`bool`): Display portability (default false)
2895
2896    Minimal Example:
2897
2898        .. literalinclude::
2899            /../../tests/publishers/fixtures/cppcheck-minimal.yaml
2900           :language: yaml
2901
2902    Full Example:
2903
2904        .. literalinclude::
2905            /../../tests/publishers/fixtures/cppcheck-full.yaml
2906           :language: yaml
2907    """
2908
2909    cppextbase = XML.SubElement(
2910        xml_parent, "org.jenkinsci.plugins.cppcheck." "CppcheckPublisher"
2911    )
2912    cppextbase.set("plugin", "cppcheck")
2913    cppext = XML.SubElement(cppextbase, "cppcheckConfig")
2914    mappings = [
2915        ("pattern", "pattern", None),
2916        ("ignoreblankfiles", "ignoreBlankFiles", False),
2917        ("allow-no-report", "allowNoReport", False),
2918    ]
2919    helpers.convert_mapping_to_xml(cppext, data, mappings, fail_required=True)
2920
2921    csev = XML.SubElement(cppext, "configSeverityEvaluation")
2922    thrsh = data.get("thresholds", {})
2923    thrsh_mappings = [
2924        ("unstable", "threshold", ""),
2925        ("new-unstable", "newThreshold", ""),
2926        ("failure", "failureThreshold", ""),
2927        ("new-failure", "newFailureThreshold", ""),
2928        ("healthy", "healthy", ""),
2929        ("unhealthy", "unHealthy", ""),
2930    ]
2931    helpers.convert_mapping_to_xml(csev, thrsh, thrsh_mappings, fail_required=True)
2932
2933    sev = thrsh.get("severity", {})
2934    sev_mappings = [
2935        ("error", "severityError", True),
2936        ("warning", "severityWarning", True),
2937        ("style", "severityStyle", True),
2938        ("performance", "severityPerformance", True),
2939        ("information", "severityInformation", True),
2940        ("nocategory", "severityNoCategory", True),
2941        ("portability", "severityPortability", True),
2942    ]
2943    helpers.convert_mapping_to_xml(csev, sev, sev_mappings, fail_required=True)
2944
2945    graph = data.get("graph", {})
2946    cgraph = XML.SubElement(cppext, "configGraph")
2947    x, y = graph.get("xysize", [500, 200])
2948    XML.SubElement(cgraph, "xSize").text = str(x)
2949    XML.SubElement(cgraph, "ySize").text = str(y)
2950    graph_mapping = [("num-builds-in-graph", "numBuildsInGraph", 0)]
2951    helpers.convert_mapping_to_xml(cgraph, graph, graph_mapping, fail_required=True)
2952
2953    gdisplay = graph.get("display", {})
2954    gdisplay_mappings = [
2955        ("sum", "displayAllErrors", True),
2956        ("error", "displayErrorSeverity", False),
2957        ("warning", "displayWarningSeverity", False),
2958        ("style", "displayStyleSeverity", False),
2959        ("performance", "displayPerformanceSeverity", False),
2960        ("information", "displayInformationSeverity", False),
2961        ("nocategory", "displayNoCategorySeverity", False),
2962        ("portability", "displayPortabilitySeverity", False),
2963    ]
2964    helpers.convert_mapping_to_xml(
2965        cgraph, gdisplay, gdisplay_mappings, fail_required=True
2966    )
2967
2968
2969def logparser(registry, xml_parent, data):
2970    """yaml: logparser
2971    Requires the Jenkins :jenkins-plugins:`Log Parser Plugin <log-parser>`.
2972
2973    :arg str parse-rules: full path to parse rules (default '')
2974    :arg bool use-project-rules: use project rules instead of global
2975        (default true)
2976    :arg bool unstable-on-warning: mark build unstable on warning
2977        (default false)
2978    :arg bool fail-on-error: mark build failed on error (default false)
2979    :arg bool show-graphs: show parser trend graphs (default true)
2980
2981    Minimal Example:
2982
2983        .. literalinclude::
2984           /../../tests/publishers/fixtures/logparser-minimal.yaml
2985           :language: yaml
2986
2987    Full Example:
2988
2989        .. literalinclude::
2990           /../../tests/publishers/fixtures/logparser-full.yaml
2991           :language: yaml
2992    """
2993
2994    clog = XML.SubElement(xml_parent, "hudson.plugins.logparser.LogParserPublisher")
2995    clog.set("plugin", "log-parser")
2996    rules_path_element = (
2997        "projectRulePath" if data.get("use-project-rules", True) else "parsingRulesPath"
2998    )
2999    mappings = [
3000        ("unstable-on-warning", "unstableOnWarning", False),
3001        ("fail-on-error", "failBuildOnError", False),
3002        ("show-graphs", "showGraphs", True),
3003        ("use-project-rules", "useProjectRule", True),
3004        ("parse-rules", rules_path_element, ""),
3005    ]
3006    helpers.convert_mapping_to_xml(clog, data, mappings, fail_required=True)
3007
3008
3009def copy_to_master(registry, xml_parent, data):
3010    """yaml: copy-to-master
3011    Copy files to master from slave.
3012
3013    Requires the Jenkins :jenkins-plugins:`Copy To Slave Plugin <copy-to-slave>`.
3014
3015    :arg list includes: list of file patterns to copy
3016    :arg list excludes: list of file patterns to exclude
3017    :arg str destination: absolute path into which the files will be copied.
3018        If left blank they will be copied into the workspace of the current job
3019        (default '')
3020    :arg bool run-after-result: If this is checked then copying files back to
3021        master will not run until the build result is finalized.(default true)
3022
3023    Example:
3024
3025    .. literalinclude::
3026        /../../tests/publishers/fixtures/copy-to-master001.yaml
3027       :language: yaml
3028    """
3029    cm = XML.SubElement(
3030        xml_parent,
3031        "com.michelin." "cio.hudson.plugins.copytoslave.CopyToMasterNotifier",
3032    )
3033    cm.set("plugin", "copy-to-slave")
3034
3035    XML.SubElement(cm, "includes").text = ",".join(data.get("includes", [""]))
3036    XML.SubElement(cm, "excludes").text = ",".join(data.get("excludes", [""]))
3037    mappings = [
3038        ("run-after-result", "runAfterResultFinalised", True),
3039        ("destination", "destinationFolder", ""),
3040    ]
3041    helpers.convert_mapping_to_xml(cm, data, mappings, fail_required=True)
3042
3043    if data.get("destination", ""):
3044        XML.SubElement(cm, "overrideDestinationFolder").text = "true"
3045
3046
3047def jira(registry, xml_parent, data):
3048    """yaml: jira
3049    Update relevant JIRA issues
3050    Requires the Jenkins :jenkins-plugins:`JIRA Plugin <jira>`.
3051
3052    Example:
3053
3054        .. literalinclude:: /../../tests/publishers/fixtures/jira001.yaml
3055           :language: yaml
3056    """
3057    XML.SubElement(xml_parent, "hudson.plugins.jira.JiraIssueUpdater")
3058
3059
3060def growl(registry, xml_parent, data):
3061    """yaml: growl
3062    Push notifications to growl client.
3063    Requires the Jenkins :jenkins-plugins:`Growl Plugin <growl>`.
3064
3065    :arg str ip: IP address to send growl notifications to (required)
3066    :arg bool notify-only-on-fail-or-recovery: send a growl only when build
3067        fails or recovers from a failure (default false)
3068
3069    Minimal Example:
3070
3071        .. literalinclude:: /../../tests/publishers/fixtures/growl-minimal.yaml
3072           :language: yaml
3073
3074    Full Example:
3075
3076        .. literalinclude:: /../../tests/publishers/fixtures/growl-full.yaml
3077           :language: yaml
3078    """
3079    growl = XML.SubElement(xml_parent, "hudson.plugins.growl.GrowlPublisher")
3080    growl.set("plugin", "growl")
3081
3082    mapping = [
3083        ("ip", "IP", None),
3084        ("notify-only-on-fail-or-recovery", "onlyOnFailureOrRecovery", False),
3085    ]
3086    helpers.convert_mapping_to_xml(growl, data, mapping, fail_required=True)
3087
3088
3089def groovy_postbuild(registry, xml_parent, data):
3090    """yaml: groovy-postbuild
3091    Execute a groovy script.
3092    Requires the Jenkins :jenkins-plugins:`Groovy Postbuild Plugin
3093    <groovy-postbuild>`.
3094
3095    Please pay attention on version of plugin you have installed.
3096    There were incompatible changes between 1.x and 2.x. Please see
3097    :jenkins-plugins:`home page <groovy-postbuild>` of this plugin
3098    for full information including migration process.
3099
3100    :arg str script: The groovy script to execute
3101    :arg list classpath: List of additional classpaths (>=1.6)
3102    :arg str on-failure: In case of script failure leave build as it is
3103        for "nothing" option, mark build as unstable
3104        for "unstable" and mark job as failure for "failed"
3105        (default 'nothing')
3106    :arg bool matrix-parent: Run script for matrix parent only (>=1.9)
3107        (default false)
3108    :arg bool sandbox: Execute script inside of groovy sandbox (>=2.0)
3109        (default false)
3110
3111    Example:
3112
3113        .. literalinclude::
3114            /../../tests/publishers/fixtures/groovy-postbuild001.yaml
3115           :language: yaml
3116    """
3117    logger = logging.getLogger("%s:groovy-postbuild" % __name__)
3118    # Backward compatibility with old format
3119    if isinstance(data, six.string_types):
3120        logger.warning(
3121            "You use deprecated configuration, please follow documentation "
3122            "to change configuration. It is not going to be supported in "
3123            "future releases!"
3124        )
3125        data = {"script": data}
3126    # There are incompatible changes, we need to know version
3127    info = registry.get_plugin_info("groovy-postbuild")
3128    # Note: Assume latest version of plugin is preferred config format
3129    version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
3130    # Version specific predicates
3131    matrix_parent_support = version >= pkg_resources.parse_version("1.9")
3132    security_plugin_support = version >= pkg_resources.parse_version("2.0")
3133    extra_classpath_support = version >= pkg_resources.parse_version("1.6")
3134
3135    root_tag = "org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildRecorder"
3136    groovy = XML.SubElement(xml_parent, root_tag)
3137
3138    behavior = data.get("on-failure")
3139    XML.SubElement(groovy, "behavior").text = {"unstable": "1", "failed": "2"}.get(
3140        behavior, "0"
3141    )
3142
3143    if matrix_parent_support:
3144        XML.SubElement(groovy, "runForMatrixParent").text = str(
3145            data.get("matrix-parent", False)
3146        ).lower()
3147
3148    classpaths = data.get("classpath", list())
3149    if security_plugin_support:
3150        script = XML.SubElement(groovy, "script")
3151        XML.SubElement(script, "script").text = data.get("script")
3152        XML.SubElement(script, "sandbox").text = str(data.get("sandbox", False)).lower()
3153        if classpaths:
3154            classpath = XML.SubElement(script, "classpath")
3155            for path in classpaths:
3156                script_path = XML.SubElement(classpath, "entry")
3157                XML.SubElement(script_path, "url").text = path
3158    else:
3159        XML.SubElement(groovy, "groovyScript").text = data.get("script")
3160        if extra_classpath_support and classpaths:
3161            classpath = XML.SubElement(groovy, "classpath")
3162            for path in classpaths:
3163                script_path = XML.SubElement(
3164                    classpath,
3165                    "org.jvnet.hudson.plugins.groovypostbuild." "GroovyScriptPath",
3166                )
3167                XML.SubElement(script_path, "path").text = path
3168
3169
3170def base_publish_over(
3171    xml_parent,
3172    data,
3173    console_prefix,
3174    plugin_tag,
3175    publisher_tag,
3176    transferset_tag,
3177    retry_tag,
3178    reference_plugin_tag,
3179    is_builder=False,
3180):
3181    outer = XML.SubElement(xml_parent, plugin_tag)
3182    # 'Publish over SSH' builder has an extra top delegate element
3183    if xml_parent.tag == "builders" or is_builder:
3184        outer = XML.SubElement(outer, "delegate")
3185
3186    XML.SubElement(outer, "consolePrefix").text = console_prefix
3187    delegate = XML.SubElement(outer, "delegate")
3188    publishers = XML.SubElement(delegate, "publishers")
3189
3190    inner = XML.SubElement(publishers, publisher_tag)
3191    XML.SubElement(inner, "configName").text = data["site"]
3192    XML.SubElement(inner, "verbose").text = str(data.get("verbose", False)).lower()
3193
3194    transfers = XML.SubElement(inner, "transfers")
3195    transfersset = XML.SubElement(transfers, transferset_tag)
3196
3197    XML.SubElement(transfersset, "remoteDirectory").text = data["target"]
3198    XML.SubElement(transfersset, "sourceFiles").text = data["source"]
3199    XML.SubElement(transfersset, "excludes").text = data.get("excludes", "")
3200    XML.SubElement(transfersset, "removePrefix").text = data.get("remove-prefix", "")
3201    XML.SubElement(transfersset, "remoteDirectorySDF").text = str(
3202        data.get("target-is-date-format", False)
3203    ).lower()
3204    XML.SubElement(transfersset, "flatten").text = str(
3205        data.get("flatten", False)
3206    ).lower()
3207    XML.SubElement(transfersset, "cleanRemote").text = str(
3208        data.get("clean-remote", False)
3209    ).lower()
3210
3211    if "command" in data:
3212        XML.SubElement(transfersset, "execCommand").text = data["command"]
3213    if "timeout" in data:
3214        XML.SubElement(transfersset, "execTimeout").text = str(data["timeout"])
3215    if "use-pty" in data:
3216        XML.SubElement(transfersset, "usePty").text = str(
3217            data.get("use-pty", False)
3218        ).lower()
3219
3220    XML.SubElement(inner, "useWorkspaceInPromotion").text = "false"
3221    XML.SubElement(inner, "usePromotionTimestamp").text = "false"
3222
3223    if "retries" in data:
3224        retry = XML.SubElement(inner, "retry", {"class": retry_tag})
3225        XML.SubElement(retry, "retries").text = str(data.get("retries", 0))
3226        XML.SubElement(retry, "retryDelay").text = str(data.get("retry-delay", 10000))
3227
3228    XML.SubElement(delegate, "continueOnError").text = "false"
3229    XML.SubElement(delegate, "failOnError").text = str(
3230        data.get("fail-on-error", False)
3231    ).lower()
3232    XML.SubElement(delegate, "alwaysPublishFromMaster").text = str(
3233        data.get("always-publish-from-master", False)
3234    ).lower()
3235    XML.SubElement(
3236        delegate,
3237        "hostConfigurationAccess",
3238        {"class": reference_plugin_tag, "reference": "../.."},
3239    )
3240
3241    return (outer, transfersset)
3242
3243
3244def cifs(registry, xml_parent, data):
3245    """yaml: cifs
3246    Upload files via CIFS.
3247    Requires the Jenkins :jenkins-plugins:`Publish over CIFS Plugin
3248    <publish-over-cifs>`.
3249
3250    :arg str site: name of the cifs site/share (required)
3251    :arg str target: destination directory (required)
3252    :arg bool target-is-date-format: whether target is a date format. If true,
3253        raw text should be quoted (default false)
3254    :arg bool clean-remote: should the remote directory be deleted before
3255        transferring files (default false)
3256    :arg str source: source path specifier (required)
3257    :arg str excludes: excluded file pattern (default '')
3258    :arg str remove-prefix: prefix to remove from uploaded file paths
3259        (default '')
3260    :arg bool fail-on-error: fail the build if an error occurs (default false).
3261    :arg bool flatten: only create files on the server, don't create
3262        directories (default false).
3263    :arg bool verbose: adds lots of detail useful for debug to the console
3264      but generally should be left off (default false)
3265    :arg int retries: the number of times to retry this server in the event of
3266      failure (optional)
3267    :arg int retry-delay: the time to wait, in milliseconds, before attempting
3268      another transfer (default 10000)
3269
3270    Minimal Example:
3271
3272        .. literalinclude:: /../../tests/publishers/fixtures/cifs-minimal.yaml
3273           :language: yaml
3274
3275    Full Example:
3276
3277        .. literalinclude::  /../../tests/publishers/fixtures/cifs-full.yaml
3278           :language: yaml
3279
3280    """
3281    console_prefix = "CIFS: "
3282    if xml_parent.tag == "publishers":
3283        plugin_tag = "jenkins.plugins.publish__over__cifs.CifsPublisherPlugin"
3284        is_builder = False
3285    else:
3286        plugin_tag = "jenkins.plugins.publish__over__cifs.CifsBuilderPlugin"
3287        is_builder = True
3288    publisher_tag = "jenkins.plugins.publish__over__cifs.CifsPublisher"
3289    transfer_tag = "jenkins.plugins.publish__over__cifs.CifsTransfer"
3290    retry_tag = "jenkins.plugins.publish_over_cifs.CifsRetry"
3291    plugin_reference_tag = "jenkins.plugins.publish_over_cifs." "CifsPublisherPlugin"
3292    base_publish_over(
3293        xml_parent,
3294        data,
3295        console_prefix,
3296        plugin_tag,
3297        publisher_tag,
3298        transfer_tag,
3299        retry_tag,
3300        plugin_reference_tag,
3301        is_builder,
3302    )
3303
3304
3305def cigame(registry, xml_parent, data):
3306    """yaml: cigame
3307    This plugin introduces a game where users get points
3308    for improving the builds.
3309    Requires the Jenkins Continuous Integration Game
3310    plugin (https://github.com/jenkinsci/ci-game-plugin).
3311
3312    Example:
3313
3314        .. literalinclude:: /../../tests/publishers/fixtures/cigame.yaml
3315           :language: yaml
3316    """
3317    XML.SubElement(xml_parent, "hudson.plugins.cigame.GamePublisher")
3318
3319
3320def sonar(registry, xml_parent, data):
3321    """yaml: sonar
3322    Sonar plugin support.
3323    Requires the Jenkins `Sonar Plugin.
3324    <https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-jenkins>`_
3325
3326    :arg str installation-name: name of the Sonar instance to use (optional)
3327    :arg str jdk: JDK to use (inherited from the job if omitted). (optional)
3328    :arg str branch: branch onto which the analysis will be posted (default '')
3329    :arg str language: source code language (default '')
3330    :arg str root-pom: Root POM (default 'pom.xml')
3331    :arg bool private-maven-repo: If true, use private Maven repository.
3332        (default false)
3333    :arg str maven-opts: options given to maven (default '')
3334    :arg str additional-properties: sonar analysis parameters (default '')
3335    :arg str maven-installation-name: the name of the Maven installation
3336      to use (optional)
3337    :arg dict skip-global-triggers:
3338        :Triggers: * **skip-when-scm-change** (`bool`): skip analysis when
3339                     build triggered by scm (default false)
3340                   * **skip-when-upstream-build** (`bool`): skip analysis when
3341                     build triggered by an upstream build (default false)
3342                   * **skip-when-envvar-defined** (`str`): skip analysis when
3343                     the specified environment variable is set to true
3344                     (default '')
3345    :arg str settings: Path to use as user settings.xml. It is possible to
3346        provide a ConfigFileProvider settings file, see Example below.
3347        (optional)
3348    :arg str global-settings: Path to use as global settings.xml. It is
3349        possible to provide a ConfigFileProvider settings file, see Example
3350        below. (optional)
3351
3352    Requires the Jenkins :jenkins-plugins:`Config File Provider Plugin
3353    <config-file-provider>`
3354    for the Config File Provider "settings" and "global-settings" config.
3355
3356    This publisher supports the post-build action exposed by the Jenkins
3357    Sonar Plugin, which is triggering a Sonar Analysis with Maven.
3358
3359    Minimal Example:
3360
3361        .. literalinclude:: /../../tests/publishers/fixtures/sonar-minimal.yaml
3362           :language: yaml
3363
3364    Full Example:
3365
3366        .. literalinclude:: /../../tests/publishers/fixtures/sonar-full.yaml
3367           :language: yaml
3368    """
3369
3370    sonar = XML.SubElement(xml_parent, "hudson.plugins.sonar.SonarPublisher")
3371    sonar.set("plugin", "sonar")
3372    if "installation-name" in data:
3373        XML.SubElement(sonar, "installationName").text = data["installation-name"]
3374    if "jdk" in data:
3375        XML.SubElement(sonar, "jdk").text = data["jdk"]
3376    if "maven-installation-name" in data:
3377        XML.SubElement(sonar, "mavenInstallationName").text = data[
3378            "maven-installation-name"
3379        ]
3380
3381    mappings = [
3382        ("branch", "branch", ""),
3383        ("language", "language", ""),
3384        ("root-pom", "rootPom", "pom.xml"),
3385        ("private-maven-repo", "usePrivateRepository", False),
3386        ("maven-opts", "mavenOpts", ""),
3387        ("additional-properties", "jobAdditionalProperties", ""),
3388    ]
3389    helpers.convert_mapping_to_xml(sonar, data, mappings, fail_required=True)
3390
3391    if "skip-global-triggers" in data:
3392        data_triggers = data["skip-global-triggers"]
3393        triggers = XML.SubElement(sonar, "triggers")
3394        triggers_mappings = [
3395            ("skip-when-scm-change", "skipScmCause", False),
3396            ("skip-when-upstream-build", "skipUpstreamCause", False),
3397            ("skip-when-envvar-defined", "envVar", ""),
3398        ]
3399        helpers.convert_mapping_to_xml(
3400            triggers, data_triggers, triggers_mappings, fail_required=True
3401        )
3402
3403    helpers.config_file_provider_settings(sonar, data)
3404
3405
3406def sounds(parser, xml_parent, data):
3407    """yaml: sounds
3408    Play audio clips locally through sound hardware,
3409    remotely by piping them through an operating system command,
3410    or simultaneously through all browsers on a Jenkins page.
3411
3412    Requires the Jenkins :jenkins-plugins:`Jenkins Sounds plugin
3413    <sounds>`
3414
3415    :arg dict success: Play on success
3416
3417        :success:
3418            .. _sound_and_cond:
3419
3420            * **sound** (`str`) - Sound name
3421            * **from** (`list`) - Previous build result (default is all)
3422                :from values:
3423                    * **success**
3424                    * **unstable**
3425                    * **failure**
3426                    * **not_build**
3427                    * **aborted**
3428
3429    :arg dict unstable: Play on unstable.
3430        Specifying sound and conditions see :ref:`above <sound_and_cond>`.
3431    :arg dict failure: Play on failure.
3432        Specifying sound and conditions see :ref:`above <sound_and_cond>`.
3433    :arg dict not_build: Play on not build.
3434        Specifying sound and conditions see :ref:`above <sound_and_cond>`.
3435    :arg dict aborted: Play on aborted.
3436        Specifying sound and conditions see :ref:`above <sound_and_cond>`.
3437
3438    Minimal example using defaults:
3439
3440    .. literalinclude::  /../../tests/publishers/fixtures/sounds001.yaml
3441       :language: yaml
3442
3443    Full example:
3444
3445    .. literalinclude::  /../../tests/publishers/fixtures/sounds003.yaml
3446       :language: yaml
3447    """
3448
3449    mapping_dict = {
3450        "success": hudson_model.SUCCESS,
3451        "unstable": hudson_model.UNSTABLE,
3452        "failure": hudson_model.FAILURE,
3453        "not_build": hudson_model.NOTBUILD,
3454        "aborted": hudson_model.ABORTED,
3455    }
3456    sounds = XML.SubElement(
3457        xml_parent, "net.hurstfrost.hudson." "sounds.HudsonSoundsNotifier"
3458    )
3459    events = XML.SubElement(sounds, "soundEvents")
3460    for status, v in data.items():
3461        try:
3462            model = mapping_dict[status]
3463        except KeyError:
3464            raise InvalidAttributeError("build status", status, mapping_dict)
3465
3466        event = XML.SubElement(
3467            events, "net.hurstfrost.hudson.sounds." "HudsonSoundsNotifier_-SoundEvent"
3468        )
3469        XML.SubElement(event, "soundId").text = v["sound"]
3470        to_result = XML.SubElement(event, "toResult")
3471        XML.SubElement(to_result, "name").text = model["name"]
3472        XML.SubElement(to_result, "ordinal").text = model["ordinal"]
3473        XML.SubElement(to_result, "color").text = model["color"]
3474        XML.SubElement(to_result, "completeBuild").text = str(model["complete"]).lower()
3475
3476        from_results = XML.SubElement(event, "fromResults")
3477        results = ["not_build", "success", "aborted", "failure", "unstable"]
3478        if "from" in v:
3479            results = v["from"]
3480        for result in results:
3481            model = mapping_dict[result]
3482            from_result = XML.SubElement(from_results, "hudson.model.Result")
3483            XML.SubElement(from_result, "name").text = model["name"]
3484            XML.SubElement(from_result, "ordinal").text = model["ordinal"]
3485            XML.SubElement(from_result, "color").text = model["color"]
3486            XML.SubElement(from_result, "completeBuild").text = str(
3487                model["complete"]
3488            ).lower()
3489
3490
3491def performance(registry, xml_parent, data):
3492    r"""yaml: performance
3493    Publish performance test results from jmeter and junit.
3494    Requires the Jenkins :jenkins-plugins:`Performance Plugin
3495    <performance>`.
3496
3497    :arg int failed-threshold: Specify the error percentage threshold that
3498        set the build failed. A negative value means don't use this threshold
3499        (default 0)
3500    :arg int unstable-threshold: Specify the error percentage threshold that
3501        set the build unstable. A negative value means don't use this threshold
3502        (default 0)
3503    :arg str unstable-response-time-threshold: Average response time threshold
3504        (default '')
3505    :arg float failed-threshold-positive: Maximum failed percentage for build
3506        comparison (default 0.0)
3507    :arg float failed-threshold-negative: Minimum failed percentage for build
3508        comparison (default 0.0)
3509    :arg float unstable-threshold-positive: Maximum unstable percentage for
3510        build comparison (default 0.0)
3511    :arg float unstable-threshold-negative: Minimum unstable percentage for
3512        build comparison (default 0.0)
3513    :arg int nth-build-number: Build number for build comparison (default 0)
3514    :arg bool mode-relative-thresholds: Relative threshold mode (default false)
3515    :arg str config-type: Compare based on (default 'ART')
3516
3517        :config-type values:
3518          * **ART** -- Average Response Time
3519          * **MRT** -- Median Response Time
3520          * **PRT** -- Percentile Response Time
3521
3522    :arg bool mode-of-threshold: Mode of threshold, true for relative threshold
3523        and false for error threshold (default false)
3524    :arg bool fail-build: Fail build when result files are not present
3525        (default false)
3526    :arg bool compare-build-previous: Compare with previous build
3527        (default false)
3528    :arg bool mode-performance-per-test-case: Performance Per Test Case Mode
3529        (default true)
3530    :arg bool mode-thoughput: Show Throughput Chart (default false)
3531
3532    :arg dict report:
3533
3534        :(jmeter or junit): (`dict` or `str`): Specify a custom report file
3535         (optional; jmeter default \**/*.jtl, junit default **/TEST-\*.xml)
3536
3537    Minimal Example:
3538
3539        .. literalinclude::
3540           /../../tests/publishers/fixtures/performance-minimal.yaml
3541           :language: yaml
3542
3543    Full Example:
3544
3545        .. literalinclude::
3546           /../../tests/publishers/fixtures/performance-full.yaml
3547           :language: yaml
3548    """
3549    perf = XML.SubElement(
3550        xml_parent, "hudson.plugins.performance." "PerformancePublisher"
3551    )
3552    perf.set("plugin", "performance")
3553    types = ["ART", "MRT", "PRT"]
3554    mappings = [
3555        ("failed-threshold", "errorFailedThreshold", 0),
3556        ("unstable-threshold", "errorUnstableThreshold", 0),
3557        ("unstable-response-time-threshold", "errorUnstableResponseTimeThreshold", ""),
3558        ("failed-threshold-positive", "relativeFailedThresholdPositive", "0.0"),
3559        ("failed-threshold-negative", "relativeFailedThresholdNegative", "0.0"),
3560        ("unstable-threshold-positive", "relativeUnstableThresholdPositive", "0.0"),
3561        ("unstable-threshold-negative", "relativeUnstableThresholdNegative", "0.0"),
3562        ("nth-build-number", "nthBuildNumber", 0),
3563        ("mode-relative-thresholds", "modeRelativeThresholds", False),
3564        ("config-type", "configType", "ART", types),
3565        ("mode-of-threshold", "modeOfThreshold", False),
3566        ("fail-build", "failBuildIfNoResultFile", False),
3567        ("compare-build-previous", "compareBuildPrevious", False),
3568        ("mode-performance-per-test-case", "modePerformancePerTestCase", True),
3569        ("mode-thoughput", "modeThroughput", False),
3570    ]
3571    helpers.convert_mapping_to_xml(perf, data, mappings, fail_required=True)
3572
3573    parsers = XML.SubElement(perf, "parsers")
3574    if "report" in data:
3575        for item in data["report"]:
3576            if isinstance(item, dict):
3577                item_name = next(iter(item.keys()))
3578                item_values = item.get(item_name, None)
3579                if item_name == "jmeter":
3580                    jmhold = XML.SubElement(
3581                        parsers, "hudson.plugins." "performance." "JMeterParser"
3582                    )
3583                    XML.SubElement(jmhold, "glob").text = str(item_values)
3584                elif item_name == "junit":
3585                    juhold = XML.SubElement(
3586                        parsers, "hudson.plugins." "performance." "JUnitParser"
3587                    )
3588                    XML.SubElement(juhold, "glob").text = str(item_values)
3589                else:
3590                    raise JenkinsJobsException(
3591                        "You have not specified jmeter "
3592                        "or junit, or you have "
3593                        "incorrectly assigned the key "
3594                        "value."
3595                    )
3596            elif isinstance(item, str):
3597                if item == "jmeter":
3598                    jmhold = XML.SubElement(
3599                        parsers, "hudson.plugins." "performance." "JMeterParser"
3600                    )
3601                    XML.SubElement(jmhold, "glob").text = "**/*.jtl"
3602                elif item == "junit":
3603                    juhold = XML.SubElement(
3604                        parsers, "hudson.plugins." "performance." "JUnitParser"
3605                    )
3606                    XML.SubElement(juhold, "glob").text = "**/TEST-*.xml"
3607                else:
3608                    raise JenkinsJobsException(
3609                        "You have not specified jmeter "
3610                        "or junit, or you have "
3611                        "incorrectly assigned the key "
3612                        "value."
3613                    )
3614
3615
3616def join_trigger(registry, xml_parent, data):
3617    """yaml: join-trigger
3618    Trigger a job after all the immediate downstream jobs have completed.
3619    Requires the Jenkins :jenkins-plugins:`Join Plugin <join>`.
3620
3621    :arg list projects: list of projects to trigger
3622    :arg list publishers: list of triggers from publishers module that
3623        defines projects that need to be triggered
3624    :arg str threshold: result threshold to trigger jobs (optional).
3625        Valid values are "success", "unstable", "failure", and "aborted".
3626    :arg bool even-if-unstable: if true jobs will trigger even if some
3627        downstream jobs are marked as unstable (default false) (DEPRECATED)
3628
3629    Example:
3630
3631        .. literalinclude::
3632           /../../tests/publishers/fixtures/join-trigger001.yaml
3633           :language: yaml
3634    """
3635    jointrigger = XML.SubElement(xml_parent, "join.JoinTrigger")
3636
3637    joinProjectsText = ",".join(data.get("projects", [""]))
3638    XML.SubElement(jointrigger, "joinProjects").text = joinProjectsText
3639
3640    publishers = XML.SubElement(jointrigger, "joinPublishers")
3641    for pub in data.get("publishers", []):
3642        for edited_node in create_publishers(registry, pub):
3643            publishers.append(edited_node)
3644
3645    unstable = str(data.get("even-if-unstable", "")).lower()
3646    if unstable:
3647        XML.SubElement(jointrigger, "evenIfDownstreamUnstable").text = unstable
3648
3649    threshold = data.get("threshold", "")
3650    if threshold:
3651        helpers.trigger_threshold(jointrigger, "resultThreshold", threshold)
3652
3653
3654def jabber(registry, xml_parent, data):
3655    """yaml: jabber
3656    Integrates Jenkins with the Jabber/XMPP instant messaging protocol
3657    Requires the Jenkins :jenkins-plugins:`Jabber Plugin <jabber>`.
3658
3659    :arg bool notify-on-build-start: Whether to send notifications
3660        to channels when a build starts (default false)
3661    :arg bool notify-scm-committers: Whether to send notifications
3662        to the users that are suspected of having broken this build
3663        (default false)
3664    :arg bool notify-scm-culprits: Also send notifications to 'culprits'
3665        from previous unstable/failed builds (default false)
3666    :arg bool notify-upstream-committers: Whether to send notifications to
3667        upstream committers if no committers were found for a broken build
3668        (default false)
3669    :arg bool notify-scm-fixers: Whether to send notifications to the users
3670        that have fixed a broken build (default false)
3671    :arg list group-targets: List of group targets to notify
3672    :arg list individual-targets: List of individual targets to notify
3673    :arg dict strategy: When to send notifications (default all)
3674
3675        :strategy values:
3676          * **all** -- Always
3677          * **failure** -- On any failure
3678          * **failure-fixed** -- On failure and fixes
3679          * **new-failure-fixed** -- On new failure and fixes
3680          * **change** -- Only on state change
3681    :arg dict message: Channel notification message (default summary-scm)
3682
3683        :message  values:
3684          * **summary-scm** -- Summary + SCM changes
3685          * **summary** -- Just summary
3686          * **summary-build** -- Summary and build parameters
3687          * **summary-scm-fail** -- Summary, SCM changes, and failed tests
3688
3689    Minimal Example:
3690
3691        .. literalinclude::
3692           /../../tests/publishers/fixtures/jabber-minimal.yaml
3693           :language: yaml
3694
3695    Full Example:
3696
3697        .. literalinclude:: /../../tests/publishers/fixtures/jabber-full.yaml
3698           :language: yaml
3699    """
3700    j = XML.SubElement(
3701        xml_parent, "hudson.plugins.jabber.im.transport." "JabberPublisher"
3702    )
3703    j.set("plugin", "jabber")
3704
3705    t = XML.SubElement(j, "targets")
3706    if "group-targets" in data:
3707        for group in data["group-targets"]:
3708            gcimt = XML.SubElement(t, "hudson.plugins.im." "GroupChatIMMessageTarget")
3709            gcimt.set("plugin", "instant-messaging")
3710            XML.SubElement(gcimt, "name").text = group
3711            XML.SubElement(gcimt, "notificationOnly").text = "false"
3712    if "individual-targets" in data:
3713        for individual in data["individual-targets"]:
3714            dimt = XML.SubElement(t, "hudson.plugins.im." "DefaultIMMessageTarget")
3715            dimt.set("plugin", "instant-messaging")
3716            XML.SubElement(dimt, "value").text = individual
3717    strategy = data.get("strategy", "all")
3718    strategydict = {
3719        "all": "ALL",
3720        "failure": "ANY_FAILURE",
3721        "failure-fixed": "FAILURE_AND_FIXED",
3722        "new-failure-fixed": "NEW_FAILURE_AND_FIXED",
3723        "change": "STATECHANGE_ONLY",
3724    }
3725    if strategy not in strategydict:
3726        raise JenkinsJobsException(
3727            "Strategy entered is not valid, must be "
3728            + "one of: all, failure, failure-fixed, or "
3729            "change"
3730        )
3731    XML.SubElement(j, "strategy").text = strategydict[strategy]
3732
3733    mappings = [
3734        ("notify-on-build-start", "notifyOnBuildStart", False),
3735        ("notify-scm-committers", "notifySuspects", False),
3736        ("notify-scm-culprits", "notifyCulprits", False),
3737        ("notify-scm-fixers", "notifyFixers", False),
3738        ("notify-upstream-committers", "notifyUpstreamCommitters", False),
3739    ]
3740    helpers.convert_mapping_to_xml(j, data, mappings, fail_required=True)
3741
3742    message = data.get("message", "summary-scm")
3743    messagedict = {
3744        "summary-scm": "DefaultBuildToChatNotifier",
3745        "summary": "SummaryOnlyBuildToChatNotifier",
3746        "summary-build": "BuildParametersBuildToChatNotifier",
3747        "summary-scm-fail": "PrintFailingTestsBuildToChatNotifier",
3748    }
3749    if message not in messagedict:
3750        raise JenkinsJobsException(
3751            "Message entered is not valid, must be one "
3752            "of: summary-scm, summary, summary-build "
3753            "or summary-scm-fail"
3754        )
3755    XML.SubElement(
3756        j,
3757        "buildToChatNotifier",
3758        {"class": "hudson.plugins.im.build_notify." + messagedict[message]},
3759    )
3760    XML.SubElement(j, "matrixMultiplier").text = "ONLY_CONFIGURATIONS"
3761
3762
3763def workspace_cleanup(registry, xml_parent, data):
3764    """yaml: workspace-cleanup (post-build)
3765
3766    Requires the Jenkins :jenkins-plugins:`Workspace Cleanup Plugin
3767    <ws-cleanup>`.
3768
3769    The pre-build workspace-cleanup is available as a wrapper.
3770
3771    :arg list include: list of files to be included
3772    :arg list exclude: list of files to be excluded
3773    :arg bool dirmatch: Apply pattern to directories too (default false)
3774    :arg list clean-if: clean depending on build status
3775
3776        :clean-if values:
3777            * **success** (`bool`) (default true)
3778            * **unstable** (`bool`) (default true)
3779            * **failure** (`bool`) (default true)
3780            * **aborted** (`bool`) (default true)
3781            * **not-built** (`bool`)  (default true)
3782    :arg bool fail-build: Fail the build if the cleanup fails (default true)
3783    :arg bool clean-parent: Cleanup matrix parent workspace (default false)
3784    :arg str external-deletion-command: external deletion command to run
3785        against files and directories
3786    :arg bool disable-deferred-wipeout: Disable improved deferred wipeout
3787        method (default false)
3788
3789    Minimal Example:
3790
3791    .. literalinclude::
3792        /../../tests/publishers/fixtures/workspace-cleanup-minimal.yaml
3793       :language: yaml
3794
3795    Full Example:
3796
3797    .. literalinclude::
3798        /../../tests/publishers/fixtures/workspace-cleanup-full.yaml
3799       :language: yaml
3800    """
3801
3802    p = XML.SubElement(xml_parent, "hudson.plugins.ws__cleanup.WsCleanup")
3803    p.set("plugin", "ws-cleanup")
3804    if "include" in data or "exclude" in data:
3805        patterns = XML.SubElement(p, "patterns")
3806
3807    for inc in data.get("include", []):
3808        ptrn = XML.SubElement(patterns, "hudson.plugins.ws__cleanup.Pattern")
3809        XML.SubElement(ptrn, "pattern").text = inc
3810        XML.SubElement(ptrn, "type").text = "INCLUDE"
3811
3812    for exc in data.get("exclude", []):
3813        ptrn = XML.SubElement(patterns, "hudson.plugins.ws__cleanup.Pattern")
3814        XML.SubElement(ptrn, "pattern").text = exc
3815        XML.SubElement(ptrn, "type").text = "EXCLUDE"
3816
3817    mappings = [
3818        ("dirmatch", "deleteDirs", False),
3819        ("clean-parent", "cleanupMatrixParent", False),
3820        ("external-deletion-command", "externalDelete", ""),
3821        ("disable-deferred-wipeout", "disableDeferredWipeout", False),
3822    ]
3823    helpers.convert_mapping_to_xml(p, data, mappings, fail_required=True)
3824
3825    mask = [
3826        ("success", "cleanWhenSuccess"),
3827        ("unstable", "cleanWhenUnstable"),
3828        ("failure", "cleanWhenFailure"),
3829        ("not-built", "cleanWhenNotBuilt"),
3830        ("aborted", "cleanWhenAborted"),
3831    ]
3832    clean = data.get("clean-if", [])
3833    cdict = dict()
3834    for d in clean:
3835        cdict.update(d)
3836    for k, v in mask:
3837        XML.SubElement(p, v).text = str(cdict.pop(k, True)).lower()
3838
3839    if len(cdict) > 0:
3840        raise ValueError("clean-if must be one of: %r" % list(mask.keys()))
3841
3842    if str(data.get("fail-build", False)).lower() == "false":
3843        XML.SubElement(p, "notFailBuild").text = "true"
3844    else:
3845        XML.SubElement(p, "notFailBuild").text = "false"
3846
3847
3848def maven_deploy(registry, xml_parent, data):
3849    """yaml: maven-deploy
3850    Deploy artifacts to Maven repository.
3851
3852    :arg str id: Repository ID
3853    :arg str url: Repository URL (optional)
3854    :arg bool unique-version: Assign unique versions to snapshots
3855      (default true)
3856    :arg bool deploy-unstable: Deploy even if the build is unstable
3857      (default false)
3858    :arg str release-env-var: If the given variable name is set to "true",
3859      the deploy steps are skipped. (optional)
3860
3861
3862    Example:
3863
3864    .. literalinclude:: /../../tests/publishers/fixtures/maven-deploy001.yaml
3865       :language: yaml
3866    """
3867
3868    p = XML.SubElement(xml_parent, "hudson.maven.RedeployPublisher")
3869    if "id" in data:
3870        XML.SubElement(p, "id").text = data["id"]
3871    if "url" in data:
3872        XML.SubElement(p, "url").text = data["url"]
3873    XML.SubElement(p, "uniqueVersion").text = str(
3874        data.get("unique-version", True)
3875    ).lower()
3876    XML.SubElement(p, "evenIfUnstable").text = str(
3877        data.get("deploy-unstable", False)
3878    ).lower()
3879    if "release-env-var" in data:
3880        XML.SubElement(p, "releaseEnvVar").text = data["release-env-var"]
3881
3882
3883def artifactory(registry, xml_parent, data):
3884    """yaml: artifactory
3885    Uses/requires the Artifactory plugin to deploy artifacts to
3886    Artifactory Server.
3887
3888    Requires the Jenkins :jenkins-plugins:`Artifactory Plugin
3889    <artifactory>`.
3890
3891    :arg str url: Artifactory server url (default '')
3892    :arg str name: Artifactory user with permissions use for
3893        connected to the selected Artifactory Server (default '')
3894    :arg str release-repo-key: Release repository name (default '')
3895    :arg str snapshot-repo-key: Snapshots repository name (default '')
3896    :arg bool publish-build-info: Push build metadata with artifacts
3897        (default false)
3898    :arg bool discard-old-builds:
3899        Remove older build info from Artifactory (default false)
3900    :arg bool discard-build-artifacts:
3901        Remove older build artifacts from Artifactory (default false)
3902    :arg bool even-if-unstable: Deploy artifacts even when the build
3903        is unstable (default false)
3904    :arg bool run-checks: Run automatic license scanning check after the
3905        build is complete (default false)
3906    :arg bool include-publish-artifacts: Include the build's published
3907        module artifacts in the license violation checks if they are
3908        also used as dependencies for other modules in this build
3909        (default false)
3910    :arg bool pass-identified-downstream: When true, a build parameter
3911        named ARTIFACTORY_BUILD_ROOT with a value of
3912        ${JOB_NAME}-${BUILD_NUMBER} will be sent to downstream builds
3913        (default false)
3914    :arg bool license-auto-discovery: Tells Artifactory not to try
3915        and automatically analyze and tag the build's dependencies
3916        with license information upon deployment (default true)
3917    :arg bool enable-issue-tracker-integration: When the Jenkins
3918        JIRA plugin is enabled, synchronize information about JIRA
3919        issues to Artifactory and attach issue information to build
3920        artifacts (default false)
3921    :arg bool aggregate-build-issues: When the Jenkins JIRA plugin
3922        is enabled, include all issues from previous builds up to the
3923        latest build status defined in "Aggregation Build Status"
3924        (default false)
3925    :arg bool allow-promotion-of-non-staged-builds: The build
3926        promotion operation will be available to all successful builds
3927        instead of only staged ones (default false)
3928    :arg bool filter-excluded-artifacts-from-build: Add the excluded
3929        files to the excludedArtifacts list and remove them from the
3930        artifacts list in the build info (default false)
3931    :arg str scopes:  A list of dependency scopes/configurations to run
3932        license violation checks on. If left empty all dependencies from
3933        all scopes will be checked (default '')
3934    :arg str violation-recipients: Recipients that need to be notified
3935        of license violations in the build info (default '')
3936    :arg list matrix-params: Semicolon-separated list of properties to
3937        attach to all deployed artifacts in addition to the default ones:
3938        build.name, build.number, and vcs.revision (default [])
3939    :arg str black-duck-app-name: The existing Black Duck Code Center
3940        application name (default '')
3941    :arg str black-duck-app-version: The existing Black Duck Code Center
3942        application version (default '')
3943    :arg str black-duck-report-recipients: Recipients that will be emailed
3944        a report after the automatic Black Duck Code Center compliance checks
3945        finished (default '')
3946    :arg str black-duck-scopes: A list of dependency scopes/configurations
3947        to run Black Duck Code Center compliance checks on. If left empty
3948        all dependencies from all scopes will be checked (default '')
3949    :arg bool black-duck-run-checks: Automatic Black Duck Code Center
3950        compliance checks will occur after the build completes
3951        (default false)
3952    :arg bool black-duck-include-published-artifacts: Include the build's
3953        published module artifacts in the license violation checks if they
3954        are also used as dependencies for other modules in this build
3955        (default false)
3956    :arg bool auto-create-missing-component-requests: Auto create
3957        missing components in Black Duck Code Center application after
3958        the build is completed and deployed in Artifactory
3959        (default true)
3960    :arg bool auto-discard-stale-component-requests: Auto discard
3961        stale components in Black Duck Code Center application after
3962        the build is completed and deployed in Artifactory
3963        (default true)
3964    :arg bool deploy-artifacts: Push artifacts to the Artifactory
3965        Server. Use deployment-include-patterns and
3966        deployment-exclude-patterns to filter deploy artifacts. (default true)
3967    :arg list deployment-include-patterns: New line or comma separated mappings
3968        of build artifacts to published artifacts. Supports Ant-style wildcards
3969        mapping to target directories. E.g.: */*.zip=>dir (default [])
3970    :arg list deployment-exclude-patterns: New line or comma separated patterns
3971        for excluding artifacts from deployment to Artifactory (default [])
3972    :arg bool env-vars-include: Include all environment variables
3973        accessible by the build process. Jenkins-specific env variables
3974        are always included. Use env-vars-include-patterns and
3975        env-vars-exclude-patterns to filter variables to publish,
3976        (default false)
3977    :arg list env-vars-include-patterns: Comma or space-separated list of
3978        environment variables that will be included as part of the published
3979        build info. Environment variables may contain the * and the ? wildcards
3980        (default [])
3981    :arg list env-vars-exclude-patterns: Comma or space-separated list of
3982        environment variables that will be excluded from the published
3983        build info (default [])
3984
3985    Example:
3986
3987    .. literalinclude:: /../../tests/publishers/fixtures/artifactory01.yaml
3988
3989    .. literalinclude:: /../../tests/publishers/fixtures/artifactory02.yaml
3990
3991    """
3992
3993    artifactory = XML.SubElement(
3994        xml_parent, "org.jfrog.hudson.ArtifactoryRedeployPublisher"
3995    )
3996
3997    # optional_props
3998    helpers.artifactory_optional_props(artifactory, data, "publishers")
3999
4000    XML.SubElement(artifactory, "matrixParams").text = ",".join(
4001        data.get("matrix-params", [])
4002    )
4003
4004    # details
4005    details = XML.SubElement(artifactory, "details")
4006    helpers.artifactory_common_details(details, data)
4007
4008    mapping = [
4009        ("release-repo-key", "repositoryKey", ""),
4010        ("snapshot-repo-key", "snapshotsRepositoryKey", ""),
4011    ]
4012    helpers.convert_mapping_to_xml(details, data, mapping, fail_required=True)
4013
4014    plugin = XML.SubElement(details, "stagingPlugin")
4015    XML.SubElement(plugin, "pluginName").text = "None"
4016
4017    # artifactDeploymentPatterns
4018    helpers.artifactory_deployment_patterns(artifactory, data)
4019
4020    # envVarsPatterns
4021    helpers.artifactory_env_vars_patterns(artifactory, data)
4022
4023
4024def test_fairy(registry, xml_parent, data):
4025    """yaml: test-fairy
4026    This plugin helps you to upload Android APKs or iOS IPA files to
4027    www.testfairy.com.
4028
4029    Requires the Jenkins :jenkins-plugins:`Test Fairy Plugin
4030    <TestFairy>`.
4031
4032    :arg str platform: Select platform to upload to, **android** or **ios**
4033        (required)
4034
4035    Android Only:
4036
4037    :arg str proguard-file: Path to Proguard file. Path of mapping.txt from
4038        your proguard output directory. (default '')
4039    :arg str storepass: Password for the keystore (default android)
4040    :arg str alias: alias for key (default androiddebugkey)
4041    :arg str keypass: password for the key (default '')
4042    :arg str keystorepath: Path to Keystore file (required)
4043
4044    IOS Only:
4045
4046    :arg str dSYM-file: Path to .dSYM.zip file (default '')
4047
4048    All:
4049
4050    :arg str apikey: TestFairy API_KEY. Find it in your TestFairy account
4051        settings (required)
4052    :arg str appfile: Path to App file (.apk) or (.ipa). For example:
4053        $WORKSPACE/[YOUR_FILE_NAME].apk or full path to the apk file.
4054        (required)
4055    :arg str tester-groups: Tester groups to notify (default '')
4056    :arg bool notify-testers: Send email with changelogs to testers
4057        (default false)
4058    :arg bool autoupdate: Automatic update (default false)
4059    :arg str max-duration: Duration of the session (default 10m)
4060
4061        :max-duration values:
4062            * **10m**
4063            * **60m**
4064            * **300m**
4065            * **1440m**
4066    :arg bool record-on-background: Record on background (default false)
4067    :arg bool data-only-wifi: Record data only in wifi (default false)
4068    :arg bool video-enabled: Record video (default true)
4069    :arg int screenshot-interval: Time interval between screenshots
4070        (default 1)
4071
4072        :screenshot-interval values:
4073            * **1**
4074            * **2**
4075            * **5**
4076    :arg str video-quality: Video quality (default high)
4077
4078        :video-quality values:
4079            * **high**
4080            * **medium**
4081            * **low**
4082    :arg bool cpu: Enable CPU metrics (default true)
4083    :arg bool memory: Enable memory metrics (default true)
4084    :arg bool logs: Enable logs metrics (default true)
4085    :arg bool network: Enable network metrics (default false)
4086    :arg bool phone-signal: Enable phone signal metrics (default false)
4087    :arg bool wifi: Enable wifi metrics (default false)
4088    :arg bool gps: Enable gps metrics (default false)
4089    :arg bool battery: Enable battery metrics (default false)
4090    :arg bool opengl: Enable opengl metrics (default false)
4091
4092    Example:
4093
4094    .. literalinclude::
4095       /../../tests/publishers/fixtures/test-fairy-android-minimal.yaml
4096       :language: yaml
4097
4098    .. literalinclude::
4099       /../../tests/publishers/fixtures/test-fairy-android001.yaml
4100       :language: yaml
4101
4102    .. literalinclude::
4103       /../../tests/publishers/fixtures/test-fairy-ios-minimal.yaml
4104       :language: yaml
4105
4106    .. literalinclude::
4107       /../../tests/publishers/fixtures/test-fairy-ios001.yaml
4108       :language: yaml
4109    """
4110    platform = data.get("platform")
4111    valid_platforms = ["android", "ios"]
4112
4113    if "platform" not in data:
4114        raise MissingAttributeError("platform")
4115    if platform == "android":
4116        root = XML.SubElement(
4117            xml_parent, "org.jenkinsci.plugins.testfairy.TestFairyAndroidRecorder"
4118        )
4119        helpers.test_fairy_common(root, data)
4120
4121        mappings = [
4122            ("proguard-file", "mappingFile", ""),
4123            ("keystorepath", "keystorePath", None),
4124            ("storepass", "storepass", "android"),
4125            ("alias", "alias", "androiddebugkey"),
4126            ("keypass", "keypass", ""),
4127        ]
4128        helpers.convert_mapping_to_xml(root, data, mappings, fail_required=True)
4129    elif platform == "ios":
4130        root = XML.SubElement(
4131            xml_parent, "org.jenkinsci.plugins.testfairy.TestFairyIosRecorder"
4132        )
4133        helpers.test_fairy_common(root, data)
4134
4135        mappings = [("dSYM-file", "mappingFile", "")]
4136        helpers.convert_mapping_to_xml(root, data, mappings, fail_required=True)
4137    else:
4138        raise InvalidAttributeError("platform", platform, valid_platforms)
4139
4140
4141def text_finder(registry, xml_parent, data):
4142    """yaml: text-finder
4143    This plugin lets you search keywords in the files you specified and
4144    additionally check build status
4145
4146    Requires the Jenkins :jenkins-plugins:`Text-finder Plugin
4147    <text-finder>`.
4148
4149    :arg str regexp: Specify a regular expression (required)
4150    :arg str fileset: Specify the path to search (optional)
4151    :arg bool also-check-console-output:
4152        Search the console output (default false)
4153    :arg bool succeed-if-found:
4154        Force a build to succeed if a string was found (default false)
4155    :arg bool unstable-if-found:
4156        Set build unstable instead of failing the build (default false)
4157    :arg bool not-built-if-found:
4158        Set build to "Not Built" instead of failing the build (default false)
4159
4160    Example:
4161
4162    .. literalinclude:: /../../tests/publishers/fixtures/text-finder001.yaml
4163       :language: yaml
4164    """
4165
4166    finder = XML.SubElement(xml_parent, "hudson.plugins.textfinder.TextFinderPublisher")
4167    finder.set("plugin", "text-finder")
4168    if "fileset" in data:
4169        XML.SubElement(finder, "fileSet").text = data["fileset"]
4170    mappings = [
4171        ("regexp", "regexp", None),
4172        ("also-check-console-output", "alsoCheckConsoleOutput", False),
4173        ("succeed-if-found", "succeedIfFound", False),
4174        ("unstable-if-found", "unstableIfFound", False),
4175        ("not-built-if-found", "notBuiltIfFound", False),
4176    ]
4177    helpers.convert_mapping_to_xml(finder, data, mappings, fail_required=True)
4178
4179
4180def html_publisher(registry, xml_parent, data):
4181    """yaml: html-publisher
4182    This plugin publishes HTML reports.
4183
4184    Requires the Jenkins :jenkins-plugins:`HTML Publisher Plugin
4185    <htmlpublisher>`.
4186
4187    :arg str name: Report name (required)
4188    :arg str dir: HTML directory to archive (required)
4189    :arg str files: Specify the pages to display (required)
4190    :arg bool keep-all: keep HTML reports for each past build (default false)
4191    :arg bool allow-missing: Allow missing HTML reports (default false)
4192    :arg bool link-to-last-build: If this and 'keep-all' both are true, it
4193        publishes the link on project level even if build failed.
4194        (default false)
4195
4196
4197    Example:
4198
4199    .. literalinclude:: /../../tests/publishers/fixtures/html-publisher001.yaml
4200       :language: yaml
4201    """
4202
4203    reporter = XML.SubElement(xml_parent, "htmlpublisher.HtmlPublisher")
4204    targets = XML.SubElement(reporter, "reportTargets")
4205    ptarget = XML.SubElement(targets, "htmlpublisher.HtmlPublisherTarget")
4206
4207    mapping = [
4208        ("name", "reportName", None),
4209        ("dir", "reportDir", None),
4210        ("files", "reportFiles", None),
4211        ("link-to-last-build", "alwaysLinkToLastBuild", False),
4212        ("keep-all", "keepAll", False),
4213        ("allow-missing", "allowMissing", False),
4214    ]
4215    helpers.convert_mapping_to_xml(ptarget, data, mapping, fail_required=True)
4216    XML.SubElement(ptarget, "wrapperName").text = "htmlpublisher-wrapper.html"
4217
4218
4219def rich_text_publisher(registry, xml_parent, data):
4220    """yaml: rich-text-publisher
4221    This plugin puts custom rich text message to the Build pages and Job main
4222    page.
4223
4224    Requires the Jenkins :jenkins-plugins:`Rich Text Publisher Plugin
4225    <rich-text-publisher-plugin>`.
4226
4227    :arg str stable-text: The stable text (required)
4228    :arg str unstable-text: The unstable text if different from stable
4229        (default '')
4230    :arg bool unstable-as-stable: The same text block is used for stable and
4231         unstable builds (default true)
4232    :arg str failed-text: The failed text if different from stable (default '')
4233    :arg bool failed-as-stable: The same text block is used for stable and
4234         failed builds (default true)
4235    :arg str parser-name: HTML, Confluence or WikiText (default 'WikiText')
4236
4237
4238    Minimal Example:
4239
4240    .. literalinclude::  /../../tests/publishers/fixtures/richtext-minimal.yaml
4241       :language: yaml
4242
4243    Full Example:
4244
4245    .. literalinclude::
4246       /../../tests/publishers/fixtures/richtext-full.yaml
4247       :language: yaml
4248    """
4249
4250    parsers = ["HTML", "Confluence", "WikiText"]
4251    reporter = XML.SubElement(
4252        xml_parent, "org.korosoft.jenkins.plugin.rtp.RichTextPublisher"
4253    )
4254    reporter.set("plugin", "rich-text-publisher-plugin")
4255
4256    mappings = [
4257        ("stable-text", "stableText", None),
4258        ("unstable-text", "unstableText", ""),
4259        ("failed-text", "failedText", ""),
4260        ("unstable-as-stable", "unstableAsStable", True),
4261        ("failed-as-stable", "failedAsStable", True),
4262        ("parser-name", "parserName", "WikiText", parsers),
4263    ]
4264    helpers.convert_mapping_to_xml(reporter, data, mappings, fail_required=True)
4265
4266
4267def tap(registry, xml_parent, data):
4268    """yaml: tap
4269    Adds support to TAP test result files
4270
4271    Requires the Jenkins :jenkins-plugins:`TAP Plugin <tap>`.
4272
4273    :arg str results: TAP test result files (required)
4274    :arg bool fail-if-no-results: Fail if no result (default false)
4275    :arg bool failed-tests-mark-build-as-failure:
4276                Mark build as failure if test fails (default false)
4277    :arg bool output-tap-to-console: Output tap to console (default true)
4278    :arg bool enable-subtests: Enable subtests (default true)
4279    :arg bool discard-old-reports: Discard old reports (default false)
4280    :arg bool todo-is-failure: Handle TODO's as failures (default true)
4281    :arg bool include-comment-diagnostics: Include comment diagnostics (#) in
4282        the results table (>=1.12) (default false)
4283    :arg bool validate-tests: Validate number of tests (>=1.13) (default false)
4284    :arg bool plan-required: TAP plan required? (>=1.17) (default true)
4285    :arg bool verbose: Print a message for each TAP stream file (>=1.17)
4286        (default true)
4287    :arg bool show-only-failures: show only test failures (>=1.17)
4288        (default false)
4289
4290    Full Example:
4291
4292    .. literalinclude:: /../../tests/publishers/fixtures/tap-full.yaml
4293       :language: yaml
4294
4295    Minimal Example:
4296
4297    .. literalinclude:: /../../tests/publishers/fixtures/tap-minimal.yaml
4298       :language: yaml
4299    """
4300
4301    tap = XML.SubElement(xml_parent, "org.tap4j.plugin.TapPublisher")
4302    tap.set("plugin", "tap")
4303
4304    mappings = [
4305        ("results", "testResults", None),
4306        ("fail-if-no-results", "failIfNoResults", False),
4307        ("failed-tests-mark-build-as-failure", "failedTestsMarkBuildAsFailure", False),
4308        ("output-tap-to-console", "outputTapToConsole", True),
4309        ("enable-subtests", "enableSubtests", True),
4310        ("discard-old-reports", "discardOldReports", False),
4311        ("todo-is-failure", "todoIsFailure", True),
4312        ("include-comment-diagnostics", "includeCommentDiagnostics", False),
4313        ("validate-tests", "validateNumberOfTests", False),
4314        ("plan-required", "planRequired", True),
4315        ("verbose", "verbose", True),
4316        ("show-only-failures", "showOnlyFailures", False),
4317    ]
4318    helpers.convert_mapping_to_xml(tap, data, mappings, fail_required=True)
4319
4320
4321def post_tasks(registry, xml_parent, data):
4322    """yaml: post-tasks
4323    Adds support to post build task plugin
4324
4325    Requires the Jenkins :jenkins-plugins:`Post Build Task plugin
4326    <postbuild-task>`.
4327
4328    :arg dict task: Post build task definition
4329    :arg list task[matches]: list of matches when to run the task
4330    :arg dict task[matches][*]: match definition
4331    :arg str task[matches][*][log-text]: text to match against the log
4332    :arg str task[matches][*][operator]: operator to apply with the next match
4333
4334        :task[matches][*][operator] values (default 'AND'):
4335            * **AND**
4336            * **OR**
4337
4338    :arg bool task[escalate-status]: Escalate the task status to the job
4339        (default 'false')
4340    :arg bool task[run-if-job-successful]: Run only if the job was successful
4341        (default 'false')
4342    :arg str task[script]: Shell script to run (default '')
4343
4344    Example:
4345
4346    .. literalinclude:: /../../tests/publishers/fixtures/post-tasks001.yaml
4347       :language: yaml
4348    """
4349
4350    pb_xml = XML.SubElement(xml_parent, "hudson.plugins.postbuildtask.PostbuildTask")
4351    tasks_xml = XML.SubElement(pb_xml, "tasks")
4352    for task in data:
4353        task_xml = XML.SubElement(
4354            tasks_xml, "hudson.plugins.postbuildtask.TaskProperties"
4355        )
4356        matches_xml = XML.SubElement(task_xml, "logTexts")
4357        for match in task.get("matches", []):
4358            lt_xml = XML.SubElement(
4359                matches_xml, "hudson.plugins.postbuildtask.LogProperties"
4360            )
4361            XML.SubElement(lt_xml, "logText").text = str(
4362                match.get("log-text", False) or ""
4363            )
4364            XML.SubElement(lt_xml, "operator").text = str(
4365                match.get("operator", "AND")
4366            ).upper()
4367        mapping = [
4368            ("escalate-status", "EscalateStatus", False),
4369            ("run-if-job-successful", "RunIfJobSuccessful", False),
4370            ("script", "script", ""),
4371        ]
4372        helpers.convert_mapping_to_xml(task_xml, task, mapping, fail_required=True)
4373
4374
4375def postbuildscript(registry, xml_parent, data):
4376    """yaml: postbuildscript
4377    Executes additional builders, script or Groovy after the build is
4378    complete.
4379
4380    Requires the Jenkins :jenkins-plugins:`Post Build Script plugin
4381    <postbuildscript>`.
4382
4383    :arg list generic-script: Series of Batch/Shell scripts to to run
4384
4385        :generic-script: * **file-path** (`str`) - Path to Batch/Shell scripts
4386                         * **role** (`str`) - Execute scripts on. One of
4387                           MASTER / SLAVE / BOTH. (default 'BOTH')
4388                         * **build-on** (`list`) - Build statuses which trigger
4389                           the scripts. Valid options:
4390                           SUCCESS / UNSTABLE / FAILURE / NOT_BUILT / ABORTED
4391                           (default 'SUCCESS')
4392                         * ** execute-on (`str`) - For matrix projects, scripts
4393                           can be run after each axis is built (`axes`), after
4394                           all axis of the matrix are built (`matrix`) or after
4395                           each axis AND the matrix are built (`both`). (default `both`)
4396
4397    :arg list groovy-script: Paths to Groovy scripts
4398
4399        :groovy-script: * **file-path** (`str`) - Path to Groovy scripts
4400                        * **role** (`str`) - Execute scripts on. One of
4401                          MASTER / SLAVE / BOTH. (default 'BOTH')
4402                        * **build-on** (`list`) - Build statuses which trigger
4403                          the scripts. Valid options:
4404                          SUCCESS / UNSTABLE / FAILURE / NOT_BUILT / ABORTED
4405                          (default 'SUCCESS')
4406                        * ** execute-on (`str`) - For matrix projects, scripts
4407                          can be run after each axis is built (`axes`), after
4408                          all axis of the matrix are built (`matrix`) or after
4409                          each axis AND the matrix are built (`both`). (default `both`)
4410
4411    :arg list groovy: Inline Groovy
4412
4413        :groovy: * **content** (`str`) - Inline Groovy script.
4414                 * **role** (`str`) - Execute scripts on. One of
4415                   MASTER / SLAVE / BOTH. (default 'BOTH')
4416                 * **build-on** (`list`) - Build statuses which trigger
4417                   the scripts. Valid options:
4418                   SUCCESS / UNSTABLE / FAILURE / NOT_BUILT / ABORTED
4419                   (default 'SUCCESS')
4420                 * ** execute-on (`str`) - For matrix projects, scripts
4421                   can be run after each axis is built (`axes`), after
4422                   all axis of the matrix are built (`matrix`) or after
4423                   each axis AND the matrix are built (`both`). (default `both`)
4424
4425    :arg list builders: Execute any number of supported Jenkins builders.
4426
4427        :builders: * **build-steps** (`str`) - Any supported builders,
4428                     see :doc:`builders`.
4429                   * **role** (`str`) - Execute scripts on. One of
4430                     MASTER / SLAVE / BOTH. (default 'BOTH')
4431                   * **build-on** (`list`) - Build statuses which trigger
4432                     the scripts. Valid options:
4433                     SUCCESS / UNSTABLE / FAILURE / NOT_BUILT / ABORTED
4434                     (default 'SUCCESS')
4435                   * ** execute-on (`str`) - For matrix projects, scripts
4436                     can be run after each axis is built (`axes`), after
4437                     all axis of the matrix are built (`matrix`) or after
4438                     each axis AND the matrix are built (`both`). (default `both`)
4439
4440    :arg bool mark-unstable-if-failed: Build will be marked unstable
4441        if job will be successfully completed but publishing script will return
4442        non zero exit code (default false)
4443
4444    Deprecated Options for versions < 2.0 of plugin:
4445
4446    :arg bool onsuccess: Deprecated, replaced with script-only-if-succeeded
4447    :arg bool script-only-if-succeeded: Scripts and builders are run only if
4448        the build succeeded (default true)
4449    :arg bool onfailure: Deprecated, replaced with script-only-if-failed
4450    :arg bool script-only-if-failed: Scripts and builders are run only if the
4451        build failed (default false)
4452    :arg str execute-on: For matrix projects, scripts can be run after each
4453        axis is built (`axes`), after all axis of the matrix are built
4454        (`matrix`) or after each axis AND the matrix are built (`both`).
4455
4456    The `script-only-if-succeeded` and `bool script-only-if-failed` options are
4457    confusing. If you want the post build to always run regardless of the build
4458    status, you should set them both to `false`.
4459
4460    Minimal Example:
4461
4462    .. literalinclude::
4463        /../../tests/publishers/fixtures/postbuildscript-minimal.yaml
4464       :language: yaml
4465
4466    Full Example:
4467
4468    .. literalinclude::
4469        /../../tests/publishers/fixtures/postbuildscript-full.yaml
4470       :language: yaml
4471
4472    Example(s) versions < 2.0:
4473
4474    .. literalinclude::
4475        /../../tests/publishers/fixtures/postbuildscript001.yaml
4476       :language: yaml
4477
4478    You can also execute :doc:`builders </builders>`:
4479
4480    .. literalinclude::
4481        /../../tests/publishers/fixtures/postbuildscript002.yaml
4482       :language: yaml
4483
4484    Run once after the whole matrix (all axes) is built:
4485
4486    .. literalinclude::
4487        /../../tests/publishers/fixtures/postbuildscript003.yaml
4488       :language: yaml
4489    """
4490
4491    pbs_xml = XML.SubElement(
4492        xml_parent, "org.jenkinsci.plugins.postbuildscript.PostBuildScript"
4493    )
4494
4495    info = registry.get_plugin_info("postbuildscript")
4496    # Note: Assume latest version of plugin is preferred config format
4497    version = pkg_resources.parse_version(info.get("version", str(sys.maxsize)))
4498    if version >= pkg_resources.parse_version("2.0"):
4499        pbs_xml = XML.SubElement(pbs_xml, "config")
4500
4501    mapping = [("mark-unstable-if-failed", "markBuildUnstable", False)]
4502    helpers.convert_mapping_to_xml(pbs_xml, data, mapping, fail_required=True)
4503
4504    if version >= pkg_resources.parse_version("2.0"):
4505
4506        def add_execute_on(bs_data, result_xml):
4507            valid_values = ("matrix", "axes", "both")
4508            execute_on = bs_data.get("execute-on")
4509            if not execute_on:
4510                return
4511            if execute_on not in valid_values:
4512                raise JenkinsJobsException(
4513                    "execute-on must be one of %s, got %s" % valid_values, execute_on
4514                )
4515            execute_on_xml = XML.SubElement(result_xml, "executeOn")
4516            execute_on_xml.text = execute_on.upper()
4517
4518        ################
4519        # Script Files #
4520        ################
4521
4522        script_mapping = [("role", "role", "BOTH"), ("file-path", "filePath", False)]
4523        sf_path = "org.jenkinsci.plugins.postbuildscript.model.ScriptFile"
4524        sf_xml = XML.SubElement(pbs_xml, "scriptFiles")
4525
4526        for gs_data in data.get("generic-script", []):
4527            x = XML.SubElement(sf_xml, sf_path)
4528            results_xml = XML.SubElement(x, "results")
4529
4530            for result in gs_data.get("build-on", ["SUCCESS"]):
4531                XML.SubElement(results_xml, "string").text = result
4532
4533            add_execute_on(gs_data, x)
4534
4535            helpers.convert_mapping_to_xml(
4536                x, gs_data, script_mapping, fail_required=True
4537            )
4538            XML.SubElement(x, "scriptType").text = "GENERIC"
4539
4540        for gs_data in data.get("groovy-script", []):
4541            x = XML.SubElement(sf_xml, sf_path)
4542            results_xml = XML.SubElement(x, "results")
4543
4544            for result in gs_data.get("build-on", ["SUCCESS"]):
4545                XML.SubElement(results_xml, "string").text = result
4546
4547            add_execute_on(gs_data, x)
4548
4549            helpers.convert_mapping_to_xml(
4550                x, gs_data, script_mapping, fail_required=True
4551            )
4552            XML.SubElement(x, "scriptType").text = "GROOVY"
4553
4554        #################
4555        # Inline Groovy #
4556        #################
4557
4558        groovy_mapping = [("role", "role", "BOTH"), ("content", "content", False)]
4559        gs_path = "org.jenkinsci.plugins.postbuildscript.model.Script"
4560        gs_xml = XML.SubElement(pbs_xml, "groovyScripts")
4561        for gs_data in data.get("groovy", []):
4562            x = XML.SubElement(gs_xml, gs_path)
4563            results_xml = XML.SubElement(x, "results")
4564
4565            for result in gs_data.get("build-on", ["SUCCESS"]):
4566                XML.SubElement(results_xml, "string").text = result
4567
4568            add_execute_on(gs_data, x)
4569
4570            helpers.convert_mapping_to_xml(
4571                x, gs_data, groovy_mapping, fail_required=True
4572            )
4573
4574        ############
4575        # Builders #
4576        ############
4577
4578        builder_mapping = [("role", "role", "BOTH")]
4579        bs_path = "org.jenkinsci.plugins.postbuildscript.model.PostBuildStep"
4580        bs_xml = XML.SubElement(pbs_xml, "buildSteps")
4581        for bs_data in data.get("builders", []):
4582            x = XML.SubElement(bs_xml, bs_path)
4583            results_xml = XML.SubElement(x, "results")
4584
4585            for result in bs_data.get("build-on", ["SUCCESS"]):
4586                XML.SubElement(results_xml, "string").text = result
4587
4588            add_execute_on(bs_data, x)
4589
4590            helpers.convert_mapping_to_xml(
4591                x, bs_data, builder_mapping, fail_required=True
4592            )
4593
4594            build_steps_xml = XML.SubElement(x, "buildSteps")
4595            for builder in bs_data.get("build-steps"):
4596                registry.dispatch("builder", build_steps_xml, builder)
4597
4598    else:  # Options below are all deprecated in version < 2.0 of plugin
4599
4600        # Shell/Groovy in a file
4601        script_types = {
4602            "generic-script": "GenericScript",
4603            "groovy-script": "GroovyScriptFile",
4604        }
4605
4606        # Assuming yaml preserves order of input data make sure
4607        # corresponding XML steps are generated in the same order
4608        build_scripts = [
4609            (k, v)
4610            for k, v in data.items()
4611            if k in script_types or k in ["groovy", "builders"]
4612        ]
4613
4614        for step, script_data in build_scripts:
4615            if step in script_types:
4616                scripts_xml = XML.SubElement(
4617                    pbs_xml, step[: -len("-script")] + "ScriptFileList"
4618                )
4619                for shell_script in script_data:
4620                    script_xml = XML.SubElement(
4621                        scripts_xml,
4622                        "org.jenkinsci.plugins.postbuildscript." + script_types[step],
4623                    )
4624                    file_path_xml = XML.SubElement(script_xml, "filePath")
4625                    file_path_xml.text = shell_script
4626
4627            # Inlined Groovy
4628            if step == "groovy":
4629                groovy_inline_xml = XML.SubElement(pbs_xml, "groovyScriptContentList")
4630                for groovy in script_data:
4631                    groovy_xml = XML.SubElement(
4632                        groovy_inline_xml,
4633                        "org.jenkinsci.plugins.postbuildscript." "GroovyScriptContent",
4634                    )
4635                    groovy_content = XML.SubElement(groovy_xml, "content")
4636                    groovy_content.text = groovy
4637
4638            # Inject builders
4639            if step == "builders":
4640                build_steps_xml = XML.SubElement(pbs_xml, "buildSteps")
4641                for builder in script_data:
4642                    registry.dispatch("builder", build_steps_xml, builder)
4643
4644        # When to run the build? Note the plugin let one specify both options
4645        # although they are antinomic
4646        # onsuccess and onfailure parameters are deprecated, this is to keep
4647        # backwards compatability
4648        success_xml = XML.SubElement(pbs_xml, "scriptOnlyIfSuccess")
4649        if "script-only-if-succeeded" in data:
4650            success_xml.text = str(data.get("script-only-if-succeeded", True)).lower()
4651        else:
4652            success_xml.text = str(data.get("onsuccess", True)).lower()
4653
4654        failure_xml = XML.SubElement(pbs_xml, "scriptOnlyIfFailure")
4655        if "script-only-if-failed" in data:
4656            failure_xml.text = str(data.get("script-only-if-failed", False)).lower()
4657        else:
4658            failure_xml.text = str(data.get("onfailure", False)).lower()
4659
4660        # TODO: we may want to avoid setting "execute-on" on non-matrix jobs,
4661        # either by skipping this part or by raising an error to let the user
4662        # know an attempt was made to set execute-on on a non-matrix job.
4663        # There are currently no easy ways to check for this though.
4664        if "execute-on" in data:
4665            valid_values = ("matrix", "axes", "both")
4666            execute_on = data["execute-on"].lower()
4667            if execute_on not in valid_values:
4668                raise JenkinsJobsException(
4669                    "execute-on must be one of %s, got %s" % valid_values, execute_on
4670                )
4671            execute_on_xml = XML.SubElement(pbs_xml, "executeOn")
4672            execute_on_xml.text = execute_on.upper()
4673
4674
4675def xml_summary(registry, xml_parent, data):
4676    """yaml: xml-summary
4677    Adds support for the Summary Display Plugin
4678
4679    Requires the Jenkins :jenkins-plugins:`Summary Display Plugin
4680    <summary_report>`.
4681
4682    :arg str files: Files to parse (required)
4683    :arg bool shown-on-project-page: Display summary on project page
4684        (default false)
4685
4686    Minimal Example:
4687
4688    .. literalinclude::
4689       /../../tests/publishers/fixtures/xml-summary-minimal.yaml
4690       :language: yaml
4691
4692    Full Example:
4693
4694    .. literalinclude:: /../../tests/publishers/fixtures/xml-summary-full.yaml
4695       :language: yaml
4696    """
4697
4698    summary = XML.SubElement(
4699        xml_parent, "hudson.plugins.summary__report.ACIPluginPublisher"
4700    )
4701    summary.set("plugin", "summary_report")
4702
4703    mapping = [
4704        ("files", "name", None),
4705        ("shown-on-project-page", "shownOnProjectPage", False),
4706    ]
4707    helpers.convert_mapping_to_xml(summary, data, mapping, fail_required=True)
4708
4709
4710def robot(registry, xml_parent, data):
4711    """yaml: robot
4712    Adds support for the Robot Framework Plugin
4713
4714    Requires the Jenkins :jenkins-plugins:`Robot Framework Plugin <robot>`.
4715
4716    :arg str output-path: Path to directory containing robot xml and html files
4717        relative to build workspace. (required)
4718    :arg str log-file-link: Name of log or report file to be linked on jobs
4719        front page (default '')
4720    :arg str report-html: Name of the html file containing robot test report
4721        (default 'report.html')
4722    :arg str log-html: Name of the html file containing detailed robot test log
4723        (default 'log.html')
4724    :arg str output-xml: Name of the xml file containing robot output
4725        (default 'output.xml')
4726    :arg str pass-threshold: Minimum percentage of passed tests to consider
4727        the build successful (default 0.0)
4728    :arg str unstable-threshold: Minimum percentage of passed test to
4729        consider the build as not failed (default 0.0)
4730    :arg bool only-critical: Take only critical tests into account when
4731        checking the thresholds (default true)
4732    :arg list other-files: list other files to archive (default '')
4733    :arg bool archive-output-xml: Archive output xml file to server
4734        (default true)
4735    :arg bool enable-cache: Enable cache for test results (default true)
4736
4737    Minimal Example:
4738
4739    .. literalinclude:: /../../tests/publishers/fixtures/robot-minimal.yaml
4740       :language: yaml
4741
4742    Full Example:
4743
4744    .. literalinclude:: /../../tests/publishers/fixtures/robot-full.yaml
4745       :language: yaml
4746    """
4747    parent = XML.SubElement(xml_parent, "hudson.plugins.robot.RobotPublisher")
4748    parent.set("plugin", "robot")
4749    mappings = [
4750        ("output-path", "outputPath", None),
4751        ("log-file-link", "logFileLink", ""),
4752        ("report-html", "reportFileName", "report.html"),
4753        ("log-html", "logFileName", "log.html"),
4754        ("output-xml", "outputFileName", "output.xml"),
4755        ("pass-threshold", "passThreshold", "0.0"),
4756        ("unstable-threshold", "unstableThreshold", "0.0"),
4757        ("only-critical", "onlyCritical", True),
4758        ("enable-cache", "enableCache", True),
4759    ]
4760    helpers.convert_mapping_to_xml(parent, data, mappings, fail_required=True)
4761
4762    other_files = XML.SubElement(parent, "otherFiles")
4763    for other_file in data.get("other-files", []):
4764        XML.SubElement(other_files, "string").text = str(other_file)
4765    XML.SubElement(parent, "disableArchiveOutput").text = str(
4766        not data.get("archive-output-xml", True)
4767    ).lower()
4768
4769
4770def warnings(registry, xml_parent, data):
4771    """yaml: warnings
4772    Generate trend report for compiler warnings in the console log or
4773    in log files.
4774
4775    Requires the Jenkins Warnings Plugin
4776    (https://github.com/jenkinsci/warnings-plugin).
4777
4778    :arg list console-log-parsers: The parser to use to scan the console
4779        log (default '')
4780    :arg dict workspace-file-scanners:
4781
4782        :workspace-file-scanners:
4783            * **file-pattern** (`str`) -- Fileset 'includes' setting that
4784                specifies the files to scan for warnings (required)
4785            * **scanner** (`str`) -- The parser to use to scan the files
4786                provided in workspace-file-pattern (default '')
4787    :arg str files-to-include: Comma separated list of regular
4788        expressions that specifies the files to include in the report
4789        (based on their absolute filename). By default all files are
4790        included
4791    :arg str files-to-ignore: Comma separated list of regular expressions
4792        that specifies the files to exclude from the report (based on their
4793        absolute filename). (default '')
4794    :arg str messages-to-ignore: Newline separated list of regular
4795        expressions that specifies the warning messages to exclude form the
4796        report (based on the warning messages). By default all warning
4797        messages are included
4798    :arg str categories-to-ignore: Newline separated list of regular
4799        expressions that specifies the warning messages to exclude form the
4800        report (based on the warning categories). By default all warning
4801        categories are included
4802    :arg bool run-always: By default, this plug-in runs only for stable or
4803        unstable builds, but not for failed builds.  Set to true if the
4804        plug-in should run even for failed builds.  (default false)
4805    :arg bool detect-modules: Determines if Ant or Maven modules should be
4806        detected for all files that contain warnings.  Activating this
4807        option may increase your build time since the detector scans
4808        the whole workspace for 'build.xml' or 'pom.xml' files in order
4809        to assign the correct module names. (default false)
4810    :arg bool resolve-relative-paths: Determines if relative paths in
4811        warnings should be resolved using a time expensive operation that
4812        scans the whole workspace for matching files.  Deactivate this
4813        option if you encounter performance problems.  (default false)
4814    :arg int health-threshold-high: The upper threshold for the build
4815        health.  If left empty then no health report is created.  If
4816        the actual number of warnings is between the provided
4817        thresholds then the build health is interpolated (default '')
4818    :arg int health-threshold-low: The lower threshold for the build
4819        health.  See health-threshold-high.  (default '')
4820    :arg dict health-priorities: Determines which warning priorities
4821        should be considered when evaluating the build health (default
4822        all-priorities)
4823
4824        :health-priorities values:
4825          * **priority-high** -- Only priority high
4826          * **high-and-normal** -- Priorities high and normal
4827          * **all-priorities** -- All priorities
4828    :arg dict total-thresholds: If the number of total warnings is greater
4829        than one of these thresholds then a build is considered as unstable
4830        or failed, respectively. (default '')
4831
4832        :total-thresholds:
4833            * **unstable** (`dict`)
4834                :unstable: * **total-all** (`int`)
4835                           * **total-high** (`int`)
4836                           * **total-normal** (`int`)
4837                           * **total-low** (`int`)
4838            * **failed** (`dict`)
4839                :failed: * **total-all** (`int`)
4840                         * **total-high** (`int`)
4841                         * **total-normal** (`int`)
4842                         * **total-low** (`int`)
4843    :arg dict new-thresholds: If the specified number of new warnings exceeds
4844        one of these thresholds then a build is considered as unstable or
4845        failed, respectively.  (default '')
4846
4847        :new-thresholds:
4848            * **unstable** (`dict`)
4849                :unstable: * **new-all** (`int`)
4850                           * **new-high** (`int`)
4851                           * **new-normal** (`int`)
4852                           * **new-low** (`int`)
4853            * **failed** (`dict`)
4854                :failed: * **new-all** (`int`)
4855                         * **new-high** (`int`)
4856                         * **new-normal** (`int`)
4857                         * **new-high** (`int`)
4858    :arg bool use-delta-for-new-warnings:  If set then the number of new
4859        warnings is calculated by subtracting the total number of warnings
4860        of the current build from the reference build. This may lead to wrong
4861        results if you have both fixed and new warnings in a build. If not set,
4862        then the number of new warnings is calculated by an asymmetric set
4863        difference of the warnings in the current and reference build. This
4864        will find all new warnings even if the number of total warnings is
4865        decreasing. However, sometimes false positives will be reported due
4866        to minor changes in a warning (refactoring of variable of method
4867        names, etc.) (default false)
4868    :arg bool use-previous-build-as-reference: If set the number of new
4869        warnings will always be computed based on the previous build, even if
4870        that build is unstable (due to a violated warning threshold).
4871        Otherwise the last build that did not violate any given threshold will
4872        be used as
4873        reference. It is recommended to uncheck this option if the plug-in
4874        should ensure that all new warnings will be finally fixed in subsequent
4875        builds. (default false)
4876    :arg bool only-use-stable-builds-as-reference: The number of new warnings
4877        will be calculated based on the last stable build, allowing reverts
4878        of unstable builds where the number of warnings was decreased.
4879        (default false)
4880    :arg str default-encoding: Default encoding when parsing or showing files
4881        Leave empty to use default encoding of platform (default '')
4882
4883    Minimal Example:
4884
4885    .. literalinclude:: /../../tests/publishers/fixtures/warnings-minimal.yaml
4886       :language: yaml
4887
4888    Full Example:
4889
4890    .. literalinclude:: /../../tests/publishers/fixtures/warnings-full.yaml
4891       :language: yaml
4892    """
4893
4894    warnings = XML.SubElement(
4895        xml_parent, "hudson.plugins.warnings." "WarningsPublisher"
4896    )
4897    warnings.set("plugin", "warnings")
4898    console = XML.SubElement(warnings, "consoleParsers")
4899    for parser in data.get("console-log-parsers", []):
4900        console_parser = XML.SubElement(
4901            console, "hudson.plugins.warnings." "ConsoleParser"
4902        )
4903        XML.SubElement(console_parser, "parserName").text = parser
4904    workspace = XML.SubElement(warnings, "parserConfigurations")
4905    for wfs in data.get("workspace-file-scanners", []):
4906        workspace_pattern = XML.SubElement(
4907            workspace, "hudson.plugins.warnings." "ParserConfiguration"
4908        )
4909        workspace_pattern_mappings = [
4910            ("file-pattern", "pattern", None),
4911            ("scanner", "parserName", ""),
4912        ]
4913        helpers.convert_mapping_to_xml(
4914            workspace_pattern, wfs, workspace_pattern_mappings, fail_required=True
4915        )
4916    prioritiesDict = {
4917        "priority-high": "high",
4918        "high-and-normal": "normal",
4919        "all-priorities": "low",
4920    }
4921    warnings_mappings = [
4922        ("files-to-include", "includePattern", ""),
4923        ("files-to-ignore", "excludePattern", ""),
4924        ("messages-to-ignore", "messagesPattern", ""),
4925        ("categories-to-ignore", "categoriesPattern", ""),
4926        ("plugin-name", "pluginName", "[WARNINGS]"),
4927        ("run-always", "canRunOnFailed", False),
4928        ("detect-modules", "shouldDetectModules", False),
4929        ("health-threshold-high", "healthy", ""),
4930        ("health-threshold-low", "unHealthy", ""),
4931        ("health-priorities", "thresholdLimit", "all-priorities", prioritiesDict),
4932        ("default-encoding", "defaultEncoding", ""),
4933    ]
4934    helpers.convert_mapping_to_xml(
4935        warnings, data, warnings_mappings, fail_required=True
4936    )
4937    # Note the logic reversal (included here to match the GUI)
4938    XML.SubElement(warnings, "doNotResolveRelativePaths").text = str(
4939        not data.get("resolve-relative-paths", False)
4940    ).lower()
4941    td = XML.SubElement(warnings, "thresholds")
4942    for base in ["total", "new"]:
4943        thresholds = data.get("%s-thresholds" % base, {})
4944        for status in ["unstable", "failed"]:
4945            bystatus = thresholds.get(status, {})
4946            for level in ["all", "high", "normal", "low"]:
4947                val = str(bystatus.get("%s-%s" % (base, level), ""))
4948                XML.SubElement(
4949                    td, "%s%s%s" % (status, base.capitalize(), level.capitalize())
4950                ).text = val
4951    if data.get("new-thresholds"):
4952        XML.SubElement(warnings, "dontComputeNew").text = "false"
4953        delta = data.get("use-delta-for-new-warnings", False)
4954        XML.SubElement(warnings, "useDeltaValues").text = str(delta).lower()
4955        use_previous_build = data.get("use-previous-build-as-reference", False)
4956        XML.SubElement(warnings, "usePreviousBuildAsReference").text = str(
4957            use_previous_build
4958        ).lower()
4959        use_stable_builds = data.get("only-use-stable-builds-as-reference", False)
4960        XML.SubElement(warnings, "useStableBuildAsReference").text = str(
4961            use_stable_builds
4962        ).lower()
4963    else:
4964        XML.SubElement(warnings, "dontComputeNew").text = "true"
4965        XML.SubElement(warnings, "useDeltaValues").text = "false"
4966        XML.SubElement(warnings, "usePreviousBuildAsReference").text = "false"
4967        XML.SubElement(warnings, "useStableBuildAsReference").text = "false"
4968
4969
4970def sloccount(registry, xml_parent, data):
4971    r"""yaml: sloccount
4972    Generates the trend report for SLOCCount
4973
4974    Requires the Jenkins :jenkins-plugins:`SLOCCount Plugin <sloccount>`.
4975
4976    :arg str report-files: Setting that specifies the generated raw
4977        SLOCCount report files. Be sure not to include any non-report files
4978        into this pattern. The report files must have been generated by
4979        sloccount using the "--wide --details" options.
4980        (default '\*\*/sloccount.sc')
4981    :arg str charset: The character encoding to be used to read the SLOCCount
4982        result files. (default 'UTF-8')
4983    :arg int builds-in-graph: Maximal number of last successful builds, that
4984        are displayed in the trend graphs. (default 0)
4985    :arg bool comment-is-code: This option is considered only in the cloc
4986        report parser and is ignored in the SLOCCount one. (default false)
4987    :arg bool ignore-build-failure: Try to process the report files even if
4988        the build is not successful. (default false)
4989
4990    Minimal Example:
4991
4992    .. literalinclude:: /../../tests/publishers/fixtures/sloccount-minimal.yaml
4993       :language: yaml
4994
4995    Full Example:
4996
4997    .. literalinclude::
4998       /../../tests/publishers/fixtures/sloccount-full.yaml
4999       :language: yaml
5000    """
5001    top = XML.SubElement(xml_parent, "hudson.plugins.sloccount.SloccountPublisher")
5002    top.set("plugin", "sloccount")
5003    mappings = [
5004        ("report-files", "pattern", "**/sloccount.sc"),
5005        ("charset", "encoding", "UTF-8"),
5006        ("builds-in-graph", "numBuildsInGraph", 0),
5007        ("comment-is-code", "commentIsCode", False),
5008        ("ignore-build-failure", "ignoreBuildFailure", False),
5009    ]
5010    helpers.convert_mapping_to_xml(top, data, mappings, fail_required=True)
5011
5012
5013def ircbot(registry, xml_parent, data):
5014    """yaml: ircbot
5015    ircbot enables Jenkins to send build notifications via IRC and lets you
5016    interact with Jenkins via an IRC bot.
5017
5018    Requires the Jenkins :jenkins-plugins:`IRC Plugin <ircbot>`.
5019
5020    :arg str strategy: When to send notifications
5021
5022        :strategy values:
5023            * **all** always (default)
5024            * **any-failure** on any failure
5025            * **failure-and-fixed** on failure and fixes
5026            * **new-failure-and-fixed** on new failure and fixes
5027            * **statechange-only** only on state change
5028    :arg bool notify-start: Whether to send notifications to channels when a
5029        build starts (default false)
5030    :arg bool notify-committers: Whether to send notifications to the users
5031        that are suspected of having broken this build (default false)
5032    :arg bool notify-culprits: Also send notifications to 'culprits' from
5033        previous unstable/failed builds (default false)
5034    :arg bool notify-upstream: Whether to send notifications to upstream
5035        committers if no committers were found for a broken build
5036        (default false)
5037    :arg bool notify-fixers: Whether to send notifications to the users that
5038        have fixed a broken build (default false)
5039    :arg str message-type: Channel Notification Message.
5040
5041        :message-type values:
5042            * **summary-scm** for summary and SCM changes (default)
5043            * **summary** for summary only
5044            * **summary-params** for summary and build parameters
5045            * **summary-scm-fail** for summary, SCM changes, failures)
5046    :arg list channels: list channels definitions
5047        If empty, it takes channel from Jenkins configuration.
5048        (default empty)
5049        WARNING: the IRC plugin requires the channel to be configured in the
5050        system wide configuration or the jobs will fail to emit notifications
5051        to the channel
5052
5053        :Channel: * **name** (`str`) Channel name
5054                  * **password** (`str`) Channel password (optional)
5055                  * **notify-only** (`bool`) Set to true if you want to
5056                    disallow bot commands (default false)
5057    :arg str matrix-notifier: notify for matrix projects
5058        instant-messaging-plugin injects an additional
5059        field in the configuration form whenever the
5060        project is a multi-configuration project
5061
5062        :matrix-notifier values:
5063            * **all**
5064            * **only-configurations** (default)
5065            * **only-parent**
5066
5067    Minimal Example:
5068
5069    .. literalinclude:: /../../tests/publishers/fixtures/ircbot-minimal.yaml
5070       :language: yaml
5071
5072    Full Example:
5073
5074    .. literalinclude:: /../../tests/publishers/fixtures/ircbot-full.yaml
5075       :language: yaml
5076    """
5077    top = XML.SubElement(xml_parent, "hudson.plugins.ircbot.IrcPublisher")
5078    top.set("plugin", "ircbot")
5079    message_dict = {
5080        "summary-scm": "DefaultBuildToChatNotifier",
5081        "summary": "SummaryOnlyBuildToChatNotifier",
5082        "summary-params": "BuildParametersBuildToChatNotifier",
5083        "summary-scm-fail": "PrintFailingTestsBuildToChatNotifier",
5084    }
5085    message = data.get("message-type", "summary-scm")
5086    if message not in message_dict:
5087        raise JenkinsJobsException(
5088            "message-type entered is not valid, must "
5089            "be one of: %s" % ", ".join(message_dict.keys())
5090        )
5091    message = "hudson.plugins.im.build_notify." + message_dict.get(message)
5092    XML.SubElement(top, "buildToChatNotifier", attrib={"class": message})
5093    targets = XML.SubElement(top, "targets")
5094    channels = data.get("channels", [])
5095    for channel in channels:
5096        sub = XML.SubElement(targets, "hudson.plugins.im.GroupChatIMMessageTarget")
5097        sub_mappings = [
5098            ("name", "name", ""),
5099            ("password", "password", ""),
5100            ("notify-only", "notificationOnly", False),
5101        ]
5102        helpers.convert_mapping_to_xml(sub, channel, sub_mappings, fail_required=True)
5103    strategy_dict = {
5104        "all": "ALL",
5105        "any-failure": "ANY_FAILURE",
5106        "failure-and-fixed": "FAILURE_AND_FIXED",
5107        "new-failure-and-fixed": "NEW_FAILURE_AND_FIXED",
5108        "statechange-only": "STATECHANGE_ONLY",
5109    }
5110    matrix_dict = {
5111        "all": "ALL",
5112        "only-configurations": "ONLY_CONFIGURATIONS",
5113        "only-parent": "ONLY_PARENT",
5114    }
5115    mappings = [
5116        ("strategy", "strategy", "all", strategy_dict),
5117        ("notify-start", "notifyOnBuildStart", False),
5118        ("notify-committers", "notifySuspects", False),
5119        ("notify-culprits", "notifyCulprits", False),
5120        ("notify-fixers", "notifyFixers", False),
5121        ("notify-upstream", "notifyUpstreamCommitters", False),
5122        ("matrix-notifier", "matrixMultiplier", "only-configurations", matrix_dict),
5123    ]
5124    helpers.convert_mapping_to_xml(top, data, mappings, fail_required=True)
5125
5126
5127def plot(registry, xml_parent, data):
5128    """yaml: plot
5129    Plot provides generic plotting (or graphing).
5130
5131    Requires the Jenkins :jenkins-plugins:`Plot Plugin <plot>`.
5132
5133    :arg str title: title for the graph (default '')
5134    :arg str yaxis: title of Y axis (default '')
5135    :arg int width: the width of the plot in pixels (default 750)
5136    :arg int height: the height of the plot in pixels (default 450)
5137    :arg str group: name of the group to which the plot belongs (required)
5138    :arg int num-builds: number of builds to plot across
5139        (default plot all builds)
5140    :arg str style:  Specifies the graph style of the plot
5141        Can be: area, bar, bar3d, line, line3d, stackedArea, stackedbar,
5142        stackedbar3d, waterfall (default 'line')
5143    :arg bool use-description: When false, the X-axis labels are formed using
5144        build numbers and dates, and the corresponding tooltips contain the
5145        build descriptions. When enabled, the contents of the labels and
5146        tooltips are swapped, with the descriptions used as X-axis labels and
5147        the build number and date used for tooltips. (default false)
5148    :arg bool exclude-zero-yaxis: When false, Y-axis contains the value zero
5149        even if it is not included in the data series. When true, the value
5150        zero is not automatically included. (default false)
5151    :arg bool logarithmic-yaxis: When true, the Y-axis will use a logarithmic
5152        scale. By default, the Y-axis uses a linear scale. (default false)
5153    :arg bool keep-records: When true, show all builds up to 'Number of
5154        builds to include'. (default false)
5155    :arg str csv-file-name: Use for choosing the file name in which the data
5156        will be persisted. If none specified and random name is generated as
5157        done in the Jenkins Plot plugin. (default random generated .csv
5158        filename, same behaviour as the Jenkins Plot plugin)
5159    :arg list series: list data series definitions
5160
5161      :Series: * **file** (`str`) : files to include
5162              * **inclusion-flag** filtering mode for CSV files. Possible
5163                values are:
5164
5165                  * **off** (default)
5166                  * **include-by-string**
5167                  * **exclude-by-string**
5168                  * **include-by-column**
5169                  * **exclude-by-column**
5170
5171              * **exclude** (`str`) : exclude pattern for CSV file.
5172              * **url** (`str`) : for 'csv' and 'xml' file types
5173                used when you click on a point (default empty)
5174              * **display-table** (`bool`) : for 'csv' file type
5175                if true, original CSV will be shown above plot (default false)
5176              * **label** (`str`) : used by 'properties' file type
5177                Specifies the legend label for this data series.
5178                (default empty)
5179              * **format** (`str`) : Type of file where we get datas.
5180                Can be: properties, csv, xml
5181              * **xpath-type** (`str`) : The result type of the expression must
5182                be supplied due to limitations in the java.xml.xpath parsing.
5183                The result can be: node, nodeset, boolean, string, or number.
5184                Strings and numbers will be converted to double. Boolean will
5185                be converted to 1 for true, and 0 for false. (default 'node')
5186              * **xpath** (`str`) : used by 'xml' file type
5187                Xpath which selects the values that should be plotted.
5188
5189
5190    Minimal Example:
5191
5192    .. literalinclude:: /../../tests/publishers/fixtures/plot-minimal.yaml
5193       :language: yaml
5194
5195    Full Example:
5196
5197    .. literalinclude:: /../../tests/publishers/fixtures/plot-full.yaml
5198       :language: yaml
5199    """
5200    top = XML.SubElement(xml_parent, "hudson.plugins.plot.PlotPublisher")
5201    plots = XML.SubElement(top, "plots")
5202    format_dict = {
5203        "properties": "hudson.plugins.plot.PropertiesSeries",
5204        "csv": "hudson.plugins.plot.CSVSeries",
5205        "xml": "hudson.plugins.plot.XMLSeries",
5206    }
5207    xpath_dict = {
5208        "nodeset": "NODESET",
5209        "node": "NODE",
5210        "string": "STRING",
5211        "boolean": "BOOLEAN",
5212        "number": "NUMBER",
5213    }
5214    inclusion_dict = {
5215        "off": "OFF",
5216        "include-by-string": "INCLUDE_BY_STRING",
5217        "exclude-by-string": "EXCLUDE_BY_STRING",
5218        "include-by-column": "INCLUDE_BY_COLUMN",
5219        "exclude-by-column": "EXCLUDE_BY_COLUMN",
5220    }
5221
5222    style_list = [
5223        "area",
5224        "bar",
5225        "bar3d",
5226        "line",
5227        "line3d",
5228        "stackedArea",
5229        "stackedbar",
5230        "stackedbar3d",
5231        "waterfall",
5232    ]
5233
5234    plot_mappings = [
5235        ("title", "title", ""),
5236        ("yaxis", "yaxis", ""),
5237        ("width", "width", "750"),
5238        ("height", "height", "450"),
5239        ("csv-file-name", "csvFileName", ""),
5240        ("group", "group", None),
5241        ("use-description", "useDescr", False),
5242        ("exclude-zero-yaxis", "exclZero", False),
5243        ("logarithmic-yaxis", "logarithmic", False),
5244        ("keep-records", "keepRecords", False),
5245        ("num-builds", "numBuilds", ""),
5246        ("style", "style", "line", style_list),
5247    ]
5248
5249    plot_csv_mappings = [
5250        ("inclusion-flag", "inclusionFlag", "off", inclusion_dict),
5251        ("exclude", "exclusionValues", ""),
5252        ("url", "url", ""),
5253        ("display-table", "displayTableFlag", False),
5254    ]
5255
5256    plot_xml_mappings = [
5257        ("url", "url", ""),
5258        ("xpath", "xpathString", ""),
5259        ("xpath-type", "nodeTypeString", "node", xpath_dict),
5260    ]
5261
5262    for plot in data:
5263        plugin = XML.SubElement(plots, "hudson.plugins.plot.Plot")
5264        helpers.convert_mapping_to_xml(plugin, plot, plot_mappings, fail_required=True)
5265
5266        topseries = XML.SubElement(plugin, "series")
5267        series = plot["series"]
5268        for serie in series:
5269            format_data = serie.get("format")
5270            if format_data not in format_dict:
5271                raise JenkinsJobsException(
5272                    "format entered is not valid, must "
5273                    "be one of: %s" % " , ".join(format_dict.keys())
5274                )
5275            subserie = XML.SubElement(topseries, format_dict.get(format_data))
5276            XML.SubElement(subserie, "file").text = serie.get("file")
5277            if format_data == "properties":
5278                XML.SubElement(subserie, "label").text = serie.get("label", "")
5279            if format_data == "csv":
5280                helpers.convert_mapping_to_xml(
5281                    subserie, serie, plot_csv_mappings, fail_required=True
5282                )
5283                if serie.get("exclude", ""):
5284                    exclude_strings = serie.get("exclude", "").split(",")
5285                    exclusionset = XML.SubElement(subserie, "strExclusionSet")
5286                    for exclude_string in exclude_strings:
5287                        XML.SubElement(exclusionset, "string").text = exclude_string
5288            if format_data == "xml":
5289                helpers.convert_mapping_to_xml(
5290                    subserie, serie, plot_xml_mappings, fail_required=True
5291                )
5292            XML.SubElement(subserie, "fileType").text = serie.get("format")
5293
5294
5295def git(registry, xml_parent, data):
5296    """yaml: git
5297    This plugin will configure the Jenkins Git plugin to
5298    push merge results, tags, and/or branches to
5299    remote repositories after the job completes.
5300
5301    Requires the Jenkins :jenkins-plugins:`Git Plugin <git>`.
5302
5303    :arg bool push-merge: push merges back to the origin specified in the
5304                          pre-build merge options (default false)
5305    :arg bool push-only-if-success: Only push to remotes if the build succeeds
5306                                    - otherwise, nothing will be pushed.
5307                                    (default true)
5308    :arg bool force-push: Add force option to git push (default false)
5309    :arg list tags: tags to push at the completion of the build
5310
5311        :tag: * **remote** (`str`) remote repo name to push to
5312                (default 'origin')
5313              * **name** (`str`) name of tag to push
5314              * **message** (`str`) message content of the tag
5315              * **create-tag** (`bool`) whether or not to create the tag
5316                after the build, if this is False then the tag needs to
5317                exist locally (default false)
5318              * **update-tag** (`bool`) whether to overwrite a remote tag
5319                or not (default false)
5320
5321    :arg list branches: branches to push at the completion of the build
5322
5323        :branch: * **remote** (`str`) remote repo name to push to
5324                   (default 'origin')
5325                 * **name** (`str`) name of remote branch to push to
5326
5327    :arg list notes: notes to push at the completion of the build
5328
5329        :note: * **remote** (`str`) remote repo name to push to
5330                 (default 'origin')
5331               * **message** (`str`) content of the note
5332               * **namespace** (`str`) namespace of the note
5333                 (default master)
5334               * **replace-note** (`bool`) whether to overwrite a note or not
5335                 (default false)
5336
5337
5338    Minimal Example:
5339
5340    .. literalinclude:: /../../tests/publishers/fixtures/git-minimal.yaml
5341       :language: yaml
5342
5343    Full Example:
5344
5345    .. literalinclude:: /../../tests/publishers/fixtures/git-full.yaml
5346       :language: yaml
5347    """
5348    mappings = [
5349        ("push-merge", "pushMerge", False),
5350        ("push-only-if-success", "pushOnlyIfSuccess", True),
5351        ("force-push", "forcePush", False),
5352    ]
5353
5354    tag_mappings = [
5355        ("remote", "targetRepoName", "origin"),
5356        ("name", "tagName", None),
5357        ("message", "tagMessage", ""),
5358        ("create-tag", "createTag", False),
5359        ("update-tag", "updateTag", False),
5360    ]
5361
5362    branch_mappings = [
5363        ("remote", "targetRepoName", "origin"),
5364        ("name", "branchName", None),
5365    ]
5366
5367    note_mappings = [
5368        ("remote", "targetRepoName", "origin"),
5369        ("message", "noteMsg", None),
5370        ("namespace", "noteNamespace", "master"),
5371        ("replace-note", "noteReplace", False),
5372    ]
5373
5374    top = XML.SubElement(xml_parent, "hudson.plugins.git.GitPublisher")
5375    XML.SubElement(top, "configVersion").text = "2"
5376    helpers.convert_mapping_to_xml(top, data, mappings, fail_required=True)
5377
5378    tags = data.get("tags", [])
5379    if tags:
5380        xml_tags = XML.SubElement(top, "tagsToPush")
5381        for tag in tags:
5382            xml_tag = XML.SubElement(
5383                xml_tags, "hudson.plugins.git.GitPublisher_-TagToPush"
5384            )
5385            helpers.convert_mapping_to_xml(
5386                xml_tag, tag["tag"], tag_mappings, fail_required=True
5387            )
5388
5389    branches = data.get("branches", [])
5390    if branches:
5391        xml_branches = XML.SubElement(top, "branchesToPush")
5392        for branch in branches:
5393            xml_branch = XML.SubElement(
5394                xml_branches, "hudson.plugins.git.GitPublisher_-BranchToPush"
5395            )
5396            helpers.convert_mapping_to_xml(
5397                xml_branch, branch["branch"], branch_mappings, fail_required=True
5398            )
5399
5400    notes = data.get("notes", [])
5401    if notes:
5402        xml_notes = XML.SubElement(top, "notesToPush")
5403        for note in notes:
5404            xml_note = XML.SubElement(
5405                xml_notes, "hudson.plugins.git.GitPublisher_-NoteToPush"
5406            )
5407            helpers.convert_mapping_to_xml(
5408                xml_note, note["note"], note_mappings, fail_required=True
5409            )
5410
5411
5412def github_notifier(registry, xml_parent, data):
5413    """yaml: github-notifier
5414    Set build status on Github commit.
5415    Requires the Jenkins :jenkins-plugins:`Github Plugin <github>`.
5416
5417    Example:
5418
5419    .. literalinclude:: /../../tests/publishers/fixtures/github-notifier.yaml
5420       :language: yaml
5421    """
5422    XML.SubElement(xml_parent, "com.cloudbees.jenkins.GitHubCommitNotifier")
5423
5424
5425def gitlab_notifier(registry, xml_parent, data):
5426    """yaml: gitlab-notifier
5427    Set build status on GitLab commit.
5428    Requires the Jenkins :jenkins-plugins:`GitLab Plugin <gitlab-plugin>`.
5429
5430    :arg str name: The name of the build in GitLab. With this you can
5431        distinguish different Jenkins jobs for the same commit in GitLab.
5432        (default 'jenkins')
5433    :arg bool mark-unstable-as-success: (default false)
5434
5435    Minimal Example:
5436
5437    .. literalinclude::
5438        /../../tests/publishers/fixtures/gitlab-notifier-minimal.yaml
5439       :language: yaml
5440
5441    Full Example:
5442
5443    .. literalinclude::
5444        /../../tests/publishers/fixtures/gitlab-notifier-full.yaml
5445       :language: yaml
5446    """
5447    top = XML.SubElement(
5448        xml_parent, "com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher"
5449    )
5450    top.set("plugin", "gitlab-plugin")
5451
5452    mappings = [
5453        ("name", "name", "jenkins"),
5454        ("mark-unstable-as-success", "markUnstableAsSuccess", False),
5455    ]
5456    helpers.convert_mapping_to_xml(top, data, mappings, fail_required=True)
5457
5458
5459def gitlab_vote(registry, xml_parent, data):
5460    """yaml: gitlab-vote
5461    Set vote for build status on GitLab merge request.
5462    Requires the Jenkins :jenkins-plugins:`GitLab Plugin <gitlab-plugin>`.
5463
5464    Example:
5465
5466    .. literalinclude::
5467        ../../tests/publishers/fixtures/gitlab-vote.yaml
5468        :language: yaml
5469    """
5470    XML.SubElement(
5471        xml_parent, "com.dabsquared.gitlabjenkins.publisher.GitLabVotePublisher"
5472    )
5473
5474
5475def gitlab_message(registry, xml_parent, data):
5476    """yaml: gitlab-message
5477    Add note with build status on GitLab merge request.
5478    Requires the Jenkins :jenkins-plugins:`GitLab Plugin <gitlab-plugin>`.
5479
5480    :arg bool failure-only: make a comment only on failure (default false)
5481    :arg bool success-note: make a comment on GitLab Merge Request
5482        if build succeeds (default false)
5483    :arg bool failure-note: make a comment on GitLab Merge Request
5484        if build failed (default false)
5485    :arg bool abort-note: make a comment on GitLab Merge Request
5486        if build aborted (default false)
5487    :arg bool unstable-note: make a comment on GitLab Merge Request
5488        if build unstable (default false)
5489
5490    :arg str success-note-text: text of comment on success build (default '')
5491    :arg str failure-note-text: text of comment on failed build (default '')
5492    :arg str abort-note-text: text of comment on aborted build (default '')
5493    :arg str unstable-note-text: text of comment on unstable build (default '')
5494
5495    Minimal Example:
5496
5497    .. literalinclude::
5498        /../../tests/publishers/fixtures/gitlab-message-minimal.yaml
5499       :language: yaml
5500
5501    Full Example:
5502
5503    .. literalinclude::
5504        /../../tests/publishers/fixtures/gitlab-message-full.yaml
5505       :language: yaml
5506    """
5507    gitlab = XML.SubElement(
5508        xml_parent, "com.dabsquared.gitlabjenkins.publisher.GitLabMessagePublisher"
5509    )
5510    gitlab.set("plugin", "gitlab-plugin")
5511
5512    mapping = [
5513        ("failure-only", "onlyForFailure", False),
5514        ("success-note", "replaceSuccessNote", False),
5515        ("failure-note", "replaceFailureNote", False),
5516        ("abort-note", "replaceAbortNote", False),
5517        ("unstable-note", "replaceUnstableNote", False),
5518        ("success-note-text", "successNoteText", ""),
5519        ("failure-note-text", "failureNoteText", ""),
5520        ("abort-note-text", "abortNoteText", ""),
5521        ("unstable-note-text", "unstableNoteText", ""),
5522    ]
5523
5524    helpers.convert_mapping_to_xml(gitlab, data, mapping, fail_required=True)
5525
5526
5527def zulip(registry, xml_parent, data):
5528    """yaml: zulip
5529    Set build status on zulip.
5530    Requires the Jenkins :jenkins-plugins:`Humbug Plugin <humbug>`.
5531
5532    Example:
5533
5534    .. literalinclude:: /../../tests/publishers/fixtures/zulip.yaml
5535       :language: yaml
5536    """
5537    XML.SubElement(xml_parent, "hudson.plugins.humbug.HumbugNotifier")
5538
5539
5540def build_publisher(registry, xml_parent, data):
5541    """yaml: build-publisher
5542    This plugin allows records from one Jenkins to be published
5543    on another Jenkins.
5544
5545    Requires the Jenkins :jenkins-plugins:`Build Publisher Plugin
5546    <build-publisher>`.
5547
5548    :arg bool publish-unstable-builds: publish unstable builds (default true)
5549    :arg bool publish-failed-builds: publish failed builds (default true)
5550    :arg int days-to-keep: days to keep when publishing results (optional)
5551    :arg int num-to-keep: number of jobs to keep in the published results
5552      (optional)
5553
5554    Minimal Example:
5555
5556    .. literalinclude::
5557        /../../tests/publishers/fixtures/build-publisher-minimal.yaml
5558       :language: yaml
5559
5560    Full Example:
5561
5562    .. literalinclude::
5563        /../../tests/publishers/fixtures/build-publisher-full.yaml
5564       :language: yaml
5565    """
5566
5567    reporter = XML.SubElement(
5568        xml_parent, "hudson.plugins.build__publisher.BuildPublisher"
5569    )
5570
5571    mappings = [
5572        ("publish-unstable-builds", "publishUnstableBuilds", True),
5573        ("publish-failed-builds", "publishFailedBuilds", True),
5574    ]
5575    helpers.convert_mapping_to_xml(reporter, data, mappings, fail_required=True)
5576    if "days-to-keep" in data or "num-to-keep" in data:
5577        logrotator = XML.SubElement(reporter, "logRotator")
5578        mappings = [
5579            ("days-to-keep", "daysToKeep", -1),
5580            ("num-to-keep", "numToKeep", -1),
5581            # hardcoded to -1 to emulate what the build publisher
5582            # plugin seem to do.
5583            ("", "artifactDaysToKeep", -1),
5584            ("", "artifactNumToKeep", -1),
5585        ]
5586        helpers.convert_mapping_to_xml(logrotator, data, mappings, fail_required=True)
5587
5588
5589def stash(registry, xml_parent, data):
5590    """yaml: stash
5591    This plugin will configure the Jenkins BitBucket Server Notifier plugin to
5592    notify Atlassian BitBucket after job completes.
5593
5594    Requires the Jenkins :jenkins-plugins:`Bitbucket Server Notifier Plugin
5595    <stashNotifier>`.
5596
5597    :arg str url: Base url of Stash Server (default "")
5598    :arg str username: Username of Stash Server (default "")
5599    :arg str password: Password of Stash Server (default "")
5600    :arg str credentials-id: Credentials of Stash Server (optional)
5601    :arg bool   ignore-ssl: Ignore unverified SSL certificate (default false)
5602    :arg str commit-sha1: Commit SHA1 to notify (default "")
5603    :arg bool   include-build-number: Include build number in key
5604                (default false)
5605
5606    Minimal Example:
5607
5608    .. literalinclude:: /../../tests/publishers/fixtures/stash-minimal.yaml
5609       :language: yaml
5610
5611    Full Example:
5612
5613    .. literalinclude:: /../../tests/publishers/fixtures/stash-full.yaml
5614       :language: yaml
5615    """
5616    top = XML.SubElement(
5617        xml_parent, "org.jenkinsci.plugins.stashNotifier.StashNotifier"
5618    )
5619
5620    XML.SubElement(top, "stashServerBaseUrl").text = data.get("url", "")
5621    if data.get("credentials-id") is not None:
5622        XML.SubElement(top, "credentialsId").text = str(data.get("credentials-id"))
5623    else:
5624        XML.SubElement(
5625            top, "stashUserName"
5626        ).text = helpers.get_value_from_yaml_or_config_file(
5627            "username", "stash", data, registry.jjb_config
5628        )
5629        XML.SubElement(
5630            top, "stashUserPassword"
5631        ).text = helpers.get_value_from_yaml_or_config_file(
5632            "password", "stash", data, registry.jjb_config
5633        )
5634    mappings = [
5635        ("ignore-ssl", "ignoreUnverifiedSSLPeer", False),
5636        ("commit-sha1", "commitSha1", ""),
5637        ("include-build-number", "includeBuildNumberInKey", False),
5638    ]
5639    helpers.convert_mapping_to_xml(top, data, mappings, fail_required=True)
5640
5641
5642def dependency_check(registry, xml_parent, data):
5643    """yaml: dependency-check
5644    Dependency-Check is an open source utility that identifies project
5645    dependencies and checks if there are any known, publicly disclosed,
5646    vulnerabilities.
5647
5648    Requires the Jenkins :jenkins-plugins:`OWASP Dependency-Check Plugin
5649    <dependency-check-jenkins-plugin>`.
5650
5651    :arg str pattern: Report filename pattern (optional)
5652    :arg bool can-run-on-failed: Also runs for failed builds, instead of just
5653        stable or unstable builds (default false)
5654    :arg bool should-detect-modules: Determines if Ant or Maven modules should
5655        be detected for all files that contain warnings (default false)
5656    :arg int healthy: Sunny threshold (optional)
5657    :arg int unhealthy: Stormy threshold (optional)
5658    :arg str health-threshold: Threshold priority for health status
5659        ('low', 'normal' or 'high', defaulted to 'low')
5660    :arg dict thresholds: Mark build as failed or unstable if the number of
5661        errors exceeds a threshold. (optional)
5662
5663        :thresholds:
5664            * **unstable** (`dict`)
5665                :unstable: * **total-all** (`int`)
5666                           * **total-high** (`int`)
5667                           * **total-normal** (`int`)
5668                           * **total-low** (`int`)
5669                           * **new-all** (`int`)
5670                           * **new-high** (`int`)
5671                           * **new-normal** (`int`)
5672                           * **new-low** (`int`)
5673
5674            * **failed** (`dict`)
5675                :failed: * **total-all** (`int`)
5676                         * **total-high** (`int`)
5677                         * **total-normal** (`int`)
5678                         * **total-low** (`int`)
5679                         * **new-all** (`int`)
5680                         * **new-high** (`int`)
5681                         * **new-normal** (`int`)
5682                         * **new-low** (`int`)
5683    :arg str default-encoding: Encoding for parsing or showing files (optional)
5684    :arg bool do-not-resolve-relative-paths: (default false)
5685    :arg bool dont-compute-new: If set to false, computes new warnings based on
5686        the reference build (default true)
5687    :arg bool use-previous-build-as-reference: determines whether to always
5688        use the previous build as the reference build (default false)
5689    :arg bool use-stable-build-as-reference: The number of new warnings will be
5690        calculated based on the last stable build, allowing reverts of unstable
5691        builds where the number of warnings was decreased. (default false)
5692    :arg bool use-delta-values: If set then the number of new warnings is
5693        calculated by subtracting the total number of warnings of the current
5694        build from the reference build.
5695        (default false)
5696
5697    Minimal Example:
5698
5699    .. literalinclude::
5700        /../../tests/publishers/fixtures/dependency-check-minimal.yaml
5701       :language: yaml
5702
5703    Full Example:
5704
5705    .. literalinclude::
5706        /../../tests/publishers/fixtures/dependency-check-full.yaml
5707       :language: yaml
5708    """
5709
5710    dependency_check = XML.SubElement(
5711        xml_parent, "org.jenkinsci.plugins.DependencyCheck.DependencyCheckPublisher"
5712    )
5713
5714    # trends
5715    helpers.build_trends_publisher("[DEPENDENCYCHECK] ", dependency_check, data)
5716
5717
5718def description_setter(registry, xml_parent, data):
5719    """yaml: description-setter
5720    This plugin sets the description for each build,
5721    based upon a RegEx test of the build log file.
5722
5723    Requires the Jenkins :jenkins-plugins:`Description Setter Plugin
5724    <description-setter>`.
5725
5726    :arg str regexp: A RegEx which is used to scan the build log file
5727        (default '')
5728    :arg str regexp-for-failed: A RegEx which is used for failed builds
5729        (default '')
5730    :arg str description: The description to set on the build (optional)
5731    :arg str description-for-failed: The description to set on
5732        the failed builds (optional)
5733    :arg bool set-for-matrix: Also set the description on
5734        a multi-configuration build (default false)
5735
5736    Minimal Example:
5737
5738    .. literalinclude::
5739        /../../tests/publishers/fixtures/description-setter-minimal.yaml
5740       :language: yaml
5741
5742    Full Example:
5743
5744    .. literalinclude::
5745        /../../tests/publishers/fixtures/description-setter-full.yaml
5746       :language: yaml
5747    """
5748
5749    descriptionsetter = XML.SubElement(
5750        xml_parent, "hudson.plugins.descriptionsetter.DescriptionSetterPublisher"
5751    )
5752    mappings = [
5753        ("regexp", "regexp", ""),
5754        ("regexp-for-failed", "regexpForFailed", ""),
5755        ("description", "description", None),
5756        ("description-for-failed", "descriptionForFailed", None),
5757        ("set-for-matrix", "setForMatrix", False),
5758    ]
5759    helpers.convert_mapping_to_xml(
5760        descriptionsetter, data, mappings, fail_required=False
5761    )
5762
5763
5764def doxygen(registry, xml_parent, data):
5765    """yaml: doxygen
5766    This plugin parses the Doxygen descriptor (Doxyfile) and provides a link to
5767    the generated Doxygen documentation.
5768
5769    Requires the Jenkins :jenkins-plugins:`Doxygen Plugin <doxygen>`.
5770
5771    :arg str doxyfile: The doxyfile path (required)
5772    :arg str slave: The node or label to pull the doxygen HTML files from
5773        (default '')
5774    :arg bool keep-all: Retain doxygen generation for each successful build
5775        (default false)
5776    :arg str folder: Folder where you run doxygen (default '')
5777
5778    Minimal Example:
5779
5780    .. literalinclude:: /../../tests/publishers/fixtures/doxygen-minimal.yaml
5781       :language: yaml
5782
5783    Full Example:
5784
5785    .. literalinclude:: /../../tests/publishers/fixtures/doxygen-full.yaml
5786       :language: yaml
5787    """
5788
5789    logger = logging.getLogger(__name__)
5790    p = XML.SubElement(xml_parent, "hudson.plugins.doxygen.DoxygenArchiver")
5791    mappings = [
5792        ("doxyfile", "doxyfilePath", None),
5793        ("slave", "runOnChild", ""),
5794        ("folder", "folderWhereYouRunDoxygen", ""),
5795    ]
5796    helpers.convert_mapping_to_xml(p, data, mappings, fail_required=True)
5797    # backward compatibility
5798    if "keepall" in data:
5799        if "keep-all" in data:
5800            XML.SubElement(p, "keepAll").text = str(data.get("keep-all", False)).lower()
5801            logger.warning(
5802                "The value of 'keepall' will be ignored " "in preference to 'keep-all'."
5803            )
5804        else:
5805            XML.SubElement(p, "keepAll").text = str(data.get("keepall", False)).lower()
5806            logger.warning("'keepall' is deprecated please use 'keep-all'")
5807    else:
5808        XML.SubElement(p, "keepAll").text = str(data.get("keep-all", False)).lower()
5809
5810
5811def docker_stop_container(registry, xml_parent, data):
5812    """yaml: docker-stop-container
5813    This plugin allows removing stopped docker containers.
5814    It requires the :jenkins-plugins:`Docker build step plugin
5815    <docker-build-step>`.
5816
5817    :arg bool remove-stopped-containers: Boolean value to remove
5818        stopped docker containers (default False)
5819
5820    Minimal Example:
5821    .. literalinclude:: /../../tests/
5822    publishers/fixtures/docker-stop-container-minimal.yaml
5823
5824    Full Example:
5825    .. literalinclude:: /../../tests/
5826    publishers/fixtures/docker-stop-container-full.yaml
5827    """
5828    docker_stop_container = XML.SubElement(
5829        xml_parent,
5830        "com.nirima.jenkins.plugins.docker" ".publisher.DockerPublisherControl",
5831    )
5832    docker_stop_container.set("plugin", "docker-plugin")
5833    mapping = [("remove-stopped-containers", "remove", False)]
5834    helpers.convert_mapping_to_xml(
5835        docker_stop_container, data, mapping, fail_required=False
5836    )
5837
5838
5839def sitemonitor(registry, xml_parent, data):
5840    """yaml: sitemonitor
5841    This plugin checks the availability of an url.
5842
5843    It requires the :jenkins-plugins:`sitemonitor plugin <sitemonitor>`.
5844
5845    :arg list sites: List of URLs to check
5846
5847    Minimal Example:
5848
5849    .. literalinclude::
5850        /../../tests/publishers/fixtures/sitemonitor-minimal.yaml
5851        :language: yaml
5852
5853    Full Example:
5854
5855    .. literalinclude:: /../../tests/publishers/fixtures/sitemonitor-full.yaml
5856        :language: yaml
5857    """
5858    mon = XML.SubElement(xml_parent, "hudson.plugins.sitemonitor.SiteMonitorRecorder")
5859    if data.get("sites"):
5860        sites = XML.SubElement(mon, "mSites")
5861        for siteurl in data.get("sites"):
5862            site = XML.SubElement(sites, "hudson.plugins.sitemonitor.model.Site")
5863            XML.SubElement(site, "mUrl").text = siteurl["url"]
5864
5865
5866def testng(registry, xml_parent, data):
5867    """yaml: testng
5868    This plugin publishes TestNG test reports.
5869
5870    Requires the Jenkins :jenkins-plugins:`TestNG Results Plugin
5871    <testng-plugin>`.
5872
5873    :arg str pattern: filename pattern to locate the TestNG XML report files
5874        (required)
5875    :arg bool escape-test-description: escapes the description string
5876      associated with the test method while displaying test method details
5877      (default true)
5878    :arg bool escape-exception-msg: escapes the test method's exception
5879      messages. (default true)
5880    :arg bool fail-on-failed-test-config: Allows for a distinction between
5881        failing tests and failing configuration methods (>=1.10) (default
5882        false)
5883    :arg bool show-failed-builds: include results from failed builds in the
5884        trend graph (>=1.6) (default false)
5885    :arg int unstable-skips: Build is marked UNSTABLE if the number/percentage
5886        of skipped tests exceeds the specified threshold (>=1.11) (default 100)
5887    :arg int unstable-fails: Build is marked UNSTABLE if the number/percentage
5888        of failed tests exceeds the specified threshold (>=1.11) (default 0)
5889    :arg int failed-skips: Build is marked FAILURE if the number/percentage of
5890        skipped tests exceeds the specified threshold (>=1.11) (default 100)
5891    :arg int failed-fails: Build is marked FAILURE if the number/percentage of
5892        failed tests exceeds the specified threshold (>=1.11) (default 100)
5893    :arg str threshold-mode: Interpret threshold as number of tests or
5894        percentage of tests (>=1.11) (default percentage)
5895
5896    Full Example:
5897
5898    .. literalinclude:: /../../tests/publishers/fixtures/testng-full.yaml
5899       :language: yaml
5900
5901    Minimal Example:
5902
5903    .. literalinclude:: /../../tests/publishers/fixtures/testng-minimal.yaml
5904       :language: yaml
5905    """
5906
5907    reporter = XML.SubElement(xml_parent, "hudson.plugins.testng.Publisher")
5908    reporter.set("plugin", "testng-plugin")
5909    threshold_modes = {"number": 1, "percentage": 2}
5910
5911    mappings = [
5912        ("pattern", "reportFilenamePattern", None),
5913        ("escape-test-description", "escapeTestDescp", True),
5914        ("escape-exception-msg", "escapeExceptionMsg", True),
5915        ("fail-on-failed-test-config", "failureOnFailedTestConfig", False),
5916        ("show-failed-builds", "showFailedBuilds", False),
5917        ("unstable-skips", "unstableSkips", 100),
5918        ("unstable-fails", "unstableFails", 0),
5919        ("failed-skips", "failedSkips", 100),
5920        ("failed-fails", "failedFails", 100),
5921        ("threshold-mode", "thresholdMode", "percentage", threshold_modes),
5922    ]
5923    helpers.convert_mapping_to_xml(reporter, data, mappings, fail_required=True)
5924
5925
5926def artifact_deployer(registry, xml_parent, data):
5927    """yaml: artifact-deployer
5928    This plugin makes it possible to copy artifacts to remote locations.
5929
5930    Requires the Jenkins :jenkins-plugins:`ArtifactDeployer Plugin
5931    <artifactdeployer>`.
5932
5933    :arg list entries:
5934        :entries:
5935            * **files** (`str`) - files to deploy
5936            * **basedir** (`str`) - the dir from files are deployed
5937            * **excludes** (`str`) - the mask to exclude files
5938            * **remote** (`str`) - a remote output directory
5939            * **flatten** (`bool`) - ignore the source directory structure
5940              (default false)
5941            * **delete-remote** (`bool`) - clean-up remote directory
5942              before deployment (default false)
5943            * **delete-remote-artifacts** (`bool`) - delete remote artifacts
5944              when the build is deleted (default false)
5945            * **fail-no-files** (`bool`) - fail build if there are no files
5946              (default false)
5947            * **groovy-script** (`str`) - execute a Groovy script
5948              before a build is deleted
5949
5950    :arg bool deploy-if-fail: Deploy if the build is failed (default false)
5951
5952    Example:
5953
5954    .. literalinclude:: /../../tests/publishers/fixtures/artifact-dep.yaml
5955       :language: yaml
5956    """
5957
5958    deployer = XML.SubElement(
5959        xml_parent,
5960        "org.jenkinsci.plugins.artifactdeployer." "ArtifactDeployerPublisher",
5961    )
5962    if data is None or "entries" not in data:
5963        raise Exception("entries field is missing")
5964    elif data.get("entries", None) is None:
5965        entries = XML.SubElement(deployer, "entries", {"class": "empty-list"})
5966    else:
5967        entries = XML.SubElement(deployer, "entries")
5968        for entry in data.get("entries"):
5969            deployer_entry = XML.SubElement(
5970                entries, "org.jenkinsci.plugins.artifactdeployer.ArtifactDeployerEntry"
5971            )
5972            XML.SubElement(deployer_entry, "includes").text = entry.get("files")
5973            XML.SubElement(deployer_entry, "basedir").text = entry.get("basedir")
5974            XML.SubElement(deployer_entry, "excludes").text = entry.get("excludes")
5975            XML.SubElement(deployer_entry, "remote").text = entry.get("remote")
5976            XML.SubElement(deployer_entry, "flatten").text = str(
5977                entry.get("flatten", False)
5978            ).lower()
5979            XML.SubElement(deployer_entry, "deleteRemote").text = str(
5980                entry.get("delete-remote", False)
5981            ).lower()
5982            XML.SubElement(deployer_entry, "deleteRemoteArtifacts").text = str(
5983                entry.get("delete-remote-artifacts", False)
5984            ).lower()
5985            XML.SubElement(deployer_entry, "failNoFilesDeploy").text = str(
5986                entry.get("fail-no-files", False)
5987            ).lower()
5988            XML.SubElement(deployer_entry, "groovyExpression").text = entry.get(
5989                "groovy-script"
5990            )
5991    deploy_if_fail = str(data.get("deploy-if-fail", False)).lower()
5992    XML.SubElement(deployer, "deployEvenBuildFail").text = deploy_if_fail
5993
5994
5995def s3(registry, xml_parent, data):
5996    """yaml: s3
5997    Upload build artifacts to Amazon S3.
5998
5999    Requires the Jenkins :jenkins-plugins:`S3 plugin <s3>`.
6000
6001    :arg str s3-profile: Globally-defined S3 profile to use
6002    :arg bool dont-wait-for-concurrent-builds: Don't wait
6003      for completion of concurrent builds before publishing to S3
6004      (default false)
6005    :arg list entries:
6006      :entries:
6007        * **destination-bucket** (`str`) - Destination S3 bucket
6008        * **source-files** (`str`) - Source files (Ant glob syntax)
6009        * **storage-class** (`str`) - S3 storage class; one of "STANDARD"
6010          or "REDUCED_REDUNDANCY"
6011        * **bucket-region** (`str`) - S3 bucket region (capitalized with
6012          underscores)
6013        * **upload-on-failure** (`bool`) - Upload files even if the build
6014          failed (default false)
6015        * **upload-from-slave** (`bool`) - Perform the upload directly from
6016          the Jenkins slave rather than the master node. (default false)
6017        * **managed-artifacts** (`bool`) - Let Jenkins fully manage the
6018          published artifacts, similar to when artifacts are published to
6019          the Jenkins master. (default false)
6020        * **s3-encryption** (`bool`) - Use S3 AES-256 server side encryption
6021          support. (default false)
6022        * **flatten** (`bool`) - Ignore the directory structure of the
6023          artifacts in the source project and copy all matching artifacts
6024          directly into the specified bucket. (default false)
6025    :arg list metadata-tags:
6026      :metadata-tags:
6027        * **key** Metadata key for files from this build. It will be
6028          prefixed by "x-amz-meta-" when uploaded to S3. Can contain macros
6029          (e.g. environment variables).
6030        * **value** Metadata value associated with the key. Can contain macros.
6031
6032    Example:
6033
6034    .. literalinclude:: /../../tests/publishers/fixtures/s3001.yaml
6035       :language: yaml
6036    """
6037    deployer = XML.SubElement(xml_parent, "hudson.plugins.s3.S3BucketPublisher")
6038    if data is None or not data.get("entries"):
6039        raise JenkinsJobsException("No filesets defined.")
6040
6041    XML.SubElement(deployer, "dontWaitForConcurrentBuildCompletion").text = str(
6042        data.get("dont-wait-for-concurrent-builds", False)
6043    ).lower()
6044
6045    XML.SubElement(deployer, "profileName").text = data.get("s3-profile")
6046
6047    entries = XML.SubElement(deployer, "entries")
6048
6049    for entry in data.get("entries"):
6050        fileset = XML.SubElement(entries, "hudson.plugins.s3.Entry")
6051
6052        # xml keys -> yaml keys
6053        settings = [
6054            ("bucket", "destination-bucket", ""),
6055            ("sourceFile", "source-files", ""),
6056            ("storageClass", "storage-class", ""),
6057            ("selectedRegion", "bucket-region", ""),
6058            ("noUploadOnFailure", "upload-on-failure", False),
6059            ("uploadFromSlave", "upload-from-slave", False),
6060            ("managedArtifacts", "managed-artifacts", False),
6061            ("useServerSideEncryption", "s3-encryption", False),
6062            ("flatten", "flatten", False),
6063        ]
6064
6065        for xml_key, yaml_key, default in settings:
6066            xml_config = XML.SubElement(fileset, xml_key)
6067            config_value = entry.get(yaml_key, default)
6068            if xml_key == "noUploadOnFailure":
6069                xml_config.text = str(not config_value).lower()
6070            elif isinstance(default, bool):
6071                xml_config.text = str(config_value).lower()
6072            else:
6073                xml_config.text = str(config_value)
6074
6075    metadata = XML.SubElement(deployer, "userMetadata")
6076    for tag in data.get("metadata-tags", []):
6077        pair = XML.SubElement(metadata, "hudson.plugins.s3.MetadataPair")
6078        XML.SubElement(pair, "key").text = tag.get("key")
6079        XML.SubElement(pair, "value").text = tag.get("value")
6080
6081
6082def ruby_metrics(registry, xml_parent, data):
6083    """yaml: ruby-metrics
6084    Rcov plugin parses rcov html report files and
6085    shows it in Jenkins with a trend graph.
6086
6087    Requires the Jenkins :jenkins-plugins:`Ruby metrics plugin
6088    <rubyMetrics>`.
6089
6090    :arg str report-dir: Relative path to the coverage report directory
6091    :arg dict targets:
6092
6093           :targets: (total-coverage, code-coverage)
6094
6095                * **healthy** (`int`): Healthy threshold
6096                * **unhealthy** (`int`): Unhealthy threshold
6097                * **unstable** (`int`): Unstable threshold
6098
6099    Example:
6100
6101    .. literalinclude:: /../../tests/publishers/fixtures/ruby-metrics.yaml
6102       :language: yaml
6103    """
6104
6105    metrics = XML.SubElement(
6106        xml_parent, "hudson.plugins.rubyMetrics.rcov.RcovPublisher"
6107    )
6108    report_dir = data.get("report-dir", "")
6109    XML.SubElement(metrics, "reportDir").text = report_dir
6110    targets = XML.SubElement(metrics, "targets")
6111    if "target" in data:
6112        for t in data["target"]:
6113            if not ("code-coverage" in t or "total-coverage" in t):
6114                raise JenkinsJobsException("Unrecognized target name")
6115            el = XML.SubElement(
6116                targets, "hudson.plugins.rubyMetrics.rcov.model.MetricTarget"
6117            )
6118            if "total-coverage" in t:
6119                XML.SubElement(el, "metric").text = "TOTAL_COVERAGE"
6120            else:
6121                XML.SubElement(el, "metric").text = "CODE_COVERAGE"
6122            for threshold_name, threshold_value in next(iter(t.values())).items():
6123                elname = threshold_name.lower()
6124                XML.SubElement(el, elname).text = str(threshold_value)
6125    else:
6126        raise JenkinsJobsException("Coverage metric targets must be set")
6127
6128
6129def fitnesse(registry, xml_parent, data):
6130    """yaml: fitnesse
6131    Publish Fitnesse test results
6132
6133    Requires the Jenkins :jenkins-plugins:`Fitnesse plugin <fitnesse>`.
6134
6135    :arg str results: path specifier for results files
6136
6137    Example:
6138
6139    .. literalinclude::  /../../tests/publishers/fixtures/fitnesse001.yaml
6140       :language: yaml
6141    """
6142    fitnesse = XML.SubElement(
6143        xml_parent, "hudson.plugins.fitnesse.FitnesseResultsRecorder"
6144    )
6145    results = data.get("results", "")
6146    XML.SubElement(fitnesse, "fitnessePathToXmlResultsIn").text = results
6147
6148
6149def valgrind(registry, xml_parent, data):
6150    """yaml: valgrind
6151    This plugin publishes Valgrind Memcheck XML results.
6152
6153    Requires the Jenkins :jenkins-plugins:`Valgrind Plugin <valgrind>`.
6154
6155    :arg str pattern: Filename pattern to locate the Valgrind XML report files
6156        (required)
6157    :arg dict thresholds: Mark build as failed or unstable if the number of
6158        errors exceeds a threshold. All threshold values are optional.
6159
6160        :thresholds:
6161            * **unstable** (`dict`)
6162                :unstable: * **invalid-read-write** (`int`)
6163                           * **definitely-lost** (`int`)
6164                           * **total** (`int`)
6165            * **failed** (`dict`)
6166                :failed: * **invalid-read-write** (`int`)
6167                         * **definitely-lost** (`int`)
6168                         * **total** (`int`)
6169    :arg bool fail-no-reports: Fail build if no reports are found
6170      (default false)
6171    :arg bool fail-invalid-reports: Fail build if reports are malformed
6172      (default false)
6173    :arg bool publish-if-aborted: Publish results for aborted builds
6174      (default false)
6175    :arg bool publish-if-failed: Publish results for failed builds
6176      (default false)
6177
6178    Example:
6179
6180    .. literalinclude:: /../../tests/publishers/fixtures/valgrind001.yaml
6181       :language: yaml
6182    """
6183    p = XML.SubElement(xml_parent, "org.jenkinsci.plugins.valgrind.ValgrindPublisher")
6184    p = XML.SubElement(p, "valgrindPublisherConfig")
6185
6186    if "pattern" not in data:
6187        raise JenkinsJobsException("A filename pattern must be specified.")
6188
6189    XML.SubElement(p, "pattern").text = data["pattern"]
6190
6191    dthresholds = data.get("thresholds", {})
6192
6193    for threshold in ["unstable", "failed"]:
6194        dthreshold = dthresholds.get(threshold, {})
6195        threshold = threshold.replace("failed", "fail")
6196
6197        ThresholdInvalidReadWrite = "%sThresholdInvalidReadWrite" % threshold
6198        ThresholdDefinitelyLost = "%sThresholdDefinitelyLost" % threshold
6199        ThresholdTotal = "%sThresholdTotal" % threshold
6200
6201        threshold_mapping = [
6202            ("invalid-read-write", ThresholdInvalidReadWrite, ""),
6203            ("definitely-lost", ThresholdDefinitelyLost, ""),
6204            ("total", ThresholdTotal, ""),
6205        ]
6206        helpers.convert_mapping_to_xml(
6207            p, dthreshold, threshold_mapping, fail_required=True
6208        )
6209
6210    mapping = [
6211        ("fail-no-reports", "failBuildOnMissingReports", False),
6212        ("fail-invalid-reports", "failBuildOnInvalidReports", False),
6213        ("publish-if-aborted", "publishResultsForAbortedBuilds", False),
6214        ("publish-if-failed", "publishResultsForFailedBuilds", False),
6215    ]
6216    helpers.convert_mapping_to_xml(p, data, mapping, fail_required=True)
6217
6218
6219def pmd(registry, xml_parent, data):
6220    """yaml: pmd
6221    Publish trend reports with PMD.
6222
6223    Requires the Jenkins PMD Plugin (https://github.com/jenkinsci/pmd-plugin).
6224
6225    The PMD component accepts a dictionary with the following values:
6226
6227    :arg str pattern: Report filename pattern (optional)
6228    :arg bool can-run-on-failed: Also runs for failed builds, instead of just
6229      stable or unstable builds (default false)
6230    :arg bool should-detect-modules: Determines if Ant or Maven modules should
6231      be detected for all files that contain warnings (default false)
6232    :arg int healthy: Sunny threshold (optional)
6233    :arg int unhealthy: Stormy threshold (optional)
6234    :arg str health-threshold: Threshold priority for health status
6235      ('low', 'normal' or 'high', defaulted to 'low')
6236    :arg dict thresholds: Mark build as failed or unstable if the number of
6237      errors exceeds a threshold. (optional)
6238
6239        :thresholds:
6240            * **unstable** (`dict`)
6241                :unstable: * **total-all** (`int`)
6242                           * **total-high** (`int`)
6243                           * **total-normal** (`int`)
6244                           * **total-low** (`int`)
6245                           * **new-all** (`int`)
6246                           * **new-high** (`int`)
6247                           * **new-normal** (`int`)
6248                           * **new-low** (`int`)
6249
6250            * **failed** (`dict`)
6251                :failed: * **total-all** (`int`)
6252                         * **total-high** (`int`)
6253                         * **total-normal** (`int`)
6254                         * **total-low** (`int`)
6255                         * **new-all** (`int`)
6256                         * **new-high** (`int`)
6257                         * **new-normal** (`int`)
6258                         * **new-low** (`int`)
6259    :arg str default-encoding: Encoding for parsing or showing files (optional)
6260    :arg bool do-not-resolve-relative-paths: (default false)
6261    :arg bool dont-compute-new: If set to false, computes new warnings based on
6262      the reference build (default true)
6263    :arg bool use-previous-build-as-reference: determines whether to always
6264        use the previous build as the reference build (default false)
6265    :arg bool use-stable-build-as-reference: The number of new warnings will be
6266      calculated based on the last stable build, allowing reverts of unstable
6267      builds where the number of warnings was decreased. (default false)
6268    :arg bool use-delta-values: If set then the number of new warnings is
6269      calculated by subtracting the total number of warnings of the current
6270      build from the reference build.
6271      (default false)
6272
6273    Example:
6274
6275    .. literalinclude::  /../../tests/publishers/fixtures/pmd001.yaml
6276       :language: yaml
6277
6278    Full example:
6279
6280    .. literalinclude::  /../../tests/publishers/fixtures/pmd002.yaml
6281       :language: yaml
6282    """
6283
6284    xml_element = XML.SubElement(xml_parent, "hudson.plugins.pmd.PmdPublisher")
6285
6286    helpers.build_trends_publisher("[PMD] ", xml_element, data)
6287
6288
6289def scan_build(registry, xml_parent, data):
6290    """yaml: scan-build
6291    Publishes results from the Clang scan-build static analyzer.
6292
6293    The scan-build report has to be generated in the directory
6294    ``${WORKSPACE}/clangScanBuildReports`` for the publisher to find it.
6295
6296    Requires the Jenkins :jenkins-plugins:`Clang Scan-Build Plugin
6297    <clang-scanbuild>`.
6298
6299    :arg bool mark-unstable: Mark build as unstable if the number of bugs
6300        exceeds a threshold (default false)
6301    :arg int threshold: Threshold for marking builds as unstable (default 0)
6302    :arg str exclude-paths: Comma separated paths to exclude from reports
6303        (>=1.5) (default '')
6304    :arg str report-folder: Folder where generated reports are located
6305        (>=1.7) (default 'clangScanBuildReports')
6306
6307    Full Example:
6308
6309    .. literalinclude:: /../../tests/publishers/fixtures/scan-build-full.yaml
6310       :language: yaml
6311
6312    Minimal Example:
6313
6314    .. literalinclude::
6315       /../../tests/publishers/fixtures/scan-build-minimal.yaml
6316       :language: yaml
6317    """
6318    p = XML.SubElement(
6319        xml_parent, "jenkins.plugins.clangscanbuild.publisher.ClangScanBuildPublisher"
6320    )
6321    p.set("plugin", "clang-scanbuild")
6322
6323    mappings = [
6324        ("mark-unstable", "markBuildUnstableWhenThresholdIsExceeded", False),
6325        ("threshold", "bugThreshold", 0),
6326        ("exclude-paths", "clangexcludedpaths", ""),
6327        ("report-folder", "reportFolderName", "clangScanBuildReports"),
6328    ]
6329    helpers.convert_mapping_to_xml(p, data, mappings, fail_required=True)
6330
6331
6332def dry(registry, xml_parent, data):
6333    """yaml: dry
6334    Publish trend reports with DRY.
6335
6336    Requires the Jenkins DRY Plugin (https://github.com/jenkinsci/dry-plugin).
6337
6338    The DRY component accepts a dictionary with the following values:
6339
6340    :arg str pattern: Report filename pattern (default '')
6341    :arg bool can-run-on-failed: Also runs for failed builds, instead of just
6342      stable or unstable builds (default false)
6343    :arg bool should-detect-modules: Determines if Ant or Maven modules should
6344      be detected for all files that contain warnings (default false)
6345    :arg int healthy: Sunny threshold (default '')
6346    :arg int unhealthy: Stormy threshold (default '')
6347    :arg str health-threshold: Threshold priority for health status
6348      ('low', 'normal' or 'high', defaulted to 'low')
6349    :arg int high-threshold: Minimum number of duplicated lines for high
6350      priority warnings. (default 50)
6351    :arg int normal-threshold: Minimum number of duplicated lines for normal
6352      priority warnings. (default 25)
6353    :arg dict thresholds: Mark build as failed or unstable if the number of
6354      errors exceeds a threshold. (default '')
6355
6356        :thresholds:
6357            * **unstable** (`dict`)
6358                :unstable: * **total-all** (`int`)
6359                           * **total-high** (`int`)
6360                           * **total-normal** (`int`)
6361                           * **total-low** (`int`)
6362                           * **new-all** (`int`)
6363                           * **new-high** (`int`)
6364                           * **new-normal** (`int`)
6365                           * **new-low** (`int`)
6366
6367            * **failed** (`dict`)
6368                :failed: * **total-all** (`int`)
6369                         * **total-high** (`int`)
6370                         * **total-normal** (`int`)
6371                         * **total-low** (`int`)
6372                         * **new-all** (`int`)
6373                         * **new-high** (`int`)
6374                         * **new-normal** (`int`)
6375                         * **new-low** (`int`)
6376    :arg str default-encoding: Encoding for parsing or showing files (optional)
6377    :arg bool do-not-resolve-relative-paths: (default false)
6378    :arg bool dont-compute-new: If set to false, computes new warnings based on
6379      the reference build (default true)
6380    :arg bool use-previous-build-as-reference: determines whether to always
6381        use the previous build as the reference build (default false)
6382    :arg bool use-stable-build-as-reference: The number of new warnings will be
6383      calculated based on the last stable build, allowing reverts of unstable
6384      builds where the number of warnings was decreased. (default false)
6385    :arg bool use-delta-values: If set then the number of new warnings is
6386      calculated by subtracting the total number of warnings of the current
6387      build from the reference build. (default false)
6388
6389    Example:
6390
6391    .. literalinclude::  /../../tests/publishers/fixtures/dry001.yaml
6392       :language: yaml
6393
6394    Full example:
6395
6396    .. literalinclude::  /../../tests/publishers/fixtures/dry004.yaml
6397       :language: yaml
6398    """
6399
6400    xml_element = XML.SubElement(xml_parent, "hudson.plugins.dry.DryPublisher")
6401
6402    helpers.build_trends_publisher("[DRY] ", xml_element, data)
6403
6404    # Add specific settings for this trends publisher
6405    settings = [
6406        ("high-threshold", "highThreshold", 50),
6407        ("normal-threshold", "normalThreshold", 25),
6408    ]
6409    helpers.convert_mapping_to_xml(xml_element, data, settings, fail_required=True)
6410
6411
6412def shining_panda(registry, xml_parent, data):
6413    """yaml: shining-panda
6414    Publish coverage.py results. Requires the Jenkins
6415    :jenkins-plugins:`ShiningPanda Plugin <shiningpanda>`.
6416
6417    :arg str html-reports-directory: path to coverage.py html results
6418                                    (optional)
6419
6420    Example:
6421
6422    .. literalinclude::  /../../tests/publishers/fixtures/shiningpanda001.yaml
6423       :language: yaml
6424    """
6425    shining_panda_plugin = XML.SubElement(
6426        xml_parent, "jenkins.plugins.shiningpanda.publishers.CoveragePublisher"
6427    )
6428
6429    mapping = [("html-reports-directory", "htmlDir", None)]
6430    helpers.convert_mapping_to_xml(
6431        shining_panda_plugin, data, mapping, fail_required=False
6432    )
6433
6434
6435def downstream_ext(registry, xml_parent, data):
6436    """yaml: downstream-ext
6437    Trigger multiple downstream jobs when a job is completed and
6438    condition is met.
6439
6440    Requires the Jenkins :jenkins-plugins:`Downstream-Ext Plugin
6441    <downstream-ext>`.
6442
6443    :arg list projects: Projects to build (required)
6444    :arg str condition: comparison condition used for the criteria.
6445      One of 'equal-or-over', 'equal-or-under', 'equal'
6446      (default 'equal-or-over')
6447    :arg str criteria: Trigger downstream job if build results meets
6448      condition. One of 'success', 'unstable', 'failure' or
6449      'aborted' (default 'success')
6450    :arg bool only-on-scm-change: Trigger only if downstream project
6451      has SCM changes (default false)
6452    :arg bool only-on-local-scm-change: Trigger only if current project
6453      has SCM changes (default false)
6454
6455    Example:
6456
6457    .. literalinclude::
6458        /../../tests/publishers/fixtures/downstream-ext002.yaml
6459       :language: yaml
6460    """
6461
6462    conditions = {
6463        "equal-or-over": "AND_HIGHER",
6464        "equal-or-under": "AND_LOWER",
6465        "equal": "EXACT",
6466    }
6467
6468    p = XML.SubElement(xml_parent, "hudson.plugins.downstream__ext.DownstreamTrigger")
6469
6470    if "projects" not in data:
6471        raise JenkinsJobsException("Missing list of downstream projects.")
6472
6473    XML.SubElement(p, "childProjects").text = ",".join(data["projects"])
6474
6475    th = XML.SubElement(p, "threshold")
6476
6477    criteria = data.get("criteria", "success").upper()
6478
6479    wr_threshold = hudson_model.THRESHOLDS[criteria]
6480    if criteria not in hudson_model.THRESHOLDS:
6481        raise JenkinsJobsException(
6482            "criteria must be one of %s" % ", ".join(hudson_model.THRESHOLDS.keys())
6483        )
6484    mapping = [
6485        ("name", "name", None),
6486        ("ordinal", "ordinal", None),
6487        ("color", "color", None),
6488        ("complete", "completeBuild", None),
6489    ]
6490    helpers.convert_mapping_to_xml(th, wr_threshold, mapping, fail_required=True)
6491
6492    condition_mapping = [
6493        ("condition", "thresholdStrategy", "equal-or-over", conditions),
6494        ("only-on-scm-change", "onlyIfSCMChanges", False),
6495        ("only-on-local-scm-change", "onlyIfLocalSCMChanges", False),
6496    ]
6497    helpers.convert_mapping_to_xml(p, data, condition_mapping, fail_required=True)
6498
6499
6500def rundeck(registry, xml_parent, data):
6501    """yaml: rundeck
6502    Trigger a rundeck job when the build is complete.
6503
6504    Requires the Jenkins :jenkins-plugins:`RunDeck Plugin <rundeck>`.
6505
6506    :arg str job-id: The RunDeck job identifier. (required)
6507        This could be:
6508        * ID example : "42"
6509        * UUID example : "2027ce89-7924-4ecf-a963-30090ada834f"
6510        * reference, in the format : "project:group/job"
6511    :arg str options: List of options for the Rundeck job, in Java-Properties
6512      format: key=value (default "")
6513    :arg str node-filters: List of filters to optionally filter the nodes
6514      included by the job. (default "")
6515    :arg str tag: Used for on-demand job scheduling on rundeck: if a tag is
6516      specified, the job will only execute if the given tag is present in the
6517      SCM changelog. (default "")
6518    :arg bool wait-for-rundeck: If true Jenkins will wait for the job to
6519      complete, if false the job will be started and Jenkins will move on.
6520      (default false)
6521    :arg bool fail-the-build: If true a RunDeck job failure will cause the
6522      Jenkins build to fail. (default false)
6523
6524    Example:
6525
6526    .. literalinclude:: /../../tests/publishers/fixtures/rundeck001.yaml
6527        :language: yaml
6528
6529    Full example:
6530
6531    .. literalinclude:: /../../tests/publishers/fixtures/rundeck002.yaml
6532        :language: yaml
6533    """
6534
6535    p = XML.SubElement(xml_parent, "org.jenkinsci.plugins.rundeck.RundeckNotifier")
6536
6537    mappings = [
6538        ("job-id", "jobId", None),
6539        ("options", "options", ""),
6540        ("node-filters", "nodeFilters", ""),
6541        ("tag", "tag", ""),
6542        ("wait-for-rundeck", "shouldWaitForRundeckJob", False),
6543        ("fail-the-build", "shouldFailTheBuild", False),
6544    ]
6545    helpers.convert_mapping_to_xml(p, data, mappings, fail_required=True)
6546
6547
6548def create_publishers(registry, action):
6549    dummy_parent = XML.Element("dummy")
6550    registry.dispatch("publisher", dummy_parent, action)
6551    return list(dummy_parent)
6552
6553
6554def conditional_publisher(registry, xml_parent, data):
6555    """yaml: conditional-publisher
6556    Conditionally execute some post-build steps. Requires the Jenkins
6557    :jenkins-plugins:`Flexible Publish Plugin <flexible-publish>`.
6558
6559    A Flexible Publish list of Conditional Actions is created in Jenkins.
6560
6561    :arg str condition-kind: Condition kind that must be verified before the
6562      action is executed. Valid values and their additional attributes are
6563      described in the conditions_ table.
6564    :arg bool condition-aggregation: If true Matrix Aggregation will be
6565      enabled. (default false)
6566    :arg str condition-aggregation-kind: Condition Aggregation kind that
6567      must be verified before the
6568      action is executed. Valid values and their additional attributes are
6569      described in the conditions_ table.
6570    :arg str on-evaluation-failure: What should be the outcome of the build
6571      if the evaluation of the condition fails. Possible values are `fail`,
6572      `mark-unstable`, `run-and-mark-unstable`, `run` and `dont-run`.
6573      Default is `fail`.
6574    :arg list action: Action to run if the condition is verified. Item
6575      can be any publisher known by Jenkins Job Builder and supported
6576      by the Flexible Publish Plugin.
6577
6578    .. _conditions:
6579
6580    ================== ====================================================
6581    Condition kind     Description
6582    ================== ====================================================
6583    always             Condition is always verified
6584    never              Condition is never verified
6585    boolean-expression Run the action if the expression expands to a
6586                       representation of true
6587
6588                         :condition-expression: Expression to expand
6589    current-status     Run the action if the current build status is
6590                       within the configured range
6591
6592                         :condition-worst: Accepted values are SUCCESS,
6593                           UNSTABLE, FAILURE, NOT_BUILD, ABORTED
6594                         :condition-best: Accepted values are SUCCESS,
6595                           UNSTABLE, FAILURE, NOT_BUILD, ABORTED
6596
6597    shell              Run the action if the shell command succeeds
6598
6599                         :condition-command: Shell command to execute
6600    windows-shell      Similar to shell, except that commands will be
6601                       executed by cmd, under Windows
6602
6603                         :condition-command: Command to execute
6604    regexp             Run the action if a regular expression matches
6605
6606                         :condition-expression: Regular Expression
6607                         :condition-searchtext: Text to match against
6608                           the regular expression
6609    file-exists        Run the action if a file exists
6610
6611                         :condition-filename: Check existence of this file
6612                         :condition-basedir: If condition-filename is
6613                           relative, it will be considered relative to
6614                           either `workspace`, `artifact-directory`,
6615                           or `jenkins-home`. Default is `workspace`.
6616    ================== ====================================================
6617
6618    Single Conditional Action Example:
6619
6620    .. literalinclude:: \
6621    /../../tests/publishers/fixtures/conditional-publisher001.yaml
6622       :language: yaml
6623
6624    Multiple Conditional Actions Example
6625    (includes example of multiple actions per condition which requires
6626    v0.13 or higher of the Flexible Publish plugin):
6627
6628    .. literalinclude:: \
6629    /../../tests/publishers/fixtures/conditional-publisher003.yaml
6630       :language: yaml
6631
6632    :download:`Multiple Conditional Actions Example for pre-v0.13 versions
6633    <../../tests/publishers/fixtures/conditional-publisher002.yaml>`
6634
6635    """
6636
6637    def publish_condition_tag(cdata, prefix, condition_tag):
6638        kind = cdata["%s-kind" % prefix]
6639        ctag = XML.SubElement(cond_publisher, condition_tag)
6640        class_pkg = "org.jenkins_ci.plugins.run_condition"
6641
6642        if kind == "always":
6643            ctag.set("class", class_pkg + ".core.AlwaysRun")
6644        elif kind == "never":
6645            ctag.set("class", class_pkg + ".core.NeverRun")
6646        elif kind == "boolean-expression":
6647            ctag.set("class", class_pkg + ".core.BooleanCondition")
6648            XML.SubElement(ctag, "token").text = cdata["%s-expression" % prefix]
6649        elif kind == "current-status":
6650            ctag.set("class", class_pkg + ".core.StatusCondition")
6651            wr = XML.SubElement(ctag, "worstResult")
6652            wr_name = cdata["%s-worst" % prefix]
6653            if wr_name not in hudson_model.THRESHOLDS:
6654                raise JenkinsJobsException(
6655                    "threshold must be one of %s"
6656                    % ", ".join(hudson_model.THRESHOLDS.keys())
6657                )
6658            wr_threshold = hudson_model.THRESHOLDS[wr_name]
6659            XML.SubElement(wr, "name").text = wr_threshold["name"]
6660            XML.SubElement(wr, "ordinal").text = wr_threshold["ordinal"]
6661            XML.SubElement(wr, "color").text = wr_threshold["color"]
6662            XML.SubElement(wr, "completeBuild").text = str(
6663                wr_threshold["complete"]
6664            ).lower()
6665
6666            br = XML.SubElement(ctag, "bestResult")
6667            br_name = cdata["%s-best" % prefix]
6668            if br_name not in hudson_model.THRESHOLDS:
6669                raise JenkinsJobsException(
6670                    "threshold must be one of %s"
6671                    % ", ".join(hudson_model.THRESHOLDS.keys())
6672                )
6673            br_threshold = hudson_model.THRESHOLDS[br_name]
6674            XML.SubElement(br, "name").text = br_threshold["name"]
6675            XML.SubElement(br, "ordinal").text = br_threshold["ordinal"]
6676            XML.SubElement(br, "color").text = br_threshold["color"]
6677            XML.SubElement(br, "completeBuild").text = str(
6678                wr_threshold["complete"]
6679            ).lower()
6680        elif kind == "shell":
6681            ctag.set("class", class_pkg + ".contributed.ShellCondition")
6682            XML.SubElement(ctag, "command").text = cdata["%s-command" % prefix]
6683        elif kind == "windows-shell":
6684            ctag.set("class", class_pkg + ".contributed.BatchFileCondition")
6685            XML.SubElement(ctag, "command").text = cdata["%s-command" % prefix]
6686        elif kind == "regexp":
6687            ctag.set("class", class_pkg + ".core.ExpressionCondition")
6688            XML.SubElement(ctag, "expression").text = cdata["%s-expression" % prefix]
6689            XML.SubElement(ctag, "label").text = cdata["%s-searchtext" % prefix]
6690        elif kind == "file-exists":
6691            ctag.set("class", class_pkg + ".core.FileExistsCondition")
6692            XML.SubElement(ctag, "file").text = cdata["%s-filename" % prefix]
6693            basedir = cdata.get("%s-basedir", "workspace")
6694            basedir_tag = XML.SubElement(ctag, "baseDir")
6695            if "workspace" == basedir:
6696                basedir_tag.set("class", class_pkg + ".common.BaseDirectory$Workspace")
6697            elif "artifact-directory" == basedir:
6698                basedir_tag.set(
6699                    "class", class_pkg + ".common." "BaseDirectory$ArtifactsDir"
6700                )
6701            elif "jenkins-home" == basedir:
6702                basedir_tag.set(
6703                    "class", class_pkg + ".common." "BaseDirectory$JenkinsHome"
6704                )
6705        else:
6706            raise JenkinsJobsException(
6707                "%s is not a valid %s-kind " "value." % (kind, prefix)
6708            )
6709
6710    def publish_condition(cdata):
6711        return publish_condition_tag(cdata, "condition", condition_tag)
6712
6713    def publish_aggregation_condition(cdata):
6714        return publish_condition_tag(
6715            cdata, "condition-aggregation", aggregation_condition_tag
6716        )
6717
6718    def publish_action(parent, action):
6719        for edited_node in create_publishers(registry, action):
6720            if not use_publisher_list:
6721                edited_node.set("class", edited_node.tag)
6722                # sort attributes alphabetically
6723                attrib = edited_node.attrib
6724                if len(attrib) > 1:
6725                    attribs = sorted(attrib.items())
6726                    attrib.clear()
6727                    attrib.update(attribs)
6728                edited_node.tag = "publisher"
6729            parent.append(edited_node)
6730
6731    flex_publisher_tag = (
6732        "org.jenkins__ci.plugins.flexible__publish." "FlexiblePublisher"
6733    )
6734    cond_publisher_tag = (
6735        "org.jenkins__ci.plugins.flexible__publish." "ConditionalPublisher"
6736    )
6737
6738    root_tag = XML.SubElement(xml_parent, flex_publisher_tag)
6739    publishers_tag = XML.SubElement(root_tag, "publishers")
6740    condition_tag = "condition"
6741    aggregation_condition_tag = "aggregationCondition"
6742
6743    evaluation_classes_pkg = "org.jenkins_ci.plugins.run_condition"
6744    evaluation_classes = {
6745        "fail": evaluation_classes_pkg + ".BuildStepRunner$Fail",
6746        "mark-unstable": evaluation_classes_pkg + ".BuildStepRunner$Unstable",
6747        "run-and-mark-unstable": evaluation_classes_pkg
6748        + ".BuildStepRunner$RunUnstable",
6749        "run": evaluation_classes_pkg + ".BuildStepRunner$Run",
6750        "dont-run": evaluation_classes_pkg + ".BuildStepRunner$DontRun",
6751    }
6752
6753    plugin_info = registry.get_plugin_info("Flexible Publish Plugin")
6754    # Note: Assume latest version of plugin is preferred config format
6755    version = pkg_resources.parse_version(plugin_info.get("version", str(sys.maxsize)))
6756
6757    # Support for MatrixAggregator was added in v0.11
6758    # See JENKINS-14494
6759    has_matrix_aggregator = version >= pkg_resources.parse_version("0.11")
6760
6761    for cond_action in data:
6762        cond_publisher = XML.SubElement(publishers_tag, cond_publisher_tag)
6763        publish_condition(cond_action)
6764        condition_aggregation = cond_action.get("condition-aggregation", False)
6765        if condition_aggregation and has_matrix_aggregator:
6766            publish_aggregation_condition(cond_action)
6767        elif condition_aggregation:
6768            raise JenkinsJobsException(
6769                "Matrix Aggregation is not supported " "in your plugin version."
6770            )
6771        evaluation_flag = cond_action.get("on-evaluation-failure", "fail")
6772        if evaluation_flag not in evaluation_classes.keys():
6773            raise JenkinsJobsException(
6774                "on-evaluation-failure value "
6775                "specified is not valid.  Must be one "
6776                "of: %s" % evaluation_classes.keys()
6777            )
6778
6779        evaluation_class = evaluation_classes[evaluation_flag]
6780        XML.SubElement(cond_publisher, "runner").set("class", evaluation_class)
6781
6782        if "action" in cond_action:
6783            actions = cond_action["action"]
6784
6785            action_parent = cond_publisher
6786
6787            # XML tag changed from publisher to publisherList in v0.13
6788            # check the plugin version to determine further operations
6789            use_publisher_list = version >= pkg_resources.parse_version("0.13")
6790
6791            if use_publisher_list:
6792                action_parent = XML.SubElement(cond_publisher, "publisherList")
6793            else:
6794                # Check the length of actions list for versions prior to 0.13.
6795                # Flexible Publish will overwrite action if more than one is
6796                # specified.  Limit the action list to one element.
6797                if len(actions) != 1:
6798                    raise JenkinsJobsException(
6799                        "Only one action may be " "specified for each condition."
6800                    )
6801            for action in actions:
6802                publish_action(action_parent, action)
6803        else:
6804            raise JenkinsJobsException("action must be set for each condition")
6805
6806
6807def scoverage(registry, xml_parent, data):
6808    """yaml: scoverage
6809    Publish scoverage results as a trend graph.
6810    Requires the Jenkins :jenkins-plugins:`Scoverage Plugin <scoverage>`.
6811
6812    :arg str report-directory: This is a directory that specifies the locations
6813        where the xml scoverage report is generated (required)
6814    :arg str report-file: This is a file name that is given to the xml
6815        scoverage report (required)
6816
6817    Example:
6818
6819    .. literalinclude::  /../../tests/publishers/fixtures/scoverage001.yaml
6820       :language: yaml
6821    """
6822    scoverage = XML.SubElement(
6823        xml_parent, "org.jenkinsci.plugins.scoverage.ScoveragePublisher"
6824    )
6825    scoverage.set("plugin", "scoverage")
6826
6827    mappings = [
6828        ("report-directory", "reportDir", None),
6829        ("report-file", "reportFile", None),
6830    ]
6831    helpers.convert_mapping_to_xml(scoverage, data, mappings, fail_required=True)
6832
6833
6834def display_upstream_changes(registry, xml_parent, data):
6835    """yaml: display-upstream-changes
6836    Display SCM changes of upstream jobs. Requires the Jenkins
6837    :jenkins-plugins:`Display Upstream Changes Plugin
6838    <display-upstream-changes>`.
6839
6840    Example:
6841
6842    .. literalinclude:: \
6843    /../../tests/publishers/fixtures/display-upstream-changes.yaml
6844    """
6845    XML.SubElement(
6846        xml_parent,
6847        "jenkins.plugins.displayupstreamchanges." "DisplayUpstreamChangesRecorder",
6848    )
6849
6850
6851def gatling(registry, xml_parent, data):
6852    """yaml: gatling
6853    Publish gatling results as a trend graph
6854    Requires the Jenkins :jenkins-plugins:`Gatling Plugin <gatling>`.
6855
6856    Example:
6857
6858    .. literalinclude:: /../../tests/publishers/fixtures/gatling001.yaml
6859       :language: yaml
6860    """
6861    gatling = XML.SubElement(xml_parent, "io.gatling.jenkins.GatlingPublisher")
6862    mapping = [("", "enabled", "true")]
6863    helpers.convert_mapping_to_xml(gatling, data, mapping, fail_required=True)
6864
6865
6866def logstash(registry, xml_parent, data):
6867    """yaml: logstash
6868    Send job's console log to Logstash for processing and analyis of
6869    your job data. Also stores test metrics from Junit.
6870    Requires the Jenkins :jenkins-plugins:`Logstash Plugin <logstash>`.
6871
6872    :arg int max-lines: The maximum number of log lines to send to Logstash.
6873        (default 1000)
6874    :arg bool fail-build: Mark build as failed if this step fails.
6875        (default false)
6876
6877    Minimal Example:
6878
6879    .. literalinclude::  /../../tests/publishers/fixtures/logstash-min.yaml
6880       :language: yaml
6881
6882    Full Example:
6883
6884    .. literalinclude::  /../../tests/publishers/fixtures/logstash-full.yaml
6885       :language: yaml
6886    """
6887
6888    logstash = XML.SubElement(xml_parent, "jenkins.plugins.logstash.LogstashNotifier")
6889    logstash.set("plugin", "logstash")
6890
6891    mapping = [("max-lines", "maxLines", 1000), ("fail-build", "failBuild", False)]
6892    helpers.convert_mapping_to_xml(logstash, data, mapping, fail_required=True)
6893
6894
6895def image_gallery(registry, xml_parent, data):
6896    """yaml: image-gallery
6897    Produce an image gallery using Javascript library. Requires the Jenkins
6898    :jenkins-plugins:`Image Gallery Plugin <image-gallery>`.
6899
6900    :arg str gallery-type:
6901
6902        :gallery-type values:
6903            * **archived-images-gallery** (default)
6904            * **in-folder-comparative-gallery**
6905            * **multiple-folder-comparative-gallery**
6906    :arg str title: gallery title (optional)
6907    :arg int image-width: width of the image (optional)
6908    :arg bool unstable-if-no-artifacts: mark build as unstable
6909        if no archived artifacts were found (default false)
6910    :arg str includes: include pattern (valid for archived-images-gallery
6911        gallery)
6912    :arg str base-root-folder: base root dir (valid for comparative gallery)
6913    :arg int image-inner-width: width of the image displayed in the inner
6914        gallery popup (valid for comparative gallery, optional)
6915
6916    Example:
6917
6918    .. literalinclude:: /../../tests/publishers/fixtures/image-gallery001.yaml
6919
6920    """
6921
6922    def include_comparative_elements(gallery_parent_elem, gallery):
6923        XML.SubElement(gallery_parent_elem, "baseRootFolder").text = str(
6924            gallery.get("base-root-folder", "")
6925        )
6926        image_inner_width = gallery.get("image-inner-width", "")
6927        if image_inner_width:
6928            XML.SubElement(gallery_parent_elem, "imageInnerWidth").text = str(
6929                image_inner_width
6930            )
6931
6932    package_prefix = "org.jenkinsci.plugins.imagegallery."
6933    builder = XML.SubElement(xml_parent, package_prefix + "ImageGalleryRecorder")
6934    image_galleries = XML.SubElement(builder, "imageGalleries")
6935    galleries = {
6936        "archived-images-gallery": package_prefix + "imagegallery."
6937        "ArchivedImagesGallery",
6938        "in-folder-comparative-gallery": package_prefix + "comparative."
6939        "InFolderComparativeArchivedImagesGallery",
6940        "multiple-folder-comparative-gallery": package_prefix + "comparative."
6941        "MultipleFolderComparativeArchivedImagesGallery",
6942    }
6943    for gallery_def in data:
6944        gallery_type = gallery_def.get("gallery-type", "archived-images-gallery")
6945        if gallery_type not in galleries:
6946            raise InvalidAttributeError("gallery-type", gallery_type, galleries.keys())
6947        gallery_config = XML.SubElement(image_galleries, galleries[gallery_type])
6948        XML.SubElement(gallery_config, "title").text = str(gallery_def.get("title", ""))
6949        image_width = str(gallery_def.get("image-width", ""))
6950        if image_width:
6951            XML.SubElement(gallery_config, "imageWidth").text = str(image_width)
6952        XML.SubElement(
6953            gallery_config, "markBuildAsUnstableIfNoArchivesFound"
6954        ).text = str(gallery_def.get("unstable-if-no-artifacts", False))
6955        if gallery_type == "archived-images-gallery":
6956            XML.SubElement(gallery_config, "includes").text = str(
6957                gallery_def.get("includes", "")
6958            )
6959        if gallery_type == "in-folder-comparative-gallery":
6960            include_comparative_elements(gallery_config, gallery_def)
6961        if gallery_type == "multiple-folder-comparative-gallery":
6962            include_comparative_elements(gallery_config, gallery_def)
6963
6964
6965def naginator(registry, xml_parent, data):
6966    """yaml: naginator
6967    Automatically reschedule a build after a build failure
6968    Requires the Jenkins :jenkins-plugins:`Naginator Plugin <naginator>`.
6969
6970    :arg bool rerun-unstable-builds: Rerun build for unstable builds as well
6971        as failures (default false)
6972    :arg bool rerun-matrix-part: Rerun build only for failed parts on the
6973        matrix (>=1.12) (default false)
6974    :arg int fixed-delay: Fixed delay in seconds before retrying build (cannot
6975        be used with progressive-delay-increment or progressive-delay-maximum.
6976        This is the default delay type.  (default 0)
6977    :arg int progressive-delay-increment: Progressive delay in seconds before
6978        retrying build increment (cannot be used when fixed-delay is being
6979        used) (default 0)
6980    :arg int progressive-delay-maximum: Progressive delay in seconds before
6981        retrying maximum delay (cannot be used when fixed-delay is being used)
6982        (default 0)
6983    :arg int max-failed-builds: Maximum number of successive failed builds
6984        (default 0)
6985    :arg str regular-expression: Only rerun build if regular expression is
6986        found in output (default '')
6987
6988    Example:
6989
6990    .. literalinclude:: /../../tests/publishers/fixtures/naginator001.yaml
6991        :language: yaml
6992    """
6993    naginator = XML.SubElement(
6994        xml_parent, "com.chikli.hudson.plugin.naginator.NaginatorPublisher"
6995    )
6996    XML.SubElement(naginator, "regexpForRerun").text = str(
6997        data.get("regular-expression", "")
6998    )
6999    XML.SubElement(naginator, "checkRegexp").text = str(
7000        "regular-expression" in data
7001    ).lower()
7002    XML.SubElement(naginator, "rerunIfUnstable").text = str(
7003        data.get("rerun-unstable-builds", False)
7004    ).lower()
7005    XML.SubElement(naginator, "rerunMatrixPart").text = str(
7006        data.get("rerun-matrix-part", False)
7007    ).lower()
7008    progressive_delay = (
7009        "progressive-delay-increment" in data or "progressive-delay-maximum" in data
7010    )
7011    if "fixed-delay" in data and progressive_delay:
7012        raise JenkinsJobsException(
7013            "You cannot specify both fixed " "and progressive delays"
7014        )
7015    if not progressive_delay:
7016        delay = XML.SubElement(
7017            naginator,
7018            "delay",
7019            {"class": "com.chikli.hudson.plugin.naginator.FixedDelay"},
7020        )
7021        XML.SubElement(delay, "delay").text = str(data.get("fixed-delay", "0"))
7022    else:
7023        delay = XML.SubElement(
7024            naginator,
7025            "delay",
7026            {"class": "com.chikli.hudson.plugin.naginator.ProgressiveDelay"},
7027        )
7028        XML.SubElement(delay, "increment").text = str(
7029            data.get("progressive-delay-increment", "0")
7030        )
7031        XML.SubElement(delay, "max").text = str(
7032            data.get("progressive-delay-maximum", "0")
7033        )
7034    XML.SubElement(naginator, "maxSchedule").text = str(
7035        data.get("max-failed-builds", "0")
7036    )
7037
7038
7039def disable_failed_job(registry, xml_parent, data):
7040    """yaml: disable-failed-job
7041    Automatically disable failed jobs.
7042
7043    Requires the Jenkins :jenkins-plugins:`Disable Failed Job Plugin
7044    <disable-failed-job>`.
7045
7046    :arg str when-to-disable: The condition to disable the job. (required)
7047        Possible values are
7048
7049        * **Only Failure**
7050        * **Failure and Unstable**
7051        * **Unstable**
7052
7053    :arg int no-of-failures: Number of consecutive failures to disable the
7054        job. (optional)
7055
7056    Example:
7057
7058    .. literalinclude::
7059        /../../tests/publishers/fixtures/disable-failed-job001.yaml
7060       :language: yaml
7061    """
7062
7063    xml_element = XML.SubElement(
7064        xml_parent,
7065        "disableFailedJob." "disableFailedJob.DisableFailedJob",
7066        {"plugin": "disable-failed-job"},
7067    )
7068
7069    valid_conditions = ["Only Failure", "Failure and Unstable", "Only Unstable"]
7070    mapping = [("when-to-disable", "whenDisable", None, valid_conditions)]
7071    helpers.convert_mapping_to_xml(xml_element, data, mapping, fail_required=True)
7072
7073    if "no-of-failures" in data:
7074        mapping = [
7075            ("no-of-failures", "failureTimes", None),
7076            ("", "optionalBrockChecked", True),
7077        ]
7078        helpers.convert_mapping_to_xml(xml_element, data, mapping, fail_required=True)
7079    else:
7080        XML.SubElement(xml_element, "optionalBrockChecked").text = "false"
7081
7082
7083def google_cloud_storage(registry, xml_parent, data):
7084    """yaml: google-cloud-storage
7085    Upload build artifacts to Google Cloud Storage. Requires the
7086    Jenkins :jenkins-plugins:`Google Cloud Storage plugin
7087    <google-storage-plugin>`.
7088
7089    Apart from the Google Cloud Storage Plugin itself, installation of Google
7090    OAuth Credentials and addition of required credentials to Jenkins is
7091    required.
7092
7093    :arg str credentials-id: The set of Google credentials registered with
7094                             the Jenkins Credential Manager for authenticating
7095                             with your project. (required)
7096    :arg list uploads:
7097        :uploads:
7098            * **expiring-elements** (`dict`)
7099                :params:
7100                    * **bucket-name** (`str`) bucket name to upload artifacts
7101                      (required)
7102                    * **days-to-retain** (`int`) days to keep artifacts
7103                      (required)
7104            * **build-log** (`dict`)
7105                :params:
7106                    * **log-name** (`str`) name of the file that the Jenkins
7107                      console log to be named (required)
7108                    * **storage-location** (`str`) bucket name to upload
7109                      artifacts (required)
7110                    * **share-publicly** (`bool`) whether to share uploaded
7111                      share uploaded artifacts with everyone (default false)
7112                    * **upload-for-failed-jobs** (`bool`) whether to upload
7113                      artifacts even if the build fails (default false)
7114                    * **show-inline** (`bool`) whether to show uploaded build
7115                      log inline in web browsers, rather than forcing it to be
7116                      downloaded (default true)
7117                    * **strip-prefix** (`str`) strip this prefix off the
7118                      file names (default not set)
7119
7120            * **classic** (`dict`)
7121                :params:
7122                    * **file-pattern** (`str`) ant style globs to match the
7123                      files to upload (required)
7124                    * **storage-location** (`str`) bucket name to upload
7125                      artifacts (required)
7126                    * **share-publicly** (`bool`) whether to share uploaded
7127                      share uploaded artifacts with everyone (default false)
7128                    * **upload-for-failed-jobs** (`bool`) whether to upload
7129                      artifacts even if the build fails (default false)
7130                    * **show-inline** (`bool`) whether to show uploaded
7131                      artifacts inline in web browsers, rather than forcing
7132                      them to be downloaded (default false)
7133                    * **strip-prefix** (`str`) strip this prefix off the
7134                      file names (default not set)
7135
7136    Example:
7137
7138    .. literalinclude::
7139        /../../tests/publishers/fixtures/google_cloud_storage001.yaml
7140       :language: yaml
7141
7142    Full example:
7143
7144    .. literalinclude::
7145        /../../tests/publishers/fixtures/google_cloud_storage002.yaml
7146       :language: yaml
7147    """
7148
7149    def expiring_elements(properties, upload_element, types):
7150        # Handle expiring elements upload action
7151
7152        xml_element = XML.SubElement(
7153            upload_element,
7154            "com.google." "jenkins.plugins.storage." "ExpiringBucketLifecycleManager",
7155        )
7156        mapping = [
7157            ("bucket-name", "bucketNameWithVars", None),
7158            ("", "sharedPublicly", False),
7159            ("", "forFailedJobs", False),
7160            ("days-to-retain", "bucketObjectTTL", None),
7161        ]
7162        helpers.convert_mapping_to_xml(
7163            xml_element, properties, mapping, fail_required=True
7164        )
7165
7166        if types.count("expiring-elements") > 1:
7167            XML.SubElement(
7168                xml_element,
7169                "module",
7170                {
7171                    "reference": "../../com.google.jenkins.plugins."
7172                    "storage.ExpiringBucketLifecycleManager/module"
7173                },
7174            )
7175        else:
7176            XML.SubElement(xml_element, "module")
7177
7178    def build_log(properties, upload_element, types):
7179        # Handle build log upload action
7180
7181        xml_element = XML.SubElement(
7182            upload_element, "com.google.jenkins." "plugins.storage.StdoutUpload"
7183        )
7184        mapping = [
7185            ("storage-location", "bucketNameWithVars", None),
7186            ("share-publicly", "sharedPublicly", False),
7187            ("upload-for-failed-jobs", "forFailedJobs", False),
7188            ("show-inline", "showInline", True),
7189            ("strip-prefix", "pathPrefix", ""),
7190            ("log-name", "logName", None),
7191        ]
7192        helpers.convert_mapping_to_xml(
7193            xml_element, properties, mapping, fail_required=True
7194        )
7195
7196        if types.count("build-log") > 1:
7197            XML.SubElement(
7198                xml_element,
7199                "module",
7200                {
7201                    "reference": "../../com.google.jenkins.plugins."
7202                    "storage.StdoutUpload/module"
7203                },
7204            )
7205        else:
7206            XML.SubElement(xml_element, "module")
7207
7208    def classic(properties, upload_element, types):
7209        # Handle classic upload action
7210
7211        xml_element = XML.SubElement(
7212            upload_element, "com.google.jenkins." "plugins.storage.ClassicUpload"
7213        )
7214        mapping = [
7215            ("storage-location", "bucketNameWithVars", None),
7216            ("share-publicly", "sharedPublicly", False),
7217            ("upload-for-failed-jobs", "forFailedJobs", False),
7218            ("show-inline", "showInline", False),
7219            ("strip-prefix", "pathPrefix", ""),
7220            ("file-pattern", "sourceGlobWithVars", None),
7221        ]
7222        helpers.convert_mapping_to_xml(
7223            xml_element, properties, mapping, fail_required=True
7224        )
7225
7226        if types.count("classic") > 1:
7227            XML.SubElement(
7228                xml_element,
7229                "module",
7230                {
7231                    "reference": "../../com.google.jenkins.plugins."
7232                    "storage.ClassicUpload/module"
7233                },
7234            )
7235        else:
7236            XML.SubElement(xml_element, "module")
7237
7238    uploader = XML.SubElement(
7239        xml_parent,
7240        "com.google.jenkins.plugins.storage." "GoogleCloudStorageUploader",
7241        {"plugin": "google-storage-plugin"},
7242    )
7243
7244    mapping = [("credentials-id", "credentialsId", None)]
7245    helpers.convert_mapping_to_xml(uploader, data, mapping, fail_required=True)
7246
7247    valid_upload_types = ["expiring-elements", "build-log", "classic"]
7248    types = []
7249
7250    upload_element = XML.SubElement(uploader, "uploads")
7251
7252    uploads = data["uploads"]
7253    for upload in uploads:
7254        for upload_type, properties in upload.items():
7255            types.append(upload_type)
7256
7257            if upload_type not in valid_upload_types:
7258                raise InvalidAttributeError("uploads", upload_type, valid_upload_types)
7259            else:
7260                locals()[upload_type.replace("-", "_")](
7261                    properties, upload_element, types
7262                )
7263
7264
7265def flowdock(registry, xml_parent, data):
7266    """yaml: flowdock
7267    This plugin publishes job build results to a Flowdock flow.
7268
7269    Requires the Jenkins :jenkins-plugins:`Flowdock Plugin
7270    <jenkins-flowdock-plugin>`.
7271
7272    :arg str token: API token for the targeted flow.
7273      (required)
7274    :arg str tags: Comma-separated list of tags to include in message
7275      (default "")
7276    :arg bool chat-notification: Send chat notification when build fails
7277      (default true)
7278    :arg bool notify-success: Send notification on build success
7279      (default true)
7280    :arg bool notify-failure: Send notification on build failure
7281      (default true)
7282    :arg bool notify-fixed: Send notification when build is fixed
7283      (default true)
7284    :arg bool notify-unstable: Send notification when build is unstable
7285      (default false)
7286    :arg bool notify-aborted: Send notification when build was aborted
7287      (default false)
7288    :arg bool notify-notbuilt: Send notification when build did not occur
7289      (default false)
7290
7291    Example:
7292
7293    .. literalinclude:: /../../tests/publishers/fixtures/flowdock001.yaml
7294       :language: yaml
7295
7296    Full example:
7297
7298    .. literalinclude:: /../../tests/publishers/fixtures/flowdock002.yaml
7299       :language: yaml
7300    """
7301
7302    def gen_notification_entry(data_item, default, text):
7303        e = XML.SubElement(nm, "entry")
7304        mapping = [
7305            ("", "com.flowdock.jenkins.BuildResult", text),
7306            (data_item, "boolean", default),
7307        ]
7308        helpers.convert_mapping_to_xml(e, data, mapping, fail_required=True)
7309
7310    parent = XML.SubElement(xml_parent, "com.flowdock.jenkins.FlowdockNotifier")
7311    mapping = [
7312        ("token", "flowToken", None),
7313        ("tags", "notificationTags", ""),
7314        ("chat-notification", "chatNotification", True),
7315        ("notify-success", "notifySuccess", True),
7316        ("notify-failure", "notifyFailure", True),
7317        ("notify-fixed", "notifyFixed", True),
7318        ("notify-unstable", "notifyUnstable", False),
7319        ("notify-aborted", "notifyAborted", False),
7320        ("notify-notbuilt", "notifyNotBuilt", False),
7321    ]
7322    helpers.convert_mapping_to_xml(parent, data, mapping, fail_required=True)
7323
7324    nm = XML.SubElement(parent, "notifyMap")
7325
7326    # notification entries
7327    gen_notification_entry("notify-success", True, "SUCCESS")
7328    gen_notification_entry("notify-failure", True, "FAILURE")
7329    gen_notification_entry("notify-fixed", True, "FIXED")
7330    gen_notification_entry("notify-unstable", False, "UNSTABLE")
7331    gen_notification_entry("notify-aborted", False, "ABORTED")
7332    gen_notification_entry("notify-notbuilt", False, "NOT_BUILT")
7333
7334
7335def clamav(registry, xml_parent, data):
7336    """yaml: clamav
7337    Check files with ClamAV, an open source antivirus engine.
7338    Requires the Jenkins :jenkins-plugins:`ClamAV Plugin <clamav>`.
7339
7340    :arg str includes: Comma separated list of files that should be scanned.
7341        Must be set for ClamAV to check for artifacts. (default '')
7342    :arg str excludes: Comma separated list of files that should be ignored
7343        (default '')
7344
7345    Full Example:
7346
7347    .. literalinclude:: /../../tests/publishers/fixtures/clamav-full.yaml
7348       :language: yaml
7349
7350    Minimal Example:
7351
7352    .. literalinclude:: /../../tests/publishers/fixtures/clamav-minimal.yaml
7353       :language: yaml
7354    """
7355    clamav = XML.SubElement(xml_parent, "org.jenkinsci.plugins.clamav.ClamAvRecorder")
7356    clamav.set("plugin", "clamav")
7357
7358    mappings = [("includes", "includes", ""), ("excludes", "excludes", "")]
7359    helpers.convert_mapping_to_xml(clamav, data, mappings, fail_required=True)
7360
7361
7362def testselector(registry, xml_parent, data):
7363    """yaml: testselector
7364    This plugin allows you to choose specific tests you want to run.
7365
7366    Requires the Jenkins :jenkins-plugins:`Tests Selector Plugin
7367    <selected-tests-executor>`.
7368
7369    :arg str name: Environment variable in which selected tests are saved
7370      (required)
7371    :arg str description: Description
7372      (default "")
7373    :arg str properties-file: Contain all your tests
7374      (required)
7375    :arg str enable-field: Imply if the test is enabled or not
7376      (default "")
7377    :arg str groupby: Plugin will group the tests by
7378      (default "")
7379    :arg str field-sperator: Separate between the fields in the tests tree
7380      (default "")
7381    :arg str show-fields: Shown in the tests tree
7382      (default "")
7383    :arg str multiplicity-field: Number of times the test should run
7384      (default "")
7385
7386    Example:
7387
7388    .. literalinclude:: /../../tests/publishers/fixtures/testselector001.yaml
7389       :language: yaml
7390    """
7391
7392    testselector = XML.SubElement(
7393        xml_parent, "il.ac.technion.jenkins.plugins" "TestExecuter"
7394    )
7395
7396    mapping = [
7397        ("name", "name", None),
7398        ("description", "description", ""),
7399        ("properties-file", "propertiesFilePath", None),
7400        ("enable-field", "enableField", ""),
7401        ("groupby", "groupBy", ""),
7402        ("field-separator", "fieldSeparator", ""),
7403        ("show-fields", "showFields", ""),
7404        ("multiplicity-field", "multiplicityField", ""),
7405    ]
7406    helpers.convert_mapping_to_xml(testselector, data, mapping, fail_required=True)
7407
7408
7409def cloudformation(registry, xml_parent, data):
7410    """yaml: cloudformation
7411    Create cloudformation stacks before running a build and optionally
7412    delete them at the end.  Requires the Jenkins :jenkins-plugins:`AWS
7413    Cloudformation Plugin <jenkins-cloudformation-plugin>`.
7414
7415    :arg list create-stacks: List of stacks to create
7416
7417        :create-stacks attributes:
7418            * **arg str name** - The name of the stack (Required)
7419            * **arg str description** - Description of the stack (Optional)
7420            * **arg str recipe** - The cloudformation recipe file (Required)
7421            * **arg list parameters** - A list of key/value pairs, will be
7422              joined together into a comma separated string (Optional)
7423            * **arg int timeout** - Number of seconds to wait before giving up
7424              creating a stack (default 0)
7425            * **arg str access-key** - The Amazon API Access Key (Required)
7426            * **arg str secret-key** - The Amazon API Secret Key (Required)
7427            * **arg int sleep** - Number of seconds to wait before continuing
7428              to the next step (default 0)
7429            * **arg array region** - The region to run cloudformation in.
7430              (Required)
7431
7432                :region values:
7433                    * **us-east-1**
7434                    * **us-west-1**
7435                    * **us-west-2**
7436                    * **eu-central-1**
7437                    * **eu-west-1**
7438                    * **ap-southeast-1**
7439                    * **ap-southeast-2**
7440                    * **ap-northeast-1**
7441                    * **sa-east-1**
7442    :arg list delete-stacks: List of stacks to delete
7443
7444        :delete-stacks attributes:
7445            * **arg list name** - The names of the stacks to delete (Required)
7446            * **arg str access-key** - The Amazon API Access Key (Required)
7447            * **arg str secret-key** - The Amazon API Secret Key (Required)
7448            * **arg bool prefix** - If selected the tear down process will look
7449              for the stack that Starts with the stack name with the oldest
7450              creation date and will delete it.  (default false)
7451            * **arg array region** - The region to run cloudformation in.
7452              (Required)
7453
7454                :region values:
7455                    * **us-east-1**
7456                    * **us-west-1**
7457                    * **us-west-2**
7458                    * **eu-central-1**
7459                    * **eu-west-1**
7460                    * **ap-southeast-1**
7461                    * **ap-southeast-2**
7462                    * **ap-northeast-1**
7463                    * **sa-east-1**
7464
7465    Example:
7466
7467    .. literalinclude:: /../../tests/publishers/fixtures/cloudformation.yaml
7468       :language: yaml
7469    """
7470    region_dict = helpers.cloudformation_region_dict()
7471    stacks = helpers.cloudformation_init(
7472        xml_parent, data, "CloudFormationPostBuildNotifier"
7473    )
7474    for stack in data.get("create-stacks", []):
7475        helpers.cloudformation_stack(
7476            xml_parent, stack, "PostBuildStackBean", stacks, region_dict
7477        )
7478    delete_stacks = helpers.cloudformation_init(
7479        xml_parent, data, "CloudFormationNotifier"
7480    )
7481    for delete_stack in data.get("delete-stacks", []):
7482        helpers.cloudformation_stack(
7483            xml_parent, delete_stack, "SimpleStackBean", delete_stacks, region_dict
7484        )
7485
7486
7487def whitesource(registry, xml_parent, data):
7488    """yaml: whitesource
7489    This plugin brings automatic open source management to Jenkins users.
7490
7491    Requires the Jenkins :jenkins-plugins:`Whitesource Plugin
7492    <whitesource>`.
7493
7494    :arg str product-token: Product name or token to update (default '')
7495    :arg str version: Product version (default '')
7496    :arg str override-token: Override the api token from the global config
7497        (default '')
7498    :arg str project-token: Token uniquely identifying the project to update
7499        (default '')
7500    :arg list includes: list of libraries to include (default '[]')
7501    :arg list excludes: list of libraries to exclude (default '[]')
7502    :arg str policies: Whether to override the global settings.  Valid values:
7503        global, enable, disable (default 'global')
7504    :arg str requester-email: Email of the WhiteSource user that requests to
7505        update WhiteSource (>=1.5.1) (default '')
7506
7507    Full Example:
7508
7509    .. literalinclude:: /../../tests/publishers/fixtures/whitesource-full.yaml
7510       :language: yaml
7511
7512    Minimal Example:
7513
7514    .. literalinclude::
7515       /../../tests/publishers/fixtures/whitesource-minimal.yaml
7516       :language: yaml
7517    """
7518    whitesource = XML.SubElement(
7519        xml_parent, "org.whitesource.jenkins." "WhiteSourcePublisher"
7520    )
7521    whitesource.set("plugin", "whitesource")
7522    policies = ["global", "enable", "disable"]
7523
7524    mappings = [
7525        ("policies", "jobCheckPolicies", "global", policies),
7526        ("override-token", "jobApiToken", ""),
7527        ("product-token", "product", ""),
7528        ("version", "productVersion", ""),
7529        ("project-token", "projectToken", ""),
7530        ("requester-email", "requesterEmail", ""),
7531    ]
7532    helpers.convert_mapping_to_xml(whitesource, data, mappings, fail_required=True)
7533
7534    XML.SubElement(whitesource, "libIncludes").text = " ".join(data.get("includes", []))
7535    XML.SubElement(whitesource, "libExcludes").text = " ".join(data.get("excludes", []))
7536    XML.SubElement(whitesource, "ignorePomModules").text = "false"
7537
7538
7539def hipchat(registry, xml_parent, data):
7540    """yaml: hipchat
7541    Publisher that sends hipchat notifications on job events
7542    Requires the Jenkins :jenkins-plugins:`Hipchat Plugin
7543    <hipchat>` version >=1.9
7544
7545    Please see documentation for older plugin version
7546    https://jenkins-job-builder.readthedocs.io/en/latest/hipchat.html
7547
7548    :arg str token: This will override the default auth token (optional)
7549    :arg list rooms: list of HipChat rooms to post messages to, overrides
7550        global default (optional)
7551    :arg bool notify-start: post messages about build start event
7552        (default false)
7553    :arg bool notify-success: post messages about successful build event
7554        (default false)
7555    :arg bool notify-aborted: post messages about aborted build event
7556        (default false)
7557    :arg bool notify-not-built: post messages about build set to NOT_BUILT.
7558        This status code is used in a multi-stage build where a problem in
7559        earlier stage prevented later stages from building. (default false)
7560    :arg bool notify-unstable: post messages about unstable build event
7561        (default false)
7562    :arg bool notify-failure:  post messages about build failure event
7563        (default false)
7564    :arg bool notify-back-to-normal: post messages about build being back to
7565        normal after being unstable or failed (default false)
7566    :arg str start-message: This will override the default start message
7567        (optional)
7568    :arg str complete-message: This will override the default complete message
7569        (optional)
7570
7571    Example:
7572
7573    .. literalinclude::  /../../tests/publishers/fixtures/hipchat001.yaml
7574       :language: yaml
7575    """
7576    hipchat = XML.SubElement(xml_parent, "jenkins.plugins.hipchat.HipChatNotifier")
7577    XML.SubElement(hipchat, "token").text = str(data.get("token", ""))
7578
7579    if "rooms" in data:
7580        XML.SubElement(hipchat, "room").text = str(",".join(data["rooms"]))
7581
7582    mapping = [
7583        ("notify-start", "startNotification", False),
7584        ("notify-success", "notifySuccess", False),
7585        ("notify-aborted", "notifyAborted", False),
7586        ("notify-not-built", "notifyNotBuilt", False),
7587        ("notify-unstable", "notifyUnstable", False),
7588        ("notify-failure", "notifyFailure", False),
7589        ("notify-back-to-normal", "notifyBackToNormal", False),
7590        ("start-message", "startJobMessage", None),
7591        ("complete-message", "completeJobMessage", None),
7592    ]
7593    helpers.convert_mapping_to_xml(hipchat, data, mapping, fail_required=False)
7594
7595
7596def slack(registry, xml_parent, data):
7597    """yaml: slack
7598    Publisher that sends slack notifications on job events.
7599
7600    Requires the Jenkins :jenkins-plugins:`Slack Plugin <slack>`
7601
7602    When using Slack Plugin version < 2.0, Slack Plugin itself requires a
7603    publisher as well as properties please note that you have to create those
7604    too.  When using Slack Plugin version >= 2.0, you should only configure the
7605    publisher.
7606
7607    For backward compatibility, the publisher needs to query version of the
7608    Slack Plugin. Hence the ``query_plugins_info`` parameter shouldn't be set
7609    to ``False`` in the ``jenkins`` section of the configuration file.
7610
7611    :arg str team-domain: Your team's domain at slack. (default '')
7612    :arg str auth-token: The integration token to be used when sending
7613        notifications. (default '')
7614    :arg str auth-token-id: Allows credentials to be stored in Jenkins.
7615        (default '')
7616    :arg str build-server-url: Specify the URL for your server installation.
7617        (default '/')
7618    :arg str room: A comma separated list of rooms / channels to post the
7619        notifications to. (default '')
7620    :arg bool notify-start: Send notification when the job starts (>=2.0).
7621        (default false)
7622    :arg bool notify-success: Send notification on success (>=2.0).
7623        (default false)
7624    :arg bool notify-aborted: Send notification when job is aborted (>=2.0).
7625        (default false)
7626    :arg bool notify-not-built: Send notification when job set to NOT_BUILT
7627        status (>=2.0). (default false)
7628    :arg bool notify-unstable: Send notification when job becomes unstable
7629        (>=2.0). (default false)
7630    :arg bool notify-failure: Send notification when job fails for the first
7631        time (previous build was a success) (>=2.0).  (default false)
7632    :arg bool notify-every-failure: Send notification every time a job fails
7633        (>=2.23). (default false)
7634    :arg bool notify-back-to-normal: Send notification when job is succeeding
7635        again after being unstable or failed (>=2.0). (default false)
7636    :arg bool notify-repeated-failure: Send notification when job fails
7637        successively (previous build was also a failure) (>=2.0).
7638        (default false)
7639    :arg bool notify-regression: Send notification when number of failed tests
7640        increased or the failed tests are different than previous build
7641        (>=2.2). (default false)
7642    :arg bool include-failed-tests: includes all failed tests when some tests
7643        failed. does nothing if no failed tests were found (>=2.2).
7644        (default false)
7645    :arg bool include-test-summary: Include the test summary (>=2.0).
7646        (default false)
7647    :arg str commit-info-choice: What commit information to include into
7648        notification message, "NONE" includes nothing about commits, "AUTHORS"
7649        includes commit list with authors only, and "AUTHORS_AND_TITLES"
7650        includes commit list with authors and titles (>=2.0). (default "NONE")
7651    :arg bool include-custom-message: Include a custom message into the
7652        notification (>=2.0). (default false)
7653    :arg str custom-message: Custom message to be included for all statuses
7654        (>=2.0). (default '')
7655    :arg str custom-message-success: Custom message for succesful builds
7656        (>=2.10). (default '')
7657    :arg str custom-message-aborted: Custom message for aborted builds
7658        (>=2.10). (default '')
7659    :arg str custom-message-not-built: Custom message for not-built
7660        (>=2.10). (default '')
7661    :arg str custom-message-unstable: Custom message for unstable builds
7662        (>=2.10). (default '')
7663    :arg str custom-message-failure: Custom message for failed builds
7664        (>=2.10). (default '')
7665    :arg str auth-token-credential-id: The ID for the integration token from
7666        the Credentials plugin to be used to send notifications to Slack.
7667        (>=2.1) (default '')
7668    :arg bool bot-user: This option indicates the token belongs to a bot user
7669        in Slack. (>=2.2) (default False)
7670    :arg str base-url: Your Slack compatible Base URL. ``bot-user`` is not
7671        supported with Base URL. (>=2.2) (default '')
7672
7673    Example (version < 2.0):
7674
7675    .. literalinclude::
7676        /../../tests/publishers/fixtures/slack001.yaml
7677        :language: yaml
7678
7679    Minimal example (version >= 2.0):
7680
7681    .. literalinclude::
7682        /../../tests/publishers/fixtures/slack003.yaml
7683        :language: yaml
7684
7685    Full example (version >= 2.10):
7686
7687    .. literalinclude::
7688        /../../tests/publishers/fixtures/slack005.yaml
7689        :language: yaml
7690
7691    """
7692
7693    def _add_xml(elem, name, value=""):
7694        if isinstance(value, bool):
7695            value = str(value).lower()
7696        XML.SubElement(elem, name).text = value
7697
7698    logger = logging.getLogger(__name__)
7699
7700    plugin_info = registry.get_plugin_info("Slack Notification Plugin")
7701    # Note: Assume latest version of plugin is preferred config format
7702    plugin_ver = pkg_resources.parse_version(
7703        plugin_info.get("version", str(sys.maxsize))
7704    )
7705
7706    mapping = (
7707        ("team-domain", "teamDomain", ""),
7708        ("auth-token", "authToken", ""),
7709        ("auth-token-id", "authTokenCredentialId", ""),
7710        ("build-server-url", "buildServerUrl", "/"),
7711        ("room", "room", ""),
7712    )
7713    mapping_20 = (
7714        ("notify-start", "startNotification", False),
7715        ("notify-success", "notifySuccess", False),
7716        ("notify-aborted", "notifyAborted", False),
7717        ("notify-not-built", "notifyNotBuilt", False),
7718        ("notify-unstable", "notifyUnstable", False),
7719        ("notify-failure", "notifyFailure", False),
7720        ("notify-every-failure", "notifyEveryFailure", False),
7721        ("notify-back-to-normal", "notifyBackToNormal", False),
7722        ("notify-regression", "notifyRegression", False),
7723        ("notify-repeated-failure", "notifyRepeatedFailure", False),
7724        ("include-test-summary", "includeTestSummary", False),
7725        ("include-failed-tests", "includeFailedTests", False),
7726        ("commit-info-choice", "commitInfoChoice", "NONE"),
7727        ("include-custom-message", "includeCustomMessage", False),
7728        ("custom-message", "customMessage", ""),
7729        ("custom-message-success", "customMessageSuccess", ""),
7730        ("custom-message-aborted", "customMessageAborted", ""),
7731        ("custom-message-not-built", "customMessageNotBuilt", ""),
7732        ("custom-message-unstable", "customMessageUnstable", ""),
7733        ("custom-message-failure", "customMessageFailure", ""),
7734        ("auth-token-credential-id", "authTokenCredentialId", ""),
7735        ("bot-user", "botUser", False),
7736        ("base-url", "baseUrl", ""),
7737    )
7738
7739    commit_info_choices = ["NONE", "AUTHORS", "AUTHORS_AND_TITLES"]
7740
7741    slack = XML.SubElement(xml_parent, "jenkins.plugins.slack.SlackNotifier")
7742
7743    if plugin_ver >= pkg_resources.parse_version("2.0"):
7744        mapping = mapping + mapping_20
7745
7746    if plugin_ver < pkg_resources.parse_version("2.0"):
7747        for yaml_name, _, default_value in mapping:
7748            # All arguments that don't have a default value are mandatory for
7749            # the plugin to work as intended.
7750            if not data.get(yaml_name, default_value):
7751                raise MissingAttributeError(yaml_name)
7752
7753        for yaml_name, _, _ in mapping_20:
7754            if yaml_name in data:
7755                logger.warning(
7756                    "'%s' is invalid with plugin version < 2.0, ignored", yaml_name
7757                )
7758
7759    for yaml_name, xml_name, default_value in mapping:
7760        value = data.get(yaml_name, default_value)
7761
7762        # 'commit-info-choice' is enumerated type
7763        if yaml_name == "commit-info-choice" and value not in commit_info_choices:
7764            raise InvalidAttributeError(yaml_name, value, commit_info_choices)
7765
7766        # Ensure that custom-message is set when include-custom-message is set
7767        # to true.
7768        if (
7769            yaml_name == "include-custom-message"
7770            and data is False
7771            and not data.get("custom-message", "")
7772        ):
7773            raise MissingAttributeError("custom-message")
7774
7775        _add_xml(slack, xml_name, value)
7776
7777
7778def phabricator(registry, xml_parent, data):
7779    """yaml: phabricator
7780    Integrate with `Phabricator <https://www.phacility.com/>`_
7781
7782    Requires the Jenkins :jenkins-plugins:`Phabricator Plugin
7783    <phabricator-plugin>`.
7784
7785    :arg bool comment-on-success: Post a *comment* when the build
7786      succeeds. (optional)
7787    :arg bool uberalls-enabled: Integrate with uberalls. (optional)
7788    :arg str comment-file: Include contents of given file if
7789      commenting is enabled. (optional)
7790    :arg int comment-size: Maximum comment character length. (optional)
7791    :arg bool comment-with-console-link-on-failure: Post a *comment*
7792      when the build fails. (optional)
7793
7794    Example:
7795
7796    .. literalinclude::
7797        /../../tests/publishers/fixtures/phabricator001.yaml
7798       :language: yaml
7799    """
7800
7801    root = XML.SubElement(
7802        xml_parent, "com.uber.jenkins.phabricator.PhabricatorNotifier"
7803    )
7804    mapping = [
7805        ("comment-on-success", "commentOnSuccess", None),
7806        ("uberalls-enabled", "uberallsEnabled", None),
7807        ("comment-file", "commentFile", None),
7808        ("comment-size", "commentSize", None),
7809        (
7810            "comment-with-console-link-on-failure",
7811            "commentWithConsoleLinkOnFailure",
7812            None,
7813        ),
7814    ]
7815    helpers.convert_mapping_to_xml(root, data, mapping, fail_required=False)
7816
7817
7818def jms_messaging(registry, xml_parent, data):
7819    """yaml: jms-messaging
7820    The JMS Messaging Plugin provides the following functionality:
7821     - A build trigger to submit jenkins jobs upon receipt
7822       of a matching message.
7823     - A builder that may be used to submit a message to the topic
7824       upon the completion of a job
7825     - A post-build action that may be used to submit a message to the topic
7826       upon the completion of a job
7827
7828
7829    JMS Messaging provider types supported:
7830        - ActiveMQ
7831        - FedMsg
7832
7833    Requires the Jenkins :jenkins-plugins:`JMS Messaging Plugin
7834    Pipeline Plugin <jms-messaging>`.
7835
7836    :arg str override-topic: If you need to override the default topic.
7837        (default '')
7838    :arg str provider-name: Name of message provider setup in the
7839        global config. (default '')
7840    :arg str msg-type: A message type
7841        (default 'CodeQualityChecksDone')
7842    :arg str msg-props: Message header to publish. (default '')
7843    :arg str msg-content: Message body to publish. (default '')
7844
7845
7846    Full Example:
7847
7848        .. literalinclude::
7849            ../../tests/publishers/fixtures/jms-messaging-full.yaml
7850           :language: yaml
7851
7852    Minimal Example:
7853
7854        .. literalinclude::
7855            ../../tests/publishers/fixtures/jms-messaging-minimal.yaml
7856           :language: yaml
7857    """
7858    helpers.jms_messaging_common(
7859        xml_parent, "com.redhat.jenkins.plugins.ci." "CIMessageNotifier", data
7860    )
7861
7862
7863def openshift_build_canceller(registry, xml_parent, data):
7864    r"""yaml: openshift-build-canceller
7865    This action is intended to provide cleanup for a Jenkins job which failed
7866    because a build is hung (instead of terminating with a failure code);
7867    this step will allow you to perform the equivalent of a oc cancel-build
7868    for the provided build config; any builds under that build config which
7869    are not previously terminated (either successfully or unsuccessfully)
7870    or cancelled will be cancelled.
7871
7872    Requires the Jenkins :jenkins-plugins:`OpenShift Pipeline Plugin
7873    <openshift-pipeline>`.
7874
7875    :arg str api-url: this would be the value you specify if you leverage the
7876        --server option on the OpenShift `oc` command.
7877        (default '\https://openshift.default.svc.cluster.local')
7878    :arg str bld-cfg: The value here should be whatever was the output
7879        form `oc project` when you created the BuildConfig you
7880        want to run a Build on (default 'frontend')
7881    :arg str namespace: If you run `oc get bc` for the project listed in
7882        "namespace", that is the value you want to put here. (default 'test')
7883    :arg str auth-token: The value here is what you supply with the --token
7884        option when invoking the OpenShift `oc` command. (default '')
7885    :arg bool verbose: This flag is the toggle for
7886        turning on or off detailed logging in this plug-in. (default false)
7887
7888    Full Example:
7889
7890        .. literalinclude::
7891            ../../tests/publishers/fixtures/openshift-build-canceller001.yaml
7892           :language: yaml
7893
7894    Minimal Example:
7895
7896        .. literalinclude::
7897            ../../tests/publishers/fixtures/openshift-build-canceller002.yaml
7898           :language: yaml
7899    """
7900
7901    osb = XML.SubElement(
7902        xml_parent, "com.openshift.jenkins.plugins.pipeline." "OpenShiftBuildCanceller"
7903    )
7904    mapping = [
7905        # option, xml name, default value
7906        ("api-url", "apiURL", "https://openshift.default.svc.cluster.local"),
7907        ("bld-cfg", "bldCfg", "frontend"),
7908        ("namespace", "namespace", "test"),
7909        ("auth-token", "authToken", ""),
7910        ("verbose", "verbose", False),
7911    ]
7912    helpers.convert_mapping_to_xml(osb, data, mapping, fail_required=True)
7913
7914
7915def openshift_deploy_canceller(registry, xml_parent, data):
7916    r"""yaml: openshift-deploy-canceller
7917    This action is intended to provide cleanup for any OpenShift deployments
7918    left running when the Job completes; this step will allow you to perform
7919    the equivalent of a oc deploy --cancel for the provided deployment config.
7920
7921    Requires the Jenkins :jenkins-plugins:`OpenShift Pipeline Plugin
7922    <openshift-pipeline>`.
7923
7924    :arg str api-url: this would be the value you specify if you leverage the
7925        --server option on the OpenShift `oc` command.
7926        (default '\https://openshift.default.svc.cluster.local')
7927    :arg str dep-cfg: The value here should be whatever was the output
7928        form `oc project` when you created the BuildConfig you want to run a
7929        Build on (default frontend)
7930    :arg str namespace: If you run `oc get bc` for the project listed in
7931        "namespace", that is the value you want to put here. (default 'test')
7932    :arg str auth-token: The value here is what you supply with the --token
7933        option when invoking the OpenShift `oc` command. (default '')
7934    :arg bool verbose: This flag is the toggle for
7935        turning on or off detailed logging in this plug-in. (default false)
7936
7937    Full Example:
7938
7939        .. literalinclude::
7940            ../../tests/publishers/fixtures/openshift-deploy-canceller001.yaml
7941           :language: yaml
7942
7943    Minimal Example:
7944
7945        .. literalinclude::
7946            ../../tests/publishers/fixtures/openshift-deploy-canceller002.yaml
7947           :language: yaml
7948    """
7949
7950    osb = XML.SubElement(
7951        xml_parent, "com.openshift.jenkins.plugins.pipeline." "OpenShiftDeployCanceller"
7952    )
7953    mapping = [
7954        # option, xml name, default value
7955        ("api-url", "apiURL", "https://openshift.default.svc.cluster.local"),
7956        ("dep-cfg", "depCfg", "frontend"),
7957        ("namespace", "namespace", "test"),
7958        ("auth-token", "authToken", ""),
7959        ("verbose", "verbose", False),
7960    ]
7961    helpers.convert_mapping_to_xml(osb, data, mapping, fail_required=True)
7962
7963
7964def github_pull_request_merge(registry, xml_parent, data):
7965    """yaml: github-pull-request-merge
7966    This action merges the pull request that triggered the build (see the
7967    github pull request trigger)
7968
7969    Requires the Jenkins :jenkins-plugins:`GitHub pull request builder plugin
7970    <ghprb>`.
7971
7972
7973    :arg bool only-admins-merge: if `true` only administrators can merge the
7974        pull request, (default false)
7975    :arg bool disallow-own-code: if `true` will allow merging your own pull
7976        requests, in opposite to needing someone else to trigger the merge.
7977        (default false)
7978    :arg str merge-comment: Comment to set on the merge commit (default '')
7979    :arg bool fail-on-non-merge: fail the job if the merge was unsuccessful
7980        (default false)
7981    :arg bool delete-on-merge: Delete the branch of the pull request on
7982        successful merge (default false)
7983
7984    Full Example:
7985
7986        .. literalinclude::
7987            ../../tests/publishers/fixtures/github-pull-request-merge001.yaml
7988           :language: yaml
7989
7990    Minimal Example:
7991
7992        .. literalinclude::
7993            ../../tests/publishers/fixtures/github-pull-request-merge002.yaml
7994           :language: yaml
7995    """
7996
7997    osb = XML.SubElement(
7998        xml_parent, "org.jenkinsci.plugins.ghprb.GhprbPullRequestMerge"
7999    )
8000    mapping = [
8001        # option, xml name, default value
8002        ("only-admins-merge", "onlyAdminsMerge", "false"),
8003        ("disallow-own-code", "disallowOwnCode", "false"),
8004        ("merge-comment", "mergeComment", ""),
8005        ("fail-on-non-merge", "failOnNonMerge", "false"),
8006        ("delete-on-merge", "deleteOnMerge", "false"),
8007    ]
8008
8009    helpers.convert_mapping_to_xml(osb, data, mapping, fail_required=True)
8010
8011
8012def chuck_norris(registry, xml_parent, data):
8013    """yaml: chuck-norris
8014    Displays a picture of Chuck Norris (instead of Jenkins the butler) and a
8015    random Chuck Norris 'The Programmer' fact on each build page.
8016
8017    Requires the Jenkins :jenkins-plugins:`ChuckNorris Plugin <chucknorris>`.
8018
8019    Example:
8020
8021        .. literalinclude:: /../../tests/publishers/fixtures/chuck-norris.yaml
8022           :language: yaml
8023    """
8024
8025    chuck = XML.SubElement(
8026        xml_parent, "hudson.plugins.chucknorris.CordellWalkerRecorder"
8027    )
8028    return XML.SubElement(chuck, "factGenerator")
8029
8030
8031def publishers_from(registry, xml_parent, data):
8032    """yaml: publishers-from
8033    Use publishers from another project.
8034    Requires the Jenkins :jenkins-plugins:`Template Project Plugin
8035    <template-project>`.
8036
8037    :arg str project-name: The name of the other project.
8038
8039    Example:
8040
8041    .. literalinclude:: ../../tests/publishers/fixtures/publishers-from.yaml
8042       :language: yaml
8043    """
8044    pbs = XML.SubElement(xml_parent, "hudson.plugins.templateproject.ProxyPublisher")
8045    mapping = [("project-name", "projectName", None)]
8046    helpers.convert_mapping_to_xml(pbs, data, mapping, fail_required=True)
8047
8048
8049def tasks(registry, xml_parent, data):
8050    """yaml: tasks
8051
8052    Scans the workspace files for open tasks and generates a trend report.
8053
8054    Requires the Jenkins Task Scanner Plugin
8055    (https://github.com/jenkinsci/tasks-plugin).
8056
8057    :arg list files-to-scan: Fileset includes setting that specifies the
8058        workspace files to scan for tasks, such as ``**/*.java``. Basedir of
8059        the fileset is the workspace root. (default '``**/*.java``')
8060    :arg list files-to-exclude: Fileset excludes setting that specifies the
8061        workspace files to exclude scanning for tasks, such as library source
8062        files. Basedir of the fileset is the workspace root. (default '')
8063    :arg list tasks-tags-high: Tags identifiers for high priority that should
8064        be looked for in the workspace files. Only alphanumerical characters
8065        are allowed as tags as these strings are pasted into a regular
8066        expression. (default '')
8067    :arg list tasks-tags-normal: Tags identifiers for normal priority that
8068        should be looked for in the workspace files. Only alphanumerical
8069        characters are allowed as tags as these strings are pasted into a
8070        regular expression. (default '')
8071    :arg list tasks-tags-low: Tags identifiers for low priority that should be
8072        looked for in the workspace files. Only alphanumerical characters are
8073        allowed as tags as these strings are pasted into a regular expression.
8074        (default '')
8075    :arg bool ignore-case: Ignore the case of the the tag identifiers. (default
8076        false)
8077    :arg bool regular-expression: Treat the tag identifiers as regular
8078        expression. Note that the regular expression must contain two capturing
8079        groups, the first one is interpreted as tag name, the second one as
8080        message. An example of such a regular expression would be
8081        ``^.*(TODO(?:[0-9]*))(.*)$``. (default false)
8082    :arg bool run-always: By default, this plug-in runs only for stable or
8083        unstable builds, but not for failed builds. If this plug-in should run
8084        even for failed builds then activate this check box. (default false)
8085    :arg bool detect-module: Determines if Ant or Maven modules should be
8086        detected for all files that contain warnings. Activating this option
8087        may increase your build time since the detector scans the whole
8088        workspace for ``build.xml`` or ``pom.xml`` files in order to assign the
8089        correct module names. (default false)
8090    :arg int health-thresholds-100: Configure the upper thresholds for the
8091        build health. If left empty then no health report is created. If the
8092        actual number of warnings is between the provided thresholds then the
8093        build health is interpolated. (default '')
8094    :arg str health-thresholds-0: Configure the lower thresholds for the build
8095        health. If left empty then no health report is created. If the actual
8096        number of warnings is between the provided thresholds then the build
8097        health is interpolated. (default '')
8098    :arg str health-priorities: Determines which warning priorities should be
8099        considered when evaluating the build health. Can be ``high`` (only
8100        priority high), ``normal`` (priorities high and normal) or ``low`` (all
8101        priorities). (default 'low')
8102    :arg dict status-thresholds: Configure the build status and health. If the
8103        number of total or new warnings is greater than one of these thresholds
8104        then a build is considered as unstable or failed, respectively. I.e., a
8105        value of 0 means that the build status is changed if there is at least
8106        one warning found. Leave this field empty if the state of the build
8107        should not depend on the number of warnings. Note that for new
8108        warnings, you need to enable the next option
8109        (``compute-new-warnings``).
8110
8111        :status-thresholds:
8112
8113            * **unstable-total-all** (`str`): Total number for all priorities,
8114              unstable threshold (default '')
8115            * **unstable-total-high** (`str`): Total number for high priority,
8116              unstable threshold (default '')
8117            * **unstable-total-normal** (`str`): Total number for normal
8118              priority, unstable threshold (default '')
8119            * **unstable-total-low** (`str`): Total number for low priority,
8120              unstable threshold (default '')
8121            * **failed-total-all** (`str`): Total number for all priorities,
8122              failure threshold (default '')
8123            * **failed-total-high** (`str`): Total number for high priority,
8124              failure threshold (default '')
8125            * **failed-total-normal** (`str`): Total number for normal
8126              priority, failure threshold (default '')
8127            * **failed-total-low** (`str`): Total number for low priority,
8128              failure threshold (default '')
8129            * **unstable-new-all** (`str`): New number for all priorities,
8130              unstable threshold (default '')
8131            * **unstable-new-high** (`str`): New number for high priority,
8132              unstable threshold (default '')
8133            * **unstable-new-normal** (`str`): New number for normal priority,
8134              unstable threshold (default '')
8135            * **unstable-new-low** (`str`): New number for low priority,
8136              unstable threshold (default '')
8137            * **failed-new-all** (`str`): New number for all priorities,
8138              failure threshold (default '')
8139            * **failed-new-high** (`str`): New number for high priority,
8140              failure threshold (default '')
8141            * **failed-new-normal** (`str`): New number for normal priority,
8142              failure threshold (default '')
8143            * **failed-new-low** (`str`): New number for low priority, failure
8144              threshold (default '')
8145
8146    :arg bool compute-new-warnings: Compute new warnings (based on the last
8147        successful build unless another reference build is chosen below).
8148        (default false)
8149    :arg bool use-delta: If set the number of new warnings is computed by
8150        subtracting the total number of warnings of the reference build from
8151        the total number of warnings of the current build. This may lead to
8152        wrong results if you have both fixed and new warnings in a build. If
8153        unset the number of new warnings is computed by a more sophisticated
8154        algorithm: instead of using totals an asymmetric set difference of the
8155        warnings in the current build and the warnings in the reference build
8156        is used. This will find all new warnings even if the number of total
8157        warnings has decreased. Note that sometimes false positives will be
8158        reported due to minor changes in a warning (e.g. refactoring of
8159        variables or method names). It is recommended to uncheck this option in
8160        order to get the most accurate results for new warnings. Depends on
8161        ``compute-new-warnings`` option. (default false)
8162    :arg bool use-prev-build-as-ref: If set the number of new warnings will
8163        always be computed based on the previous build, even if that build is
8164        unstable (due to a violated warning threshold). Otherwise the last
8165        build that did not violate any given threshold will be used as
8166        reference. It is recommended to uncheck this option if the plug-in
8167        should ensure that all new warnings will be finally fixed in subsequent
8168        builds. Depends on ``compute-new-warnings`` option. (default false)
8169    :arg bool only-use-stable-as-ref: Use the last stable build as the
8170        reference to compute the number of new warnings against. This allows
8171        you to ignore interim unstable builds for which the number of warnings
8172        decreased. Note that the last stable build is evaluated only by
8173        inspecting the unit test failures. The static analysis results are not
8174        considered. Depends on ``compute-new-warnings`` option. (default false)
8175    :arg str default-encoding: Default encoding when parsing or showing files.
8176        Leave this field empty to use the default encoding of the platform.
8177        (default '')
8178
8179    Minimal Example:
8180
8181    .. literalinclude:: /../../tests/publishers/fixtures/tasks-minimal.yaml
8182       :language: yaml
8183
8184    Full Example:
8185
8186    .. literalinclude:: /../../tests/publishers/fixtures/tasks-full.yaml
8187       :language: yaml
8188    """
8189
8190    root = XML.SubElement(xml_parent, "hudson.plugins.tasks.TasksPublisher")
8191    root.set("plugin", "tasks")
8192
8193    if "files-to-scan" in data:
8194        XML.SubElement(root, "pattern").text = str(",".join(data["files-to-scan"]))
8195
8196    if "files-to-exclude" in data:
8197        XML.SubElement(root, "excludePattern").text = str(
8198            ",".join(data["files-to-exclude"])
8199        )
8200
8201    for prio in ["high", "normal", "low"]:
8202        if "tasks-tags-" + prio in data:
8203            XML.SubElement(root, prio).text = str(",".join(data["tasks-tags-" + prio]))
8204
8205    # on the UI, we can see compute-new-warnings but we need the opposite (XML)
8206    if "compute-new-warnings" in data and data["compute-new-warnings"]:
8207        XML.SubElement(root, "dontComputeNew").text = "false"
8208    else:
8209        XML.SubElement(root, "dontComputeNew").text = "true"
8210
8211    # Two parameters we cannot modify from the UI
8212    XML.SubElement(root, "pluginName").text = "[TASKS] "
8213    XML.SubElement(root, "doNotResolveRelativePaths").text = "false"
8214
8215    mappings = [
8216        ("ignore-case", "ignoreCase", False),
8217        ("regular-expression", "asRegexp", False),
8218        ("run-always", "canRunOnFailed", False),
8219        ("detect-module", "shouldDetectModules", False),
8220        ("health-thresholds-100", "healthy", ""),
8221        ("health-thresholds-0", "unHealthy", ""),
8222        ("health-priorities", "thresholdLimit", "low"),
8223        ("use-delta", "useDeltaValues", False),
8224        ("use-prev-build-as-ref", "usePreviousBuildAsReference", False),
8225        ("only-use-stable-as-ref", "useStableBuildAsReference", False),
8226        ("default-encoding", "defaultEncoding", ""),
8227    ]
8228    helpers.convert_mapping_to_xml(root, data, mappings, fail_required=True)
8229
8230    thrsh_xml = XML.SubElement(root, "thresholds")
8231    thrsh_xml.set("plugin", "analysis-core")
8232    thrsh_data = data.get("status-thresholds", {})
8233    thrsh_mappings = [
8234        ("unstable-total-all", "unstableTotalAll", ""),
8235        ("unstable-total-high", "unstableTotalHigh", ""),
8236        ("unstable-total-normal", "unstableTotalNormal", ""),
8237        ("unstable-total-low", "unstableTotalLow", ""),
8238        ("unstable-new-all", "unstableNewAll", ""),
8239        ("unstable-new-high", "unstableNewHigh", ""),
8240        ("unstable-new-normal", "unstableNewNormal", ""),
8241        ("unstable-new-low", "unstableNewLow", ""),
8242        ("failed-total-all", "failedTotalAll", ""),
8243        ("failed-total-high", "failedTotalHigh", ""),
8244        ("failed-total-normal", "failedTotalNormal", ""),
8245        ("failed-total-low", "failedTotalLow", ""),
8246        ("failed-new-all", "failedNewAll", ""),
8247        ("failed-new-high", "failedNewHigh", ""),
8248        ("failed-new-normal", "failedNewNormal", ""),
8249        ("failed-new-low", "failedNewLow", ""),
8250    ]
8251    helpers.convert_mapping_to_xml(
8252        thrsh_xml, thrsh_data, thrsh_mappings, fail_required=True
8253    )
8254
8255
8256def packer(registry, xml_parent, data):
8257    """yaml: packer
8258    This plugin allows for a job to publish an image generated Packer
8259    Requires the Jenkins :jenkins-plugins:`Packer Plugin <packer>`.
8260
8261    :arg str name: Name of the packer installation (required)
8262    :arg str json-template: Path to a Packer JSON template file (default '')
8263    :arg str json-template-text: Text of Packer JSON template (default '')
8264    :arg str add-params: Specify which additional parameters
8265        to pass to packer (default '')
8266    :arg bool use-debug: adds -debug argument when packer executes
8267        (default false)
8268    :arg str change-dir: If set, the current directory will be changed
8269        to this before starting packer (default '')
8270    :arg str template-mode: Packer template option used (default global)
8271
8272        :template-mode values:
8273            * **global**
8274            * **file**
8275            * **text**
8276    :arg list file-entries: File entries for the packer
8277        configuration (default [])
8278    :arg str variable-name: Variable name for a file to used in the
8279        configuration JSON (default '')
8280    :arg str contents: File contents of the configuration JSON (default '')
8281
8282    Minimal Example:
8283
8284    .. literalinclude::
8285        /../../tests/publishers/fixtures/packer-minimal.yaml
8286       :language: yaml
8287
8288    Full Example:
8289
8290    .. literalinclude::
8291        /../../tests/publishers/fixtures/packer-full.yaml
8292        :language: yaml
8293    """
8294
8295    root = XML.SubElement(
8296        xml_parent, "biz.neustar.jenkins.plugins.packer.PackerPublisher"
8297    )
8298
8299    template_valid_types = ["global", "file", "text"]
8300    mapping = [
8301        ("name", "name", None),
8302        ("json-template", "jsonTemplate", ""),
8303        ("json-template-text", "jsonTemplateText", ""),
8304        ("add-params", "params", ""),
8305        ("use-debug", "useDebug", False),
8306        ("change-dir", "changeDir", ""),
8307        ("template-mode", "templateMode", "global", template_valid_types),
8308    ]
8309    helpers.convert_mapping_to_xml(root, data, mapping, fail_required=True)
8310
8311    format_dict = {
8312        "packer-file-entry": "biz.neustar.jenkins.plugins.packer.PackerFileEntry"
8313    }
8314    if "file-entries" in data:
8315        file_entries_tag = XML.SubElement(root, "fileEntries")
8316        for file_entries in data["file-entries"]:
8317            for file, params in file_entries.items():
8318                packer_file_entry_tag = XML.SubElement(
8319                    file_entries_tag, format_dict.get("packer-file-entry")
8320                )
8321                XML.SubElement(packer_file_entry_tag, "varFileName").text = params.get(
8322                    "variable-name", ""
8323                )
8324                XML.SubElement(packer_file_entry_tag, "contents").text = params.get(
8325                    "contents", ""
8326                )
8327
8328
8329class Publishers(jenkins_jobs.modules.base.Base):
8330    sequence = 70
8331
8332    component_type = "publisher"
8333    component_list_type = "publishers"
8334
8335    def gen_xml(self, xml_parent, data):
8336        if data.get("project-type", "freestyle") == "pipeline":
8337            logger.debug("Publishers skipped for Pipeline job")
8338            return
8339
8340        publishers = XML.SubElement(xml_parent, "publishers")
8341
8342        for action in data.get("publishers", []):
8343            self.registry.dispatch("publisher", publishers, action)
8344