I like Jenkins a lot. Even with a plethora of systems that have a vastly better web UI and many of them tailored for specific platforms, it is still my first choice. Not for many other people and they are right, because you can easily shoot yourself on the foot the worst of times. That is why when people are new to Jenkins, I have an opinionated method to start them working with it. You work only with Multibranch pipelines (even when with a single branch) and also them being declarative pipelines:
Introduction
Multibranch pipelines which are what we would like to make use of at work, are driven by Jenkinsfiles. The language to program a Jenkinsfile is a DSL based on the Groovy language. Groovy is based on (and resembles) Java and thus is vast, as is the Jenkins declarative pipeline DSL and the multitude of plugins that are supported. This guide aims to help you write your first Jenkinsfile when you have no prior experience. As such it is opinionated. You are welcome to deviate from it once you get more experience with the tooling.
So, with your editor open a new file named Jenkinsfile at the top of your repository and let’s start!
Define a pipeline
To define a pipeline simply type
pipeline {
}
That’s it! You have defined a pipeline!
Lock the pipeline
Assuming we do not want two builds of the same project running concurrently, we acquire a temporary lock
pipeline {
options {
lock('poc-pipeline')
}
}
Now if two different people start the same build, the builds will be executed sequentially
But where will the build run?
Builds run on Jenkins agents. Jenkins agents are labeled and we can select them based on their labels. In the general case we run docker based builds and as such we need to select an agent that has docker installed and also provide a container image to be launched for the build to run
pipeline {
options {
lock('poc-pipeline')
}
agent {
docker {
label 'docker'
image 'busybox'
}
}
}
So with the above we select a Jenkins node labeled docker which will launch a docker container inside which all our intended operations will run
Build stages
Builds in Jenkins happen in stages. As such we define a stages section in the Jenkinsfile
pipeline {
options {
lock('poc-pipeline')
}
agent {
docker {
label 'docker'
image 'busybox'
}
}
stages {
stage("build") {
}
stage("test") {
}
stage("deploy") {
}
}
}
Above we have defined three stages, build, test and deploy, which will run in any of the Jenkins agents labeled as docker and not necessarily on the same one. Because this can lead to confusion, we require, for now, that all of our build runs on the same node. One way to do this is to have “substages” within a stage in Jenkins. The syntax becomes a bit convoluted when you are not much experienced, but let’s see how it transforms
pipeline {
options {
lock('poc-pipeline')
}
agent {
docker {
label 'docker'
image 'busybox'
}
}
stages {
stage("acquire node") {
stages {
stage("build") {
}
stage("test") {
}
stage("deploy") {
}
}
}
}
}
The stage acquire node is assigned to a node labeled docker and the “sub-stages” build, test and deploy will run within this node.
Each stage has steps
Each stage in a pipeline executes a series of steps
pipeline {
options {
lock('poc-pipeline')
}
agent {
docker {
label 'docker'
image 'busybox'
}
}
stages {
stage("acquire node") {
stages {
stage("build") {
steps {
}
}
stage("test") {
steps {
}
}
stage("deploy") {
steps {
}
}
}
}
}
}
Time to say Hello, World!
It is now time to make something meaningful with the Jenkinsfile like have it tell us Hello, World!. We will show you two ways to do this, one via a script section which allows us to run some Groovy code (in case we need to check some logic or something) and one using direct sh commands:
pipeline {
options {
lock('poc-pipeline')
}
agent {
docker {
label 'docker'
image 'busybox'
}
}
stages {
stage("acquire node") {
stages {
stage("build") {
steps {
script {
// This is Groovy code here
println "This is the build stage executing"
}
}
}
stage("test") {
steps {
sh """
echo This is the test stage executing
"""
}
}
stage("deploy") {
steps {
sh """
echo This is the deploy stage executing
"""
script {
println "Hello, World!"
}
}
}
}
}
}
}
Congratulations! You have now created a complete Jenkinsfile.
Epilogue
Where do we go from here? You are set for your Jenkins journey. By using the above boilerplate and understanding how it is created, you can now specify jobs, have them described in code and run. Most likely you will need to read about credentials in order to perform operations to services where authentication is needed.
I understand there is a lot of curly-brace hell, which can be abstracted by extending the pipeline DSL (I am, very slowly, experimenting with Pkl to see how to best achieve this, but here is a book for Groovy DSLs if you like).