46

How can I add to a Jenkins pipeline an old-style post-build task which sends email when the build fails? I cannot find "Post-build actions" in the GUI for a pipeline. I know that I can wrap the entire build script try/catch, however, this seems ugly when the build script is large and continues to send emails even when the job was aborted manually. I would like to achieve the same functionality as with the previous email-ext based post-build action.

try {
    // Do sth
} catch(e) {
    emailext body: '$DEFAULT_CONTENT', 
        recipientProviders: [
            [$class: 'CulpritsRecipientProvider'],
            [$class: 'DevelopersRecipientProvider'],
            [$class: 'RequesterRecipientProvider']
        ], 
        replyTo: '$DEFAULT_REPLYTO', 
        subject: '$DEFAULT_SUBJECT',
        to: '$DEFAULT_RECIPIENTS'
    throw err
}
Yoon5oo
  • 496
  • 5
  • 11
Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
  • 2
    What did you end up doing? I have a similar requirement and keep on getting surprised when it comes to Jenkins design. How is email alert something we may want to duplicated all around and within pipelines? are we really being suggested to mix code revisions with email notification settings - this makes no sense to me. – NicolasW Mar 23 '18 at 18:51
  • @NicolasW i'm still using the `emailext body...` method above. Haven't found anything better and wasted enough time on this :) – Mariusz Jamro Apr 03 '18 at 13:46
  • 1
    Ok thanks, really not impressed with Jenkins! – NicolasW Apr 27 '18 at 16:59
  • 2
    @NicolasW Pipilines in Jenkins are a hurried response to "guys, it's 201*, people are maintaining their configuration as code, let's do something so that they don't dump us". – Abhijit Sarkar Dec 08 '18 at 01:43

6 Answers6

60

This answer worked on my Jenkins ver. 2.96.

Jenkins pipeline email not sent on build failure - Stack Overflow

 pipeline {  
     agent any  
     stages {  
         stage('Test') {  
             steps {  
                 sh 'echo "Fail!"; exit 1'  
             }  
         }  
     }  
     post {  
         always {  
             echo 'This will always run'  
         }  
         success {  
             echo 'This will run only if successful'  
         }  
         failure {  
             mail bcc: '', body: "<b>Example</b><br>Project: ${env.JOB_NAME} <br>Build Number: ${env.BUILD_NUMBER} <br> URL de build: ${env.BUILD_URL}", cc: '', charset: 'UTF-8', from: '', mimeType: 'text/html', replyTo: '', subject: "ERROR CI: Project name -> ${env.JOB_NAME}", to: "foo@foomail.com";  
         }  
         unstable {  
             echo 'This will run only if the run was marked as unstable'  
         }  
         changed {  
             echo 'This will run only if the state of the Pipeline has changed'  
             echo 'For example, if the Pipeline was previously failing but is now successful'  
         }  
     }  
 }

Official documentation for the mail step

Snailedlt
  • 460
  • 6
  • 14
kujiy
  • 5,833
  • 1
  • 29
  • 35
  • 1
    I had to remove the 2nd backslash in \n\ to get this to work. – Christopher Markieta Aug 29 '18 at 01:25
  • 7
    What is `mail`? Is it from a plugin? Is it different than `emailext`? – cowlinator Nov 06 '19 at 21:35
  • @kujiy how can I send only one email after failure and wait until the build will be successful? I don't want to send another email if the next build will be a failure. – andrew Jan 14 '21 at 12:34
  • 1
    `mail` is different than `emailext` - I'm still trying to find the documentation O_o – JGlass Jan 25 '22 at 22:19
  • I assume this works after setting up smtp credentials? I did that and was able to send a test email, it does not send from my pipeline. – Michael Mar 11 '22 at 22:04
  • 1
    @JGlass This is all I could find -> https://www.jenkins.io/doc/pipeline/steps/workflow-basic-steps/#mail-mail – Snailedlt Aug 09 '22 at 10:31
  • @andrew You can use the 'changed' post method, and check wether currentBuild.currentResult=="FAILURE" (with a script clause if you use declarative script) – Akseli Arvaja Jan 17 '23 at 07:30
29

For taking action only when build status has changed you can use the post > changed block.

And for checking, what state the status has changed to you can use the script block in combination with checking the value of currentBuild.currentResult property.

Like so:

