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 pipeline any 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 file

      pipeline {
          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, and changed that you can execute your scripts on for example Let's break down the explanation into parts

    Always:

    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. The BRANCH_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 is true. You would set and update the value of codechanges 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 pipeline

      pipeline {
          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 the withCredentials wrapper.

    Option 1: Defining in environment Block

      pipeline {
          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 Wrapper

      pipeline {
          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:

  1. Locate your pipeline job.

  2. Click on "Build with Parameters."

  3. Provide values for the parameters.

  4. 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!