1# Copyright 2012 Hewlett-Packard Development Company, L.P.
2# Copyright 2020 Liberty Global B.V.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16
17"""
18The Properties module supplies a wide range of options that are
19implemented as Jenkins job properties.
20
21**Component**: properties
22  :Macro: property
23  :Entry Point: jenkins_jobs.properties
24
25Example::
26
27  job:
28    name: test_job
29
30    properties:
31      - github:
32          url: https://github.com/openstack-infra/jenkins-job-builder/
33"""
34
35import logging
36import pkg_resources
37import xml.etree.ElementTree as XML
38
39from jenkins_jobs.errors import InvalidAttributeError
40from jenkins_jobs.errors import JenkinsJobsException
41from jenkins_jobs.errors import MissingAttributeError
42from jenkins_jobs.errors import AttributeConflictError
43import jenkins_jobs.modules.base
44import jenkins_jobs.modules.helpers as helpers
45
46
47def builds_chain_fingerprinter(registry, xml_parent, data):
48    """yaml: builds-chain-fingerprinter
49    Builds chain fingerprinter.
50
51    Requires the Jenkins :jenkins-github:`Builds chain fingerprinter Plugin
52    <builds-chain-fingerprinter-plugin>`.
53
54    :arg bool per-builds-chain: enable builds hierarchy fingerprinting
55        (default false)
56    :arg bool per-job-chain: enable jobs hierarchy fingerprinting
57        (default false)
58
59    Example:
60
61    .. literalinclude:: /../../tests/properties/fixtures/fingerprinter.yaml
62       :language: yaml
63    """
64    fingerprinter = XML.SubElement(
65        xml_parent,
66        "org.jenkinsci.plugins."
67        "buildschainfingerprinter."
68        "AutomaticFingerprintJobProperty",
69    )
70    mapping = [
71        ("per-builds-chain", "isPerBuildsChainEnabled", False),
72        ("per-job-chain", "isPerJobsChainEnabled", False),
73    ]
74    helpers.convert_mapping_to_xml(fingerprinter, data, mapping, fail_required=True)
75
76
77def ownership(registry, xml_parent, data):
78    """yaml: ownership
79    Plugin provides explicit ownership for jobs and slave nodes.
80
81    Requires the Jenkins :jenkins-plugins:`Ownership Plugin <ownership>`.
82
83    :arg bool enabled: whether ownership enabled (default : true)
84    :arg str owner: the owner of job
85    :arg list co-owners: list of job co-owners
86
87    Example:
88
89    .. literalinclude:: /../../tests/properties/fixtures/ownership.yaml
90       :language: yaml
91    """
92    ownership_plugin = XML.SubElement(
93        xml_parent,
94        "com.synopsys.arc.jenkins.plugins.ownership.jobs.JobOwnerJobProperty",
95    )
96    ownership = XML.SubElement(ownership_plugin, "ownership")
97    owner = str(data.get("enabled", True)).lower()
98    XML.SubElement(ownership, "ownershipEnabled").text = owner
99
100    XML.SubElement(ownership, "primaryOwnerId").text = data.get("owner")
101
102    coownersIds = XML.SubElement(ownership, "coownersIds")
103    for coowner in data.get("co-owners", []):
104        XML.SubElement(coownersIds, "string").text = coowner
105
106
107def promoted_build(registry, xml_parent, data):
108    """yaml: promoted-build
109    Marks a build for promotion. A promotion process with an identical
110    name must be created via the web interface in the job in order for the job
111    promotion to persist. Promotion processes themselves cannot be configured
112    by jenkins-jobs due to the separate storage of plugin configuration files.
113
114    Requires the Jenkins :jenkins-plugins:`Promoted Builds Plugin
115    <promoted-builds>`.
116
117    :arg list names: the promoted build names (optional)
118
119    Example:
120
121    .. literalinclude:: /../../tests/properties/fixtures/promoted_build.yaml
122       :language: yaml
123    """
124    promoted = XML.SubElement(
125        xml_parent, "hudson.plugins.promoted__builds." "JobPropertyImpl"
126    )
127    names = data.get("names", [])
128    if names:
129        active_processes = XML.SubElement(promoted, "activeProcessNames")
130        for n in names:
131            XML.SubElement(active_processes, "string").text = str(n)
132
133
134def gitbucket(parser, xml_parent, data):
135    """yaml: gitbucket
136    Integrate GitBucket to Jenkins.
137
138    Requires the Jenkins :jenkins-plugins:`GitBucket Plugin <gitbucket>`.
139
140    :arg str url: GitBucket URL to issue (required)
141    :arg bool link-enabled: Enable hyperlink to issue (default false)
142
143    Minimal Example:
144
145    .. literalinclude:: /../../tests/properties/fixtures/gitbucket-minimal.yaml
146       :language: yaml
147
148    Full Example:
149
150    .. literalinclude:: /../../tests/properties/fixtures/gitbucket-full.yaml
151       :language: yaml
152    """
153    gitbucket = XML.SubElement(
154        xml_parent, "org.jenkinsci.plugins.gitbucket.GitBucketProjectProperty"
155    )
156    gitbucket.set("plugin", "gitbucket")
157
158    mapping = [("url", "url", None), ("link-enabled", "linkEnabled", False)]
159    helpers.convert_mapping_to_xml(gitbucket, data, mapping, fail_required=True)
160
161
162def github(registry, xml_parent, data):
163    """yaml: github
164    Sets the GitHub URL for the project.
165
166    :arg str url: the GitHub URL (required)
167    :arg str display-name: This value will be used as context name for commit
168        status if status builder or status publisher is defined for this
169        project. (>= 1.14.1) (default '')
170
171    Minimal Example:
172
173    .. literalinclude:: /../../tests/properties/fixtures/github-minimal.yaml
174       :language: yaml
175
176    Full Example:
177
178    .. literalinclude:: /../../tests/properties/fixtures/github-full.yaml
179       :language: yaml
180    """
181    github = XML.SubElement(
182        xml_parent, "com.coravy.hudson.plugins.github.GithubProjectProperty"
183    )
184    github.set("plugin", "github")
185
186    mapping = [("url", "projectUrl", None), ("display-name", "displayName", "")]
187    helpers.convert_mapping_to_xml(github, data, mapping, fail_required=True)
188
189
190def gitlab(registry, xml_parent, data):
191    """yaml: gitlab
192    Sets the GitLab connection for the project. Configured via Jenkins Global
193    Configuration.
194
195    Requires the Jenkins :jenkins-plugins:`GitLab Plugin <gitlab-plugin>`.
196
197    :arg str connection: the GitLab connection name (required)
198
199    Example:
200
201    .. literalinclude:: /../../tests/properties/fixtures/gitlab.yaml
202       :language: yaml
203    """
204    gitlab = XML.SubElement(
205        xml_parent,
206        "com.dabsquared.gitlabjenkins.connection." "GitLabConnectionProperty",
207    )
208    mapping = [("connection", "gitLabConnection", None)]
209    helpers.convert_mapping_to_xml(gitlab, data, mapping, fail_required=True)
210
211
212def gitlab_logo(registry, xml_parent, data):
213    """yaml: gitlab-logo
214    Configures the GitLab-Logo Plugin.
215
216    Requires the Jenkins :jenkins-plugins:`GitLab Logo Plugin
217    <gitlab-logo>`.
218
219    :arg str repository-name: the GitLab repository name (required)
220
221    Example:
222
223    .. literalinclude:: /../../tests/properties/fixtures/gitlab-logo.yaml
224       :language: yaml
225    """
226    logo = XML.SubElement(
227        xml_parent, "org.jenkinsci.plugins.gitlablogo." "GitlabLogoProperty"
228    )
229    mapping = [("repository-name", "repositoryName", None)]
230    helpers.convert_mapping_to_xml(logo, data, mapping, fail_required=True)
231
232
233def gogs(registry, xml_parent, data):
234    """yaml: gogs
235    Sets the Gogs webhook properties for the project.
236
237    Requires the Jenkins :jenkins-plugins:`Gogs Plugin <gogs-webhook>`.
238
239    :arg str secret: webhook secret (default '')
240    :arg str branch-filter: filter which needs to match to trigger a job (default '')
241
242    Minimal Example:
243
244    .. literalinclude:: /../../tests/properties/fixtures/gogs-minimal.yaml
245       :language: yaml
246
247    Full Example:
248
249    .. literalinclude:: /../../tests/properties/fixtures/gogs-full.yaml
250       :language: yaml
251    """
252    gogs = XML.SubElement(xml_parent, "org.jenkinsci.plugins.gogs.GogsProjectProperty")
253    mapping = [("branch-filter", "gogsBranchFilter", ""), ("secret", "gogsSecret", "")]
254    helpers.convert_mapping_to_xml(gogs, data, mapping)
255
256
257def naginator_opt_out(registry, xml_parent, data):
258    """yaml: naginator-opt-out
259    Lets you opt-out so no rebuild option for Naginator is added.
260
261    Requires the Jenkins :jenkins-plugins:`Naginator Plugin <naginator>`.
262
263    :arg bool opt-out: disables the rebuild option if True (default False).
264
265    Example:
266
267    .. literalinclude:: /../../tests/properties/fixtures/naginator-opt-out002.yaml
268       :language: yaml
269    """
270
271    opt_out = XML.SubElement(
272        xml_parent, "com.chikli.hudson.plugin.naginator." "NaginatorOptOutProperty"
273    )
274    mapping = [("opt-out", "optOut", False)]
275    helpers.convert_mapping_to_xml(opt_out, data, mapping, fail_required=True)
276
277
278def disk_usage(registry, xml_parent, data):
279    """yaml: disk-usage
280    Enables the Disk Usage Plugin.
281
282    Requires the Jenkins :jenkins-plugins:`Disk Usage Plugin <disk-usage>`.
283
284    Example:
285
286    .. literalinclude:: /../../tests/properties/fixtures/disk-usage.yaml
287       :language: yaml
288    """
289    XML.SubElement(xml_parent, "hudson.plugins.disk__usage." "DiskUsageProperty")
290
291
292def least_load(registry, xml_parent, data):
293    """yaml: least-load
294    Enables the Least Load Plugin.
295
296    Requires the Jenkins :jenkins-plugins:`Least Load Plugin <leastload>`.
297
298    :arg bool disabled: whether or not leastload is disabled (default true)
299
300    Example:
301
302    .. literalinclude:: /../../tests/properties/fixtures/least-load002.yaml
303       :language: yaml
304    """
305    least = XML.SubElement(
306        xml_parent,
307        "org.bstick12.jenkinsci.plugins.leastload." "LeastLoadDisabledProperty",
308    )
309    mapping = [("disabled", "leastLoadDisabled", True)]
310    helpers.convert_mapping_to_xml(least, data, mapping, fail_required=True)
311
312
313def throttle(registry, xml_parent, data):
314    """yaml: throttle
315    Throttles the number of builds for this job.
316
317    Requires the Jenkins :jenkins-plugins:`Throttle Concurrent Builds Plugin
318    <throttle-concurrents>`.
319
320    :arg str option: throttle `project` (throttle the project alone)
321         or `category` (throttle the project as part of one or more categories)
322    :arg int max-per-node: max concurrent builds per node (default 0)
323    :arg int max-total: max concurrent builds (default 0)
324    :arg bool enabled: whether throttling is enabled (default true)
325    :arg list categories: multiproject throttle categories
326    :arg bool matrix-builds: throttle matrix master builds (default true)
327    :arg bool matrix-configs: throttle matrix config builds (default false)
328    :arg str parameters-limit: prevent jobs with matching parameters from
329         running concurrently (default false)
330    :arg list parameters-check-list: Comma-separated list of parameters
331        to use when comparing jobs (optional)
332
333    Example:
334
335    .. literalinclude:: /../../tests/properties/fixtures/throttle001.yaml
336       :language: yaml
337    """
338    throttle = XML.SubElement(
339        xml_parent, "hudson.plugins.throttleconcurrents." "ThrottleJobProperty"
340    )
341    mapping = [
342        ("max-per-node", "maxConcurrentPerNode", "0"),
343        ("max-total", "maxConcurrentTotal", "0"),
344        ("enabled", "throttleEnabled", True),
345    ]
346    helpers.convert_mapping_to_xml(throttle, data, mapping, fail_required=True)
347    cat = data.get("categories", [])
348    if cat:
349        cn = XML.SubElement(throttle, "categories")
350        for c in cat:
351            XML.SubElement(cn, "string").text = str(c)
352
353    options_list = ("category", "project")
354    option = data.get("option")
355    if option not in options_list:
356        raise InvalidAttributeError("option", option, options_list)
357    mapping = [
358        ("", "throttleOption", option),
359        ("", "configVersion", "1"),
360        ("parameters-limit", "limitOneJobWithMatchingParams", False),
361    ]
362    helpers.convert_mapping_to_xml(throttle, data, mapping, fail_required=True)
363
364    matrixopt = XML.SubElement(throttle, "matrixOptions")
365    mapping = [
366        ("matrix-builds", "throttleMatrixBuilds", True),
367        ("matrix-configs", "throttleMatrixConfigurations", False),
368    ]
369    helpers.convert_mapping_to_xml(matrixopt, data, mapping, fail_required=True)
370
371    params_to_use = data.get("parameters-check-list", [])
372    XML.SubElement(throttle, "paramsToUseForLimit").text = ",".join(params_to_use)
373
374
375def branch_api(registry, xml_parent, data):
376    """yaml: branch-api
377    Enforces a minimum time between builds based on the desired maximum rate.
378
379    Requires the Jenkins :jenkins-plugins:`Branch API Plugin <branch-api>`.
380
381    :arg int number-of-builds: The maximum number of builds allowed within
382        the specified time period. (default 1)
383    :arg str time-period: The time period within which the maximum number
384        of builds will be enforced. (default 'Hour')
385
386        :valid values: **Second** **Minute** **Hour**, **Day**, **Week**, **Month**, **Year**
387    :arg bool skip-rate-limit: Permit user triggered builds to
388        skip the rate limit (default false)
389
390    Minimal Example:
391
392        .. literalinclude::
393           /../../tests/properties/fixtures/branch-api-minimal.yaml
394           :language: yaml
395
396    Full example:
397
398        .. literalinclude::
399           /../../tests/properties/fixtures/branch-api-full.yaml
400           :language: yaml
401    """
402    branch = XML.SubElement(
403        xml_parent, "jenkins.branch." "RateLimitBranchProperty_-JobPropertyImpl"
404    )
405    branch.set("plugin", "branch-api")
406
407    valid_time_periods = ["Second", "Minute", "Hour", "Day", "Week", "Month", "Year"]
408
409    mapping = [
410        ("time-period", "durationName", "Hour", valid_time_periods),
411        ("number-of-builds", "count", 1),
412        ("skip-rate-limit", "userBoost", False),
413    ]
414    helpers.convert_mapping_to_xml(branch, data, mapping, fail_required=True)
415
416
417def sidebar(registry, xml_parent, data):
418    """yaml: sidebar
419    Allows you to add links in the sidebar.
420    Requires the Jenkins :jenkins-plugins:`Sidebar-Link Plugin <sidebar-link>`.
421
422    :arg str url: url to link to (optional)
423    :arg str text: text for the link (optional)
424    :arg str icon: path to icon (optional)
425
426    Example:
427
428    .. literalinclude:: /../../tests/properties/fixtures/sidebar02.yaml
429       :language: yaml
430    """
431    sidebar = xml_parent.find("hudson.plugins.sidebar__link.ProjectLinks")
432    if sidebar is None:
433        sidebar = XML.SubElement(
434            xml_parent, "hudson.plugins.sidebar__link.ProjectLinks"
435        )
436        links = XML.SubElement(sidebar, "links")
437    else:
438        links = sidebar.find("links")
439    action = XML.SubElement(links, "hudson.plugins.sidebar__link.LinkAction")
440    mapping = [("url", "url", ""), ("text", "text", ""), ("icon", "icon", "")]
441    helpers.convert_mapping_to_xml(action, data, mapping, fail_required=True)
442
443
444def inject(registry, xml_parent, data):
445    """yaml: inject
446    Allows you to inject environment variables into the build.
447
448    Requires the Jenkins :jenkins-plugins:`EnvInject Plugin <envinject>`.
449
450    :arg str properties-file: file to read with properties (optional)
451    :arg str properties-content: key=value properties (optional)
452    :arg str script-file: file with script to run (optional)
453    :arg str script-content: script to run (optional)
454    :arg str groovy-content: groovy script to run (optional)
455    :arg bool groovy-sandbox: run groovy script in sandbox (default false)
456    :arg bool load-from-master: load files from master (default false)
457    :arg bool enabled: injection enabled (default true)
458    :arg bool keep-system-variables: keep system variables (default true)
459    :arg bool keep-build-variables: keep build variable (default true)
460    :arg bool override-build-parameters: override build parameters
461        (default false)
462
463    Example:
464
465    .. literalinclude:: /../../tests/properties/fixtures/inject001.yaml
466       :language: yaml
467
468    """
469    inject = XML.SubElement(xml_parent, "EnvInjectJobProperty")
470    info = XML.SubElement(inject, "info")
471
472    mapping = [
473        ("properties-file", "propertiesFilePath", None),
474        ("properties-content", "propertiesContent", None),
475        ("script-file", "scriptFilePath", None),
476        ("script-content", "scriptContent", None),
477        ("load-from-master", "loadFilesFromMaster", False),
478    ]
479    helpers.convert_mapping_to_xml(info, data, mapping, fail_required=False)
480
481    # determine version of plugin
482    plugin_info = registry.get_plugin_info("Groovy")
483    version = pkg_resources.parse_version(plugin_info.get("version", "0"))
484
485    if version >= pkg_resources.parse_version("2.0.0"):
486        secure_groovy_script = XML.SubElement(info, "secureGroovyScript")
487        mapping = [
488            ("groovy-content", "script", None),
489            ("groovy-sandbox", "sandbox", False),
490        ]
491        helpers.convert_mapping_to_xml(
492            secure_groovy_script, data, mapping, fail_required=False
493        )
494    else:
495        mapping = [("groovy-content", "groovyScriptContent", None)]
496        helpers.convert_mapping_to_xml(info, data, mapping, fail_required=False)
497
498    mapping = [
499        ("enabled", "on", True),
500        ("keep-system-variables", "keepJenkinsSystemVariables", True),
501        ("keep-build-variables", "keepBuildVariables", True),
502        ("override-build-parameters", "overrideBuildParameters", False),
503    ]
504    helpers.convert_mapping_to_xml(inject, data, mapping, fail_required=True)
505
506
507def authenticated_build(registry, xml_parent, data):
508    """yaml: authenticated-build
509    Specifies an authorization matrix where only authenticated users
510    may trigger a build.
511
512    .. deprecated:: 0.1.0. Please use :ref:`authorization <authorization>`.
513
514    Example:
515
516    .. literalinclude::
517        /../../tests/properties/fixtures/authenticated_build.yaml
518       :language: yaml
519
520    """
521    # TODO: generalize this
522    security = XML.SubElement(
523        xml_parent, "hudson.security." "AuthorizationMatrixProperty"
524    )
525    XML.SubElement(
526        security, "permission"
527    ).text = "hudson.model.Item.Build:authenticated"
528
529
530def authorization(registry, xml_parent, data, job_data):
531    """yaml: authorization
532    Specifies an authorization matrix
533
534    .. _authorization:
535
536    :arg list <name>: `<name>` is the name of the group or user, containing
537        the list of rights to grant.
538
539       :<name> rights:
540            * **credentials-create**
541            * **credentials-delete**
542            * **credentials-manage-domains**
543            * **credentials-update**
544            * **credentials-view**
545            * **job-build**
546            * **job-cancel**
547            * **job-configure**
548            * **job-delete**
549            * **job-discover**
550            * **job-extended-read**
551            * **job-move**
552            * **job-read**
553            * **job-status**
554            * **job-workspace**
555            * **ownership-jobs**
556            * **run-delete**
557            * **run-replay**
558            * **run-update**
559            * **scm-tag**
560
561    Example:
562
563    .. literalinclude:: /../../tests/properties/fixtures/authorization.yaml
564       :language: yaml
565    """
566
567    is_a_folder = job_data.get("project-type") in ("folder", "multibranch")
568
569    credentials = "com.cloudbees.plugins.credentials.CredentialsProvider."
570    ownership = "com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin."
571
572    mapping = {
573        "credentials-create": "".join((credentials, "Create")),
574        "credentials-delete": "".join((credentials, "Delete")),
575        "credentials-manage-domains": "".join((credentials, "ManageDomains")),
576        "credentials-update": "".join((credentials, "Update")),
577        "credentials-view": "".join((credentials, "View")),
578        "job-build": "hudson.model.Item.Build",
579        "job-cancel": "hudson.model.Item.Cancel",
580        "job-configure": "hudson.model.Item.Configure",
581        "job-delete": "hudson.model.Item.Delete",
582        "job-discover": "hudson.model.Item.Discover",
583        "job-extended-read": "hudson.model.Item.ExtendedRead",
584        "job-move": "hudson.model.Item.Move",
585        "job-read": "hudson.model.Item.Read",
586        "job-status": "hudson.model.Item.ViewStatus",
587        "job-workspace": "hudson.model.Item.Workspace",
588        "ownership-jobs": "".join((ownership, "Jobs")),
589        "run-delete": "hudson.model.Run.Delete",
590        "run-replay": "hudson.model.Run.Replay",
591        "run-update": "hudson.model.Run.Update",
592        "scm-tag": "hudson.scm.SCM.Tag",
593    }
594
595    if data:
596        if is_a_folder:
597            element_name = "com.cloudbees.hudson.plugins.folder.properties.AuthorizationMatrixProperty"
598        else:
599            element_name = "hudson.security.AuthorizationMatrixProperty"
600        matrix = XML.SubElement(xml_parent, element_name)
601        XML.SubElement(
602            matrix,
603            "inheritanceStrategy",
604            {
605                "class": "org.jenkinsci.plugins.matrixauth.inheritance.InheritParentStrategy"
606            },
607        )
608
609        for (username, perms) in data.items():
610            for perm in perms:
611                pe = XML.SubElement(matrix, "permission")
612                try:
613                    pe.text = "{0}:{1}".format(mapping[perm], username)
614                except KeyError:
615                    raise InvalidAttributeError(username, perm, mapping.keys())
616
617
618def priority_sorter(registry, xml_parent, data):
619    """yaml: priority-sorter
620    Allows simple ordering of builds, using a configurable job priority.
621
622    Requires the Jenkins :jenkins-plugins:`Priority Sorter Plugin
623    <PrioritySorter>`.
624
625    :arg int priority: Priority of the job.  Higher value means higher
626        priority, with 3 as the default priority. (required)
627
628    Example:
629
630    .. literalinclude::
631        /../../tests/properties/fixtures/priority_sorter002.yaml
632       :language: yaml
633    """
634    plugin_info = registry.get_plugin_info("PrioritySorter")
635    version = pkg_resources.parse_version(plugin_info.get("version", "0"))
636
637    if version >= pkg_resources.parse_version("3.0"):
638        priority_sorter_tag = XML.SubElement(
639            xml_parent,
640            "jenkins.advancedqueue.jobinclusion." "strategy.JobInclusionJobProperty",
641        )
642
643        mapping = [("use", "useJobGroup", True), ("priority", "jobGroupName", None)]
644    elif version >= pkg_resources.parse_version("2.0"):
645        priority_sorter_tag = XML.SubElement(
646            xml_parent, "jenkins.advancedqueue.priority." "strategy.PriorityJobProperty"
647        )
648
649        mapping = [("use", "useJobPriority", True), ("priority", "priority", None)]
650    else:
651        priority_sorter_tag = XML.SubElement(
652            xml_parent, "hudson.queueSorter." "PrioritySorterJobProperty"
653        )
654
655        mapping = [("priority", "priority", None)]
656
657    helpers.convert_mapping_to_xml(
658        priority_sorter_tag, data, mapping, fail_required=True
659    )
660
661
662def build_blocker(registry, xml_parent, data):
663    """yaml: build-blocker
664    This plugin keeps the actual job in the queue
665    if at least one name of currently running jobs
666    is matching with one of the given regular expressions.
667
668    Requires the Jenkins :jenkins-plugins:`Build Blocker Plugin
669    <build-blocker-plugin>`.
670
671    :arg bool use-build-blocker: Enable or disable build blocker (default true)
672    :arg list blocking-jobs: One regular expression per line to select
673        blocking jobs by their names (required)
674    :arg str block-level: block build globally ('GLOBAL') or per node ('NODE')
675        (default 'GLOBAL')
676    :arg str queue-scanning: scan build queue for all builds ('ALL') or only
677        buildable builds ('BUILDABLE') (default 'DISABLED')
678
679    Example:
680
681    Minimal Example:
682
683    .. literalinclude::
684       /../../tests/properties/fixtures/build-blocker-minimal.yaml
685       :language: yaml
686
687    Full Example:
688
689    .. literalinclude::
690       /../../tests/properties/fixtures/build-blocker-full.yaml
691       :language: yaml
692    """
693    blocker = XML.SubElement(
694        xml_parent, "hudson.plugins." "buildblocker.BuildBlockerProperty"
695    )
696    if data is None or "blocking-jobs" not in data:
697        raise JenkinsJobsException("blocking-jobs field is missing")
698    elif data.get("blocking-jobs", None) is None:
699        raise JenkinsJobsException("blocking-jobs list must not be empty")
700
701    jobs = ""
702    for setting, value in data.items():
703        if setting == "blocking-jobs":
704            jobs = "\n".join(value)
705    block_level_types = ["GLOBAL", "NODE"]
706    queue_scan_types = ["DISABLED", "ALL", "BUILDABLE"]
707    mapping = [
708        ("use-build-blocker", "useBuildBlocker", True),
709        ("", "blockingJobs", jobs),
710        ("block-level", "blockLevel", "GLOBAL", block_level_types),
711        ("queue-scanning", "scanQueueFor", "DISABLED", queue_scan_types),
712    ]
713    helpers.convert_mapping_to_xml(blocker, data, mapping, fail_required=True)
714
715
716def copyartifact(registry, xml_parent, data):
717    """yaml: copyartifact
718    Specify a list of projects that have access to copy the artifacts of
719    this project.
720
721    Requires the Jenkins :jenkins-plugins:`Copy Artifact plugin
722    <copyartifact>`.
723
724    :arg str projects: comma separated list of projects that can copy
725        artifacts of this project. Wild card character '*' is available.
726
727    Example:
728
729    .. literalinclude::
730        /../../tests/properties/fixtures/copyartifact.yaml
731       :language: yaml
732
733    """
734    copyartifact = XML.SubElement(
735        xml_parent,
736        "hudson.plugins." "copyartifact." "CopyArtifactPermissionProperty",
737        plugin="copyartifact",
738    )
739    if not data or not data.get("projects", None):
740        raise JenkinsJobsException("projects string must exist and " "not be empty")
741    projectlist = XML.SubElement(copyartifact, "projectNameList")
742    for project in str(data.get("projects")).split(","):
743        XML.SubElement(projectlist, "string").text = project
744
745
746def batch_tasks(registry, xml_parent, data):
747    """yaml: batch-tasks
748    Batch tasks can be tasks for events like releases, integration, archiving,
749    etc. In this way, anyone in the project team can execute them in a way that
750    leaves a record.
751
752    A batch task consists of a shell script and a name. When you execute
753    a build, the shell script gets run on the workspace, just like a build.
754    Batch tasks and builds "lock" the workspace, so when one of those
755    activities is in progress, all the others will block in the queue.
756
757    Requires the Jenkins :jenkins-plugins:`Batch Task Plugin <batch-task>`.
758
759    :arg list batch-tasks: Batch tasks.
760
761        :Tasks:
762            * **name** (`str`) Task name.
763            * **script** (`str`) Task script.
764
765    Example:
766
767    .. literalinclude:: /../../tests/properties/fixtures/batch-task.yaml
768       :language: yaml
769
770    """
771    pdef = XML.SubElement(xml_parent, "hudson.plugins.batch__task.BatchTaskProperty")
772    tasks = XML.SubElement(pdef, "tasks")
773    for task in data:
774        batch_task = XML.SubElement(tasks, "hudson.plugins.batch__task.BatchTask")
775        mapping = [("name", "name", None), ("script", "script", None)]
776        helpers.convert_mapping_to_xml(batch_task, task, mapping, fail_required=True)
777
778
779def heavy_job(registry, xml_parent, data):
780    """yaml: heavy-job
781    This plugin allows you to define "weight" on each job,
782    and making each job consume that many executors
783
784    Requires the Jenkins :jenkins-plugins:`Heavy Job Plugin <heavy-job>`.
785
786    :arg int weight: Specify the total number of executors
787        that this job should occupy (default 1)
788
789    Example:
790
791    .. literalinclude:: /../../tests/properties/fixtures/heavy-job.yaml
792       :language: yaml
793
794    """
795    heavyjob = XML.SubElement(
796        xml_parent, "hudson.plugins." "heavy__job.HeavyJobProperty"
797    )
798    mapping = [("weight", "weight", 1)]
799    helpers.convert_mapping_to_xml(heavyjob, data, mapping, fail_required=True)
800
801
802def slave_utilization(registry, xml_parent, data):
803    """yaml: slave-utilization
804    This plugin allows you to specify the percentage of a slave's capacity a
805    job wants to use.
806
807    Requires the Jenkins :jenkins-plugins:`Slave Utilization Plugin
808    <slave-utilization-plugin>`.
809
810    :arg int slave-percentage: Specify the percentage of a slave's execution
811        slots that this job should occupy (default 0)
812    :arg bool single-instance-per-slave: Control whether concurrent instances
813        of this job will be permitted to run in parallel on a single slave
814        (default false)
815
816    Example:
817
818    .. literalinclude::
819        /../../tests/properties/fixtures/slave-utilization1.yaml
820       :language: yaml
821    """
822    utilization = XML.SubElement(
823        xml_parent, "com.suryagaddipati.jenkins.SlaveUtilizationProperty"
824    )
825
826    percent = int(data.get("slave-percentage", 0))
827    exclusive_node_access = True if percent else False
828
829    mapping = [
830        ("", "needsExclusiveAccessToNode", exclusive_node_access),
831        ("", "slaveUtilizationPercentage", percent),
832        ("single-instance-per-slave", "singleInstancePerSlave", False),
833    ]
834    helpers.convert_mapping_to_xml(utilization, data, mapping, fail_required=True)
835
836
837def delivery_pipeline(registry, xml_parent, data):
838    """yaml: delivery-pipeline
839    Requires the Jenkins :jenkins-plugins:`Delivery Pipeline Plugin
840    <delivery-pipeline-plugin>`.
841
842    :arg str stage: Name of the stage for this job (default '')
843    :arg str task: Name of the task for this job (default '')
844    :arg str description: task description template for this job
845        (default '')
846
847    Minimal Example:
848
849    .. literalinclude::
850       /../../tests/properties/fixtures/delivery-pipeline-minimal.yaml
851       :language: yaml
852
853    Full Example:
854
855    .. literalinclude::
856       /../../tests/properties/fixtures/delivery-pipeline-full.yaml
857       :language: yaml
858    """
859    pipeline = XML.SubElement(xml_parent, "se.diabol.jenkins.pipeline.PipelineProperty")
860    pipeline.set("plugin", "delivery-pipeline-plugin")
861
862    mapping = [
863        ("stage", "stageName", ""),
864        ("task", "taskName", ""),
865        ("description", "descriptionTemplate", ""),
866    ]
867    helpers.convert_mapping_to_xml(pipeline, data, mapping, fail_required=True)
868
869
870def zeromq_event(registry, xml_parent, data):
871    """yaml: zeromq-event
872    This is a Jenkins plugin that will publish Jenkins Job run events
873    (start, complete, finish) to a ZMQ PUB socket.
874
875    Requires the Jenkins `ZMQ Event Publisher.
876    <https://opendev.org/x/zmq-event-publisher>`_
877
878    Example:
879
880    .. literalinclude::
881        /../../tests/properties/fixtures/zeromq-event.yaml
882       :language: yaml
883
884    """
885
886    zmq_event = XML.SubElement(
887        xml_parent,
888        "org.jenkinsci.plugins." "ZMQEventPublisher.HudsonNotificationProperty",
889    )
890    mapping = [("", "enabled", True)]
891    helpers.convert_mapping_to_xml(zmq_event, data, mapping, fail_required=True)
892
893
894def slack(registry, xml_parent, data):
895    """yaml: slack
896    Requires the Jenkins :jenkins-plugins:`Slack Plugin <slack>`.
897
898    When using Slack Plugin version < 2.0, Slack Plugin itself requires a
899    publisher aswell as properties please note that you have to add the
900    publisher to your job configuration aswell. When using Slack Plugin
901    version >= 2.0, you should only configure the publisher.
902
903    :arg bool notify-start: Send notification when the job starts
904        (default false)
905    :arg bool notify-success: Send notification on success. (default false)
906    :arg bool notify-aborted: Send notification when job is aborted. (
907        default false)
908    :arg bool notify-not-built: Send notification when job set to NOT_BUILT
909        status. (default false)
910    :arg bool notify-unstable: Send notification when job becomes unstable.
911        (default false)
912    :arg bool notify-failure: Send notification when job fails.
913        (default false)
914    :arg bool notify-back-to-normal: Send notification when job is
915        succeeding again after being unstable or failed. (default false)
916    :arg bool 'notify-repeated-failure': Send notification when job is
917        still failing after last failure. (default false)
918    :arg bool include-test-summary: Include the test summary. (default
919        False)
920    :arg bool include-custom-message: Include a custom message into the
921        notification. (default false)
922    :arg str custom-message: Custom message to be included. (default '')
923    :arg str room: A comma separated list of rooms / channels to send
924        the notifications to. (default '')
925
926    Example:
927
928    .. literalinclude::
929        /../../tests/properties/fixtures/slack001.yaml
930        :language: yaml
931    """
932    logger = logging.getLogger(__name__)
933
934    plugin_info = registry.get_plugin_info("Slack Notification Plugin")
935    plugin_ver = pkg_resources.parse_version(plugin_info.get("version", "0"))
936
937    if plugin_ver >= pkg_resources.parse_version("2.0"):
938        logger.warning("properties section is not used with plugin version >= 2.0")
939
940    mapping = (
941        ("notify-start", "startNotification", False),
942        ("notify-success", "notifySuccess", False),
943        ("notify-aborted", "notifyAborted", False),
944        ("notify-not-built", "notifyNotBuilt", False),
945        ("notify-unstable", "notifyUnstable", False),
946        ("notify-failure", "notifyFailure", False),
947        ("notify-back-to-normal", "notifyBackToNormal", False),
948        ("notify-repeated-failure", "notifyRepeatedFailure", False),
949        ("include-test-summary", "includeTestSummary", False),
950        ("include-custom-message", "includeCustomMessage", False),
951        ("custom-message", "customMessage", ""),
952        ("room", "room", ""),
953    )
954
955    slack = XML.SubElement(
956        xml_parent, "jenkins.plugins.slack.SlackNotifier_-SlackJobProperty"
957    )
958
959    # Ensure that custom-message is set when include-custom-message is set
960    # to true.
961    if data.get("include-custom-message", False):
962        if not data.get("custom-message", ""):
963            raise MissingAttributeError("custom-message")
964
965    helpers.convert_mapping_to_xml(slack, data, mapping, fail_required=True)
966
967
968def rebuild(registry, xml_parent, data):
969    """yaml: rebuild
970    This plug-in allows the user to rebuild a parameterized build without
971    entering the parameters again.It will also allow the user to edit the
972    parameters before rebuilding.
973
974    Requires the Jenkins :jenkins-plugins:`Rebuild Plugin <rebuild>`.
975
976    :arg bool auto-rebuild: Rebuild without asking for parameters
977        (default false)
978    :arg bool rebuild-disabled: Disable rebuilding for this job
979        (default false)
980
981    Minimal Example:
982
983    .. literalinclude:: /../../tests/properties/fixtures/rebuild-minimal.yaml
984       :language: yaml
985
986    Full Example:
987
988    .. literalinclude:: /../../tests/properties/fixtures/rebuild-full.yaml
989       :language: yaml
990    """
991    sub_element = XML.SubElement(xml_parent, "com.sonyericsson.rebuild.RebuildSettings")
992    sub_element.set("plugin", "rebuild")
993
994    mapping = [
995        ("auto-rebuild", "autoRebuild", False),
996        ("rebuild-disabled", "rebuildDisabled", False),
997    ]
998    helpers.convert_mapping_to_xml(sub_element, data, mapping, fail_required=True)
999
1000
1001def build_discarder(registry, xml_parent, data):
1002    """yaml: build-discarder
1003
1004    :arg int days-to-keep: Number of days to keep builds for (default -1)
1005    :arg int num-to-keep: Number of builds to keep (default -1)
1006    :arg int artifact-days-to-keep: Number of days to keep builds with
1007        artifacts (default -1)
1008    :arg int artifact-num-to-keep: Number of builds with artifacts to keep
1009        (default -1)
1010
1011    Example:
1012
1013    .. literalinclude::
1014        /../../tests/properties/fixtures/build-discarder-001.yaml
1015       :language: yaml
1016
1017    .. literalinclude::
1018        /../../tests/properties/fixtures/build-discarder-002.yaml
1019       :language: yaml
1020    """
1021    base_sub = XML.SubElement(xml_parent, "jenkins.model.BuildDiscarderProperty")
1022    strategy = XML.SubElement(base_sub, "strategy")
1023    strategy.set("class", "hudson.tasks.LogRotator")
1024
1025    mappings = [
1026        ("days-to-keep", "daysToKeep", -1),
1027        ("num-to-keep", "numToKeep", -1),
1028        ("artifact-days-to-keep", "artifactDaysToKeep", -1),
1029        ("artifact-num-to-keep", "artifactNumToKeep", -1),
1030    ]
1031    helpers.convert_mapping_to_xml(strategy, data, mappings, fail_required=True)
1032
1033
1034def slave_prerequisites(registry, xml_parent, data):
1035    """yaml: slave-prerequisites
1036    This plugin allows you to check prerequisites on slave before
1037    a job can run a build on it
1038
1039    Requires the Jenkins :jenkins-plugins:`Slave Prerequisites Plugin
1040    <slave-prerequisites>`.
1041
1042    :arg str script: A script to be executed on slave node.
1043        If returning non 0 status, the node will be vetoed from hosting
1044        the build. (required)
1045    :arg str interpreter: Command line interpreter to be used for executing
1046        the prerequisite script - either `shell` for Unix shell or `cmd` for
1047        Windows batch script. (default shell)
1048
1049    Example:
1050
1051    .. literalinclude::
1052        /../../tests/properties/fixtures/slave-prerequisites-minimal.yaml
1053       :language: yaml
1054
1055    .. literalinclude::
1056        /../../tests/properties/fixtures/slave-prerequisites-full.yaml
1057       :language: yaml
1058    """
1059    prereqs = XML.SubElement(xml_parent, "com.cloudbees.plugins.JobPrerequisites")
1060
1061    mappings = [
1062        ("script", "script", None),
1063        (
1064            "interpreter",
1065            "interpreter",
1066            "shell",
1067            {"cmd": "windows batch command", "shell": "shell script"},
1068        ),
1069    ]
1070    helpers.convert_mapping_to_xml(prereqs, data, mappings, fail_required=True)
1071
1072
1073def groovy_label(registry, xml_parent, data):
1074    """yaml: groovy-label
1075    This plugin allows you to use Groovy script to restrict where this project
1076    can be run.
1077
1078    Requires the Jenkins :jenkins-plugins:`Groovy Label Assignment Plugin
1079    <groovy-label-assignment>`.
1080
1081    Return value from Groovy script is treated as Label Expression.
1082    It is treated as followings:
1083
1084    - A non-string value will be converted to a string using toString()
1085    - When null or blank string is returned, node restriction does not take
1086      effect (or is not overwritten).
1087    - When exception occurred or Label Expression is not parsed correctly,
1088      builds are canceled.
1089
1090    :arg str script: Groovy script (default '')
1091    :arg bool sandbox: Use Groovy Sandbox. (default false)
1092        If checked, run this Groovy script in a sandbox with limited abilities.
1093        If unchecked, and you are not a Jenkins administrator, you will need to
1094        wait for an administrator to approve the script
1095    :arg list classpath: Additional classpath entries accessible from
1096        the script, each of which should be an absolute local path or
1097        URL to a JAR file, according to "The file URI Scheme" (optional)
1098
1099    Minimal Example:
1100
1101    .. literalinclude::
1102        /../../tests/properties/fixtures/groovy-label-minimal.yaml
1103       :language: yaml
1104
1105    Full Example:
1106
1107    .. literalinclude::
1108        /../../tests/properties/fixtures/groovy-label-full.yaml
1109       :language: yaml
1110    """
1111    sub_element = XML.SubElement(
1112        xml_parent,
1113        "jp.ikedam.jenkins.plugins."
1114        "groovy__label__assignment."
1115        "GroovyLabelAssignmentProperty",
1116    )
1117    sub_element.set("plugin", "groovy-label-assignment")
1118    security = XML.SubElement(sub_element, "secureGroovyScript")
1119    security.set("plugin", "script-security")
1120    mapping = [("script", "script", ""), ("sandbox", "sandbox", False)]
1121
1122    helpers.convert_mapping_to_xml(security, data, mapping, fail_required=True)
1123    if data and "classpath" in data:
1124        classpath = XML.SubElement(security, "classpath")
1125        for value in data["classpath"]:
1126            entry = XML.SubElement(classpath, "entry")
1127            XML.SubElement(entry, "url").text = value
1128
1129
1130def lockable_resources(registry, xml_parent, data):
1131    """yaml: lockable-resources
1132    Requires the Jenkins :jenkins-plugins:`Lockable Resources Plugin
1133    <lockable-resources>`.
1134
1135    :arg str resources: List of required resources, space separated.
1136        (required, mutual exclusive with label)
1137    :arg str label: If you have created a pool of resources, i.e. a label,
1138        you can take it into use here. The build will select the resource(s)
1139        from the pool that includes all resources sharing the given label.
1140        (required, mutual exclusive with resources)
1141    :arg str var-name: Name for the Jenkins variable to store the reserved
1142        resources in. Leave empty to disable. (default '')
1143    :arg int number: Number of resources to request, empty value or 0 means
1144        all. This is useful, if you have a pool of similar resources,
1145        from which you want one or more to be reserved. (default 0)
1146    :arg str match-script: Groovy script to reserve resource based on its
1147        properties. Leave empty to disable. (default None)
1148    :arg bool groovy-sandbox: Execute the provided match-script in Groovy
1149        sandbox. Leave empty to disable. (default False)
1150
1151    Example:
1152
1153    .. literalinclude::
1154        /../../tests/properties/fixtures/lockable_resources_minimal.yaml
1155       :language: yaml
1156
1157    .. literalinclude::
1158        /../../tests/properties/fixtures/lockable_resources_label.yaml
1159       :language: yaml
1160
1161    .. literalinclude::
1162        /../../tests/properties/fixtures/lockable_resources_full.yaml
1163       :language: yaml
1164
1165    .. literalinclude::
1166        /../../tests/properties/fixtures/lockable_resources_groovy.yaml
1167       :language: yaml
1168    """
1169    lockable_resources = XML.SubElement(
1170        xml_parent, "org.jenkins.plugins.lockableresources.RequiredResourcesProperty"
1171    )
1172    if data.get("resources") and data.get("label"):
1173        raise AttributeConflictError("resources", ("label",))
1174    mapping = [
1175        ("resources", "resourceNames", ""),
1176        ("var-name", "resourceNamesVar", ""),
1177        ("number", "resourceNumber", 0),
1178        ("label", "labelName", ""),
1179    ]
1180    helpers.convert_mapping_to_xml(
1181        lockable_resources, data, mapping, fail_required=True
1182    )
1183    secure_groovy_script = XML.SubElement(lockable_resources, "resourceMatchScript")
1184    mapping = [("match-script", "script", None), ("groovy-sandbox", "sandbox", False)]
1185    helpers.convert_mapping_to_xml(
1186        secure_groovy_script, data, mapping, fail_required=False
1187    )
1188
1189
1190def docker_container(registry, xml_parent, data):
1191    """yaml: docker-container
1192    Requires the Jenkins: :jenkins-plugins:`Docker Plugin <docker-plugin>`.
1193
1194    :arg str docker-registry-url: URL of the Docker registry. (default '')
1195    :arg str credentials-id: Credentials Id for the Docker registey.
1196        (default '')
1197    :arg bool commit-on-success: When a job completes, the docker slave
1198        instance is committed with repository based on the job name and build
1199        number as tag. (default false)
1200    :arg str additional-tag: Additional tag to apply to the docker slave
1201        instance when committing it. (default '')
1202    :arg bool push-on-success: Also push the resulting image when committing
1203        the docker slave instance. (default false)
1204    :arg bool clean-local-images: Clean images from the local daemon after
1205        building. (default true)
1206
1207    Minimal Example:
1208
1209    .. literalinclude::
1210        /../../tests/properties/fixtures/docker-container-minimal.yaml
1211        :language: yaml
1212
1213    Full Example:
1214
1215    .. literalinclude::
1216        /../../tests/properties/fixtures/docker-container-full.yaml
1217        :language: yaml
1218    """
1219    xml_docker = XML.SubElement(
1220        xml_parent, "com.nirima.jenkins.plugins.docker.DockerJobProperty"
1221    )
1222
1223    registry = XML.SubElement(xml_docker, "registry")
1224    registry.set("plugin", "docker-commons")
1225    registry_mapping = [
1226        ("docker-registry-url", "url", ""),
1227        ("credentials-id", "credentialsId", ""),
1228    ]
1229    helpers.convert_mapping_to_xml(
1230        registry, data, registry_mapping, fail_required=False
1231    )
1232    mapping = [
1233        ("commit-on-success", "tagOnCompletion", False),
1234        ("additional-tag", "additionalTag", ""),
1235        ("push-on-success", "pushOnSuccess", False),
1236        ("clean-local-images", "cleanImages", True),
1237    ]
1238    helpers.convert_mapping_to_xml(xml_docker, data, mapping, fail_required=True)
1239
1240
1241def disable_resume(registry, xml_parent, data):
1242    """yaml: disable-resume
1243    Do not allow the pipeline to resume if the master restarts
1244
1245    Requires the Jenkins :jenkins-plugins:`Pipeline Job Plugin
1246    <workflow-aggregator>`.
1247
1248    Example:
1249
1250    .. literalinclude::
1251        /../../tests/properties/fixtures/disable-resume.yaml
1252       :language: yaml
1253
1254    """
1255    XML.SubElement(
1256        xml_parent,
1257        "org.jenkinsci.plugins.workflow.job.properties." "DisableResumeJobProperty",
1258    )
1259
1260
1261def resource_gating(registry, xml_parent, data):
1262    """yaml: resource-gating
1263    Jenkins Gating enables requiring external resources to be available before
1264    build starts.
1265
1266    Requires the Jenkins: :jenkins-plugins:`Jenkins Gating <gating-core>`.
1267
1268    :arg list resources: Resource identifiers to be up before building
1269
1270    Example:
1271
1272    .. literalinclude:: /../../tests/properties/fixtures/gating-core.yaml
1273        :language: yaml
1274    """
1275    if "resources" not in data.keys():
1276        raise MissingAttributeError("resources")
1277
1278    gating = XML.SubElement(
1279        xml_parent, "io.jenkins.plugins.gating.ResourceRequirementProperty"
1280    )
1281    gating.set("plugin", "gating-core")
1282
1283    resources = XML.SubElement(gating, "resources")
1284    resources.set("class", "java.util.Collections$UnmodifiableRandomAccessList")
1285    resources.set("resolves-to", "java.util.Collections$UnmodifiableList")
1286
1287    c = XML.SubElement(resources, "c")
1288    c.set("class", "list")
1289    for resource in data["resources"]:
1290        XML.SubElement(c, "string").text = str(resource)
1291
1292    lst = XML.SubElement(resources, "list")
1293    lst.set("reference", "../c")
1294
1295
1296def cachet_gating(registry, xml_parent, data):
1297    """yaml: cachet-gating
1298    The Cachet Gating Plugin provides a gating mechanism
1299    based on the availability of resources.
1300
1301    Requires the Jenkins: :jenkins-plugins:`Cachet Gate Plugin
1302    <cachet-gating>`.
1303
1304    :arg bool required-resources: Confirm availability of listed
1305        resources before building. Requires the list of resources to
1306        also be defined. (default true)
1307    :arg list resources: which resources to gate
1308
1309    Example:
1310
1311    .. literalinclude:: /../../tests/properties/fixtures/cachet-gating.yaml
1312        :language: yaml
1313    """
1314    cachet = XML.SubElement(
1315        xml_parent, "com.redhat.jenkins.plugins.cachet.CachetJobProperty"
1316    )
1317    cachet.set("plugin", "cachet-gating")
1318
1319    mapping = [("required-resources", "requiredResources", True)]
1320    helpers.convert_mapping_to_xml(cachet, data, mapping, fail_required=True)
1321
1322    resources_data = data.get("resources", [])
1323    if resources_data:
1324        resources = XML.SubElement(cachet, "resources")
1325        for resource in resources_data:
1326            XML.SubElement(resources, "string").text = str(resource)
1327
1328
1329def office_365_connector(registry, xml_parent, data):
1330    """yaml: office-365-connector
1331    Used to send actionable messages to MS Outlook or Teams
1332
1333    Requires the Jenkins: :jenkins-plugins:` Office-365-Connector Plugin
1334    <Office-365-Connector>`.
1335
1336    :arg list webhooks: List of webhooks (required)
1337
1338        * **url** (srt): URL generated in the Office 365 Connectors page (required)
1339        * **name** (str): Allows to provide name fo the connection. Name is not
1340            mandatory but helps managing when there are many connection
1341            assigned to the build (optional, default '')
1342        * **start-notification** (bool): If the notification should be sent on
1343            start of build (optional, default False)
1344        * **notify-success** (bool): If the notification should be sent on
1345            succeeded build (optional, default True)
1346        * **notify-aborted** (bool): If the notification should be sent on
1347            aborted build (optional, default False)
1348        * **notify-not-built** (bool): If the notification should be sent on
1349            not built build (optional, default False)
1350        * **notify-unstable** (bool): If the notification should be sent on
1351            unstable build (optional, default True)
1352        * **notify-failure** (bool): If the notification should be sent on
1353            failed build (optional, default True)
1354        * **notify-back-to-normal** (bool): If the notification should be sent on
1355            back to normal build (optional, default True)
1356        * **notify-repeated-failure** (bool): If the notification should be sent on
1357            repeated failures (optional, default False)
1358        * **timeout** (int): connection timeout (in milliseconds) for TCP and HTTP
1359            (optional, default 30000)
1360        * **macros** (list): List of macros
1361
1362            * **template** (str)
1363              **value** (str)
1364
1365        * **fact-definitions** (list): List of fact definitions
1366
1367            * **name** (str)
1368              **template** (str)
1369
1370    Example:
1371
1372    .. literalinclude:: /../../tests/properties/fixtures/office-365-connector-full.yaml
1373        :language: yaml
1374    """
1375
1376    office_365_connector = XML.SubElement(
1377        xml_parent, "jenkins.plugins.office365connector.WebhookJobProperty"
1378    )
1379    office_365_connector.set("plugin", "Office-365-Connector")
1380    webhooks = XML.SubElement(office_365_connector, "webhooks")
1381
1382    webhook_mapping = [
1383        ("url", "url", None),
1384        ("name", "name", ""),
1385        ("start-notification", "startNotification", False),
1386        ("notify-success", "notifySuccess", True),
1387        ("notify-aborted", "notifyAborted", False),
1388        ("notify-not-built", "notifyNotBuilt", False),
1389        ("notify-unstable", "notifyUnstable", True),
1390        ("notify-failure", "notifyFailure", True),
1391        ("notify-back-to-normal", "notifyBackToNormal", True),
1392        ("notify-repeated-failure", "notifyRepeatedFailure", False),
1393        ("timeout", "timeout", 30000),
1394    ]
1395    macro_mapping = [("template", "template", None), ("value", "value", None)]
1396    fact_definition_mapping = [("name", "name", None), ("template", "template", None)]
1397
1398    if "webhooks" not in data.keys():
1399        raise MissingAttributeError("webhooks")
1400
1401    for webhook_data in data["webhooks"]:
1402        webhook_element = XML.SubElement(
1403            webhooks, "jenkins.plugins.office365connector.Webhook"
1404        )
1405        helpers.convert_mapping_to_xml(
1406            webhook_element, webhook_data, webhook_mapping, fail_required=True
1407        )
1408        if "macros" in webhook_data.keys():
1409            macros = XML.SubElement(webhook_element, "macros")
1410            for macro_data in webhook_data["macros"]:
1411                macro_element = XML.SubElement(
1412                    macros, "jenkins.plugins.office365connector.model.Macro"
1413                )
1414                helpers.convert_mapping_to_xml(
1415                    macro_element, macro_data, macro_mapping, fail_required=True
1416                )
1417        if "fact-definitions" in webhook_data.keys():
1418            fact_definitions = XML.SubElement(webhook_element, "factDefinitions")
1419            for fact_definition_data in webhook_data["fact-definitions"]:
1420                fact_definition_element = XML.SubElement(
1421                    fact_definitions,
1422                    "jenkins.plugins.office365connector.model.FactDefinition",
1423                )
1424                helpers.convert_mapping_to_xml(
1425                    fact_definition_element,
1426                    fact_definition_data,
1427                    fact_definition_mapping,
1428                    fail_required=True,
1429                )
1430
1431
1432def speed_durability(registry, xml_parent, data):
1433    """yaml: speed-durability
1434    This setting allows users to change the default durability mode
1435    for running Pipelines.
1436
1437    :arg str hint: speed durability hint to be used, can be performance-optimized,
1438        survivable-non-atomic, max-survivability
1439
1440    Example:
1441
1442    .. literalinclude::
1443        /../../tests/properties/fixtures/speed-durability.yaml
1444       :language: yaml
1445    """
1446    dhp = XML.SubElement(
1447        xml_parent,
1448        "org.jenkinsci.plugins.workflow.job.properties.DurabilityHintJobProperty",
1449    )
1450    choicedict = {
1451        "performance-optimized": "PERFORMANCE_OPTIMIZED",
1452        "survivable-non-atomic": "SURVIVABLE_NONATOMIC",
1453        "max-survivability": "MAX_SURVIVABILITY",
1454    }
1455    mapping = [("hint", "hint", None, choicedict)]
1456    helpers.convert_mapping_to_xml(dhp, data, mapping, fail_required=True)
1457
1458
1459class Properties(jenkins_jobs.modules.base.Base):
1460    sequence = 20
1461
1462    component_type = "property"
1463    component_list_type = "properties"
1464
1465    def gen_xml(self, xml_parent, data):
1466        properties = xml_parent.find("properties")
1467        if properties is None:
1468            properties = XML.SubElement(xml_parent, "properties")
1469
1470        for prop in data.get("properties", []):
1471            self.registry.dispatch("property", properties, prop, job_data=data)
1472