This section also applies to Agent, Option, Trigger, Tool, Parameter, Trigger, and When,
but the implementation for those is a little bit simpler
When implementing a step, there are two pieces of work you have to do:
com.code42.jenkins.pipelinekt.core.step.Step
com.code42.jenkins.pipelinekt.core.step.SingletonStep or com.code42.jenkins.pipelinekt.core.step.NestedStepcom.code42.jenkins.pipelinekt.core.step.DeclarativeStep or com.code42.jenkins.pipelinekt.core.step.ScriptedStep data class MyNewStep(...) : SingletonStep, DeclarativeStep
//or
data class MyStepWithANestedStep(..., override val steps: Step): NestedStep, DeclarativeStep
SingletonStep is for steps that don’t contain more steps like sh or stashNestedStep is for steps contain other steps, like withCredentials or withEnv or nodeDeclarativeStep and ScriptedStep are for determining whether a step runs inside of the declarative or scripted context.
ScriptedStep can be used to implement custom code blocks as stepsDeclarativeStep implement toGroovy, if extending ScriptedStep implement scriptedGroovyinternal module, otherwise just put it in your project somewhere
dsl. the dsl module
will not expose classes from internal to users.Create the dsl methods
```
This would expose a new step that can be used anywhere you can use a step, for example:
stage("my stage") {
steps {
myNewStep(args...)
myStepWithANestedStep(args...) {
sh("env")
}
}
}
See the existing examples:
com.code42.jenkins.pipelinekt.internal.step.Sh is a good example of a simple step - it is a data class that can serialize itself to groovycom.code42.jenkins.pipelinekt.internal.step.WithCredentials is a simple example of a nested stepThe library also allows you to implement complex steps that may take in configuration. A good example of this is the
gradle build dsl and the docker dsl
Lets take a look at part of the gradle build dsl:
data class GradleBuildDsl(
val gradleCredentials: UsernamePassword? = null,
val gradleUserProperty: String = "gradle.wrapperUser",
val gradlePasswordProperty: String = "gradle.wrapperPassword"
) {
...
fun DslContext<Step>.gradleCommand(command: String, additionalBuildArgs: Var.Literal.Str) =
withEnv(
mapOf("GRADLE_USER_HOME" to "${"WORKSPACE".environmentVar()}/.gradle-home-tmp",
"JENKINS_NODE_COOKIE" to "dontKillMe")
) { artifactoryAuthenticated {
sh(("./gradlew --stacktrace --build-cache " +
(gradleCredentials?.let { "-D$gradleUserProperty=\\\"\\\${${it.usernameVariable.value}}\\\" -D$gradlePasswordProperty=\\\"\\\${${it.passwordVariable.value}}\\\" " } ?: "") +
"$additionalBuildArgs $command").strDouble())
} }
...
Gradle build defines a public method that is an extension method, but we can’t call this method with just the dsl,
however we can run this method inside of our dsk script if we wrap it with a call to gradleBuildDsl.run { ... }:
val gradle = GradleBuildDsl()
/**
* Pipeline
*/
fun PipelineDsl.gradleBuildPipeline() =
gradle.run {
pipeline {
stages {
stage("Build") {
steps {
gradleCommand("build $gradleArgs")
}
}
stage("Publish") {
steps {
gradleCommand("publish $gradleArgs")
}
}
}
}
}
By wrapping the pipeline in gradle.run, the method gradleCommand is exposed on DslContext
This gives the benefit of being able to inject some configuration into the dsl, for gradle the configuration is a credentialsId and username/password properties. This prevents users from having to repeat the same rather verbose sh calls all over our pipelines and allows us to build pipelines where we can easily swap out credentials.