pipeline {
    ...

    post {
        changed {
            script {
                if (currentBuild.currentResult == 'FAILURE') { // Other values: SUCCESS, UNSTABLE
                    // Send an email only if the build status has changed from green/unstable to red
                    emailext subject: '$DEFAULT_SUBJECT',
                        body: '$DEFAULT_CONTENT',
                        recipientProviders: [
                            [$class: 'CulpritsRecipientProvider'],
                            [$class: 'DevelopersRecipientProvider'],
                            [$class: 'RequesterRecipientProvider']
                        ], 
                        replyTo: '$DEFAULT_REPLYTO',
                        to: '$DEFAULT_RECIPIENTS'
                }
            }
        }
    }

}
Jonas Masalskis
  • 1,206
  • 1
  • 15
  • 14
19

At the moment you have to use this procedure with a try-catch to get a post-build action.

But in the future this step is planned (actually it is in review). You can read the blog post for further information (see the declarative pipeline part). See also the the Jenkins Jira ticket and the pull request.

Update:

The Jenkins declarative pipeline now has the post section that will run steps conditionally at the end of each build. See the docs for more information.

post {
 always {
   sh 'echo "This will always run"'
 }
 success {
  sh 'echo "This will run only if successful"'
 }
 failure {
  sh 'echo "This will run only if failed"'
 }
 unstable {
  sh 'echo "This will run only if the run was marked as unstable"'
 }
 changed {
  sh 'echo "This will run only if the state of the Pipeline has changed"'
  sh 'echo "For example, the Pipeline was previously failing but is now successful"'
  sh 'echo "... or the other way around :)"'
 }
}
jpyams
  • 4,030
  • 9
  • 41
  • 66
ansib
  • 524
  • 3
  • 10
  • @MariuszJamro they should. They could wrap existing scripting code in `script` blocks. – Abhijit Sarkar Dec 08 '18 at 01:44
  • @AbhijitSarkar Not necessarily - there are still some usecases where declarative won't do the trick - see the discussion here for instance: https://issues.jenkins-ci.org/browse/JENKINS-41335 – sonicwave Dec 12 '18 at 13:10
12

This is working well on my Jenkins (current ver 2.164.2 and emailext) with declarative pipeline:

pipeline {
...

environment {
        EMAIL_TO = 'someone@host.com'
    }
post {
        failure {
            emailext body: 'Check console output at $BUILD_URL to view the results. \n\n ${CHANGES} \n\n -------------------------------------------------- \n${BUILD_LOG, maxLines=100, escapeHtml=false}', 
                    to: "${EMAIL_TO}", 
                    subject: 'Build failed in Jenkins: $PROJECT_NAME - #$BUILD_NUMBER'
        }
        unstable {
            emailext body: 'Check console output at $BUILD_URL to view the results. \n\n ${CHANGES} \n\n -------------------------------------------------- \n${BUILD_LOG, maxLines=100, escapeHtml=false}', 
                    to: "${EMAIL_TO}", 
                    subject: 'Unstable build in Jenkins: $PROJECT_NAME - #$BUILD_NUMBER'
        }
        changed {
            emailext body: 'Check console output at $BUILD_URL to view the results.', 
                    to: "${EMAIL_TO}", 
                    subject: 'Jenkins build is back to normal: $PROJECT_NAME - #$BUILD_NUMBER'
        }
    }
}

You can control email for failed build or changed status. Also I've put build log to the email body

akokskis
  • 1,486
  • 15
  • 32
Milan
  • 181
  • 2
  • 5
2

to DRY your scripted pipelines you can easily extend Jenkins with your own shared library and define a custom step emailOnError by creating vars/emailOnError.groovy with the following:

def call(Closure action) {
    try {
        action()
    } catch(e){
        emailext    body: '$DEFAULT_CONTENT', 
                    recipientProviders: [
                        [$class: 'DevelopersRecipientProvider'],
                        [$class: 'RequesterRecipientProvider']
                    ], 
                    replyTo: '$DEFAULT_REPLYTO', 
                    subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - ERROR!',
                    to: '$DEFAULT_RECIPIENTS'
        throw err
    }   
}

Note that this shared method emailOnError comes from vars/emailOnError.groovy and can be used like this:

emailOnError() {
    // Do sth   
}
enorl76
  • 2,562
  • 1
  • 25
  • 38
culmat
  • 1,156
  • 11
  • 12
2

Almost all above answers are correct but this one I am using in post section when the build is failed. Maybe this can be useful

post {
always {
  script {
    if (currentBuild.currentResult == 'FAILURE') {
      step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "abc.xyz@blabl.com", sendToIndividuals: true])
    }
  }
}
Dashrath Mundkar
  • 7,956
  • 2
  • 28
  • 42