# 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. """ Builders define actions that the Jenkins job should execute. Examples include shell scripts or maven targets. The ``builders`` attribute in the :ref:`Job` definition accepts a list of builders to invoke. They may be components defined below, locally defined macros (using the top level definition of ``builder:``, or locally defined components found via the ``jenkins_jobs.builders`` entry point. **Component**: builders :Macro: builder :Entry Point: jenkins_jobs.builders Example:: job: name: test_job builders: - shell: "make test" """ import xml.etree.ElementTree as XML import jenkins_jobs.modules.base from jenkins_jobs.modules import hudson_model from jenkins_jobs.errors import JenkinsJobsException import logging logger = logging.getLogger(__name__) def shell(parser, xml_parent, data): """yaml: shell Execute a shell command. :arg str parameter: the shell command to execute Example: .. literalinclude:: /../../tests/builders/fixtures/shell.yaml :language: yaml """ shell = XML.SubElement(xml_parent, 'hudson.tasks.Shell') XML.SubElement(shell, 'command').text = data def python(parser, xml_parent, data): """yaml: python Execute a python command. Requires the Jenkins `Python plugin. `_ :arg str parameter: the python command to execute Example: .. literalinclude:: /../../tests/builders/fixtures/python.yaml :language: yaml """ python = XML.SubElement(xml_parent, 'hudson.plugins.python.Python') XML.SubElement(python, 'command').text = data def copyartifact(parser, xml_parent, data): """yaml: copyartifact Copy artifact from another project. Requires the Jenkins `Copy Artifact plugin. `_ :arg str project: Project to copy from :arg str filter: what files to copy :arg str target: Target base directory for copy, blank means use workspace :arg bool flatten: Flatten directories (default: false) :arg bool optional: If the artifact is missing (for any reason) and optional is true, the build won't fail because of this builder (default: false) :arg str which-build: which build to get artifacts from (optional, default last-successful) :arg str build-number: specifies the build number to get when when specific-build is specified as which-build :arg str permalink: specifies the permalink to get when permalink is specified as which-build :arg bool stable: specifies to get only last stable build when last-successful is specified as which-build :arg bool fallback-to-last-successful: specifies to fallback to last successful build when upstream-build is specified as which-build :arg string param: specifies to use a build parameter to get the build when build-param is specified as which-build :arg string parameter-filters: Filter matching jobs based on these parameters (optional) :which-build values: * **last-successful** * **specific-build** * **last-saved** * **upstream-build** * **permalink** * **workspace-latest** * **build-param** :permalink values: * **last** * **last-stable** * **last-successful** * **last-failed** * **last-unstable** * **last-unsuccessful** Example: .. literalinclude:: ../../tests/builders/fixtures/copy-artifact001.yaml :language: yaml """ t = XML.SubElement(xml_parent, 'hudson.plugins.copyartifact.CopyArtifact') # Warning: this only works with copy artifact version 1.26+, # for copy artifact version 1.25- the 'projectName' element needs # to be used instead of 'project' XML.SubElement(t, 'project').text = data["project"] XML.SubElement(t, 'filter').text = data.get("filter", "") XML.SubElement(t, 'target').text = data.get("target", "") flatten = data.get("flatten", False) XML.SubElement(t, 'flatten').text = str(flatten).lower() optional = data.get('optional', False) XML.SubElement(t, 'optional').text = str(optional).lower() XML.SubElement(t, 'parameters').text = data.get("parameter-filters", "") select = data.get('which-build', 'last-successful') selectdict = {'last-successful': 'StatusBuildSelector', 'specific-build': 'SpecificBuildSelector', 'last-saved': 'SavedBuildSelector', 'upstream-build': 'TriggeredBuildSelector', 'permalink': 'PermalinkBuildSelector', 'workspace-latest': 'WorkspaceSelector', 'build-param': 'ParameterizedBuildSelector'} if select not in selectdict: raise JenkinsJobsException("which-build entered is not valid must be " "one of: last-successful, specific-build, " "last-saved, upstream-build, permalink, " "workspace-latest, or build-param") permalink = data.get('permalink', 'last') permalinkdict = {'last': 'lastBuild', 'last-stable': 'lastStableBuild', 'last-successful': 'lastSuccessfulBuild', 'last-failed': 'lastFailedBuild', 'last-unstable': 'lastUnstableBuild', 'last-unsuccessful': 'lastUnsuccessfulBuild'} if permalink not in permalinkdict: raise JenkinsJobsException("permalink entered is not valid must be " "one of: last, last-stable, " "last-successful, last-failed, " "last-unstable, or last-unsuccessful") selector = XML.SubElement(t, 'selector', {'class': 'hudson.plugins.copyartifact.' + selectdict[select]}) if select == 'specific-build': XML.SubElement(selector, 'buildNumber').text = data['build-number'] if select == 'last-successful': XML.SubElement(selector, 'stable').text = str( data.get('stable', False)).lower() if select == 'upstream-build': XML.SubElement(selector, 'fallbackToLastSuccessful').text = str( data.get('fallback-to-last-successful', False)).lower() if select == 'permalink': XML.SubElement(selector, 'id').text = permalinkdict[permalink] if select == 'build-param': XML.SubElement(selector, 'parameterName').text = data['param'] def change_assembly_version(parser, xml_parent, data): """yaml: change-assembly-version Change the assembly version. Requires the Jenkins `Change Assembly Version. `_ :arg str version: Set the new version number for replace (default 1.0.0) :arg str assemblyFile: The file name to search (default AssemblyInfo.cs) Example: .. literalinclude:: \ /../../tests/builders/fixtures/changeassemblyversion001.yaml :language: yaml """ cav_builder_tag = 'org.jenkinsci.plugins.changeassemblyversion.' \ 'ChangeAssemblyVersion' cav = XML.SubElement(xml_parent, cav_builder_tag) XML.SubElement(cav, 'task').text = data.get('version', '1.0.0') XML.SubElement(cav, 'assemblyFile').text = str( data.get('assembly-file', 'AssemblyInfo.cs')) def ant(parser, xml_parent, data): """yaml: ant Execute an ant target. Requires the Jenkins `Ant Plugin. `_ To setup this builder you can either reference the list of targets or use named parameters. Below is a description of both forms: *1) Listing targets:* After the ant directive, simply pass as argument a space separated list of targets to build. :Parameter: space separated list of Ant targets Example to call two Ant targets: .. literalinclude:: ../../tests/builders/fixtures/ant001.yaml :language: yaml The build file would be whatever the Jenkins Ant Plugin is set to use per default (i.e build.xml in the workspace root). *2) Using named parameters:* :arg str targets: the space separated list of ANT targets. :arg str buildfile: the path to the ANT build file. :arg list properties: Passed to ant script using -Dkey=value (optional) :arg str ant-name: the name of the ant installation, (default 'default') (optional) :arg str java-opts: java options for ant, can have multiples, must be in quotes (optional) Example specifying the build file too and several targets: .. literalinclude:: ../../tests/builders/fixtures/ant002.yaml :language: yaml """ ant = XML.SubElement(xml_parent, 'hudson.tasks.Ant') if type(data) is str: # Support for short form: -ant: "target" data = {'targets': data} for setting, value in sorted(data.items()): if setting == 'targets': targets = XML.SubElement(ant, 'targets') targets.text = value if setting == 'buildfile': buildfile = XML.SubElement(ant, 'buildFile') buildfile.text = value if setting == 'properties': properties = data['properties'] prop_string = '' for prop, val in properties.items(): prop_string += "%s=%s\n" % (prop, val) prop_element = XML.SubElement(ant, 'properties') prop_element.text = prop_string if setting == 'java-opts': javaopts = data['java-opts'] jopt_string = ' '.join(javaopts) jopt_element = XML.SubElement(ant, 'antOpts') jopt_element.text = jopt_string XML.SubElement(ant, 'antName').text = data.get('ant-name', 'default') def trigger_builds(parser, xml_parent, data): """yaml: trigger-builds Trigger builds of other jobs. Requires the Jenkins `Parameterized Trigger Plugin. `_ :arg str project: the Jenkins project to trigger :arg str predefined-parameters: key/value pairs to be passed to the job (optional) :arg str property-file: Pass properties from file to the other job (optional) :arg bool current-parameters: Whether to include the parameters passed to the current build to the triggered job. :arg bool svn-revision: Whether to pass the svn revision to the triggered job :arg bool block: whether to wait for the triggered jobs to finish or not (default false) :arg bool same-node: Use the same node for the triggered builds that was used for this build (optional) Example: .. literalinclude:: /../../tests/builders/fixtures/trigger-builds001.yaml :language: yaml """ tbuilder = XML.SubElement(xml_parent, 'hudson.plugins.parameterizedtrigger.' 'TriggerBuilder') configs = XML.SubElement(tbuilder, 'configs') for project_def in data: if 'project' not in project_def or project_def['project'] == '': logger.debug("No project specified - skipping trigger-build") continue tconfig = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.' 'BlockableBuildTriggerConfig') tconfigs = XML.SubElement(tconfig, 'configs') if(project_def.get('current-parameters')): XML.SubElement(tconfigs, 'hudson.plugins.parameterizedtrigger.' 'CurrentBuildParameters') if(project_def.get('svn-revision')): XML.SubElement(tconfigs, 'hudson.plugins.parameterizedtrigger.' 'SubversionRevisionBuildParameters') if(project_def.get('same-node')): XML.SubElement(tconfigs, 'hudson.plugins.parameterizedtrigger.' 'NodeParameters') if 'property-file' in project_def: params = XML.SubElement(tconfigs, 'hudson.plugins.parameterizedtrigger.' 'FileBuildParameters') propertiesFile = XML.SubElement(params, 'propertiesFile') propertiesFile.text = project_def['property-file'] failOnMissing = XML.SubElement(params, 'failTriggerOnMissing') failOnMissing.text = 'true' 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'] if(len(list(tconfigs)) == 0): tconfigs.set('class', 'java.util.Collections$EmptyList') projects = XML.SubElement(tconfig, 'projects') projects.text = project_def['project'] condition = XML.SubElement(tconfig, 'condition') condition.text = 'ALWAYS' trigger_with_no_params = XML.SubElement(tconfig, 'triggerWithNoParameters') trigger_with_no_params.text = 'false' build_all_nodes_with_label = XML.SubElement(tconfig, 'buildAllNodesWithLabel') build_all_nodes_with_label.text = 'false' block = project_def.get('block', False) if(block): block = XML.SubElement(tconfig, 'block') bsft = XML.SubElement(block, 'buildStepFailureThreshold') XML.SubElement(bsft, 'name').text = \ hudson_model.FAILURE['name'] XML.SubElement(bsft, 'ordinal').text = \ hudson_model.FAILURE['ordinal'] XML.SubElement(bsft, 'color').text = \ hudson_model.FAILURE['color'] ut = XML.SubElement(block, 'unstableThreshold') XML.SubElement(ut, 'name').text = \ hudson_model.UNSTABLE['name'] XML.SubElement(ut, 'ordinal').text = \ hudson_model.UNSTABLE['ordinal'] XML.SubElement(ut, 'color').text = \ hudson_model.UNSTABLE['color'] ft = XML.SubElement(block, 'failureThreshold') XML.SubElement(ft, 'name').text = \ hudson_model.FAILURE['name'] XML.SubElement(ft, 'ordinal').text = \ hudson_model.FAILURE['ordinal'] XML.SubElement(ft, 'color').text = \ hudson_model.FAILURE['color'] # If configs is empty, remove the entire tbuilder tree. if(len(configs) == 0): logger.debug("Pruning empty TriggerBuilder tree.") xml_parent.remove(tbuilder) def builders_from(parser, xml_parent, data): """yaml: builders-from Use builders from another project. Requires the Jenkins `Template Project Plugin. `_ :arg str projectName: the name of the other project Example: .. literalinclude:: ../../tests/builders/fixtures/builders-from.yaml :language: yaml """ pbs = XML.SubElement(xml_parent, 'hudson.plugins.templateproject.ProxyBuilder') XML.SubElement(pbs, 'projectName').text = data def inject(parser, xml_parent, data): """yaml: inject Inject an environment for the job. Requires the Jenkins `EnvInject Plugin. `_ :arg str properties-file: the name of the property file (optional) :arg str properties-content: the properties content (optional) Example: .. literalinclude:: ../../tests/builders/fixtures/inject.yaml :language: yaml """ eib = XML.SubElement(xml_parent, 'EnvInjectBuilder') info = XML.SubElement(eib, 'info') jenkins_jobs.modules.base.add_nonblank_xml_subelement( info, 'propertiesFilePath', data.get('properties-file')) jenkins_jobs.modules.base.add_nonblank_xml_subelement( info, 'propertiesContent', data.get('properties-content')) def artifact_resolver(parser, xml_parent, data): """yaml: artifact-resolver Allows one to resolve artifacts from a maven repository like nexus (without having maven installed) Requires the Jenkins `Repository Connector Plugin `_ :arg bool fail-on-error: Whether to fail the build on error (default false) :arg bool repository-logging: Enable repository logging (default false) :arg str target-directory: Where to resolve artifacts to :arg list artifacts: list of artifacts to resolve :Artifact: * **group-id** (`str`) -- Group ID of the artifact * **artifact-id** (`str`) -- Artifact ID of the artifact * **version** (`str`) -- Version of the artifact * **classifier** (`str`) -- Classifier of the artifact (default '') * **extension** (`str`) -- Extension of the artifact (default 'jar') * **target-file-name** (`str`) -- What to name the artifact (default '') Example: .. literalinclude:: ../../tests/builders/fixtures/artifact-resolver.yaml :language: yaml """ ar = XML.SubElement(xml_parent, 'org.jvnet.hudson.plugins.repositoryconnector.' 'ArtifactResolver') XML.SubElement(ar, 'targetDirectory').text = data['target-directory'] artifacttop = XML.SubElement(ar, 'artifacts') artifacts = data['artifacts'] for artifact in artifacts: rcartifact = XML.SubElement(artifacttop, 'org.jvnet.hudson.plugins.' 'repositoryconnector.Artifact') XML.SubElement(rcartifact, 'groupId').text = artifact['group-id'] XML.SubElement(rcartifact, 'artifactId').text = artifact['artifact-id'] XML.SubElement(rcartifact, 'classifier').text = artifact.get( 'classifier', '') XML.SubElement(rcartifact, 'version').text = artifact['version'] XML.SubElement(rcartifact, 'extension').text = artifact.get( 'extension', 'jar') XML.SubElement(rcartifact, 'targetFileName').text = artifact.get( 'target-file-name', '') XML.SubElement(ar, 'failOnError').text = str(data.get( 'fail-on-error', False)).lower() XML.SubElement(ar, 'enableRepoLogging').text = str(data.get( 'repository-logging', False)).lower() XML.SubElement(ar, 'snapshotUpdatePolicy').text = 'never' XML.SubElement(ar, 'releaseUpdatePolicy').text = 'never' XML.SubElement(ar, 'snapshotChecksumPolicy').text = 'warn' XML.SubElement(ar, 'releaseChecksumPolicy').text = 'warn' def gradle(parser, xml_parent, data): """yaml: gradle Execute gradle tasks. Requires the Jenkins `Gradle Plugin. `_ :arg str tasks: List of tasks to execute :arg str gradle-name: Use a custom gradle name (optional) :arg bool wrapper: use gradle wrapper (default false) :arg bool executable: make gradlew executable (default false) :arg list switches: Switches for gradle, can have multiples :arg bool use-root-dir: Whether to run the gradle script from the top level directory or from a different location (default false) :arg str root-build-script-dir: If your workspace has the top-level build.gradle in somewhere other than the module root directory, specify the path (relative to the module root) here, such as ${workspace}/parent/ instead of just ${workspace}. Example: .. literalinclude:: ../../tests/builders/fixtures/gradle.yaml :language: yaml """ gradle = XML.SubElement(xml_parent, 'hudson.plugins.gradle.Gradle') XML.SubElement(gradle, 'description').text = '' XML.SubElement(gradle, 'tasks').text = data['tasks'] XML.SubElement(gradle, 'buildFile').text = '' XML.SubElement(gradle, 'rootBuildScriptDir').text = data.get( 'root-build-script-dir', '') XML.SubElement(gradle, 'gradleName').text = data.get( 'gradle-name', '') XML.SubElement(gradle, 'useWrapper').text = str(data.get( 'wrapper', False)).lower() XML.SubElement(gradle, 'makeExecutable').text = str(data.get( 'executable', False)).lower() switch_string = '\n'.join(data.get('switches', [])) XML.SubElement(gradle, 'switches').text = switch_string XML.SubElement(gradle, 'fromRootBuildScriptDir').text = str(data.get( 'use-root-dir', False)).lower() def _groovy_common_scriptSource(data): """Helper function to generate the XML element common to groovy builders """ scriptSource = XML.Element("scriptSource") if 'command' in data and 'file' in data: raise JenkinsJobsException("Use just one of 'command' or 'file'") if 'command' in data: command = XML.SubElement(scriptSource, 'command') command.text = str(data['command']) scriptSource.set('class', 'hudson.plugins.groovy.StringScriptSource') elif 'file' in data: scriptFile = XML.SubElement(scriptSource, 'scriptFile') scriptFile.text = str(data['file']) scriptSource.set('class', 'hudson.plugins.groovy.FileScriptSource') else: raise JenkinsJobsException("A groovy command or file is required") return scriptSource def groovy(parser, xml_parent, data): """yaml: groovy Execute a groovy script or command. Requires the Jenkins `Groovy Plugin `_ :arg str file: Groovy file to run. (Alternative: you can chose a command instead) :arg str command: Groovy command to run. (Alternative: you can chose a script file instead) :arg str version: Groovy version to use. (default '(Default)') :arg str parameters: Parameters for the Groovy executable. (optional) :arg str script-parameters: These parameters will be passed to the script. (optional) :arg str properties: Instead of passing properties using the -D parameter you can define them here. (optional) :arg str java-opts: Direct access to JAVA_OPTS. Properties allows only -D properties, while sometimes also other properties like -XX need to be setup. It can be done here. This line is appended at the end of JAVA_OPTS string. (optional) :arg str class-path: Specify script classpath here. Each line is one class path item. (optional) Examples: .. literalinclude:: ../../tests/builders/fixtures/groovy001.yaml :language: yaml .. literalinclude:: ../../tests/builders/fixtures/groovy002.yaml :language: yaml """ root_tag = 'hudson.plugins.groovy.Groovy' groovy = XML.SubElement(xml_parent, root_tag) groovy.append(_groovy_common_scriptSource(data)) XML.SubElement(groovy, 'groovyName').text = \ str(data.get('version', "(Default)")) XML.SubElement(groovy, 'parameters').text = str(data.get('parameters', "")) XML.SubElement(groovy, 'scriptParameters').text = \ str(data.get('script-parameters', "")) XML.SubElement(groovy, 'properties').text = str(data.get('properties', "")) XML.SubElement(groovy, 'javaOpts').text = str(data.get('java-opts', "")) XML.SubElement(groovy, 'classPath').text = str(data.get('class-path', "")) def system_groovy(parser, xml_parent, data): """yaml: system-groovy Execute a system groovy script or command. Requires the Jenkins `Groovy Plugin `_ :arg str file: Groovy file to run. (Alternative: you can chose a command instead) :arg str command: Groovy command to run. (Alternative: you can chose a script file instead) :arg str bindings: Define variable bindings (in the properties file format). Specified variables can be addressed from the script. (optional) :arg str class-path: Specify script classpath here. Each line is one class path item. (optional) Examples: .. literalinclude:: ../../tests/builders/fixtures/system-groovy001.yaml :language: yaml .. literalinclude:: ../../tests/builders/fixtures/system-groovy002.yaml :language: yaml """ root_tag = 'hudson.plugins.groovy.SystemGroovy' sysgroovy = XML.SubElement(xml_parent, root_tag) sysgroovy.append(_groovy_common_scriptSource(data)) XML.SubElement(sysgroovy, 'bindings').text = str(data.get('bindings', "")) XML.SubElement(sysgroovy, 'classpath').text = \ str(data.get('class-path', "")) def batch(parser, xml_parent, data): """yaml: batch Execute a batch command. :Parameter: the batch command to execute Example: .. literalinclude:: ../../tests/builders/fixtures/batch.yaml :language: yaml """ batch = XML.SubElement(xml_parent, 'hudson.tasks.BatchFile') XML.SubElement(batch, 'command').text = data def msbuild(parser, xml_parent, data): """yaml: msbuild Build .NET project using msbuild. Requires the `Jenkins MSBuild Plugin `_. :arg str msbuild-version: which msbuild configured in Jenkins to use (optional) :arg str solution-file: location of the solution file to build :arg str extra-parameters: extra parameters to pass to msbuild (optional) :arg bool pass-build-variables: should build variables be passed to msbuild (default true) :arg bool continue-on-build-failure: should the build continue if msbuild returns an error (default false) Example: .. literalinclude:: ../../tests/builders/fixtures/msbuild.yaml :language: yaml """ msbuilder = XML.SubElement(xml_parent, 'hudson.plugins.msbuild.MsBuildBuilder') XML.SubElement(msbuilder, 'msBuildName').text = data.get('msbuild-version', '(Default)') XML.SubElement(msbuilder, 'msBuildFile').text = data['solution-file'] XML.SubElement(msbuilder, 'cmdLineArgs').text = \ data.get('extra-parameters', '') XML.SubElement(msbuilder, 'buildVariablesAsProperties').text = \ str(data.get('pass-build-variables', True)).lower() XML.SubElement(msbuilder, 'continueOnBuildFailure').text = \ str(data.get('continue-on-build-failure', False)).lower() def create_builders(parser, step): dummy_parent = XML.Element("dummy") parser.registry.dispatch('builder', parser, dummy_parent, step) return list(dummy_parent) def conditional_step(parser, xml_parent, data): """yaml: conditional-step Conditionally execute some build steps. Requires the Jenkins `Conditional BuildStep Plugin `_. Depending on the number of declared steps, a `Conditional step (single)` or a `Conditional steps (multiple)` is created in Jenkins. :arg str condition-kind: Condition kind that must be verified before the steps are executed. Valid values and their additional attributes are described in the conditions_ table. :arg str on-evaluation-failure: What should be the outcome of the build if the evaluation of the condition fails. Possible values are `fail`, `mark-unstable`, `run-and-mark-unstable`, `run` and `dont-run`. Default is `fail`. :arg list steps: List of steps to run if the condition is verified. Items in the list can be any builder known by Jenkins Job Builder. .. _conditions: ================== ==================================================== Condition kind Description ================== ==================================================== always Condition is always verified never Condition is never verified boolean-expression Run the step if the expression expends to a representation of true :condition-expression: Expression to expand current-status Run the build step if the current build status is within the configured range :condition-worst: Accepted values are SUCCESS, UNSTABLE, FAILURE, NOT_BUILD, ABORTED :condition-best: Accepted values are SUCCESS, UNSTABLE, FAILURE, NOT_BUILD, ABORTED shell Run the step if the shell command succeed :condition-command: Shell command to execute windows-shell Similar to shell, except that commands will be executed by cmd, under Windows :condition-command: Command to execute file-exists Run the step if a file exists :condition-filename: Check existence of this file :condition-basedir: If condition-filename is relative, it will be considered relative to either `workspace`, `artifact-directory`, or `jenkins-home`. Default is `workspace`. ================== ==================================================== Example: .. literalinclude:: \ /../../tests/builders/fixtures/conditional-step-success-failure.yaml :language: yaml """ def build_condition(cdata): kind = cdata['condition-kind'] ctag = XML.SubElement(root_tag, condition_tag) if kind == "always": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.core.AlwaysRun') elif kind == "never": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.core.NeverRun') elif kind == "boolean-expression": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.core.' 'BooleanCondition') XML.SubElement(ctag, "token").text = cdata['condition-expression'] elif kind == "current-status": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.core.' 'StatusCondition') wr = XML.SubElement(ctag, 'worstResult') wr_name = cdata['condition-worst'] if wr_name not in hudson_model.THRESHOLDS: raise JenkinsJobsException( "threshold must be one of %s" % ", ".join(hudson_model.THRESHOLDS.keys())) wr_threshold = hudson_model.THRESHOLDS[wr_name] XML.SubElement(wr, "name").text = wr_threshold['name'] XML.SubElement(wr, "ordinal").text = wr_threshold['ordinal'] XML.SubElement(wr, "color").text = wr_threshold['color'] XML.SubElement(wr, "completeBuild").text = \ str(wr_threshold['complete']).lower() br = XML.SubElement(ctag, 'bestResult') br_name = cdata['condition-best'] if not br_name in hudson_model.THRESHOLDS: raise JenkinsJobsException( "threshold must be one of %s" % ", ".join(hudson_model.THRESHOLDS.keys())) br_threshold = hudson_model.THRESHOLDS[br_name] XML.SubElement(br, "name").text = br_threshold['name'] XML.SubElement(br, "ordinal").text = br_threshold['ordinal'] XML.SubElement(br, "color").text = br_threshold['color'] XML.SubElement(br, "completeBuild").text = \ str(wr_threshold['complete']).lower() elif kind == "shell": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.contributed.' 'ShellCondition') XML.SubElement(ctag, "command").text = cdata['condition-command'] elif kind == "windows-shell": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.contributed.' 'BatchFileCondition') XML.SubElement(ctag, "command").text = cdata['condition-command'] elif kind == "file-exists": ctag.set('class', 'org.jenkins_ci.plugins.run_condition.core.' 'FileExistsCondition') XML.SubElement(ctag, "file").text = cdata['condition-filename'] basedir = cdata.get('condition-basedir', 'workspace') basedir_tag = XML.SubElement(ctag, "baseDir") if "workspace" == basedir: basedir_tag.set('class', 'org.jenkins_ci.plugins.run_condition.common.' 'BaseDirectory$Workspace') elif "artifact-directory" == basedir: basedir_tag.set('class', 'org.jenkins_ci.plugins.run_condition.common.' 'BaseDirectory$ArtifactsDir') elif "jenkins-home" == basedir: basedir_tag.set('class', 'org.jenkins_ci.plugins.run_condition.common.' 'BaseDirectory$JenkinsHome') def build_step(parent, step): for edited_node in create_builders(parser, step): if not has_multiple_steps: edited_node.set('class', edited_node.tag) edited_node.tag = 'buildStep' parent.append(edited_node) cond_builder_tag = 'org.jenkinsci.plugins.conditionalbuildstep.' \ 'singlestep.SingleConditionalBuilder' cond_builders_tag = 'org.jenkinsci.plugins.conditionalbuildstep.' \ 'ConditionalBuilder' steps = data['steps'] has_multiple_steps = len(steps) > 1 if has_multiple_steps: root_tag = XML.SubElement(xml_parent, cond_builders_tag) steps_parent = XML.SubElement(root_tag, "conditionalbuilders") condition_tag = "runCondition" else: root_tag = XML.SubElement(xml_parent, cond_builder_tag) steps_parent = root_tag condition_tag = "condition" build_condition(data) evaluation_classes_pkg = 'org.jenkins_ci.plugins.run_condition' evaluation_classes = { 'fail': evaluation_classes_pkg + '.BuildStepRunner$Fail', 'mark-unstable': evaluation_classes_pkg + '.BuildStepRunner$Unstable', 'run-and-mark-unstable': evaluation_classes_pkg + '.BuildStepRunner$RunUnstable', 'run': evaluation_classes_pkg + '.BuildStepRunner$Run', 'dont-run': evaluation_classes_pkg + '.BuildStepRunner$DontRun', } evaluation_class = evaluation_classes[data.get('on-evaluation-failure', 'fail')] XML.SubElement(root_tag, "runner").set('class', evaluation_class) for step in steps: build_step(steps_parent, step) def maven_target(parser, xml_parent, data): """yaml: maven-target Execute top-level Maven targets :arg str goals: Goals to execute :arg str properties: Properties for maven, can have multiples :arg str pom: Location of pom.xml (default 'pom.xml') :arg bool private-repository: Use private maven repository for this job (default false) :arg str maven-version: Installation of maven which should be used (optional) :arg str java-opts: java options for maven, can have multiples, must be in quotes (optional) :arg str settings: Path to use as user settings.xml (optional) :arg str global-settings: Path to use as global settings.xml (optional) Example: .. literalinclude:: /../../tests/builders/fixtures/maven-target-doc.yaml :language: yaml """ maven = XML.SubElement(xml_parent, 'hudson.tasks.Maven') XML.SubElement(maven, 'targets').text = data['goals'] prop_string = '\n'.join(data.get('properties', [])) XML.SubElement(maven, 'properties').text = prop_string if 'maven-version' in data: XML.SubElement(maven, 'mavenName').text = str(data['maven-version']) if 'pom' in data: XML.SubElement(maven, 'pom').text = str(data['pom']) use_private = str(data.get('private-repository', False)).lower() XML.SubElement(maven, 'usePrivateRepository').text = use_private if 'java-opts' in data: javaoptions = ' '.join(data.get('java-opts', [])) XML.SubElement(maven, 'jvmOptions').text = javaoptions if 'settings' in data: settings = XML.SubElement(maven, 'settings', {'class': 'jenkins.mvn.FilePathSettingsProvider'}) XML.SubElement(settings, 'path').text = data.get('settings') else: XML.SubElement(maven, 'settings', {'class': 'jenkins.mvn.DefaultSettingsProvider'}) if 'global-settings' in data: provider = 'jenkins.mvn.FilePathGlobalSettingsProvider' global_settings = XML.SubElement(maven, 'globalSettings', {'class': provider}) XML.SubElement(global_settings, 'path').text = data.get( 'global-settings') else: XML.SubElement(maven, 'globalSettings', {'class': 'jenkins.mvn.DefaultGlobalSettingsProvider'}) def multijob(parser, xml_parent, data): """yaml: multijob Define a multijob phase. Requires the Jenkins `Multijob Plugin. `_ This builder may only be used in \ :py:class:`jenkins_jobs.modules.project_multijob.MultiJob` projects. :arg str name: MultiJob phase name :arg str condition: when to trigger the other job (default 'SUCCESSFUL') :arg list projects: list of projects to include in the MultiJob phase :Project: * **name** (`str`) -- Project name * **current-parameters** (`bool`) -- Pass current build parameters to the other job (default false) * **node-label-name** (`str`) -- Define a list of nodes on which the job should be allowed to be executed on. Requires NodeLabel Parameter Plugin (optional) * **node-label** (`str`) -- Define a label of 'Restrict where this project can be run' on the fly. Requires NodeLabel Parameter Plugin (optional) * **git-revision** (`bool`) -- Pass current git-revision to the other job (default false) * **property-file** (`str`) -- Pass properties from file to the other job (optional) * **predefined-parameters** (`str`) -- Pass predefined parameters to the other job (optional) * **kill-phase-on** (`str`) -- Stop the phase execution on specific job status. Can be 'FAILURE', 'UNSTABLE', 'NEVER'. (optional) Example: .. literalinclude:: /../../tests/builders/fixtures/multibuild.yaml :language: yaml """ builder = XML.SubElement(xml_parent, 'com.tikal.jenkins.plugins.multijob.' 'MultiJobBuilder') XML.SubElement(builder, 'phaseName').text = data['name'] condition = data.get('condition', 'SUCCESSFUL') XML.SubElement(builder, 'continuationCondition').text = condition phaseJobs = XML.SubElement(builder, 'phaseJobs') kill_status_list = ('FAILURE', 'UNSTABLE', 'NEVER') for project in data.get('projects', []): phaseJob = XML.SubElement(phaseJobs, 'com.tikal.jenkins.plugins.' 'multijob.PhaseJobsConfig') XML.SubElement(phaseJob, 'jobName').text = project['name'] # Pass through the current build params currParams = str(project.get('current-parameters', False)).lower() XML.SubElement(phaseJob, 'currParams').text = currParams # Pass through other params configs = XML.SubElement(phaseJob, 'configs') nodeLabelName = project.get('node-label-name') nodeLabel = project.get('node-label') if (nodeLabelName and nodeLabel): node = XML.SubElement( configs, 'org.jvnet.jenkins.plugins.nodelabelparameter.' 'parameterizedtrigger.NodeLabelBuildParameter') XML.SubElement(node, 'name').text = nodeLabelName XML.SubElement(node, 'nodeLabel').text = nodeLabel # Git Revision if project.get('git-revision', False): param = XML.SubElement(configs, 'hudson.plugins.git.' 'GitRevisionBuildParameters') combine = XML.SubElement(param, 'combineQueuedCommits') combine.text = 'false' # Properties File properties_file = project.get('property-file', False) if properties_file: param = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.' 'FileBuildParameters') propertiesFile = XML.SubElement(param, 'propertiesFile') propertiesFile.text = properties_file failOnMissing = XML.SubElement(param, 'failTriggerOnMissing') failOnMissing.text = 'true' # Predefined Parameters predefined_parameters = project.get('predefined-parameters', False) if predefined_parameters: param = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.' 'PredefinedBuildParameters') properties = XML.SubElement(param, 'properties') properties.text = predefined_parameters # Kill phase on job status kill_status = project.get('kill-phase-on') if kill_status is not None: kill_status = kill_status.upper() if kill_status not in kill_status_list: raise JenkinsJobsException( 'multijob kill-phase-on must be one of: %s' + ','.join(kill_status_list)) XML.SubElement( phaseJob, 'killPhaseOnJobResultCondition' ).text = kill_status def grails(parser, xml_parent, data): """yaml: grails Execute a grails build step. Requires the `Jenkins Grails Plugin. `_ :arg bool use-wrapper: Use a grails wrapper (default false) :arg str name: Select a grails installation to use (optional) :arg bool force-upgrade: Run 'grails upgrade --non-interactive' first (default false) :arg bool non-interactive: append --non-interactive to all build targets (default false) :arg str targets: Specify target(s) to run separated by spaces :arg str server-port: Specify a value for the server.port system property (optional) :arg str work-dir: Specify a value for the grails.work.dir system property (optional) :arg str project-dir: Specify a value for the grails.project.work.dir system property (optional) :arg str base-dir: Specify a path to the root of the Grails project (optional) :arg str properties: Additional system properties to set (optional) :arg bool plain-output: append --plain-output to all build targets (default false) :arg bool stack-trace: append --stack-trace to all build targets (default false) :arg bool verbose: append --verbose to all build targets (default false) :arg bool refresh-dependencies: append --refresh-dependencies to all build targets (default false) Example: .. literalinclude:: ../../tests/builders/fixtures/grails.yaml :language: yaml """ grails = XML.SubElement(xml_parent, 'com.g2one.hudson.grails.' 'GrailsBuilder') XML.SubElement(grails, 'targets').text = data['targets'] XML.SubElement(grails, 'name').text = data.get( 'name', '(Default)') XML.SubElement(grails, 'grailsWorkDir').text = data.get( 'work-dir', '') XML.SubElement(grails, 'projectWorkDir').text = data.get( 'project-dir', '') XML.SubElement(grails, 'projectBaseDir').text = data.get( 'base-dir', '') XML.SubElement(grails, 'serverPort').text = data.get( 'server-port', '') XML.SubElement(grails, 'properties').text = data.get( 'properties', '') XML.SubElement(grails, 'forceUpgrade').text = str( data.get('force-upgrade', False)).lower() XML.SubElement(grails, 'nonInteractive').text = str( data.get('non-interactive', False)).lower() XML.SubElement(grails, 'useWrapper').text = str( data.get('use-wrapper', False)).lower() XML.SubElement(grails, 'plainOutput').text = str( data.get('plain-output', False)).lower() XML.SubElement(grails, 'stackTrace').text = str( data.get('stack-trace', False)).lower() XML.SubElement(grails, 'verbose').text = str( data.get('verbose', False)).lower() XML.SubElement(grails, 'refreshDependencies').text = str( data.get('refresh-dependencies', False)).lower() def sbt(parser, xml_parent, data): """yaml: sbt Execute a sbt build step. Requires the Jenkins `Sbt Plugin. `_ :arg str name: Select a sbt installation to use. If no name is provided, the first in the list of defined SBT builders will be used. (default to first in list) :arg str jvm-flags: Parameters to pass to the JVM (default '') :arg str actions: Select the sbt tasks to execute (default '') :arg str sbt-flags: Add flags to SBT launcher (default '-Dsbt.log.noformat=true') :arg str subdir-path: Path relative to workspace to run sbt in (default '') Example: .. literalinclude:: ../../tests/builders/fixtures/sbt.yaml :language: yaml """ sbt = XML.SubElement(xml_parent, 'org.jvnet.hudson.plugins.' 'SbtPluginBuilder') XML.SubElement(sbt, 'name').text = data.get( 'name', '') XML.SubElement(sbt, 'jvmFlags').text = data.get( 'jvm-flags', '') XML.SubElement(sbt, 'sbtFlags').text = data.get( 'sbt-flags', '-Dsbt.log.noformat=true') XML.SubElement(sbt, 'actions').text = data.get( 'actions', '') XML.SubElement(sbt, 'subdirPath').text = data.get( 'subdir-path', '') def critical_block_start(parser, xml_parent, data): """yaml: critical-block-start Designate the start of a critical block. Must be used in conjuction with critical-block-end. Must also add a build wrapper (exclusion), specifying the resources that control the critical block. Otherwise, this will have no effect. Requires Jenkins `Exclusion Plugin. `_ Example:: wrappers: - exclusion: resources: myresource1 builders: - critical-block-start - ... other builders - critical-block-end """ cbs = \ XML.SubElement(xml_parent, 'org.jvnet.hudson.plugins.exclusion.CriticalBlockStart') cbs.set('plugin', 'Exclusion') def critical_block_end(parser, xml_parent, data): """yaml: critical-block-end Designate the end of a critical block. Must be used in conjuction with critical-block-start. Must also add a build wrapper (exclusion), specifying the resources that control the critical block. Otherwise, this will have no effect. Requires Jenkins `Exclusion Plugin. `_ Example:: wrappers: - exclusion: resources: myresource1 builders: - critical-block-start - ... other builders - critical-block-end """ cbs = \ XML.SubElement(xml_parent, 'org.jvnet.hudson.plugins.exclusion.CriticalBlockEnd') cbs.set('plugin', 'Exclusion') class Builders(jenkins_jobs.modules.base.Base): sequence = 60 component_type = 'builder' component_list_type = 'builders' def gen_xml(self, parser, xml_parent, data): for alias in ['prebuilders', 'builders', 'postbuilders']: if alias in data: builders = XML.SubElement(xml_parent, alias) for builder in data[alias]: self.registry.dispatch('builder', parser, builders, builder) # Make sure freestyle projects always have a entry # or Jenkins v1.472 (at least) will NPE. project_type = data.get('project-type', 'freestyle') if project_type in ('freestyle', 'matrix') and 'builders' not in data: XML.SubElement(xml_parent, 'builders') def shining_panda(parser, xml_parent, data): """yaml: shining-panda Execute a command inside various python environments. Requires the Jenkins `ShiningPanda plugin `_. :arg str build-environment: Building environment to set up (Required). :build-environment values: * **python**: Use a python installation configured in Jenkins. * **custom**: Use a manually installed python. * **virtualenv**: Create a virtualenv For the **python** environment :arg str python-version: Name of the python installation to use. Must match one of the configured installations on server \ configuration (default: System-CPython-2.7) For the **custom** environment: :arg str home: path to the home folder of the custom installation \ (Required) For the **virtualenv** environment: :arg str python-version: Name of the python installation to use. Must match one of the configured installations on server \ configuration (default: System-CPython-2.7) :arg str name: Name of this virtualenv. Two virtualenv builders with \ the same name will use the same virtualenv installation (optional) :arg bool clear: If true, delete and recreate virtualenv on each build. (default: false) :arg bool use-distribute: if true use distribute, if false use \ setuptools. (default: true) :arg bool system-site-packages: if true, give access to the global site-packages directory to the virtualenv. (default: false) Common to all environments: :arg str nature: Nature of the command field. (default: shell) :nature values: * **shell**: execute the Command contents with default shell * **xshell**: like **shell** but performs platform conversion \ first * **python**: execute the Command contents with the Python \ executable :arg str command: The command to execute :arg bool ignore-exit-code: mark the build as failure if any of the commands exits with a non-zero exit code. (default: false) Examples: .. literalinclude:: \ /../../tests/builders/fixtures/shining-panda-pythonenv.yaml :language: yaml .. literalinclude:: \ /../../tests/builders/fixtures/shining-panda-customenv.yaml :language: yaml .. literalinclude:: \ /../../tests/builders/fixtures/shining-panda-virtualenv.yaml :language: yaml """ pluginelementpart = 'jenkins.plugins.shiningpanda.builders.' buildenvdict = {'custom': 'CustomPythonBuilder', 'virtualenv': 'VirtualenvBuilder', 'python': 'PythonBuilder'} envs = (buildenvdict.keys()) try: buildenv = data['build-environment'] except KeyError: raise JenkinsJobsException("A build-environment is required") if buildenv not in envs: errorstring = ("build-environment '%s' is invalid. Must be one of %s." % (buildenv, ', '.join("'{0}'".format(env) for env in envs))) raise JenkinsJobsException(errorstring) t = XML.SubElement(xml_parent, '%s%s' % (pluginelementpart, buildenvdict[buildenv])) if buildenv in ('python', 'virtualenv'): XML.SubElement(t, 'pythonName').text = data.get("python-version", "System-CPython-2.7") if buildenv in ('custom'): try: homevalue = data["home"] except KeyError: raise JenkinsJobsException("'home' argument is required for the" " 'custom' environment") XML.SubElement(t, 'home').text = homevalue if buildenv in ('virtualenv'): XML.SubElement(t, 'home').text = data.get("name", "") clear = data.get("clear", False) XML.SubElement(t, 'clear').text = str(clear).lower() use_distribute = data.get('use-distribute', False) XML.SubElement(t, 'useDistribute').text = str(use_distribute).lower() system_site_packages = data.get('system-site-packages', False) XML.SubElement(t, 'systemSitePackages').text = str( system_site_packages).lower() # Common arguments nature = data.get('nature', 'shell') naturetuple = ('shell', 'xshell', 'python') if nature not in naturetuple: errorstring = ("nature '%s' is not valid: must be one of %s." % (nature, ', '.join("'{0}'".format(naturevalue) for naturevalue in naturetuple))) raise JenkinsJobsException(errorstring) XML.SubElement(t, 'nature').text = nature XML.SubElement(t, 'command').text = data.get("command", "") ignore_exit_code = data.get('ignore-exit-code', False) XML.SubElement(t, 'ignoreExitCode').text = str(ignore_exit_code).lower() def managed_script(parser, xml_parent, data): """yaml: managed-script This step allows to reference and execute a centrally managed script within your build. Requires the Jenkins `Managed Script Plugin. `_ :arg str script-id: Id of script to execute (Required) :arg str type: Type of managed file (default: script) :type values: * **batch**: Execute managed windows batch * **script**: Execute managed script :arg list args: Arguments to be passed to referenced script Example: .. literalinclude:: /../../tests/builders/fixtures/managed-script.yaml :language: yaml .. literalinclude:: /../../tests/builders/fixtures/managed-winbatch.yaml :language: yaml """ step_type = data.get('type', 'script').lower() if step_type == 'script': step = 'ScriptBuildStep' script_tag = 'buildStepId' elif step_type == 'batch': step = 'WinBatchBuildStep' script_tag = 'command' else: raise JenkinsJobsException("type entered is not valid must be " "one of: script or batch") ms = XML.SubElement(xml_parent, 'org.jenkinsci.plugins.managedscripts.' + step) try: script_id = data['script-id'] except KeyError: raise JenkinsJobsException("A script-id is required for " "managed-script") XML.SubElement(ms, script_tag).text = script_id args = XML.SubElement(ms, 'buildStepArgs') for arg in data.get('args', []): XML.SubElement(args, 'string').text = arg