DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Trending

  • AWS Multi-Region Resiliency Aurora MySQL Global DB With Headless Clusters
  • What Is mTLS? How To Implement It With Istio
  • 8 Data Anonymization Techniques to Safeguard User PII Data
  • Mainframe Development for the "No Mainframe" Generation
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. DevOps and CI/CD
  4. Sending Notifications in Pipeline

Sending Notifications in Pipeline

Liam Newman explains how to make Jenkins send you notifications for when builds start, when they succeed, and when they fail.

Liam Newman user avatar by
Liam Newman
·
Nov. 03, 16 · Tutorial
Like (4)
Save
Tweet
Share
54.93K Views

Join the DZone community and get the full member experience.

Join For Free

Rather than sitting and watching Jenkins for the job status, I want Jenkins to send notifications when events occur. There are Jenkins plugins for Slack, HipChat, and even email among others.

Note: Something Is Happening!

I think we can all agree that getting notified when events occur is preferable to having to constantly monitor them just in case. I'm going to continue from where I left off in my previous post with the Hermann project. I added a Jenkins Pipeline with an HTML publisher for code coverage. This week, I'd like to make Jenkins notify me when builds start and when they succeed or fail.

Setup and Configuration

First, I select targets for my notifications. For this blog post, I'll use sample targets that I control. I've created Slack and HipChat organizations called bitwiseman, each with one member: me. For email, I'm running a Ruby SMTP server called Mailcatcher that is perfect for local testing such as this. Aside from these concessions, the configuration will be much the same in a non-demo situation.

Next, I install and add server-wide configuration for the Slack, HipChat, and email-ext plugins. Slack and HipChat use API tokens. Both products have integration points on their side that generate tokens that I copy into my Jenkins configuration. Mailcatcher SMTP runs locally. I just point Jenkins at it.

Here's what the Jenkins configuration section for each of these looks like:

Slack Configuration

HipChat Configuration

Email Configuration

Original Pipeline

Now I can start adding notification steps. Just like in the previous post, I'll use the Jenkins Pipeline Snippet Generator to explore the step syntax for the notification plugins.

Here's the base pipeline before I start making changes:

 stage 'Build' 

 node {
   // Checkout 
   checkout scm 
   
   // install required bundles 
   sh 'bundle install' 
   
   // build and run tests with coverage 
   sh 'bundle exec rake build spec'
   
   // Archive the built artifacts 
   archive (includes: 'pkg/*.gem') 
   
   // publish html 
   // snippet generator doesn't include "target:" 
   // https://issues.jenkins-ci.org/browse/JENKINS-29711. 
   publishHTML (target: [ 
       allowMissing: false, 
       alwaysLinkToLastBuild: false, 
       keepAll: true, 
       reportDir: 'coverage', 
       reportFiles: 'index.html', 
       reportName: "RCov Report" 
      ]) 
 } 

Note: This pipeline expects to be run from a Jenkinsfile in SCM. To copy and paste it directly into a Jenkins Pipeline job, replace the checkout scm step with git 'https://github.com/reiseburo/hermann.git'.

Job Started Notification

For the first change, I decide to add a "Job Started" notification. The snippet generator and reformatting make this straightforward:

 node {
   
   notifyStarted()
   
   /* ... existing build steps ... */ 
 }

 def notifyStarted() {
   // send to Slack 
   slackSend (color: '#FFFF00', message: "STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") 
   // send to HipChat 
   hipchatSend (color: 'YELLOW', notify: true, 
       message: "STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})" 
     ) 
   
   // send to email
   emailext ( 
       subject: "STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", 
       body: """<p>STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':</p>
         <p>Check console output at "<a href="${env.BUILD_URL}">${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
       recipientProviders: [[$class: 'DevelopersRecipientProvider']]
     )
 }

Since Pipeline is a Groovy-based DSL, I can use string interpolation and variables to add the exact details that I want in my notification messages. When I run this, I get the following notifications:

Started Notifications

Started Email Notification

Job Successful Notification

The next logical choice is to get notifications when a job succeeds. I'll copy and paste based on the notifyStarted method for now and do some refactoring later.

 node {
 
   notifyStarted()
 
   /* ... existing build steps ... */
 
   notifySuccessful()
 }
 
 def notifyStarted() { /* .. */ }
 
 def notifySuccessful() {
   slackSend (color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
 
   hipchatSend (color: 'GREEN', notify: true,
       message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
     )
 
   emailext (
       subject: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
       body: """<p>SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':</p>
         <p>Check console output at "<a href="${env.BUILD_URL}">${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
       recipientProviders: [[$class: 'DevelopersRecipientProvider']]
     )
 }

