Introduction:
I just had started my first job as a software developer, when the first news about the deprecation of the Build Flow Plugin came around. At that time the company had already had a huge, complex CI, with a lot of workflow organized Jenkins jobs. It was clear that a quick fix will be to simply replace the build flow jobs with pipelines, and keep the party going as it is. And of course who would be the best to fix this minor issue, if not the new intern! The stage was set, and I was sent to wrap the job up.
...but there was a catch
As I realized no one knew the syntax of this esoteric pipeline scripts yet, so I had to figure out how to use them to exactly replace the build flow scripts with it. There came a lot of interesting information about pipelines, that made me rethink how it has to be used to replace certain elements in build flows.
During my research, I had not found any comprehensive source on the web about specific use-cases of certain pipeline steps. Here I'm planning to collect and share all my findings. I will specifically concentrate on how to replace certain solutions done before by build flows, or other old deprecated plugins like Active Choice.
Anyway I know you didn't come for the story so let's see the code!
Pipeline Basics
The first thing you should do if you are getting started with pipelines is to read the official pipeline documentation.
Here you can learn what a pipeline is, what the Jenkinsfile is, and all the basics, that I'm not going to explain in detail.
This documentation, however, has a dirty little secret, a piece of information you can easily disregard. Pipeline scripts can be written in two ways: Declarative Style and Scripted Style.
Example of a Declarative Pipeline script
Luckily Declarative pipelines are well documented, you can find a lot of examples and documentation on this page. Anyhow, here is an example by me. Notice that here I specifically created a script which is implementing a job scheduling solution, just as a build flow scripts did before.
Also notice that a Declarative Pipeline always has a pipeline {}
block with an agent any
part in it, and is separated into stages.
#!/usr/bin/env groovy
pipeline {
agent any
stages {
stage("build") {
steps {
retry(3) { build("build-job") }
}
}
stage("tests") {
steps {
parallel (
"unit test" : {
build("unit-test-job")
},
"component test" : {
build("component-test-job")
}
)
}
}
}
}
Example of a Scripted Pipeline script
Scripted Pipeline syntax, on the other hand, offers a general purpose DSL syntax. Scripted Pipelines always are enclosed within a node {}
block. Notice that I used a stage
but it is not necessary. You can read about specific Scripted Pipeline steps, at the documentations page, but why would you do that? From any jenkins you can reach a pipeline snippet generator, which helps you figure out the syntax. Anyway here is an example.
#!/usr/bin/env groovy
node {
def workspace = pwd()
echo "Building Job at ${workspace}"
build 'builder-job'
stage('Shell') {
try {
sh returnStdout: true, script: 'demo.sh'
}
catch (exc) {
echo 'Something failed!'
}
}
}
Usefull to know, that - yes! - You can have both syntaxes in a Jenkinsfile.
Scripted Pipeline IN Declarative Pipeline
In case it is necessary you can actually create an embedded Scripted Pipeline block IN a Declarative Pipeline step! We use the script{}
block for this. Here's how it is:
#!/usr/bin/env groovy
pipeline {
agent any
stages {
stage("robot test") {
steps {
script {
MYLIST = []
MYLIST += "param-one"
MYLIST += "param-two"
MYLIST += "param-three"
MYLIST += "param-four"
MYLIST += "param-five"
for (def element = 0; element < MYLIST.size(); element++) {
build(
job: 'parameterized-job',
parameters: [
[
$class: 'StringParameterValue',
name: 'MYLIST',
value: MYLIST[element]
]
]
)
}
}
}
}
}
}
Notice that here I used the scripted block to be able to use for loop.
In the later chapters we will discover several other use cases as well.
Top comments (4)
This is not really correct. You only need the node block if you actually do something on a node.
In your example, you could just put the node block around the 'sh' command together with its 'def workspace = pwd()' (which you could omit, as it is default).
It is a good practice not to waste executors if you don't need them, e.g. for just triggering other jobs and echoing in the log, you don't need a node at all.
Still thanks for your nice blog!
For the record, I came for the story. ;)
Talk is cheap ,show me the code. :D
There is a feature of dev.to with which you can bind all 3 parts of your blog posts into one series. You should use it :)