Comprehensive Guide To Jenkins Declarative Pipeline [With Examples]

What is Jenkinsfile?

Jenkinsfile is just a text file, usually checked in along with the project’s source code in Git repo. Ideally, every application will have its own Jenkinsfile.

What is Jenkins Scripted Pipeline?

Jenkins pipelines are traditionally written as scripted pipelines. Ideally, the scripted pipeline is stored in Jenkins webUI as a Jenkins file. The end-to-end scripted pipeline script is written in Groovy.

  • Jenkinsfile starts with the word node.
  • Can contain standard programming constructs like if-else block, try-catch block, etc.
node {
stage('Stage 1') {
echo 'hello'
}
}

What is Jenkins Declarative Pipeline?

The Declarative Pipeline subsystem in Jenkins Pipeline is relatively new, and provides a simplified, opinionated syntax on top of the Pipeline subsystems.

  • Jenkins declarative pipeline needs to use the predefined constructs to create pipelines. Hence, it is not flexible as a scripted pipeline.
  • Jenkinsfile starts with the word pipeline.

Our First Declarative Pipeline

pipeline {
agent any
stages {
stage('Welcome Step') {
steps {
echo 'Welcome to LambdaTest'
}
}
}
}

Running Your First Declarative Pipeline

Now that you are well-acquainted with the Jenkins pipeline’s basics, it’s time to dive deeper. In this section, we will learn how to run a Jenkins declarative pipeline. You can refer to our Jenkins Pipeline Tutorial for more.

Console Output

Jenkins Declarative Pipeline Syntax

In this section, we will look at the most commonly used Jenkins declarative pipeline examples or syntax. Typically, declarative pipelines contain one or more declarative steps or directives, as explained below.

pipeline {
}
pipeline {
agent any
}
pipeline {
agent none
}
pipeline {
agent {
label 'linux-machine'
}
}

stages

stages block constitutes different executable stage blocks. At least one stage block is mandatory inside stages block.

pipeline {
agent {
label 'linux-machine'
}
stages {
}
}

stage

stage block contains the actual execution steps. Stage block has to be defined within stages block. It’s mandatory to have at least one stage block inside the stage block. Also its mandatory to name each stage block & this name will be shown in the Stage View after we run the job.

pipeline {
agent {
label 'linux-machine'
}
stages {
stage('build step') {
}
}
}

steps

steps block contains the actual build step. It’s mandatory to have at least one step block inside a stage block.

pipeline {
agent any
stages {
stage('build step') {
steps {
echo "Build stage is running"
}
}
}
}

parameters

The parameters directive provides a way for Jenkins job to interact with Jenkins CI/CD users during the running of the build job.

pipeline {
agent any
parameters {
string(name: 'NAME', description: 'Please tell me your name?')
text(name: 'DESC', description: 'Describe about the job details')booleanParam(name: 'SKIP_TEST', description: 'Want to skip running Test cases?')choice(name: 'BRANCH', choices: ['Master', 'Dev'], description: 'Choose branch')password(name: 'SONAR_SERVER_PWD', description: 'Enter SONAR password')
}
stages {
stage('Printing Parameters') {
steps {
echo "Hello ${params.NAME}"
echo "Job Details: ${params.DESC}"echo "Skip Running Test case ?: ${params.SKIP_TEST}"echo "Branch Choice: ${params.BRANCH}"echo "SONAR Password: ${params.SONAR_SERVER_PWD}"
}
}
}
}

script

script block helps us run Groovy code inside the Jenkins declarative pipeline.

pipeline {
agent any
parameters {
string(name: 'NAME', description: 'Please tell me your name')
choice(name: 'GENDER', choices: ['Male', 'Female'], description: 'Choose Gender')
}
stages {
stage('Printing name') {
steps {
script {
def name = "${params.NAME}"
def gender = "${params.GENDER}"
if(gender == "Male") {
echo "Mr. $name"
} else {
echo "Mrs. $name"
}
}
}
}
}
}

environment

Key value pairs which helps passing values to job during job runtime from outside of Jenkinsfile. It’s one way of externalizing configuration.

Method 1 : Configure Environment Variable in Jenkins CI/CD portal

