Email notifications in Jenkins

Importance of notifications

Notifications are a basic mechanism for any CI/CD tool. As you know, regarding to Continuous Integration, fast feedback is a key feature that allows the team to respond rapidly when a build fails, and notifications are an effective way of achieving it.

Talking about Continuous Delivery, another clear example of event to be notified is when a build is ready to be deployed in production but a manual approval is required.

I’m not enumerating all possible notification events in a CI/CD process, but you can find some relevant examples below:

  • Unit or integration tests failing

  • Deploy success in stage environment in order for the testers to start testing

  • Smoke test failing

  • Manual intervention required

  • Deployment rejection

  • App/infra deployment result

In this post I will show you how to setup and use notifications in Jenkins by email, the asynchronous communication mean by excellence. In later posts I will tackle synchronous communication channels like Microsoft Teams or Slack

Setting up emails in Jenkins

First step is to set the email address that will be used as sender for all notifications and the base URL that all links included in the emails will be based on. This is configured in Manage JenkinsConfigure System in which you will find a section called Jenkins Location

Jenkins Location

Jenkins provides three ways of sending emails: mail step, Mailer plugin and Email Extension

We will have a look into how to setup use all of them.

Basic email step

Email step is a simple Jenkins pipeline step that provides the minimum functionality to send emails, normally not related with build results

Configuration parameters are retrieved from JenkinsConfigure System at the bottom of the page in a section titled Email Notification

Email Notification

Basically you have to setup a SMTP server, normally the corporate one. For demo purposes I have setup one online for free in Mailjet.

There is an useful feature that allows you to send a test email. You only have to check the option Test configuration by sending test e-mail, enter the email recipient and click on Test configuration button.

Once you have confirmed the email reception, you are ready to start sending notifications in your pipelines.

mail step example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
pipeline {
    agent any

    stages {
        stage('Stage 1') {
            steps {
                mail to: 'user@mydomain.com',               (1)
                    subject: "Job $JOB_NAME failed" ,
                    body: """Build $BUILD_NUMBER failed.    (2)
Go to $BUILD_URL for more info."""
            }
        }
    }
}
1 Sending email with mail step
2 Multiline String syntax in Groovy. Single quotes can be used as well by they don’t allow variable evaluation.


There is little to explain here. Basically we are sending an email in "Stage 1" by means of the mail step. Note that you can use available variables provided by Jenkins to create a more detailed subject and body.

This step enables you to send basic emails but it falls short in terms of features. In the probably case you want to set up the delivery of email notifications depending on the build result, the Mailer and Email Extension plugins are more suitable.

Mailer plugin

This plugin allows you configure email notifications for build results using the step step. This step is known as the generic build step and it enables you setup different post-build actions, being Email Notification one of them. This action enables you to send notification for builds with result SUCCESS, FAILURE or UNSTABLE. To be able to send all of them you should define the step in the always clause within the post section.

Mailer plugin example
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
pipeline {
    agent any
    parameters {
        booleanParam(
            defaultValue: false,
            description: 'Force error',
            name: 'FORCE_ERROR')
    }
    stages {
        stage('Stage 1') {
            steps {
                echo 'Do some stuff here'
            }
        }
        stage('Stage 2') {
            when { expression { return params.FORCE_ERROR } }
            steps {
                // Simulate failure
                error('An error occurred on stage 1')
            }
        }
    }
    post {
        always {
            step([$class: 'Mailer', notifyEveryUnstableBuild: true,
                recipients: 'user@mydomain.com'])
        }
    }
}

The notification behavior would be the following:

  • SUCCESS build result will be notified only if the previous build result was FAILURE

  • FAILURE and UNSTABLE build results will be notified always (you can disable it for UNSTABLE build result by setting notifyEveryUnstableBuild to false)

You can notice some limitations in this approach:

  1. Unable to customize the email to be sent (subject and body)

  2. Lack of flexibility when setting the recipients of the notification

  3. Unable to change sending behavior for failed builds

These limitations can be overcome by the Email Extension plugin

Email Extension plugin (EmailExt)

This plugin can be installed during Jenkins installation as part of the recommended plugins and it provides the following advantages over the Mailer plugin:

  1. Allows the use of dynamic templates in text plain or HTML format

  2. Allows to attach the build log in email

  3. Provides more control over the email to be sent (headers)

  4. Setting recipients depending on their role in the project

  5. Conditional sending of emails

  6. Execute post actions after sending the email

