Everything About How To Build Jenkins Pipeline
Ever wondered how things automate everyone has heard of the name Jenkins while learning DevOps and dealing with automation in your everyday process so i started automating projects but when i came across Jenkins and building Jenkinsfile it was new to me and it was a little overwhelming but when you understand and divide them into parts and learn while building you gonna understand how to write a Jenkins pipeline easily so, let's begin
what is Jenkinsfile
Jenkins file if you're familiar with Jenkins usually you would create new jobs or new builds and you would configure them so basically what Jenkins file is that instead of you creating and configuring jobs on the Jenkins user interface you would write a file or you would script that build and it's configuration in a file so Jenkins file is pipeline as a code it's a scripted pipeline which is part of this whole infrastructure as a code concept and so respectively you would create Jenkins file in your repository with your code
Basic structure of Jenkins file
now how do you create a Jenkins file? how does the syntax look like? let's discuss them one by one. So, basically Jenkinsfile can be written in two ways: scripted or declarative.
Scripted Pipeline vs. Declarative Pipeline
Scripted Pipeline:
the scripted pipeline was the first syntax of Jenkinsfile. So, what it basically is it allows writing the whole configuration of the Jenkinsfile using groovy script. So, the only structure that you have there is here and inside of this is basically groovy scripts and you can write the whole configuration here in a flexible manner as you wish. So, there is no predefined structure.
node {
// Groovy script
// Your configuration goes here
}
Declarative Pipeline:
Declarative pipeline syntax for Jenkinsfile is easier to get started with. Of course, it's not as powerful as the scripted pipeline because here you're not limited in any way but it's easier to get started with because you have a predefined structure
pipeline {
agent any
stages {
stage('Build') {
steps {
}
}
}
}
While scripted pipelines offer flexibility, declarative syntax is more beginner-friendly.
Let's dive into the essential attributes of a Jenkinsfile:
pipeline {
agent any
stages {
stage('Build') {
steps {
}
}
}
}
Key attributes include:
pipeline
: Top-level definition.agent
: Specifies where to execute the pipelineany
specifies you can run any available agent.stages
: Defines different stages of the pipeline. you can have multiple stages in your project you can define them in the filepipeline { agent any stages { stage('Build') { steps { } } stage('Test') { steps { } } stage('Deploy') { steps { } } } }
steps
: Defines the specific tasks or actions to be executed within each stage.pipeline { agent any stages { stage('Build') { steps { echo 'Building the application' } } stage('Test') { steps { echo 'Testing the application' } } stage('Deploy') { steps { echo 'Deploying the application' } } } }
Post attributes in Jenkinsfile
how can we see what attributes we can use to configure different parts of the
build so this is the basic syntax where we have the base construct inside stages
where we defined three different stages another thing you can do with Jenkinsfile is that at the end of it you can define post attribute so what it does is
basically executes some kind of logic or some kind of scripts after all the
stages are done and inside the post, there are different conditions like
always
,success
,failure
, andchanged
that you can execute your scripts on for example Let's break down the explanation into partsAlways:
The
always
condition in the post block ensures that the specified script or logic is executed regardless of the build result (success or failure).post { always { script { echo 'Always executed, regardless of build result' // Add logic or scripts here } } }
Success:
The
success
condition in the post block executes a script or logic only if the build is successful.post { success { script { echo 'Executed only on successful build' // Add logic or scripts here for successful build } } }
Failure:
The
failure
condition in the post block runs a script or logic only if the build fails.post { failure { script { echo 'Executed only on failed build' // Add logic or scripts here for failed build } } }
Changed:
The
changed
condition in the post block executes a script or logic if the build status changes (e.g., from failure to success).post { changed { script { echo 'Executed when build status changes' // Add logic or scripts here for status change } } }
So this is finally how the whole file looks when it's built with post conditions along with steps and stages
pipeline { agent any stages { stage('Build') { steps { // Your build steps here script { echo 'Building...' } } } stage('Test') { steps { // Your test steps here script { echo 'Testing...' } } } stage('Deploy') { steps { // Your deployment steps here script { echo 'Deploying...' } } } } post { always { // This script will be executed always script { echo 'Post-build actions - Always' // Add logic or scripts that should run regardless of build result } } success { // This script will be executed only if the build is successful script { echo 'Post-build actions - Success' // Add logic or scripts for successful build } } failure { // This script will be executed only if the build fails script { echo 'Post-build actions - Failure' // Add logic or scripts for failed build } } changed { // This script will be executed if the build status changes (e.g., from failure to success) script { echo 'Post-build actions - Build status changed' // Add logic or scripts for when build status changes } } } }
Define Conditionals / When expression
conditionals for each stage so for example you only want to run the tests
on development branch build you don't want to run tests for a feature branch
or any other builds what you can do here is inside the stage block you
can define so-called when expressions so when should this stage execute and the syntax for that is like this and inside you have expression block
Checking Branch Name:
You can use
when
expressions inside a stage block to conditionally execute that stage based on the branch name. TheBRANCH_NAME
environment variable provided by Jenkins can be used for this purpose.stages { stage('Tests') { when { expression { return BRANCH_NAME == 'development' } } steps { // Your test steps here script { echo 'Running tests for the development branch' } } } }
In this example, the 'Tests' stage will only execute if the branch name is 'development'.
Checking Code Changes:
You can also use
when
expressions to conditionally execute a stage based on whether there are code changes. This involves using a custom variable that you define to track code changes.def codeChanges = true // Assume code changes initially stages { stage('Build') { when { expression { return BRANCH_NAME == 'development' && codeChanges } } steps { // Your build steps here script { echo 'Building...' } } } }
In this example, the 'Build' stage will only execute if the branch name is 'development' and the
codeChanges
variable istrue
. You would set and update the value ofcodechanges
based on a script that checks for changes in the code.Using Environmental Variables in Jenkinsfile
Jenkins provides several out-of-the-box environmental variables that you can use in your Jenkinsfile. You can find a practical list with descriptions at the following URL:
http://your-jenkins-url/env-vars.html
Defining Custom Environmental Variables:
In Jenkinsfile, you can define your own environmental variables using the
environment
block. These variables will be available for all stages in the pipelinepipeline { agent any environment { version = '1.0.0' // Define a version variable // You can define other variables as needed } stages { stage('Build') { steps { script { echo "Building version ${version}" // Use the version variable in your script } } } } }
Using Credentials in Jenkinsfile:
If you have credentials defined in Jenkins, you can use them in Jenkinsfile by either defining them in the
environment
block or using thewithCredentials
wrapper.Option 1: Defining in
environment
Blockpipeline { agent any environment { serverCredentials = credentials('server-credentials-id') // Define credentials in the environment block } stages { stage('Deploy') { steps { script { sh "echo 'Deploying using credentials'" sh "echo ${serverCredentials_USR} ${serverCredentials_PSW}" // Use credentials in your script } } } } }
Option 2: Using
withCredentials
Wrapperpipeline { agent any stages { stage('Deploy') { steps { script { withCredentials([usernamePassword(credentialsId: 'server-credentials-id', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')]) { sh "echo 'Deploying using credentials'" sh "echo ${USERNAME} ${PASSWORD}" // Use credentials in your script } } } } } }
Using Tools attribute for making build tools available
let's understand tools attribute so what tools does is basically provides you
with build tools for your projects so if you have a front-end application
you will have some build tools configured for that it could be maven, Gradle or some other tool that you use and obviously when you're building an application you want to have those tools available so for example in build you would run something like maven install or Gradle build whatever and you need to have those tools available in one way to have those tools available is through these tools attribute and there are three tools right now that's jenkins file support that's Gradle maven and JDK
pipeline { agent any tools { // Define Maven tool maven 'Maven3' // Define Gradle tool gradle 'Gradle' // Define JDK tool jdk 'JDK8' } stages { stage('Build') { steps { script { // Maven command will be available here sh 'mvn clean install' } } } stage('Test') { steps { script { // Gradle command will be available here sh 'gradle test' } } } stage('Deploy') { steps { script { // JDK commands will be available here sh 'java -version' } } } } }
Before using the
tools
attribute in Jenkinsfile, you need to configure these tools in Jenkins at the global level. You can do this in "Manage Jenkins" > "Global Tool Configuration."Ensure that the names you specify in the Jenkinsfile match the names configured in the tool configuration.
Defining Parameters
In Jenkinsfile, you can define parameters that allow external configuration. Parameters can include types such as string, choice, and boolean.
pipeline {
agent any
parameters {
string(name: 'version', defaultValue: '', description: 'Version to deploy on prod')
choice(name: 'versions', choices: ['1.0', '2.0', '3.0'], description: 'Available versions to deploy')
booleanParam(name: 'executeTests', defaultValue: true, description: 'Execute tests')
}
stages {
stage('Build') {
steps {
script {
echo "Building version ${params.version}"
}
}
}
stage('Test') {
when {
expression {
return params.executeTests == true
}
}
steps {
script {
echo 'Running tests...'
}
}
}
stage('Deploy') {
steps {
script {
echo "Deploying version ${params.version}"
}
}
}
}
}
In this example:
string
: Defines a string parameter named 'version' with a default value and a description.choice
: Defines a choice parameter named 'versions' with predefined choices and a description.booleanParam
: Defines a boolean parameter named 'executeTests' with a default value and a description.
Using Parameters in Stages:
You can use parameters in stages using expressions. For example, you can use a parameter to conditionally execute a stage:
stage('Test') {
when {
expression {
return params.executeTests == true
}
}
steps {
script {
echo 'Running tests...'
}
}
}
Executing with Parameters in Jenkins:
When you trigger a build in Jenkins, if parameters are defined, you will see a "Build with Parameters" option. You can provide values for the defined parameters.
In Jenkins:
Locate your pipeline job.
Click on "Build with Parameters."
Provide values for the parameters.
Click on "Build."
These parameters will be passed to your Jenkinsfile during the build execution.
Scenario and Use Case:
In complex Jenkins pipelines, you may have multiple stages with various logic written in Groovy scripts.
It's beneficial to clean up the Jenkinsfile by extracting these scripts into external files.
This approach makes Jenkinsfile more organized and allows for better management of logic.
Groovy Scripts in External Files:
Create a separate Groovy script file (e.g.,
script.groovy
) containing functions or logic.Example Groovy script with functions:
// script.groovy def buildApp() { echo 'Building application...' // Additional logic } def testApp() { echo 'Testing application...' // Additional logic } def deployApp(version) { echo "Deploying version ${version}..." // Additional logic }
Importing and Using External Scripts in Jenkinsfile:
Use a script block in the Jenkinsfile to load the external Groovy script.
Assign the script content to a variable for global access.
Call the functions from the imported script in your pipeline stages.
pipeline {
agent any
stages {
stage('Build') {
steps {
script {
groovyScript = load 'script.groovy'
groovyScript.buildApp()
}
}
}
stage('Test') {
steps {
script {
groovyScript.testApp()
}
}
}
stage('Deploy') {
steps {
script {
groovyScript.deployApp(params.version)
}
}
}
}
}
Advantages:
Keeps Jenkinsfile slim and readable.
Easy to manage and update logic in external scripts.
Supports using parameters and environmental variables in Groovy scripts.
Testing and Replay:
Utilize the Jenkins replay feature to test changes in Jenkinsfile and external scripts without committing.
Allows for quick iteration and verification of pipeline modifications.
Closing:
Encourages cleaner and modular Jenkinsfile structure.
External Groovy scripts provide flexibility for managing and evolving pipeline logic.
Conclusion
whether you're new to Jenkinsfile or refining an existing one, this guide helps you create a smoother workflow. Learn to structure your pipeline, manage stages, use conditions, and integrate external scripts. These practices make your Jenkinsfile simpler, easier to understand, and more powerful for automating your projects. Happy coding!