Again, I get notifications, as expected. This build is so fast that some of them are even on the screen at the same time:

Multiple Notifications

Job Failed Notification

Next, I want to add a failure notification. Here's where we really start to see the power and expressiveness of Jenkins Pipeline. A Pipeline is a Groovy script, so as we'd expect in any Groovy script, we can handle errors using try-catch blocks.

 node {
   try {
     notifyStarted()
 
     /* ... existing build steps ... */
 
     notifySuccessful()
   } catch (e) {
     currentBuild.result = "FAILED"
     notifyFailed()
     throw e
   }
 }
 
 def notifyStarted() { /* .. */ }
 
 def notifySuccessful() { /* .. */ }
 
 def notifyFailed() {
   slackSend (color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
 
   hipchatSend (color: 'RED', notify: true,
       message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
     )
 
   emailext (
       subject: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
       body: """<p>FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':</p>
         <p>Check console output at "<a href="${env.BUILD_URL}">${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>""",
       recipientProviders: [[$class: 'DevelopersRecipientProvider']]
     )
 }

Failed Notifications

Code Cleanup

Lastly, now that I have it all working, I'll do some refactoring. I'll unify all the notifications in one method and move the final success/failure notification into a finally block.

 stage 'Build'
 
 node {
   try {
     notifyBuild('STARTED')
 
     /* ... existing build steps ... */
 
   } catch (e) {
     // If there was an exception thrown, the build failed
     currentBuild.result = "FAILED"
     throw e
   } finally {
     // Success or failure, always send notifications
     notifyBuild(currentBuild.result)
   }
 }
 
 def notifyBuild(String buildStatus = 'STARTED') {
   // build status of null means successful
   buildStatus =  buildStatus ?: 'SUCCESSFUL'
 
   // Default values
   def colorName = 'RED'
   def colorCode = '#FF0000'
   def subject = "${buildStatus}: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'"
   def summary = "${subject} (${env.BUILD_URL})"
   def details = """<p>STARTED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':</p>
     <p>Check console output at "<a href="${env.BUILD_URL}">${env.JOB_NAME} [${env.BUILD_NUMBER}]</a>"</p>"""
 
   // Override default values based on build status
   if (buildStatus == 'STARTED') {
     color = 'YELLOW'
     colorCode = '#FFFF00'
   } else if (buildStatus == 'SUCCESSFUL') {
     color = 'GREEN'
     colorCode = '#00FF00'
   } else {
     color = 'RED'
     colorCode = '#FF0000'
   }
 
   // Send notifications
   slackSend (color: colorCode, message: summary)
 
   hipchatSend (color: color, notify: true, message: summary)
 
   emailext (
       subject: subject,
       body: details,
       recipientProviders: [[$class: 'DevelopersRecipientProvider']]
     )
 }

You Have Been Notified!

I now get notified twice per build on three different channels. I'm not sure that I need to get notified this much for such a short build. However, for a longer or complex CD pipeline, I might want exactly that. If needed, I could even improve this to handle other status strings and call it as needed throughout my pipeline.

Pipeline (software)

Published at DZone with permission of Liam Newman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • AWS Multi-Region Resiliency Aurora MySQL Global DB With Headless Clusters
  • What Is mTLS? How To Implement It With Istio
  • 8 Data Anonymization Techniques to Safeguard User PII Data
  • Mainframe Development for the "No Mainframe" Generation

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: