# Copyright 2012 Hewlett-Packard Development Company, L.P. # Copyright 2012 Varnish Software AS # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Publishers define actions that the Jenkins job should perform after the build is complete. **Component**: publishers :Macro: publisher :Entry Point: jenkins_jobs.publishers Example:: job: name: test_job publishers: - scp: site: 'example.com' source: 'doc/build/html/**/*' target_path: 'project' """ import xml.etree.ElementTree as XML import jenkins_jobs.modules.base import logging def archive(parser, xml_parent, data): """yaml: archive Archive build artifacts :arg str artifacts: path specifier for artifacts to archive :arg str excludes: path specifier for artifacts to exclude :arg bool latest_only: only keep the artifacts from the latest successful build Example:: publishers: - archive: artifacts: *.tar.gz """ archiver = XML.SubElement(xml_parent, 'hudson.tasks.ArtifactArchiver') artifacts = XML.SubElement(archiver, 'artifacts') artifacts.text = data['artifacts'] if 'excludes' in data: excludes = XML.SubElement(archiver, 'excludes') excludes.text = data['excludes'] latest = XML.SubElement(archiver, 'latestOnly') latest_only = data.get('latest_only', False) if latest_only: latest.text = 'true' else: latest.text = 'false' def trigger_parameterized_builds(parser, xml_parent, data): """yaml: trigger-parameterized-builds Trigger parameterized builds of other jobs. Requires the Jenkins `Parameterized Trigger Plugin. `_ :arg str project: name of the job to trigger :arg str predefined-parameters: parameters to pass to the other job (optional) :arg str condition: when to trigger the other job (default 'ALWAYS') Example:: publishers: - trigger-parameterized-builds: project: other_job """ tbuilder = XML.SubElement(xml_parent, 'hudson.plugins.parameterizedtrigger.BuildTrigger') configs = XML.SubElement(tbuilder, 'configs') for project_def in data: tconfig = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.BuildTriggerConfig') tconfigs = XML.SubElement(tconfig, 'configs') if 'predefined-parameters' in project_def: params = XML.SubElement(tconfigs, 'hudson.plugins.parameterizedtrigger.PredefinedBuildParameters') properties = XML.SubElement(params, 'properties') properties.text = project_def['predefined-parameters'] else: tconfigs.set('class', 'java.util.Collections$EmptyList') projects = XML.SubElement(tconfig, 'projects') projects.text = project_def['project'] condition = XML.SubElement(tconfig, 'condition') condition.text = project_def.get('condition', 'ALWAYS') trigger_with_no_params = XML.SubElement(tconfig, 'triggerWithNoParameters') trigger_with_no_params.text = 'false' def trigger(parser, xml_parent, data): """yaml: trigger Trigger non-parametrised builds of other jobs. :arg str project: name of the job to trigger :arg str threshold: when to trigger the other job (default 'SUCCESS'), alternatives: SUCCESS, UNSTABLE, FAILURE Example:: publishers: - trigger: project: other_job """ thresholds = { 'SUCCESS': { 'ordinal': '0', 'color': 'BLUE' }, 'UNSTABLE': { 'ordinal': '1', 'color': 'YELLOW' }, 'FAILURE': { 'ordinal': '2', 'color': 'RED' } } tconfig = XML.SubElement(xml_parent, 'hudson.tasks.BuildTrigger') childProjects = XML.SubElement(tconfig, 'childProjects') childProjects.text = data['project'] tthreshold = XML.SubElement(tconfig, 'threshold') threshold = data.get('threshold', 'SUCCESS') if threshold not in thresholds.keys(): raise Exception("threshold must be one of " + ", ".join(threshold.keys())) tname = XML.SubElement(tthreshold, 'name') tname.text = threshold tordinal = XML.SubElement(tthreshold, 'ordinal') tordinal.text = thresholds[threshold]['ordinal'] tcolor = XML.SubElement(tthreshold, 'color') tcolor.text = thresholds[threshold]['color'] def coverage(parser, xml_parent, data): """yaml: coverage Generate a cobertura coverage report. Requires the Jenkins `Cobertura Coverage Plugin. `_ Example:: publishers: - coverage """ cobertura = XML.SubElement(xml_parent, 'hudson.plugins.cobertura.CoberturaPublisher') XML.SubElement(cobertura, 'coberturaReportFile').text = '**/coverage.xml' XML.SubElement(cobertura, 'onlyStable').text = 'false' healthy = XML.SubElement(cobertura, 'healthyTarget') targets = XML.SubElement(healthy, 'targets', { 'class': 'enum-map', 'enum-type': 'hudson.plugins.cobertura.targets.CoverageMetric'}) entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'CONDITIONAL' XML.SubElement(entry, 'int').text = '70' entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'LINE' XML.SubElement(entry, 'int').text = '80' entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'METHOD' XML.SubElement(entry, 'int').text = '80' unhealthy = XML.SubElement(cobertura, 'unhealthyTarget') targets = XML.SubElement(unhealthy, 'targets', { 'class': 'enum-map', 'enum-type': 'hudson.plugins.cobertura.targets.CoverageMetric'}) entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'CONDITIONAL' XML.SubElement(entry, 'int').text = '0' entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'LINE' XML.SubElement(entry, 'int').text = '0' entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'METHOD' XML.SubElement(entry, 'int').text = '0' failing = XML.SubElement(cobertura, 'failingTarget') targets = XML.SubElement(failing, 'targets', { 'class': 'enum-map', 'enum-type': 'hudson.plugins.cobertura.targets.CoverageMetric'}) entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'CONDITIONAL' XML.SubElement(entry, 'int').text = '0' entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'LINE' XML.SubElement(entry, 'int').text = '0' entry = XML.SubElement(targets, 'entry') XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric' ).text = 'METHOD' XML.SubElement(entry, 'int').text = '0' XML.SubElement(cobertura, 'sourceEncoding').text = 'ASCII' def ftp(parser, xml_parent, data): """yaml: ftp Upload files via FTP. Requires the Jenkins `Publish over FTP Plugin. `_ :arg str site: name of the ftp site :arg str target: destination directory :arg str source: source path specifier :arg str excludes: excluded file pattern (optional) :arg str remove-prefix: prefix to remove from uploaded file paths (optional) Example:: publishers: - ftp: site: 'ftp.example.com' target: 'dest/dir' source: 'base/source/dir/**' remove-prefix: 'base/source/dir' excludes: '**/*.excludedfiletype' """ outer_ftp = XML.SubElement(xml_parent, 'jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin') XML.SubElement(outer_ftp, 'consolePrefix').text = 'FTP: ' delegate = XML.SubElement(outer_ftp, 'delegate') publishers = XML.SubElement(delegate, 'publishers') ftp = XML.SubElement(publishers, 'jenkins.plugins.publish__over__ftp.BapFtpPublisher') XML.SubElement(ftp, 'configName').text = data['site'] XML.SubElement(ftp, 'verbose').text = 'true' transfers = XML.SubElement(ftp, 'transfers') ftp_transfers = XML.SubElement(transfers, 'jenkins.plugins.publish__over__ftp.BapFtpTransfer') XML.SubElement(ftp_transfers, 'remoteDirectory').text = data['target'] XML.SubElement(ftp_transfers, 'sourceFiles').text = data['source'] XML.SubElement(ftp_transfers, 'excludes').text = data['excludes'] XML.SubElement(ftp_transfers, 'removePrefix').text = data['remove-prefix'] XML.SubElement(ftp_transfers, 'remoteDirectorySDF').text = 'false' XML.SubElement(ftp_transfers, 'flatten').text = 'false' XML.SubElement(ftp_transfers, 'cleanRemote').text = 'false' XML.SubElement(ftp_transfers, 'asciiMode').text = 'false' XML.SubElement(ftp, 'useWorkspaceInPromotion').text = 'false' XML.SubElement(ftp, 'usePromotionTimestamp').text = 'false' XML.SubElement(delegate, 'continueOnError').text = 'false' XML.SubElement(delegate, 'failOnError').text = 'false' XML.SubElement(delegate, 'alwaysPublishFromMaster').text = 'false' XML.SubElement(delegate, 'hostConfigurationAccess', {'class': 'jenkins.plugins.publish_over_ftp.BapFtpPublisherPlugin', 'reference': '../..'}) def junit(parser, xml_parent, data): """yaml: junit Publish JUnit test results. :arg str results: results filename Example:: publishers: - junit: results: nosetests.xml """ junitresult = XML.SubElement(xml_parent, 'hudson.tasks.junit.JUnitResultArchiver') XML.SubElement(junitresult, 'testResults').text = data['results'] XML.SubElement(junitresult, 'keepLongStdio').text = "true" XML.SubElement(junitresult, 'testDataPublishers') def xunit(parser, xml_parent, data): """yaml: xunit Publish tests results. Requires the Jenkins `xUnit Plugin. `_ :arg str thresholdmode: whether thresholds represents an absolute \ number of tests or a percentage. Either 'number' or 'percent', will \ default to 'number' if ommitted. :arg dict thresholds: list containing the thresholds for both \ 'failed' and 'skipped' tests. Each entry should in turn have a \ list of "threshold name: values". The threshold names are \ 'unstable', 'unstablenew', 'failure', 'failurenew'. Omitting a \ value will resort on xUnit default value (should be 0). :arg dict types: per framework configuration. The key should be \ one of the internal types we support:\ 'aunit', 'boosttest', 'checktype', 'cpptest', 'cppunit', 'fpcunit', \ 'junit', 'mstest', 'nunit', 'phpunit', 'tusar', 'unittest', 'valgrind'. \ The 'custom' type is not supported. Each framework type can be configured using the following parameters: :arg str pattern: An Ant pattern to look for Junit result files, \ relative to the workspace root. :arg bool requireupdate: fail the build whenever fresh tests \ results have not been found (default: true). :arg bool deleteoutput: delete temporary JUnit files (default: true) :arg bool stoponerror: Fail the build whenever an error occur during \ a result file processing (default: true). Example:: publishers: - xunit: thresholdmode: 'percent' thresholds: - failed: unstable: 0 unstablenew: 0 failure: 0 failurenew: 0 - skipped: unstable: 0 unstablenew: 0 failure: 0 failurenew: 0 types: - phpunit: pattern: junit.log - cppUnit: pattern: cppunit.log """ logger = logging.getLogger(__name__) xunit = XML.SubElement(xml_parent, 'xunit') # Map our internal types to the XML element names used by Jenkins plugin types_to_plugin_types = { 'aunit': 'AUnitJunitHudsonTestType', 'boosttest': 'AUnitJunitHudsonTestType', 'checktype': 'CheckType', 'cpptest': 'CppTestJunitHudsonTestType', 'cppunit': 'CppUnitJunitHudsonTestType', 'fpcunit': 'FPCUnitJunitHudsonTestType', 'junit': 'JUnitType', 'mstest': 'MSTestJunitHudsonTestType', 'nunit': 'NUnitJunitHudsonTestType', 'phpunit': 'PHPUnitJunitHudsonTestType', 'tusar': 'TUSARJunitHudsonTestType', 'unittest': 'UnitTestJunitHudsonTestType', 'valgrind': 'ValgrindJunitHudsonTestType', # FIXME should implement the 'custom' type } implemented_types = types_to_plugin_types.keys() # shortcut # Unit framework we are going to generate xml for supported_types = [] for configured_type in data['types']: type_name = configured_type.keys()[0] if type_name not in implemented_types: logger.warn("Requested xUnit type '%s' is not yet supported" % type_name) else: # Append for generation supported_types.append(configured_type) # Generate XML for each of the supported framework types for supported_type in supported_types: framework_name = supported_type.keys()[0] xmltypes = XML.SubElement(xunit, 'types') xmlframework = XML.SubElement(xmltypes, types_to_plugin_types[framework_name]) XML.SubElement(xmlframework, 'pattern').text = \ supported_type[framework_name].get('pattern', '') XML.SubElement(xmlframework, 'failedIfNotNew').text = \ str(supported_type[framework_name].get( 'requireupdate', 'true')).lower() XML.SubElement(xmlframework, 'deleteOutputFiles').text = \ str(supported_type[framework_name].get( 'deleteoutput', 'true')).lower() XML.SubElement(xmlframework, 'stopProcessingIfError').text = \ str(supported_type[framework_name].get( 'stoponerror', 'true')).lower() xmlthresholds = XML.SubElement(xunit, 'thresholds') if 'thresholds' in data: for t in data['thresholds']: if not ('failed' in t or 'skipped' in t): logger.warn( "Unrecognized threshold, should be 'failed' or 'skipped'") continue elname = "org.jenkinsci.plugins.xunit.threshold.%sThreshold" \ % t.keys()[0].title() el = XML.SubElement(xmlthresholds, elname) for threshold_name, threshold_value in t.values()[0].items(): # Normalize and craft the element name for this threshold elname = "%sThreshold" % threshold_name.lower().replace( 'new', 'New') XML.SubElement(el, elname).text = threshold_value # Whether to use percent of exact number of tests. # Thresholdmode is either: # - 1 : absolute (number of tests), default. # - 2 : relative (percentage of tests) thresholdmode = '1' if 'percent' == data.get('thresholdmode', 'number'): thresholdmode = '2' XML.SubElement(xunit, 'thresholdMode').text = \ thresholdmode def _violations_add_entry(xml_parent, name, data): vmin = data.get('min', 10) vmax = data.get('max', 999) vunstable = data.get('unstable', 999) pattern = data.get('pattern', None) entry = XML.SubElement(xml_parent, 'entry') XML.SubElement(entry, 'string').text = name tconfig = XML.SubElement(entry, 'hudson.plugins.violations.TypeConfig') XML.SubElement(tconfig, 'type').text = name XML.SubElement(tconfig, 'min').text = str(vmin) XML.SubElement(tconfig, 'max').text = str(vmax) XML.SubElement(tconfig, 'unstable').text = str(vunstable) XML.SubElement(tconfig, 'usePattern').text = 'false' if pattern: XML.SubElement(tconfig, 'pattern').text = pattern else: XML.SubElement(tconfig, 'pattern') def violations(parser, xml_parent, data): """yaml: violations Publish code style violations. Requires the Jenkins `Violations Plugin. `_ The violations component accepts any number of dictionaries keyed by the name of the violations system. The dictionary has the following values: :arg int min: sunny threshold :arg int max: stormy threshold :arg int unstable: unstable threshold :arg str pattern: report filename pattern Any system without a dictionary provided will use default values. Valid systems are: checkstyle, codenarc, cpd, cpplint, csslint, findbugs, fxcop, gendarme, jcreport, jslint, pep8, pmd, pylint, simian, stylecop Example:: publishers: - violations: pep8: min: 0 max: 1 unstable: 1 pattern: '**/pep8.txt' """ violations = XML.SubElement(xml_parent, 'hudson.plugins.violations.ViolationsPublisher') config = XML.SubElement(violations, 'config') suppressions = XML.SubElement(config, 'suppressions', {'class': 'tree-set'}) XML.SubElement(suppressions, 'no-comparator') configs = XML.SubElement(config, 'typeConfigs') XML.SubElement(configs, 'no-comparator') for name in [ 'checkstyle', 'codenarc', 'cpd', 'cpplint', 'csslint', 'findbugs', 'fxcop', 'gendarme', 'jcreport', 'jslint', 'pep8', 'pmd', 'pylint', 'simian', 'stylecop']: _violations_add_entry(configs, name, data.get(name, {})) XML.SubElement(config, 'limit').text = '100' XML.SubElement(config, 'sourcePathPattern') XML.SubElement(config, 'fauxProjectPath') XML.SubElement(config, 'encoding').text = 'default' def scp(parser, xml_parent, data): """yaml: scp Upload files via SCP Requires the Jenkins `SCP Plugin. `_ :arg str site: name of the scp site :arg str target: destination directory :arg str source: source path specifier :arg bool keep-hierarchy: keep the file hierarchy when uploading (default false) :arg bool copy-after-failure: copy files even if the job fails (default false) :arg bool copy-console: copy the console log (default false); if specified, omit 'target' Example:: publishers: - scp: site: 'example.com' target: 'dest/dir' source: 'base/source/dir/**' """ site = data['site'] scp = XML.SubElement(xml_parent, 'be.certipost.hudson.plugin.SCPRepositoryPublisher') XML.SubElement(scp, 'siteName').text = site entries = XML.SubElement(scp, 'entries') for entry in data['files']: entry_e = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry') XML.SubElement(entry_e, 'filePath').text = entry['target'] XML.SubElement(entry_e, 'sourceFile').text = entry.get('source', '') if entry.get('keep-hierarchy', False): XML.SubElement(entry_e, 'keepHierarchy').text = 'true' else: XML.SubElement(entry_e, 'keepHierarchy').text = 'false' if entry.get('copy-console', False): XML.SubElement(entry_e, 'copyConsoleLog').text = 'true' else: XML.SubElement(entry_e, 'copyConsoleLog').text = 'false' if entry.get('copy-after-failure', False): XML.SubElement(entry_e, 'copyAfterFailure').text = 'true' else: XML.SubElement(entry_e, 'copyAfterFailure').text = 'false' def pipeline(parser, xml_parent, data): """yaml: pipeline Specify a downstream project in a pipeline. Requires the Jenkins `Build Pipeline Plugin. `_ :Parameter: the name of the downstream project Example:: publishers: - pipleline: deploy You can build pipeline jobs that are re-usable in different pipelines by using a :ref:`job-template` to define the pipeline jobs, and variable substitution to specify the name of the downstream job in the pipeline. Job-specific substitutions are useful here (see :ref:`project`). See 'samples/pipeline.yaml' for an example pipeline implementation. """ if data != '': pippub = XML.SubElement(xml_parent, 'au.com.centrumsystems.hudson.plugin.' 'buildpipeline.trigger.BuildPipelineTrigger') XML.SubElement(pippub, 'downstreamProjectNames').text = data def email(parser, xml_parent, data): """yaml: email Email notifications on build failure. :arg str recipients: Recipient email addresses :arg bool notify-every-unstable-build: Send an email for every unstable build (default true) :arg bool send-to-individuals: Send an email to the individual who broke the build (default false) Example:: publishers: - email: recipients: breakage@example.com """ # TODO: raise exception if this is applied to a maven job mailer = XML.SubElement(xml_parent, 'hudson.tasks.Mailer') XML.SubElement(mailer, 'recipients').text = data['recipients'] # Note the logic reversal (included here to match the GUI if data.get('notify-every-unstable-build', True): XML.SubElement(mailer, 'dontNotifyEveryUnstableBuild').text = 'false' else: XML.SubElement(mailer, 'dontNotifyEveryUnstableBuild').text = 'true' XML.SubElement(mailer, 'sendToIndividuals').text = str( data.get('send-to-individuals', False)).lower() def claimbuild(parser, xml_parent, data): """yaml: claim-build Claim build failures Requires the Jenkins `Claim Plugin. `_ Example:: publishers: - claim-build """ XML.SubElement(xml_parent, 'hudson.plugins.claim.ClaimPublisher') def base_email_ext(parser, xml_parent, data, ttype): trigger = XML.SubElement(xml_parent, 'hudson.plugins.emailext.plugins.trigger.' + ttype) email = XML.SubElement(trigger, 'email') XML.SubElement(email, 'recipientList').text = '' XML.SubElement(email, 'subject').text = '$PROJECT_DEFAULT_SUBJECT' XML.SubElement(email, 'body').text = '$PROJECT_DEFAULT_CONTENT' XML.SubElement(email, 'sendToDevelopers').text = 'false' XML.SubElement(email, 'sendToRequester').text = 'false' XML.SubElement(email, 'includeCulprits').text = 'false' XML.SubElement(email, 'sendToRecipientList').text = 'true' def email_ext(parser, xml_parent, data): """yaml: email-ext Extend Jenkin's built in email notification Requires the Jenkins `Email-ext Plugin. `_ :arg str recipients: Comma separated list of emails :arg str subject: Subject for the email, can include variables like ${BUILD_NUMBER} or even groovy or javascript code :arg str body: Content for the body of the email, can include variables like ${BUILD_NUMBER}, but the real magic is using groovy or javascript to hook into the Jenkins API itself :arg bool unstable: Send an email for an unstable result (default false) :arg bool first-failure: Send an email for just the first failure (default false) :arg bool not-built: Send an email if not built (default false) :arg bool aborted: Send an email if the build is aborted (default false) :arg bool regression: Send an email if there is a regression (default false) :arg bool failure: Send an email if the build fails (default false) :arg bool improvement: Send an email if the build improves (default false) :arg bool still-failing: Send an email if the build is still failing (default false) :arg bool success: Send an email for a successful build (default false) :arg bool fixed: Send an email if the build is fixed (default false) :arg bool still-unstable: Send an email if the build is still unstable (default false) :arg bool pre-build: Send an email before the build (default false) Example:: publishers: - email-ext: recipients: foo@example.com, bar@example.com subject: Subject for Build ${BUILD_NUMBER} body: The build has finished unstable: true first-failure: true not-built: true aborted: true regression: true failure: true improvement: true still-failing: true success: true fixed: true still-unstable: true pre-build: true """ emailext = XML.SubElement(xml_parent, 'hudson.plugins.emailext.ExtendedEmailPublisher') XML.SubElement(emailext, 'recipientList').text = data['recipients'] ctrigger = XML.SubElement(emailext, 'configuredTriggers') if data.get('unstable', False): base_email_ext(parser, ctrigger, data, 'UnstableTrigger') if data.get('first-failure', False): base_email_ext(parser, ctrigger, data, 'FirstFailureTrigger') if data.get('not-built', False): base_email_ext(parser, ctrigger, data, 'NotBuiltTrigger') if data.get('aborted', False): base_email_ext(parser, ctrigger, data, 'AbortedTrigger') if data.get('regression', False): base_email_ext(parser, ctrigger, data, 'RegressionTrigger') if data.get('failure', False): base_email_ext(parser, ctrigger, data, 'FailureTrigger') if data.get('improvement', False): base_email_ext(parser, ctrigger, data, 'ImprovementTrigger') if data.get('still-failing', False): base_email_ext(parser, ctrigger, data, 'StillFailingTrigger') if data.get('success', False): base_email_ext(parser, ctrigger, data, 'SuccessTrigger') if data.get('fixed', False): base_email_ext(parser, ctrigger, data, 'FixedTrigger') if data.get('still-unstable', False): base_email_ext(parser, ctrigger, data, 'StillUnstableTrigger') if data.get('pre-build', False): base_email_ext(parser, ctrigger, data, 'PreBuildTrigger') XML.SubElement(emailext, 'contentType').text = 'default' XML.SubElement(emailext, 'defaultSubject').text = data.get('subject', '') XML.SubElement(emailext, 'defaultContent').text = data.get('body', '') XML.SubElement(emailext, 'attachmentsPattern').text = '' XML.SubElement(emailext, 'presendScript').text = '' def fingerprint(parser, xml_parent, data): """yaml: fingerprint Fingerprint files to track them across builds :arg str files: files to fingerprint, follows the @includes of Ant fileset (default is blank) :arg bool record-artifacts: fingerprint all archived artifacts (default false) Example:: publishers: - fingerprint: files: builddir/test*.xml record-artifacts: false """ finger = XML.SubElement(xml_parent, 'hudson.tasks.Fingerprinter') XML.SubElement(finger, 'targets').text = data.get('files', '') XML.SubElement(finger, 'recordBuildArtifacts').text = str(data.get( 'record-artifacts', False)).lower() def aggregate_tests(parser, xml_parent, data): """yaml: aggregate-tests Aggregate downstream test results :arg bool include-failed-builds: whether to include failed builds Example:: publishers: - aggregate-tests: include-failed-builds: true """ agg = XML.SubElement(xml_parent, 'hudson.tasks.test.AggregatedTestResultPublisher') XML.SubElement(agg, 'includeFailedBuilds').text = str(data.get( 'include-failed-builds', False)).lower() class Publishers(jenkins_jobs.modules.base.Base): sequence = 70 def gen_xml(self, parser, xml_parent, data): publishers = XML.SubElement(xml_parent, 'publishers') for action in data.get('publishers', []): self._dispatch('publisher', 'publishers', parser, publishers, action)