15

I want to send notifications from my Jenkins pipeline build when the job recovers. Meaning when the current build is successful and the last build was erroneous (failed, aborted etc).

I know how to send notifications. I think my question boils down to how to check the status of the previous build, but I might be mistaken.

I have tried "currentBuild.rawBuild.getPreviousBuild()?.getResult()", but got exception "org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild". If I turn sandbox off, it should work. Would it be possible with the sandbox?

Joosep Simm
  • 462
  • 1
  • 5
  • 13
  • Have you tried to manually approve the method signature ? In `Jenkins > Administer > In-Process Script Approval` – Pom12 Mar 06 '17 at 09:19
  • I haven't tried that. I cannot find Administer link from anywhere. I'm not the Jenkins administrator too. The solution macg33zr gave is good because it works without any special tweaks. – Joosep Simm Mar 06 '17 at 10:15
  • Indeed, but it only works for declarative pipelines, not classic pipelines – Pom12 Mar 06 '17 at 10:26
  • You can also use a specialized tool like [CatLight build notifier](https://catlight.io) that will show current build status and history to every team member. – alex Mar 24 '17 at 00:46

6 Answers6

23

I've found another working solution, which doesn't require you to manually keep track of your build result. Although, it requires use of a script element :(

pipeline {
  agent any
  post {
    success {
      script {
        if (currentBuild.getPreviousBuild() && 
            currentBuild.getPreviousBuild().getResult().toString() != "SUCCESS") {
          echo 'Build is back to normal!'
        }
      }
    }
  }
}
Kolky
  • 2,917
  • 1
  • 21
  • 42
  • I like this solution best, because it's easy to read and totally agnostic of the build itself. – dokaspar Aug 10 '17 at 08:23
  • 1
    This is a good solution but, the first time the job runs, the script gives an exception: java.lang.NullPointerException: Cannot invoke method getResult() on null object – DavidA Sep 25 '17 at 08:57
  • @DavidA noticed that too, added a null-check. – Kolky Oct 30 '17 at 20:07
12

Interesting question. You can do it in Jenkins declarative pipeline using the 'changed' part of a post{} section. But you will need to set currentBuild.result to SUCCESS or FAILURE in the job and check it in the post section. There does not seem to be an easy way to get the current build status (fail, success etc) as far as Jenkins is concerned without tracking it in your pipeline - unless I have missed something subtle. Here is an example, you would send the notification in the changed section where it checks for SUCCESS:

pipeline {
    agent any

    parameters {
        string(name: 'FAIL',     defaultValue: 'false', description: 'Whether to fail')
    }

    stages {
        stage('Test') {

            steps {

                script {

                    if(params.FAIL == 'true') {
                        echo "This build will fail"
                        currentBuild.result = 'FAILURE'
                        error("Build has failed")
                    }
                    else {
                        echo "This build is a success"
                        currentBuild.result = 'SUCCESS'
                    }

                }
            }
        }
    }

    post {
        always  {
            echo "Build completed. currentBuild.result = ${currentBuild.result}"
        }

        changed {
            echo 'Build result changed'

            script {
                if(currentBuild.result == 'SUCCESS') {
                    echo 'Build has changed to SUCCESS status'
                }
            }
        }

        failure {
            echo 'Build failed'
        }

        success {
            echo 'Build was a success'
        }
        unstable {
            echo 'Build has gone unstable'
        }
    }
}

--Bill

macg33zr
  • 1,725
  • 15
  • 13
  • I'll give it a try. Setting current build status is totally fine. My script should know when it succeeds or fails. – Joosep Simm Mar 03 '17 at 15:09
  • This worked nicely. Do you know if and how it's possible with scripted pipeline environment? I understood from the documentation that scripted environment is more powerful than declarative, but it lacks documentation what is possible. – Joosep Simm Mar 06 '17 at 10:12
  • 1
    I haven't figured out how to do this in scripted pipeline. This is why I quickly moved to the declarative pipeline mode after doing a lot of work in scripted - the declarative pipeline has the post build handling which was a killer app for me. I find I can do quite a lot in declarative using script sections and groovy code in shared libraries or outside the pipeline section. If you go to the developers unit tests for declarative pipeline on GitHub they have a lot of examples: https://github.com/jenkinsci/pipeline-model-definition-plugin/tree/master/pipeline-model-definition/src/test/resources – macg33zr Mar 06 '17 at 20:23
  • I am using a post { changed { notify that status changed} } model in declarative pipeline, but what I really want is only notify that it's changed to success. I notify on failure always, so right now we get duplicates when it changes from success to fail. I was trying to find a way to get the current build status in the post block and could find no way to do that. `currentBuild.result` only works if you manage and set it yourself. I think this is a pretty big hole in the pipeline functionality right now. – sporkthrower Mar 15 '17 at 22:20
  • It's kind of quirky and not sure 100% legit syntax but I found you can put a 'floating' scripted stage after the main pipeline and it will execute in the non-failure case. So if you track the failed / changed status with some booleans the changed to success case can be notified in this floating stage. My code for it is here: https://github.com/macg33zr/jenkins-experimental-pipelines/blob/master/changed-notify-success.groovy – macg33zr Mar 16 '17 at 00:25
  • OK figured out this only notify change on success without the quirk. You can track a failure status bool in the post { failure {} } section of each stage and check it in the pipeline top level post { changed {} } section. Like this one: https://github.com/macg33zr/jenkins-experimental-pipelines/blob/master/changed-notify-success-2.groovy – macg33zr Mar 16 '17 at 00:51
  • Is this a scalable solution? For example if you have many stages with many steps... then won't you have to set the current build status in multiple places? – dokaspar Aug 10 '17 at 08:20
  • 1
    `currentBuild.currentResult` gives you what the pipeline things the result will be -- which should be accurate in a pipeline post section. – simon.watts Jan 24 '18 at 17:54
4

similar to answer by @Kolky, this would be the snippet for "Scripted pipeline", where you use " node{ stage1... stage2... etc }":

    stage('Email') {
        if (currentBuild.getPreviousBuild().getResult().toString() != "SUCCESS") {
            echo 'Build is back to normal!'
            stage('Send build recovered email') {
                mail body: 'My build back to successful',
//                    from: '', replyTo: '',
                        subject: 'My build back to successful',
                        to: 'mymail@server.com'
            }

        }
    }
Alex
  • 2,589
  • 3
  • 35
  • 44
  • This improved if-condition avoids NPE and notifies only if build status changes from no success to success: `if (currentBuild.currentResult == 'SUCCESS' && currentBuild.previousBuild?.result != "SUCCESS")` – dmoebius Aug 15 '19 at 14:46
3

Alternatively, you could move your script logic outside of the pipeline (ideally - to Jenkins shared pipeline library to be re-usable & keep the pipeline clean) so you wouldn't need the script block:

def sendNotification(buildStatus) {
  if (buildStatus != 'SUCCESS') {
    // Do nothing - only interested when status becomes GREEN
    return
  }

  mattermostSend "${env.JOB_NAME} has recovered! (<${env.BUILD_URL}|See the build>)"
}

pipeline {
  ...

  post {
    changed {
      sendNotification(currentBuild.currentResult)
    }
  }
}
Jonas Masalskis
  • 1,206
  • 1
  • 15
  • 14
  • Hi @Jonas Maseskis, It's been already a couple of years since you answered here. Which is very interesting alternative which I've been looking for, but still not the complete way of [how to move entire `post {}` block of code to a shared library](https://stackoverflow.com/questions/55602956/is-there-a-way-to-move-the-entire-post-build-section-in-jenkinsfile-to-the-gl) any idea how to do that? – DelphyM Apr 14 '19 at 22:28
0

Attached documentation will help:

Pipeline Ref Card

Ras
  • 543
  • 1
  • 9
  • 25
0

macg33zr gave a good answer. In the meanwhile, there is an even simpler way to perform some steps in a declarative pipeline after pipeline recovery: There is a new fixed condition in the post section. The post steps in the fixed condition section are only run "if the current Pipeline’s or stage’s run is successful and the previous run failed or was unstable." (see Jenkins' declarative pipeline documentation for details).

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Do Something that could fail the pipeline.'
            }
        }
    }
    post { 
        fixed { 
            echo 'Build is back to normal!'
        }
    }
}

If you like to send notifications, there are several Jenkins plugins, for Slack for example. The notification code for Slack would look like this.

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Do Something that could fail the pipeline.'
            }
        }
    }
    post { 
        fixed { 
            slackSend channel: '#your-slack-target-channel',
                  color: 'good',
                  message: "The build is back to normal. :sweat_smile: The build _${currentBuild.fullDisplayName}_ was successful again.\nGo to pipeline: ${env.BUILD_URL}"
        }
    }
}