Environment variables
In echo variables can be accessed using either ${env.variable_name} or ${variable_name}
In sh variables can only be accessed using ${variable_name}
stage-level environment block variables can override pipeline-level (global) environment block variables
But env variables inside script block cannot override global-env or stage-env variables
variables inside withEnv block can override all levels of env vars.
If we assign a true or false value to a variable. It will be treated as a string. You need to typecast explicitly using toBoolean()
Generally sh won`t return any output, so if you want to get any output out of sh use it with a script like this
COUNT_FILES= sh(script: "ls -la /tmp | tail -n +4 | wc -l", returnStdout: true)Take care when using a null value, because if a function returns null and you capture it in a variable. It is most likely to store a string with the value null. Use Elvis operator as shown below
If you want to access variable inside a script of particular stage outside its scope(i.e. any where in the pipeline including post block then create a variable using env and assign it to that)
EG: In Jenkins pipelines, variables defined within ascript
block usingdef
are scoped to that block. This means that you won't be able to access them outside of thatscript
block directly. However, if you want to access a variable in thepost
block, you can set it as an environment variable or use other mechanisms to make it globally accessible.Here are a couple of methods to achieve this:
Using
env
to Set Environment Variables:Inside your
script
block, you can set the variable as an environment variable using theenv
global variable. This way, the value becomes accessible throughout the Jenkins pipeline, including in thepost
block.groovyCopy codestage('Some Stage') { steps { script { def myLocalVar = "someValue" env.MY_GLOBAL_VAR = myLocalVar } } } post { always { script { echo "Value of MY_GLOBAL_VAR: ${env.MY_GLOBAL_VAR}" } } }
In Jenkins, parameters declared for a pipeline are automatically made available as environment variables. This means if you have a parameter named
MY_PARAM
, you can access its value anywhere in the pipeline script usingenv.MY
_PARAM
.Here's an example of how you can use pipeline parameters:
groovyCopy codepipeline { agent any parameters { string(name: 'MY_PARAM', defaultValue: 'Default Value', description: 'A description of my parameter') } stages { stage('Print Parameter') { steps { echo "Parameter value is: ${env.MY_PARAM}" } } stage('Another Stage') { steps { script { if (env.MY_PARAM == 'SomeValue') { echo "Parameter is set to SomeValue" } else { echo "Parameter is set to something else" } } } } } post { always { echo "Post block: Parameter value is: ${env.MY_PARAM}" } } }
Very very Imp:
In a Jenkins pipeline, you can access environment variables inside a
script
block as well as inside ansh
step. Environment variables can be accessed using theenv
prefix.Here are some examples to illustrate this:
1. Accessing
env
variable inside ascript
block:groovyCopy codepipeline { agent any environment { MY_ENV_VAR = 'Hello from env' } stages { stage('Demo Stage') { steps { script { echo "Accessing env variable in script block: ${env.MY_ENV_VAR}" } } } } }
2. Accessing
env
variable inside ansh
step within ascript
block:groovyCopy codepipeline { agent any environment { MY_ENV_VAR = 'Hello from env' } stages { stage('Demo Stage') { steps { script { echo "Accessing env variable in script block: ${env.MY_ENV_VAR}" // Accessing inside sh step sh """ echo "Accessing env variable in sh block: $MY_ENV_VAR" """ } } } } }
Note that when you access the environment variable inside the
sh
step, you don't need to use theenv
prefix. You can simply reference the variable by its name.However, remember that this works because Groovy is interpolating the variable inside the double-quoted string (
"""..."""
). If you used single quotes ('''...'''
), the variable would not be interpolated.The code snippet used in the video
pipeline{
agent { label 'slave' }
environment{
// Available to all stages
NAME = "Bhuvan"
ID = 90
}
stages{
stage("Print default vars"){
steps{
sh 'printenv | sort'
}
}
stage("Use Env vars"){
environment{
// Available to this particular stage only
NAME ="Chand"
ID = 30
}
steps{
// Here env is optional we can use ${BUILD_NUMBER} directly but it is best practice to use env.
echo "Build Number = ${env.BUILD_NUMBER}"
echo "Build Number(without env.) = ${BUILD_NUMBER}"
// It overrides global username
echo "NAME = ${env.NAME}" // chand
// For shell we don`t use env. If we use we ill get error
sh 'echo ${NAME}' // chand
// Even number will be treated as string, as you can see here
echo "ID = ${env.ID} and its type is ${env.ID.class}" //ID = 30 and its type is class java.lang.String
script{
env.GROUP_NAME = "test-group"
env.TRIGGER_NEXT=true
env.NAME = "dummy-name"
}
echo "Group name =${env.GROUP_NAME}" // test-group
sh 'echo group name is ${GROUP_NAME}' // test-group
// script block cannot overide name
echo "NAME is: ${env.NAME}" // chand
withEnv(["USER_PWD=secret","USER_IS_ADMIN=false","NAME=Maddi"]){
echo "user password is: ${env.USER_PWD}" // secret
sh 'echo user is admin?: ${USER_IS_ADMIN}' // false
echo "Name is: ${env.NAME}" // Maddi
}
}
}
stage("Using boolean env vars"){
when{
expression{
// env.TRIGGER_NEXT == true --> This will result in error because env.TRIGGER_NEXT will be treated as a string
env.TRIGGER_NEXT.toBoolean() == true
}
}
// other way is to use when {environmanet name: "TRIGGER_NEXT", value = "true" and set value in script block to string by double quoating "true"}
steps {
echo "Type of trigger next is: ${env.TRIGGER_NEXT.class}"
echo "Type of trigger next is: ${env.TRIGGER_NEXT.toBoolean().class}"
}
}
stage("capturing output from other step"){
environment{
// By default sh doesn`t return anything so sh 'ls -la /tmp | tail -n +4 | wc -l' output will be null.
COUNT_FILES= sh(script: "ls -la /tmp | tail -n +4 | wc -l", returnStdout: true)
}
steps{
echo "No of files in /tmp folder are: ${env.COUNT_FILES}"
}
}
stage("Handling null values"){
environment{
// SOME_VAL = someVal() //It captures string(null value string with 4 characters) not null
// This is elvis opetaror. Refere blog: https://blog.mrhaki.com/2009/08/groovy-goodness-elvis-operator.html
SOME_VAL = "${someVal() ?: ''}"
}
steps{
echo "The value of someval is ${env.SOME_VAL} and its type is ${env.SOME_VAL?.class}"
}
}
}
}
def someVal(){
return null
}
Script and steps blocks:
Use steps block inside the stage wherever possible. If something cannot be achieved using the steps block then use the script block. You can even use scripts bloc within steps.
Using Only
steps
Block: This is the most common and recommended way to define steps in a Declarative pipeline. You can define individual steps directly within thesteps
block without using a separatescript
block.Example:
groovyCopy codepipeline { agent any stages { stage('Example') { steps { echo "Hello from the steps block!" sh 'ls -la' } } } }
Using Only
script
Block: If you need to execute complex scripted Groovy code or use scripted pipeline steps that are not available as Declarative steps, you can use only ascript
block. In this case, thesteps
block is not required.Example:
groovyCopy codepipeline { agent any stages { stage('Example') { script { echo "Hello from the script block!" sh 'ls -la' } } } }
Using
script
Insidesteps
Block: You can use ascript
block within asteps
block when you need to perform complex or custom logic for a specific step.Example:
groovyCopy codepipeline { agent any stages { stage('Example') { steps { script { echo "Running complex logic here..." def result = sh(returnStdout: true, script: 'echo "Hello from the script!"').trim() echo "Script Result: ${result}" } echo "Continuing with regular steps..." } } } }
Using Multiple
script
Blocks Insidesteps
Block: You can use multiplescript
blocks within asteps
block when you have separate steps that require custom scripted logic.Example:
groovyCopy codepipeline { agent any stages { stage('Example') { steps { script { echo "Step 1: Performing a custom action..." } script { echo "Step 2: Running a custom script..." sh 'ls -la' } } } } }
Remember that while the script
block allows you to perform advanced and custom logic, it's best to use Declarative pipeline steps whenever possible. Declarative steps provide better readability and maintainability, and using them follows the spirit of the Declarative pipeline approach. Reserve the script
block for situations where you genuinely need scripted Groovy code or require specific scripted pipeline steps that are not available in Declarative steps.
Capturing the output or return code of a command
Capturing the exit status: If you want to capture the exit status of the shell command (0 for success, non-zero for failure), you can use the returnStatus option. When returnStatus is true, the sh step will return the exit status of the command instead of its output. Here's an example:
pipeline {
agent any
stages {
stage('Example') {
steps {
script {
def exitStatus = sh(returnStatus: true, script: 'ls some_nonexistent_directory')
if (exitStatus == 0) {
echo "Command succeeded!"
} else {
echo "Command failed with exit code: ${exitStatus}"
}
}
}
}
}
}
Capturing the command output: If you want to capture the output (standard output) of the shell command, you can assign the sh
step to a variable. The sh
step will return the output as a string. Here's an example:
groovyCopy codepipeline {
agent any
stages {
stage('Example') {
steps {
script {
def commandOutput = sh(script: 'echo "Hello, Jenkins!"', returnStdout: true).trim()
echo "Command output: ${commandOutput}"
}
}
}
}
}
Script Block
In Jenkins, the script
block is a section in a Jenkins pipeline where you can write and execute arbitrary Groovy code. The script
block is used when you need to perform more complex logic or need access to the underlying Jenkins API that is not available directly in declarative pipeline steps.
Within a
steps
block: Although most steps in Declarative pipelines are designed to work without needing ascript
block, there may be situations where you want to include one within thesteps
block. This can be useful if you need to perform more complex logic. Here's an example:groovyCopy codepipeline { agent any stages { stage('Example') { steps { script { // Your scripted Groovy code here def message = "Hello, Jenkins!" echo message } } } } }
Within a
post
block: Thepost
block allows you to define actions that should be executed after the entire pipeline or specific stages have completed. If you need to include scripted Groovy logic in a post-action, you can use ascript
block within thepost
block. Here's an example:groovyCopy codepipeline { agent any stages { stage('Example') { steps { // Your regular steps here } } } post { always { script { // Your scripted Groovy code here echo "This will always run, regardless of the pipeline result." } } success { script { // Your success-specific scripted Groovy code here echo "This will run only if the pipeline is successful." } } failure { script { // Your failure-specific scripted Groovy code here echo "This will run only if the pipeline fails." } } } }
Please note that while the script
block can be powerful, it's best to use Declarative pipeline steps whenever possible, as they provide a higher level of abstraction and better readability. Only use the script
block when you need to perform advanced or custom logic that cannot be achieved using regular Declarative pipeline steps.
Functions & Conditional statements
Functions blog: here
Conditional statements(if/else and when) blog: here
Note: If you want to capture the return type of any function. There are 2 ways.
If you are using steps{} block(declarative syntax with simple logic). use def in front of a variable name as shown.
def myFunction() { return "Hello from the function!" } pipeline { agent any stages { stage('Example') { steps { // Calling the function directly in the steps block def result = myFunction() echo "Function Return Value: ${result}" } } } }
If you are using script block then no worries. You can directlyc capture it
def mul() { def a=2 def b=3 def mul=a*b return mul } pipeline { agent any stages { stage('Hello') { steps{ script{ output= mul() echo "The mul is ${output}" } } } } }
Scope of variables in Jenkins Declarative pipeline
Variables in a Stage: Variables defined within a stage using the
environment
directive are only available within that stage. They are not accessible in other stages or post blocks. This is because each stage in a Declarative pipeline runs in a separate workspace and has its own environment.Example:
groovyCopy codepipeline { agent any stages { stage('Stage 1') { environment { MY_VARIABLE = "Value 1" } steps { echo "Variable in Stage 1: ${MY_VARIABLE}" } } stage('Stage 2') { steps { echo "Variable in Stage 2: ${MY_VARIABLE}" // This will not work } } } }
Variables in a Script Block: Variables defined(using def) inside a
script
block within a stage or post block have local scope and are limited to that specific block. They are not accessible outside of the block.Example:
groovyCopy codepipeline { agent any stages { stage('Example') { steps { script { def localVariable = "Local Value" echo "Local Variable: ${localVariable}" } } } } post { always { script { echo "Post Block Variable: ${localVariable}" // This will not work } } } }
Note: However if you want to use a variable defined in stage 1 in stage 2, then you need to use env variable in stage 1 instead of local def.
Example:
Here's an example using environment variables to share data between stages:groovyCopy codepipeline { agent any stages { stage('Stage 1') { steps { script { // Define a variable in Stage 1 env.MY_VARIABLE = 'Data from Stage 1' } } } stage('Stage 2') { steps { script { // Access the variable defined in Stage 1 def dataFromStage1 = env.MY_VARIABLE echo "Data from Stage 1: ${dataFromStage1}" } } } } }
In this example,
MY_VARIABLE
is defined in "Stage 1" and then accessed in "Stage 2" using theenv
object. This allows you to pass data between stages through environment variables.Variables in Nested Stages and Post Blocks: Variables defined within nested stages or post blocks have the same scope rules as described above. Nested stages or post blocks do not have access to variables defined in their parent stages or vice versa.
Example:
groovyCopy codepipeline { agent any stages { stage('Parent Stage') { steps { script { def parentVariable = "Parent Value" echo "Parent Variable: ${parentVariable}" } } stage('Nested Stage') { steps { script { def nestedVariable = "Nested Value" echo "Nested Variable: ${nestedVariable}" echo "Parent Variable in Nested Stage: ${parentVariable}" // This will not work } } } } } }
you can access variables defined in the parent stage using the
environment
block within a nested stage. Theenvironment
block defined at the parent stage level will be available to all child stages within that stage's workspace.
Here's the updated example to demonstrate this:
groovyCopy codepipeline {
agent any
stages {
stage('Parent Stage') {
environment {
PARENT_VARIABLE = "Parent Value"
}
steps {
echo "Parent Variable: ${PARENT_VARIABLE}"
}
stage('Nested Stage') {
steps {
script {
echo "Nested Variable: ${PARENT_VARIABLE}" // This will work
}
}
}
}
}
}
In conclusion, variables defined in one stage or context (e.g., script block) are not directly accessible in other stages, nested stages, or post blocks. If you need to share data between different stages, you can use external means like writing to files, using Jenkins workspace, or using shared libraries. Alternatively, you can also pass parameters between stages using the input
step or using the stash
and unstash
steps to move files between stages.
Note: If we declare a variable using def then only that variable can be accessed within script block. But if we define a variable without def just like other variable then it can be accessed outside of the script block as well. See in below example
pipeline {
agent any
stages {
stage('Example') {
steps {
script {
// Local variable declaration within the script block
def localVariable = "Local Value"
NORMAL_VAR = "Normal var"
echo "Local Variable: ${localVariable}" // Local Value
// Since NORMAL_VAR is in script block don1t use env. before variable name which resolves to null
echo "Normal variable: ${NORMAL_VAR}" // Normal var
echo "Normal variable with env: ${env.NORMAL_VAR}" //null
}
// Since NORMAL_VAR is in script block don1t use env. before variable name which resolves to null
echo "Normal Variable outside script block: ${NORMAL_VAR}" // Normal var
}
}
}
}
Post block
In Jenkins Declarative pipeline, the post
block is used to define actions that should be executed after the main stages of the pipeline have been completed. It allows you to specify different behaviours based on the outcome of the pipeline or specific stages within the pipeline. The post
block contains one or more postconditions, which can be associated with a specific result (e.g., success, failure) or run unconditionally.
The syntax of the post
block is as follows:
groovyCopy codepipeline {
// Pipeline configuration
post {
// Post conditions go here
}
}
Within the post
block, you can define different sections, each associated with a specific outcome of the pipeline run. The available sections are:
always
: Thealways
section specifies actions that should be executed regardless of the pipeline result. This section is always executed, regardless of whether the pipeline is successful or not.groovyCopy codepost { always { // Actions to run always } }
success
: Thesuccess
section specifies actions that should be executed only if the pipeline is successful (i.e., all stages are completed without any failure).groovyCopy codepost { success { // Actions to run on successful pipeline } }
failure
: Thefailure
section specifies actions that should be executed only if the pipeline fails (i.e., at least one stage fails).groovyCopy codepost { failure { // Actions to run on failed pipeline } }
unstable
: Theunstable
section specifies actions that should be executed if the pipeline result is unstable (e.g., some tests failed, but the build itself succeeded).groovyCopy codepost { unstable { // Actions to run on unstable pipeline } }
changed
: Thechanged
section specifies actions that should be executed only if the state of the pipeline has changed from the previous run. This can be useful when triggering actions based on code changes or other external factors.groovyCopy codepost { changed { // Actions to run if the pipeline state has changed } }
Combining Post Conditions: You can combine multiple post conditions within the
post
block to specify different actions based on different pipeline outcomes.groovyCopy codepost { always { // Actions to run always } success { // Actions to run on successful pipeline } failure { // Actions to run on failed pipeline } }
Remember that each section of the post
block is optional, and you can include only the sections that are relevant to your pipeline. The sections are executed in the order they are defined.
The post
block is a powerful feature in Declarative pipelines that allows you to handle post-build actions efficiently and cleanly. It can be used to perform tasks such as sending notifications, cleaning up resources, publishing reports, and more based on the pipeline's results.
Commonly used pre-defined function
In Jenkins Declarative pipeline, there are indeed some commonly used predefined functions and steps that are specifically designed to be used within the post
block. Here are a few of them:
cleanWs
: ThecleanWs
function is used to clean up the workspace at the end of a pipeline run. It helps to free up disk space by deleting the workspace of the current build.Example:
groovyCopy codepipeline { agent any stages { // Define stages } post { always { cleanWs() } } }
archiveArtifacts
: ThearchiveArtifacts
step is used to archive artifacts (e.g., build outputs) after a successful build. The artifacts can be accessed later or used for deployments.Example:
groovyCopy codepipeline { agent any stages { // Define stages } post { success { archiveArtifacts artifacts: '**/*.jar', allowEmptyArchive: true } } }
mail
: Themail
step is used to send an email notification after the pipeline run. It is useful for notifying team members or stakeholders about the build result.Example:
groovyCopy codepipeline { agent any stages { // Define stages } post { failure { mail to: 'team@example.com', subject: 'Pipeline Failed', body: 'The pipeline has failed.' } } }
slackSend
: TheslackSend
step is used to send messages to Slack channels. It is helpful for integrating Jenkins with Slack for build notifications.Example:
groovyCopy codepipeline { agent any stages { // Define stages } post { always { slackSend message: 'Pipeline completed.', channel: '#build-notifications' } } }
These are just a few examples of commonly used predefined functions and steps in the post
block of Jenkins Declarative pipeline. There are many more available, and you can find additional functions provided by plugins or write your own custom functions as needed. The post
block is a powerful mechanism to execute specific actions based on the outcome of the pipeline, and it enables you to perform various post-build tasks like cleanup, notifications, archiving, etc.
Multi Branch pipeline
Environment variables available throughout the pipeline:
Environment variables defined in the below 3 ways can be accessed anywhere in the pipeline.
1. global environment block
2. external files3. pipeline parameters
Eg: Supply environment variables using external file using load
In a Jenkins Declarative Pipeline, you can use theload
step to read and evaluate Groovy scripts from external files. This can be useful for modularizing your pipeline code or for reading configuration or shared code from external files. Here's how you can use theload
step:- Create an external Groovy script file. This file can contain any Groovy code you want to use in your pipeline. For example, you can create a file named
myScript.groovy
with the following content:
- Create an external Groovy script file. This file can contain any Groovy code you want to use in your pipeline. For example, you can create a file named
// App vars
env.APP_NAME = 'dummy-app'
- In your Jenkins Declarative Pipeline script, use the
load
step to load and evaluate the external script file. You can then call functions or use variables defined in the loaded script:
pipeline {
agent any
stages {
stage('Load Script') {
steps {
script {
load("myScript.groovy")
echo "App name: ${env.APP_NAME}"
}
}
}
}
}
In this example, we load the myScript.groovy
file using load
, and then we call the env.APP_NAME variable defined in that external script.
Make sure the external script file is located in the same directory as your Jenkinsfile or provide the full path to the script file if it's located elsewhere. Additionally, ensure that the script file is secure and trusted, as loading external scripts can introduce security risks.
So when the same variable is declared in all 3 places. Then the precedence order will be like this.
Global env >> external file >> pipeline parametersExample: Extend the above example by providing the same APP_NAME env in other 2 locations i.e. global environment block and pipeline parameter.
pipeline { agent any environemnt { APP_NAME = "dummy value from global env block" } stages { stage('Load Script') { steps { script { load("myScript.groovy") echo "App name: ${env.APP_NAME}" } } } } }
Defined a string parameter to the pipeline with the same value
case 1: If I trigger the pipeline now then I will get "dummy value from global env block" as ouput(since it has highest precedence)
case 2: comment envi block var and the trigger pipeline again, then you will get "dummy app" from the external file as output
case3: Now comment the external file value also and trigger pipeline finally you will get "dummy app from params"