Configuration for this plugin can be found under Extended E-mail Notification section in Manage JenkinsGlobal Configuration

Email Extension configuration

This plugin provides more advantages for Freestyle projects like default configuration and triggers but I will not discuss them because these projects are kind of the old way of working in Jenkins and I think it is more interesting to focus on Pipeline projects.

Apart from the standard SMTP configuration, the only parameters you can leverage are those which value is made available as Jenkins variables to the pipeline. These are the following ones:

  • Default Recipients: available as $DEFAULT_RECIPIENTS variable

  • Reply To List: available as $DEFAULT_REPLYTO variable

  • Default Pre-send Script: available as $DEFAULT_PRESEND_SCRIPT variable

  • Default Post-send Script: available as $DEFAULT_POSTSEND_SCRIPT variable

This plugin also provides functionality to the Mailer plugin since it allows to customize the recipients using the emailextrecipients step.

The post section in the previous example could be updated like this:

Mailer plugin example
1
2
3
4
5
6
7
post {
    always {
        step([$class: 'Mailer', notifyEveryUnstableBuild: true,
            recipients: emailextrecipients([[$class: 'CulpritsRecipientProvider'],      (1)
                                            [$class: 'RequesterRecipientProvider']])])
    }
}
1 Email will be send the developers who committed a change since the last non-broken build (CulpritsRecipientProvider) and to the user who requested the build (RequesterRecipientProvider). More info on recipient providers here


Besides that, lets say we want to customize the sending behavior to send notifications only when the build result changed from the previous one. This can be easily achieved with EmailExt plugin.

Email notification for job status change
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
pipeline {
    agent any
    parameters {
        booleanParam(   (1)
            defaultValue: false,
            description: 'Force error',
            name: 'FORCE_ERROR')
    }
    stages {
        stage('Stage 1') {
            steps {
                // Simulate failure
                echo 'Do some stuff here'
            }
        }
        stage('Stage 2') {
            when { expression { return params.FORCE_ERROR } }   (2)
            steps {
                // Simulate failure
                error('An error occurred on stage 1')   (3)
            }
        }
    }
    post {
        always {
            emailext subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!',     (4)
                        recipientProviders: [requestor()],                                  (5)
                        replyTo: "$DEFAULT_REPLYTO",                                        (6)
                        body: '${SCRIPT, template="groovy-html.template"}',                 (7)
                        mimeType: 'text/html',
                        presendScript: "cancel=run.getPreviousBuild().result.toString().equals(run.result.toString())"    (8)
        }
    }
}
1 Boolean parameter to force a job error
2 This step is only executed if set the job to force a failure
3 Force an error in build
4 Step to send an email with EmailExt
5 Recipient set to the user who requested the build
6 Set the reply-to email header with the value set in the plugin configuration
7 Use default HTML template for the email body. You could use your own template if you save it to $JENKINS_HOME/email-templates folder
8 Send condition set in cancel variable


As you can see, you can execute the job forcing an error by checking the parameter FORCE_ERROR before starting the build.

Since we are using the always clause in the post section, the emailext step is always executed. However, since we have set a sending condition through the parameter presendScript, the email is only sent when the result of the previous execution is different to the current one (we have to do it like this because the build status is not available since the current job has not finished yet).

This can be useful when the developer team only wants to receive one notification after a failure and not for the subsequent failures until the build get fixed. This would avoid the delivery of to many emails than could cause an important event to go unnoticed.

When the job is executed, a permission error arises with the following message: "Pre-send script tried to access secured objects: Scripts not permitted to use method hudson.model.Run getPreviousBuild"

This error is caused because access to the previous build is not allowed by default in scripts. This can be solved by authorizing the method in Jenkins Script Approval page ( Manage JenkinsIn-process Script Approval).

Approve signature
A better way to fix this error would be by moving the emailext step to a Shared Library but we will tackle this in a different post.

Conclusion

As you have seen, setting up email notifications in Jenkins is pretty straightforward.

Sometimes there is some confusion because of the different ways to send emails and plugins available, specially with the EmailExt plugin, given all the parameters available in the configuration that only apply to Freestyle and not Pipeline projects.

I hope I have helped you to make it more clear. As usual, you can find examples in this post on Github

comments powered by Disqus