Step 1: Open Jenkins Server URL (http://localhost:8080)

pipeline {
agent any
stages {
stage('Initialization') {
steps {
echo "${JAVA_INSTALLATION_PATH}"
}
}
}
}

Method 2 : Creating & Referring Environment Variable in Jenkinsfile

We can create key value pairs of environment variables under environment block. It’s an optional block under pipeline.

pipeline {
agent any
environment {
DEPLOY_TO = 'production'
}
stages {
stage('Initialization') {
steps {
echo "${DEPLOY_TO}"
}
}
}
}

Method 3 : Initialize Environment variables using sh scripts in Jenkinsfile

Let’s say we need the timestamp when the job gets run for logging purposes. We can create an Environment variable which can hold the timestamp. But how to initialize it ?

>  date '+%A %W %Y %X' 
Tuesday 03 2021 22:03:31
pipeline {
agent any
stages {
stage('Initialization') {
environment {
JOB_TIME = sh (returnStdout: true, script: "date '+%A %W %Y %X'").trim()
}
steps {
sh 'echo $JOB_TIME'
}
}
}
}

Load credentials via environment block

credentials() is a special method that can be used inside the environment block. This method helps loading the credentials defined at Jenkins configuration.

  • Scope — Global
  • Username — admin
  • Password — root123
  • ID — MY_SECRET
  • Description — Secret to access server files
pipeline {
agent any
environment {
MY_CRED = credentials('MY_SECRET')
}
stages {
stage('Load Credentials') {
steps {
echo "Username is $MY_CRED_USR"
echo "Password is $MY_CRED_PSW"
}
}
}
}

when

Acts like if condition to decide whether to run the particular stage or not . Its an optional block.

pipeline {
agent any
stages {
stage('build') {
when {
branch 'dev'
}
steps {
echo "Working on dev branch"
}
}
}
}
pipeline {
agent any
stages {
stage('build') {
when {
expression {
return env.BRANCH_NAME == 'dev';
}
}
steps {
echo "Working on dev branch"
}
}
}
}
pipeline {
agent any
environment {
DEPLOY_TO = 'production'
}
stages {
stage('Welcome Step') {
when {
environment name: 'DEPLOY_TO', value: 'production'
}
steps {
echo 'Welcome to LambdaTest'
}
}
}
}
pipeline {
agent any
environment {
DEPLOY_TO = 'production'
}
stages {
stage('Welcome Step') {
when {
allOf {
branch 'master';
environment name: 'DEPLOY_TO', value: 'production'
}
}
steps {
echo 'Welcome to LambdaTest'
}
}
}
}
pipeline {
agent any
environment {
DEPLOY_TO = 'production'
}
stages {
stage('Welcome Step') {
when {
anyOf {
branch 'master';
branch 'staging'
}
}
steps {
echo 'Welcome to LambdaTest'
}
}
}
}

tools

This block lets us add pre configured tools like Maven or Java to our job’s PATH. It’s an optional block.

  • MAVEN_HOME — Enter the maven installation path in local
pipeline {
agent any
tools {
maven 'MAVEN_PATH'
}
stages {
stage('Load Tools') {
steps {
sh "mvn -version"
}
}
}
}

parallel

parallel blocks allow us to run multiple stage blocks concurrently. It’s ideal to parallelize stages which can run independently. We can define agents for each stage within a parallel block, so each stage will run on its own agent.

pipeline {
agent any
stages {
stage('Parallel Stage') {
parallel {
stage('windows script') {
agent {
label "windows"
}
steps {
echo "Running in windows agent"
bat 'echo %PATH%'
}
}
stage('linux script') {
agent {
label "linux"
}
steps {
sh "Running in Linux agent"
}
}
}
}
}
}

post

post block contains additional actions to be performed on completion of the pipeline execution. It can contain one or many of the following conditional blocks — always, changed, aborted, failure, success, unstable etc.

pipeline {
agent any
stages {
stage('build step') {
steps {
echo "Build stage is running"
}
}
}
post {
always {
echo "You can always see me"
}
success {
echo "I am running because the job ran successfully"
}
unstable {
echo "Gear up ! The build is unstable. Try fix it"
}
failure {
echo "OMG ! The build failed"
}
}
}

Marking build as Unstable

There are scenarios where we don’t want to mark build as failure but want to mark build as unstable.

pipeline {
agent any
tools {
maven 'MAVEN_PATH'
jdk 'jdk8'
}
stages {
stage("Tools initialization") {
steps {
sh "mvn --version"
sh "java -version"
}
}
stage("Checkout Code") {
steps {
git branch: 'master',
url: "https://github.com/iamvickyav/spring-boot-data-H2-embedded.git"
}
}
stage("Building Application") {
steps {
sh "mvn clean package"
}
}
stage("Code coverage") {
steps {
jacoco(
execPattern: '**/target/**.exec',
classPattern: '**/target/classes',
sourcePattern: '**/src',
inclusionPattern: 'com/iamvickyav/**',
changeBuildStatus: true,
minimumInstructionCoverage: '30',
maximumInstructionCoverage: '80')
}
}
}
}

triggers

Instead of triggering the build manually, we can configure build to run in certain time intervals using the triggers block. We can use the special method called cron() within triggers block to configure the build schedule.

pipeline {
agent any
triggers {
cron('H/15 * * * *')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}

Conclusion

In this blog, we have done an in-depth dive into Jenkins Declarative pipeline examples and their usage. Instead of configuring build steps using UI in a remote Jenkins portal, we would always recommend you to prefer creating Jenkinsfile with Declarative pipeline syntax. Jenkinsfile, since part of the application’s source code, will provide more control over CI/CD steps to developers. That’s the best way to make the most of Jenkins CI/CD and all the features it has to offer!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store