Best approach to check if my branch has being changed

Hi community!

In my declarative pipeline I’m trying to get the information if my branch has changes since my last Jenkins build. I was able to achieve that using currentBuild.changeSets.size() > 0, but with this approach I’m not able to share this information in a variable, so that I can also reuse it in my post section.

Here is my example code.

def changeCount = 0

pipeline {
    environment {
        BACKUP_FILE = "${'backup_' + env.BUILD_ID + '_tar.gz'}"
        DEPLOY_FOLDER = "${'deploy' + env.BUILD_ID}"
    }

    agent none

    stages {
        stage ("Check changes") {
            steps {
                echo "Check if there are any changes pushed into this branch..."

                script {
                    changeCount = currentBuild.changeSets.size()
                }

                echo "${changeCount} commit(s) since last buid."
            }
        }

        stage('start process') {
            matrix {
                agent {
                    label 'mylabel'
                }

                axes {
                    axis {
                        name 'AGENTS'
                        values 'agent1', 'agent2'
                    }
                }

                stages {
                    stage('PREPARE ENVIRONMENT') {
                        when {
                            expression {
                                changeCount > 0
                            }
                        }

                        steps {
                            sh '''
                                ...
                            '''
                        }
                    }

                    stage('Build') {
                        when {
                            expression {
                                changeCount > 0
                            }
                        }

                        steps {
                            sh '''
                                ...
                            '''
                        }
                    }

                    stage('Test') {
                        when {
                            expression {
                                changeCount > 0
                            }
                        }

                        steps {
                            sh '''
                                ...
                            '''
                        }
                    }

                    stage('Deploy') {
                        when {
                            expression {
                                changeCount > 0
                            }
                        }

                        steps {
                            sh '''
                                ...
                            '''
                        }
                    }
                }
            }
        }
    }

    post {
        always {
            script {
                if (currentBuild.changeSets.size() > 0) {
                    emailext (
                        attachLog: true,
                        compressLog: true,
                        to: 'myemail@domain.com;',
                        subject: "Application - Build # ${BUILD_NUMBER}",
                        body: "Status: ${currentBuild.currentResult}\nBuild number: ${env.BUILD_NUMBER}\nTo see log details open the attached file or click on the link: ${env.BUILD_URL}"
                    )
                } else {
                    emailext (
                        attachLog: false,
                        compressLog: false,
                        to: 'myemail@domain.com;',
                        subject: "Application - Build # ${BUILD_NUMBER}",
                        body: "There are no changes pushed since last build."
                    )
                }
            }
        }
    }
}

Questions:

  1. How can I update the variable changeCount and reuse it in my post section?
  2. Is there a better way of achieving the same outcome?
  3. I also tried to actually create a variable for the email body content, and update this variable according to the changeCount variable, and then use the variable in my emailtext command. I tried to declare a local variable, but it didn’t work well. Any suggestions on that?
  4. It seems that when using matrix I’m not able to retrieve the number of changes. How can I get this information with this approach?
  5. Is it possible to prevent the SCM checkout from happening in case there are no changes in my branch?

Thank you very much in advance.

Best regards,
Alex

What worked for me quite well is the “branch indexing” feature of Multibranch pipeline - new builds get triggered if there are new changes, otherwise nothing happens.

you could just put currentBuild.changeSets.size() in the expression, its a pretty simple statement.

I’m curious why your thinking its not working? You could change your top level to a function with an echo, then you can see when the when block is calling it.

Hi saper!

In this case I’m using a simple pipeline.
Thanks though.

Cheers,
Alex

Hi halkeye!

Thank you very much for your reply.

Yes, it is a pretty simple statement. It is weird that it stop working.

Before I start using matrix I was able to retrieve the changeCount in the stages section, but not in the post section, after start using matrix I cannot retrieve in both sections. Maybe I have to place the code to get the changeCount somewhere else? Any suggestions?

I was also trying to avoid having to call this function twice and share the value I’ve got from the first call with the two sections. Is it possible to accomplish that?

Thank you very much!

Alex

Like I said, your solution should work, so there’s probably something else going on. You can store strings and maybe ints to the global env variable (env.FOO = “bar”)

But that being said, I think readable trumps smart or fast. Size would be o(1) anyways, so it checking the size everywhere works, and that’s readible, it should be fast enough.

Nothing prevents you from using scripted pipeline in the multibranch project. It makes little sense to me to reinvent the wheel…

Hi @saper!
Thanks for your reply.
I will play with the multibranch and see how it goes.
But I’m not sure if will make much sense though, or if it will be too much, because for this exercise I only need to deal with one branch.
Thank you!

Hi @halkeye.

I was able to use the matrix successfully for what I need to do, and I kept the currentBuild.changeSets.size() for the places where I need to check. It seems to be OK performance wise. However, I noticed that the stages a run in parallel, which is OK, but not for stage when I run the unit tests.

Is it possible to run one of the stages sequentially?
Or perhaps, is it possible to use matrix and run all stages in loop manner by each agent sequentially?

Thank you very much!

It does not matter how many you have. Good thing is that you can just scan the repository and it will do nothing if there are no changes in there.

However, I noticed that the stages a run in parallel, which is OK, but not for stage when I run the unit tests.

Do you mean each matrix cell in the pipeline needs to run a build stage and a test stage, but the test stages must not run in parallel? If so, you could use https://plugins.jenkins.io/lockable-resources/ to make them wait for each other.

Hi @kon!

Thanks for your reply.
Ideally, I would like to loop through each agent/node and run each stage for that agent.
Something like this:

  • AGENT X
  • stage Build
  • stage Unit test
  • stage Deploy
  • AGENT Y
  • stage Build
  • stage Unit test
  • stage Deploy
  • AGENT Z
  • stage Build
  • stage Unit test
  • stage Deploy

Despite the agents are different computers (virtual machines), I don’t want to execute the stage Unit Test at the same time with the other agents. That’s the main goal. If I can isolate just one stage to do not run concurrently is better because I’d save time, but not a big deal if I can also run them sequentially.

Any suggestions?

Thank you!

I think you can use the lockable resources plugin for this. Define a lockable resource with a known name, and make the unit test stage lock that resource before it runs the tests.

Possible caveats:

  • I haven’t used lockable resources in a matrix build myself. Only in a multibranch pipeline to reserve resources for testing so that parallel builds of different branches or pull requests won’t try to use the same resource at the same time.
  • If the unit test stage is waiting for a lockable resource, I don’t remember whether Jenkins keeps the executor reserved for that job or whether it can run some other jobs there during the wait.
1 Like