Pipeline and plugins
Jenkins Pipeline is a suite of plugins which supports implementing and integrating continuous delivery pipelines into Jenkins.
Not all Jenkins plugins are compatible with Jenkins Pipeline (check COMPATIBILITY.md).
With “old” Jenkins plugins installation and usage is straightforward. But it’s not true for Jenkins Pipeline. Even if you find a plugin which is compatible with Jenkins Pipeline, it’s not always obvious how to use it in Jenkinsfile.
How to use Jenkins plugins in Jenkinsfile
There are different ways to identify how to use a Jenkins plugin in Jenkinsfile.
Easy way #1
Easiest way is when plugin maintainer makes the Jenkins plugin compatible with Jenkins Pipeline and includes an example of usage directly on plugin wiki page.
Few examples of plugins which have Jenkins Pipeline support examples on wiki pages:
Easy way #2
We can use snippet generator from Jenkins.
Hard way
It’s harder when you find that plugin is compatible with Jenkins Pipeline in COMPATIBILITY.md document, but there’re no examples of how to use it in Jenkins Pipeline.
To succeed with this task we need to have a background of what makes Jenkins plugin compatible with Jenkins Pipeline.
Such information can be found in workflow-step-api-plugin
repository README.md file.
The most important parts for us are:
Extend
Step
. Define mandatory parameters in a@DataBoundConstructor
. Define optional parameters using@DataBoundSetter
. (Both need matching getters.)
Extend
StepDescriptor
. Besides a display name, pick a function name which will be used from Groovy scripts.
Build steps and post-build actions in Jenkins core as of 2.2, and in some plugins according to their individual changelogs, have defined
symbols
which allow for a more concise syntax. Snippet Generator will offer this when available.
node { sh 'make something' archiveArtifacts 'something' }
Also, we need to refer to Jenkins Pipeline plugin DEVGUIDE.md#build steps:
See the user documentation for background. The metastep is
step
. To add support for use of a Builder or Publisher from a pipeline, depend on Jenkins 1.577+, typically 1.580.1 (tips). Then implementSimpleBuildStep
, following the guidelines in its Javadoc. Also, prefer@DataBoundSetters
to a sprawling@DataBoundConstructor
.
From the documentation is clear what we should look for in Jenkins plugin source code to find out how to execute it as a Jenkins Pipeline step
.
-
STEP_CLASS_NAME
(check example from below)- find a class in plugin repository which extends from
SimpleBuildStep
- find a class in plugin repository which extends from
-
STEP_PARAMETERS
(check example from below)- check constructor marked with
@DataBoundConstructor
annotation for mandatory step parameters - check methods marked with
@DataBoundSetter
annotation
- check constructor marked with
-
STEP_FUNCTION_NAME
(check example from below)- check for
@Symbol
annotation on top of descriptor class
- check for
This should be enough to call plugin step from Jenkinsfile:
step([$class : 'STEP_CLASS_NAME', STEP_PARAMETERS])
or
STEP_FUNCTION_NAME STEP_PARAMETERS
Let’s take JaCoCo as an example (we made sure first that plugin is compatible with Jenkins Pipeline). We navigate to plugin source code and try to find required information.
-
Class which extends from
SimpleBuildStep
public class JacocoPublisher extends Recorder implements SimpleBuildStep {
-
Parameters
-
DataBoundConstructor
without parameters which sets defaults@DataBoundConstructor public JacocoPublisher() { this.execPattern = "**/**.exec"; this.classPattern = "**/classes"; this.sourcePattern = "**/src/main/java"; this.inclusionPattern = ""; this.exclusionPattern = ""; this.skipCopyOfSrcFiles = false; this.minimumInstructionCoverage = "0"; this.minimumBranchCoverage = "0"; this.minimumComplexityCoverage = "0"; this.minimumLineCoverage = "0"; this.minimumMethodCoverage = "0"; this.minimumClassCoverage = "0"; this.maximumInstructionCoverage = "0"; this.maximumBranchCoverage = "0"; this.maximumComplexityCoverage = "0"; this.maximumLineCoverage = "0"; this.maximumMethodCoverage = "0"; this.maximumClassCoverage = "0"; this.changeBuildStatus = false; this.deltaInstructionCoverage = "0"; this.deltaBranchCoverage = "0"; this.deltaComplexityCoverage = "0"; this.deltaLineCoverage = "0"; this.deltaMethodCoverage = "0"; this.deltaClassCoverage = "0"; this.buildOverBuild = false; }
-
List of
DataBoundSetter
’s... @DataBoundSetter public void setExecPattern(String execPattern) { this.execPattern = execPattern; } @DataBoundSetter public void setClassPattern(String classPattern) { this.classPattern = classPattern; } @DataBoundSetter public void setSourcePattern(String sourcePattern) { this.sourcePattern = sourcePattern; } @DataBoundSetter public void setInclusionPattern(String inclusionPattern) { this.inclusionPattern = inclusionPattern; } @DataBoundSetter public void setExclusionPattern(String exclusionPattern) { this.exclusionPattern = exclusionPattern; } ...
-
Symbol annotation with function name
@Extension @Symbol("jacoco") public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {
-
Now when we found all the required information, we can start using JaCoCo Publisher step in Jenkins Pipeline:
-
via step class name
step([$class: 'JacocoPublisher', execPattern:'**/**.exec', classPattern: '**/classes', sourcePattern: '**/src/main/java'])
-
via step function name
jacoco execPattern:'**/**.exec', classPattern: '**/classes', sourcePattern: '**/src/main/java'])
Few more examples
Few more examples of Jenkins plugins which are compatible with Jenkins Pipeline, but don’t provide usage steps:
-
ArtifactsArchiver
-
From source code:
public class ArtifactArchiver extends Recorder implements SimpleBuildStep {
@DataBoundConstructor public ArtifactArchiver(String artifacts) { this.artifacts = artifacts.trim(); allowEmptyArchive = false; }
@Extension @Symbol("archiveArtifacts") public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {
-
In Jenkinsfile:
step([$class: 'ArtifactArchiver', artifacts:'someFile.txt'])
or
archiveArtifacts artifacts: 'someFile.txt'
-
-
ArtifactArchiverStep (yes, 2nd archiver )
-
From source code:
public class ArtifactArchiverStep extends AbstractStepImpl {
@DataBoundConstructor public ArtifactArchiverStep(String includes) { this.includes = includes; }
@Extension public static class DescriptorImpl extends StepDescriptor { @Override public String getFunctionName() { return "archive"; } @Override public String getDisplayName() { return "Archive artifacts"; } }
-
In Jenkinsfile:
archive artifacts: 'someFile.txt'
Since
class ArtifactArchiverStep extends AbstractStepImpl
and notextends SimpleBuildStep
we can’t use following syntax:step([$class: 'ArtifactArchiverStep', artifacts:'someFile.txt'])
and following exception will be thrown:
java.lang.UnsupportedOperationException: no known implementation of interface jenkins.tasks.SimpleBuildStep is named ArtifactArchiverStep
Step resolving happens in DescribableModel#resolveClass method.
-
-
HockeyappRecorder
-
From source code:
public class HockeyappRecorder extends Recorder implements SimpleBuildStep {
@DataBoundConstructor public HockeyappRecorder(List<HockeyappApplication> applications, boolean debugMode, BaseUrlHolder baseUrlHolder, boolean failGracefully) { this.applications = applications; this.debugMode = debugMode; this.baseUrlHolder = baseUrlHolder; if (baseUrlHolder != null) { this.baseUrl = baseUrlHolder.baseUrl; } this.failGracefully = failGracefully; }
Constructor takes an array of
HockeyApplication
objects@DataBoundConstructor public HockeyappApplication(String apiToken, String appId, boolean notifyTeam, String filePath, String dsymPath, String libsPath, String tags, String teams, boolean mandatory, boolean downloadAllowed, OldVersionHolder oldVersionHolder, RadioButtonSupport releaseNotesMethod, RadioButtonSupport uploadMethod) { this.schemaVersion = SCHEMA_VERSION_NUMBER; this.apiToken = Util.fixEmptyAndTrim(apiToken); this.appId = Util.fixEmptyAndTrim(appId); this.notifyTeam = notifyTeam; this.filePath = Util.fixEmptyAndTrim(filePath); this.dsymPath = Util.fixEmptyAndTrim(dsymPath); this.libsPath = Util.fixEmptyAndTrim(libsPath); this.tags = Util.fixEmptyAndTrim(tags); this.downloadAllowed = downloadAllowed; this.oldVersionHolder = oldVersionHolder; this.releaseNotesMethod = releaseNotesMethod; this.uploadMethod = uploadMethod; this.teams = Util.fixEmptyAndTrim(teams); this.mandatory = mandatory; }
-
In Jenkinsfile:
step([$class: 'HockeyappRecorder', applications: [[apiToken: 'token123', downloadAllowed: true, filePath: 'file123', mandatory: false, notifyTeam: true, releaseNotesMethod: [$class: 'ChangelogReleaseNotes'], uploadMethod: [$class: 'AppCreation', publicPage: false]]], debugMode: false, failGracefully: false])
As there is no
Symbol
and no function name provided for the descriptor in HockeyappRecorder.java this is the only way to callHockeyappRecorder
step.
-
We learned how Jenkins Pipeline works with Jenkins plugins and how to use them in Jenkinsfile. With the help of the community , we can increase coverage of plugins compatibility with Jenkins Pipeline.