I have the following post failure section:
post {
failure {
mail subject: "\u2639 ${env.JOB_NAME} (${env.BUILD_NUMBER}) has failed",
body: """Build ${env.BUILD_URL} is failing!
|Somebody should do something about that""",
to: "devel#example.com",
replyTo: "devel#example.com",
from: 'jenkins#example.com'
}
}
I would like to include the reasons why the build failed in the body of the error message.
How can I do that?
If not, is there a way to attach the build log file to the email?
I don't know of a way to retrieve the failure reason automatically out of thin air.
However, you can use "post{ failure {" blocks in each phase to capture at least the phase in which it failed into a environment variable (e.g. env.FAILURE_REASON), and access that env var in the final (global scope) notification block.
For more granularity, you can reuse the same mechanism of the global env variable, but use try { } catch { } blocks to capture which specific step failed.
A generic example would be:
pipeline {
stages {
stage('Build') {
steps {
...
}
post {
failure {
script { env.FAILURE_STAGE = 'Build' }
}
}
}
stage('Deploy') {
steps {
...
}
post {
failure {
script { env.FAILURE_STAGE = 'Deploy' }
}
}
}
...
}
post {
failure {
mail subject: "\u2639 ${env.JOB_NAME} (${env.BUILD_NUMBER}) has failed",
body: """Build ${env.BUILD_URL} is failing in ${env.FAILURE_STAGE} stage!
|Somebody should do something about that""",
to: "devel#example.com",
replyTo: "devel#example.com",
from: 'jenkins#example.com'
}
}
}
Technically, you can even do some automated triage based on the failing stage and send a more targeted notification, or even create specific (e.g. Jira) tickets.
For attaching the console log to the email notification, you'd want to look at
emailext and the 'attachLog: true' attribute
Related
I have a Jenkins Pipeline where I am doing deployment, running automated tests and then posting the results to the Test Management System.
If Deploy stage fails, I don't want to go ahead with Run Tests and Post Results stages.
If Run Tests fail, I still want to go ahead and post the pass + failed test results to the Test Management System.
On each stage, I want to trigger an email that the respective stage failed.
pipeline {
agent { label 'my-agent' }
stages {
stage('Deploy') {
steps {
// carry out deployment
}
post {
failure {
// send email that deployment failed
}
}
}
stage('Run Tests') {
steps {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
// carry out run
}
}
post {
failure {
// send email that run tests failed
}
}
}
stage('Post Results') {
steps {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
// post the results to the test management system
}
}
post {
failure {
// send email that posting results encountered error
}
}
}
}
}
The problem:
The email triggers for Deploy and Run Tests are working fine. However, when Run Tests has failures; even though the results are successfully posted to the Test Management System, the control is entering into failure part of the post for Post Results stage.
I tried making the buildResult as SUCCESS and stageResult as FAILURE. However, the control is not going into the failure part of even the same stage.
What changes do I need to make to avoid sending email for the Post Test failure even if it passes but the earlier Run Tests has failed?
This is not ideal solution. But I have solved the above issue by shifting the post results error email trigger from the post section to a separate stage.
The post result execution stage has touch import_success statement at the end. It gets executed only when the posting of results is successful.
The post results error email trigger stage has a when clause to check existence of the import_success file to send the email (trigger email when import_success file doesn't exist).
Here is the final pipeline,
pipeline {
agent { label 'my-agent' }
stages {
stage('Deploy') {
steps {
// carry out deployment
}
post {
failure {
// send email that deployment failed
}
}
}
stage('Run Tests') {
steps {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
// carry out run
}
}
post {
failure {
// send email that run tests failed
}
}
}
stage('Post Results') {
steps {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
// post the results to the test management system
sh 'touch import_success' // this is executed only when post results is successful
}
}
}
stage ('Post Results Error Email Trigger') {
when {
not {
expression {
fileExists 'import_success'
}
}
}
steps {
// send email that posting results encountered error
}
}
}
}
Hi I was trying to send email alert whenever there is a build failed for Jenkins
node ('abc') {
stage ('checkout'){
some codes inside
}
stage ('build'){
some code inside
}
stage ('test') {
some code inside
}
stage ('deploy'){
some code inside
}
post {
failure {
mail bcc: '', body: '''Hi,
The pipeline at Jenkins has failed. Pleas go over to the Jenkins and
check it out.
Thanks!''', cc: '', from: '', replyTo: '', subject: 'The pipeline has failed!', to: 'abc#gmail.com'
}
}
}
And I have error message of
java.lang.NoSuchMethodError: No such DSL method 'post' found among
steps
You can achieve this by doing try-catch-finally block.
node ('abc') {
try {
stage ('checkout'){
some codes inside
}
stage ('build'){
some code inside
}
stage ('test') {
some code inside
}
stage ('deploy'){
some code inside
}
stage ('notify'){
Notification for JOB Success
}
}catch(e){
Notification for JOB Failure
}finally{
}
}
See official Jenkins documentation:
https://jenkins.io/doc/pipeline/tour/running-multiple-steps/#finishing-up
(If you don't want to use declarative pipeline, see 'Toggle Scripted Pipeline' section).
I am using Jenkins declarative pipeline and want to perform some post build actions depending on the build status.
To be more precise, I want to send an email when the build status changed (from success to failure, or success to unstable, or failure to success).
Here is my pipeline:
pipeline {
agent none
stages {
stage('test') {
agent any
steps {
sh './tests.sh'
}
}
}
post {
changed {
// Send different emails depending on build status
// Success -> anything else
// Anything else -> Success
}
}
}
Any idea ?
For writing conditions you can define your own methods.
For example, if you want to send an email only when the build status changes:
def notifyStatusChangeViaEmail(buildStatus) {
def status
switch (buildStatus) {
case 'SUCCESS':
status = 'is now green again!'
break
case 'UNSTABLE':
status = 'has become unstable..'
break
case 'FAILURE':
status = 'has turned RED :('
break
}
emailext (
subject: "Job '${env.JOB_NAME}' ${status}",
body: "See ${env.BUILD_URL} for more details",
recipientProviders: [
[$class: 'DevelopersRecipientProvider'],
[$class: 'RequesterRecipientProvider']
]
)
}
pipeline {
...
post {
changed {
// Will trigger only when job status changes: GREEN -> RED, RED -> GREEN, etc
notifyStatusChangeViaEmail(currentBuild.currentResult)
}
}
}
Ideally, you would also want to put notifyStatusChangeViaEmail method definiton in your shared pipeline library so that it could be re-used in other jobs/pipelines.
Refer to this pipeline:
post {
success {
emailext (
subject: '${DEFAULT_SUBJECT}'+'SUCESSFUL',
body: '${DEFAULT_CONTENT}',
to: '${EMAIL_RECIPIENTS}'
);
slackSend (color: 'good', message: ":csp_operational: ${env.JOB_NAME} - #${env.BUILD_NUMBER} Success (<${env.BUILD_URL}|Open>)");
}
failure {
emailext (
subject: '${DEFAULT_SUBJECT}'+'FAILED!',
body: '${DEFAULT_CONTENT}',
to: '${EMAIL_RECIPIENTS}'
);
slackSend (color: 'danger', message: ":x: ${env.JOB_NAME} - #${env.BUILD_NUMBER} Failure (<${env.BUILD_URL}|Open>)");
}
}
You can set the Default Email parameters using Extended Email Plugin, Jenkins-> Configure Jenkins -> Extended Email Configuration.
Exemple : failure & unstable to success
if (currentBuild.result == 'SUCCESS') { if(hudson.model.Result.FAILURE.equals(currentBuild.rawBuild.getPreviousBuild()?.getResult()) || hudson.model.Result.UNSTABLE.equals(currentBuild.rawBuild.getPreviousBuild()?.getResult())) {
SEND MAIL()
}
}
for send email : https://jenkins.io/blog/2017/02/15/declarative-notifications/
#psalvi was imho on the right track, but #mikael-gibert wanted in his comment to get notifications only for the first fixed build. Jenkins declarative pipelines can do that, see https://www.jenkins.io/doc/book/pipeline/syntax/#post. Here an example:
pipeline {
// [...]
post {
always {
script {
// Examples of stuff you could do, based on some custom parameters and functions
if (params.buildAndPublish && branchIsMain()) {
currentBuild.description = 'NOT promoted '
}
currentBuild.description = (currentBuild.description ?: '') + '[' + (params.customArtifactVersion ?: mavenArtifactVersion) + ']'
if (params.buildAndPublish) {
// Tell Bitbucket about this build and its result
notifyBitbucket(credentialsId: config.credentialsId)
// Archive test results
junit(allowEmptyResults: true, testResults: "**/target/surefire-reports/**.xml,**/target/failsafe-reports/**.xml")
}
}
}
failure {
emailext recipientProviders: [culprits(), requestor()],
subject: "[Jenkins] Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Hello,\n\nBuild failed: ${env.JOB_NAME} / ${env.BRANCH_NAME} #${env.BUILD_NUMBER}.\nDetails: ${env.BUILD_URL}\n\nJenkins"
}
fixed {
emailext recipientProviders: [culprits(), requestor()],
subject: "[Jenkins] Build fixed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Hello,\n\nBuild fixed: ${env.JOB_NAME} / ${env.BRANCH_NAME} #${env.BUILD_NUMBER}.\nDetails: ${env.BUILD_URL}\n\nJenkins"
}
}
}
I am having problems receiving email notifications for a pipeline I have setup like so:
#!groovy
pipeline {
agent any
stages {
stage('Build Prep') {
steps {
sh '...'
}
}
stage('Build') {
steps {
sh '...'
}
}
stage('Tests') {
steps {
parallel (
"Jasmine": {
sh '...'
},
"Mocha": {
sh '...'
}
)
}
}
stage('Deploy') {
steps {
sh "..."
}
}
}
post {
success {
emailext to:"me#me.com", subject:"SUCCESS: ${currentBuild.fullDisplayName}", body: "Yay, we passed."
}
failure {
emailext to:"me#me.com", subject:"FAILURE: ${currentBuild.fullDisplayName}", body: "Boo, we failed."
}
unstable {
emailext to:"me#me.com", subject:"UNSTABLE: ${currentBuild.fullDisplayName}", body: "Huh, we're unstable."
}
changed {
emailext to:"me#me.com", subject:"CHANGED: ${currentBuild.fullDisplayName}", body: "Wow, our status changed!"
}
}
}
The build logs confirm an email is sent, but I dont get anything in my inbox, nothing in spam either.
I have come across this https://jenkins.io/blog/2016/07/18/pipline-notifications/
But I am unsure if I can use using the syntax I have, I don't have any nodes defined, should I?
Your script works fine. I did receive the email using the above code. Please check your other settings or if you had created a rule for Jenkins emails. You can make your code look easier by adding your notifications to the Jenkins shared library. Please take a look at the below link.
https://jenkins.io/doc/book/pipeline/shared-libraries/
I have a Jenkins pipeline which has multiple stages, for example:
node("nodename") {
stage("Checkout") {
git ....
}
stage("Check Preconditions") {
...
if(!continueBuild) {
// What do I put here? currentBuild.xxx ?
}
}
stage("Do a lot of work") {
....
}
}
I want to be able to cancel (not fail) the build if certain preconditions are not met and there is no actual work to be done. How can I do this? I know the currentBuild variable is available, but I can't find the documentation for it.
You can mark the build as ABORTED, and then use the error step to cause the build to stop:
if (!continueBuild) {
currentBuild.result = 'ABORTED'
error('Stopping early…')
}
In the Stage View, this will show that the build stopped at this stage, but the build overall will be marked as aborted, rather than failed (see the grey icon for build #9):
After some testing I came up with the following solution:
def autoCancelled = false
try {
stage('checkout') {
...
if (your condition) {
autoCancelled = true
error('Aborting the build to prevent a loop.')
}
}
} catch (e) {
if (autoCancelled) {
currentBuild.result = 'ABORTED'
echo('Skipping mail notification')
// return here instead of throwing error to keep the build "green"
return
}
// normal error handling
throw e
}
This will result into following stage view:
failed stage
If you don't like the failed stage, you have to use return. But be aware you have to skip each stage or wrapper.
def autoCancelled = false
try {
stage('checkout') {
...
if (your condition) {
autoCancelled = true
return
}
}
if (autoCancelled) {
error('Aborting the build to prevent a loop.')
// return would be also possible but you have to be sure to quit all stages and wrapper properly
// return
}
} catch (e) {
if (autoCancelled) {
currentBuild.result = 'ABORTED'
echo('Skipping mail notification')
// return here instead of throwing error to keep the build "green"
return
}
// normal error handling
throw e
}
The result:
custom error as indicator
You can also use a custom message instead of a local variable:
final autoCancelledError = 'autoCancelled'
try {
stage('checkout') {
...
if (your condition) {
echo('Aborting the build to prevent a loop.')
error(autoCancelledError)
}
}
} catch (e) {
if (e.message == autoCancelledError) {
currentBuild.result = 'ABORTED'
echo('Skipping mail notification')
// return here instead of throwing error to keep the build "green"
return
}
// normal error handling
throw e
}
Following this documentation from Jenkins, you should be able to generate an error to stop the build and set the build result like this:
currentBuild.result = 'ABORTED'
Hope that helps.
The thing that we use is:
try {
input 'Do you want to abort?'
} catch (Exception err) {
currentBuild.result = 'ABORTED';
return;
}
The "return" at the end makes sure that no further code is executed.
I handled in a declarative way as shown below:
Based on catchError block it will execute post block.
If post result falls under failure category, the error block will be executed to stop upcoming stages like Production, PreProd etc.
pipeline {
agent any
stages {
stage('Build') {
steps {
catchError {
sh '/bin/bash path/To/Filename.sh'
}
}
post {
success {
echo 'Build stage successful'
}
failure {
echo 'Compile stage failed'
error('Build is aborted due to failure of build stage')
}
}
}
stage('Production') {
steps {
sh '/bin/bash path/To/Filename.sh'
}
}
}
}
Inspired by all the answers I have put all the stuff together into one Scripted Pipeline. Keep in mind this is not a Declarative Pipeline.
To get this example working you will need:
QuickFIX form this answer Jenkins CI Pipeline Scripts not permitted to use method groovy.lang.GroovyObject
discord notifier plugin - https://plugins.jenkins.io/discord-notifier/
Discord channel webhook url filled in the code
The idea I had was to abort the pipeline if it is "replayed" vs started by "run button"(in branches tab of Jenskins BlueOcean):
def isBuildAReplay() {
// https://stackoverflow.com/questions/51555910/how-to-know-inside-jenkinsfile-script-that-current-build-is-an-replay/52302879#52302879
def replyClassName = "org.jenkinsci.plugins.workflow.cps.replay.ReplayCause"
currentBuild.rawBuild.getCauses().any{ cause -> cause.toString().contains(replyClassName) }
}
node {
try {
stage('check replay') {
if (isBuildAReplay()) {
currentBuild.result = 'ABORTED'
error 'Biuld REPLAYED going to EXIT (please use RUN button)'
} else {
echo 'NOT replay'
}
}
stage('simple stage') {
echo 'hello from simple stage'
}
stage('error stage') {
//error 'hello from simple error'
}
stage('unstable stage') {
unstable 'hello from simple unstable'
}
stage('Notify sucess') {
//Handle SUCCESS|UNSTABLE
discordSend(description: "${currentBuild.currentResult}: Job ${env.JOB_NAME} \nBuild: ${env.BUILD_NUMBER} \nMore info at: \n${env.BUILD_URL}", footer: 'No-Code', unstable: true, link: env.BUILD_URL, result: "${currentBuild.currentResult}", title: "${JOB_NAME} << CLICK", webhookURL: 'https://discordapp.com/api/webhooks/')
}
} catch (e) {
echo 'This will run only if failed'
if(currentBuild.result == 'ABORTED'){
//Handle ABORTED
discordSend(description: "${currentBuild.currentResult}: Job ${env.JOB_NAME} \nBuild: ${env.BUILD_NUMBER} \nMore info at: \n${env.BUILD_URL}\n\nERROR.toString():\n"+e.toString()+"\nERROR.printStackTrace():\n"+e.printStackTrace()+" ", footer: 'No-Code', unstable: true, link: env.BUILD_URL, result: "ABORTED", title: "${JOB_NAME} << CLICK", webhookURL: 'https://discordapp.com/api/webhooks/')
throw e
}else{
//Handle FAILURE
discordSend(description: "${currentBuild.currentResult}: Job ${env.JOB_NAME} \nBuild: ${env.BUILD_NUMBER} \nMore info at: \n${env.BUILD_URL}\n\nERROR.toString():\n"+e.toString()+"\nERROR.printStackTrace():\n"+e.printStackTrace()+" ", footer: 'No-Code', link: env.BUILD_URL, result: "FAILURE", title: "${JOB_NAME} << CLICK", webhookURL: 'https://discordapp.com/api/webhooks/')
throw e
}
} finally {
echo 'I will always say Hello again!'
}
}
Main trick was the order of lines to achive abort state:
currentBuild.result = 'ABORTED'
error 'Biuld REPLAYED going to EXIT (please use RUN button)'
First set the state then throw an exception.
In the catch block both work:
currentBuild.result
currentBuild.currentResult
If you're able to approve the constructor for FlowInterruptedException, then you can do the following:
throw new FlowInterruptedException(Result.ABORTED, new UserInterruption(getCurrentUserId()))
You can add to your shared library repo a file var/abortError.groovy:
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
import jenkins.model.CauseOfInterruption.UserInterruption
def call(message)
{
currentBuild.displayName = "#${env.BUILD_NUMBER} $message"
echo message
currentBuild.result = 'ABORTED'
throw new FlowInterruptedException(Result.ABORTED, new UserInterruption(env.BUILD_USER_ID))
}
Then you can use it this way (after importing library):
abortError("some message")
Note that if you se following error in console logs:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use new org.jenkinsci.plugins.workflow.steps.FlowInterruptedException hudson.model.Result jenkins.model.CauseOfInterruption[]
You need follow the link form log and approve security exception.
You can go to the script console of Jenkins and run the following to abort a hung / any Jenkins job build/run:
Jenkins .instance.getItemByFullName("JobName")
.getBuildByNumber(JobNumber)
.finish(hudson.model.Result.ABORTED, new java.io.IOException("Aborting build"));
This is the way to abort the currently running build pipeline in Jenkins UI(in Build History there is a cancel button), for capture:
The Executor.interrupt(Result) method is the cleanest, most direct way I could find to stop a build prematurely and choose the result.
script {
currentBuild.getRawBuild().getExecutor().interrupt(Result.ABORTED)
sleep(1) // Interrupt is not blocking and does not take effect immediately.
}
Pros:
Works in a declarative pipeline just as well as a scripted one.
No try/catch or exceptions to handle.
Marks the calling stage and any successive stages as green/passing in the UI.
Cons:
Requires a number of in-process script approvals, including one that is considered insecure. Approve and use with caution.
Taken from my answer on devops.stackexchange.com.
As for currentBuild, have a look at the docs for the RunWrapper class.