Abort a build immediately with success status in Jenkins pipeline - jenkins

Is there any pipeline step available to abort a build on certain cases with success status?
There is an error step available to abort a build with failure status. But I don't know about the success status.

As was said in other replies, there isn't a step to abort in this way. As Christopher suggested you can use try-catch around the aborting code and use error(). I think you will need to track the abort status of your build - you could define an abort method globally in the pipeline to set this status and raise an error so it will abort other steps in your stage.
If you used the declarative pipeline you can use a 'when' declaration with an expression in later stages so they don't execute when the abort status is set.
I am interested in this problem myself so I worked out an example of a pipeline that does this here:
/**
* Tracking if the build was aborted
*/
Boolean buildAborted = false
/**
* Abort the build with a message
*/
def abortBuild = { String abortMessage ->
buildAborted = true
error(abortMessage)
}
pipeline {
agent any
parameters {
string(name: 'FailOrAbort', defaultValue: 'ok', description: "Enter 'fail','abort' or 'ok'")
}
stages {
stage('One') {
steps {
echo "FailOrAbort = ${params.FailOrAbort}"
script {
try {
echo 'Doing stage 1'
if(params.FailOrAbort == 'fail') {
echo "This build will fail"
error("Build has failed")
}
else if(params.FailOrAbort == 'abort') {
echo "This build will abort with SUCCESS status"
abortBuild("This build was aborted")
}
else {
echo "This build is a success"
}
echo "Stage one steps..."
}
catch(e) {
echo "Error in Stage 1: ${e.getMessage()}"
if(buildAborted) {
echo "It was aborted, ignoring error status"
}
else {
error(e.getMessage())
}
}
}
}
post {
failure {
echo "Stage 1 failed"
}
}
}
stage('Two') {
when {
expression {
return !buildAborted
}
}
steps {
echo "Doing stage 2"
}
}
stage('Three') {
when {
expression {
return !buildAborted
}
}
steps {
echo "Doing stage 3"
}
}
}
post {
always {
echo "Build completed. currentBuild.result = ${currentBuild.result}"
}
failure {
echo "Build failed"
}
success {
script {
if(buildAborted) {
echo "Build was aborted"
} else {
echo 'Build was a complete success'
}
}
}
unstable {
echo 'Build has gone unstable'
}
}
}
As a side note there is a property 'currentBuild.result' you can adjust in the pipeline but once set to 'FAILURE' it cannot be cleared back to 'SUCCESS' - the Jenkins model doesn't allow it AFAIK.

No, the normal process of a pipeline is to go from start to end.
What you could however do is to test for your success status and just not call the rest of your code, in an if or something like that. Functions could help you achieve that quite easily, e.g. :
node() {
// Part 1
def isSuccess = part1();
if(!isSuccess) {
part2()
}
}
// Part 2
def function part2() {
// Part 2 code
}
However, you should be careful with that kind of things, maybe it highlights the fact that your pipeline is not properly designed. If that is not what you want, please provide more details, like a use case.

First of all, I'm not aware of such a step.
But you could use the error step to abort the build with a certain message, if it should succeed. Catch this error in a try{}catch(){} block, check for the message and set the build status to success.

Related

Continuous Integration pipeline

I am looking to trigger the on_failure step in my pipeline. I have a very simple script.
2 resources and 1 job. The job has a run step in which I would like to trigger failure manually. I have tried many things and they all leaded to an error.
Is there an shell script exit code that could make the task to fail and not being errored
Triggering post → failure and not failing the build is not possible:
failure
Only run the steps in post if the current Pipeline’s or stage’s run has a "failed" status, typically denoted by red in the web UI.
However, you can do the following:
def status
pipeline {
agent any
stages {
stage('Failing stage') {
steps {
script {
status = sh script: 'exit 99', returnStatus: true
}
}
}
}
post {
always {
script {
if ( status == 99 )
echo 'Script failed...'
else
echo 'Script succeeded...'
}
}
}
}
Example post -> failure
post {
always {
cleanWs()
}
success {
sendEmail('SUCCESSFUL')
}
unstable {
sendEmail('UNSTABLE')
}
failure {
sendEmail('FAILED')
}
}

Jenkinsfile fail a step and continue the rest

I have a Jenkinsfile like this:
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '7'))
disableConcurrentBuilds()
timeout(time: 10, unit: 'MINUTES')
timestamps()
}
stages {
stage('Admin') {
steps {
script {
try {
result = "FAIL"
} catch(error) {
result = "FAIL"
}
}
}
}
stage('Normal') {
steps {
script {
try {
sh("echo 'hi'")
} catch(error) {
}
}
}
}
}
}
How do I fail the first step and have it show red and have the pipeline continue for more steps?
I have looked at all the SO answers for this and can't make it work.
Setting result = "FAIL" does not cause the step to fail. How can I do this but continue the next step(s)?
You can mark a build status as failed by setting a new value for the global jenkins variable currentBuild. The variable has 3 states: SUCCESS (green), FAILURE (red) and UNSTABLE (yellow). The catch should prevent the build from exiting so you can set the status of the currentBuild and simply continue.
try {
//so something
} catch(error) {
//mark build as failed
currentBuild.result = 'FAILURE'
}
}
I think this should work. Sadly I don't have a way to validate this atm. If it does not work, please tell me.
As per my knowledge we can implement same as below.
Use script step and try-catch block
stage('someStage') {
steps {
script {
try {
//Do something
} catch (err) {
echo err
}
}
echo currentBuild.result = 'FAILURE'
}
}
You can use propagate: false which is available in build step. Somethng like below
stage("example") {
b = build(job: "example-job", propagate: false).result
if(b == 'FAILURE') {
echo "First job failed"
currentBuild.result = 'FAILURE' // of FAILURE
}
}
stage("test") {
build("test-job")
}
Links :
https://jenkins.io/doc/pipeline/steps/pipeline-build-step/
https://medium.com/#Lenkovits/jenkins-pipelines-and-their-dirty-secrets-1-9e535cd603f4
Do you want to show one stage "red" and all other (not failing) "green"?
As far as I know, that is not possible.
Do you want to know which stage is failing and which one runs correctly?
Well, it depends how you want it to be displayed
The easy way:
as suggested in a previous answer, you can use "echo" to print it in the console
The 'I want it full power' way:
capsulate a try-catch inside another try-catch and use the "error" signal:
stages {
try {
stage ('Normal') {
try {
/* Do your thing */
} catch (err) {
/* Mark this stage as "failure" using error
error 'Stage Normal failing'
}
} // stage 'Normal'
} catch (err) {
// here is the 'error' signal captured and omitted by now
}
Stage failing appears with the text "failed" shown in the box and in a darker red

Abort current build from pipeline in Jenkins

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.

How can I catch any pipeline error in Jenkins?

I have a Jenkins pipeline script that for the most part works fine and I surround most things that will fire a fatal error with try catches. However from time to time really unexpected things happen and I'd like to be able to have a safe catch-all available to do some final reporting before failing the build.
Is there no final default 'stage' I can define that runs whenever an error isn't caught?
Although already been answered for a scripted pipeline I would like to point out that for a declarative pipeline this is done with a post section:
pipeline {
agent any
stages {
stage('No-op') {
steps {
sh 'ls'
}
}
}
post {
always {
echo 'One way or another, I have finished'
deleteDir() /* clean up our workspace */
}
success {
echo 'I succeeeded!'
}
unstable {
echo 'I am unstable :/'
}
failure {
echo 'I failed :('
}
changed {
echo 'Things were different before...'
}
}
}
Each stage can also have it's own section when required.
You can do it by wrapping all your build stages in a big try/catch/finally {} block, for example:
node('yournode') {
try {
stage('stage1') {
// build steps here...
}
stage('stage2') {
// ....
}
} catch (e) {
// error handling, if needed
// throw the exception to jenkins
throw e
} finally {
// some common final reporting in all cases (success or failure)
}
}

Show a Jenkins pipeline stage as failed without failing the whole job

Here's the code I'm playing with
node {
stage 'build'
echo 'build'
stage 'tests'
echo 'tests'
stage 'end-to-end-tests'
def e2e = build job:'end-to-end-tests', propagate: false
result = e2e.result
if (result.equals("SUCCESS")) {
stage 'deploy'
build 'deploy'
} else {
?????? I want to just fail this stage
}
}
Is there any way for me to mark the 'end-to-end-tests' stage as failed without failing the whole job? Propagate false just always marks the stage as true, which is not what I want, but Propagate true marks the job as failed which I also don't want.
This is now possible, even with declarative pipelines:
pipeline {
agent any
stages {
stage('1') {
steps {
sh 'exit 0'
}
}
stage('2') {
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh "exit 1"
}
}
}
stage('3') {
steps {
sh 'exit 0'
}
}
}
}
In the example above, all stages will execute, the pipeline will be successful, but stage 2 will show as failed:
As you might have guessed, you can freely choose the buildResult and stageResult, in case you want it to be unstable or anything else. You can even fail the build and continue the execution of the pipeline.
Just make sure your Jenkins is up to date, since this is a fairly new feature.
Stage takes a block now, so wrap the stage in try-catch. Try-catch inside the stage makes it succeed.
The new feature mentioned earlier will be more powerful. In the meantime:
try {
stage('end-to-end-tests') {
node {
def e2e = build job:'end-to-end-tests', propagate: false
result = e2e.result
if (result.equals("SUCCESS")) {
} else {
sh "exit 1" // this fails the stage
}
}
}
} catch (e) {
result = "FAIL" // make sure other exceptions are recorded as failure too
}
stage('deploy') {
if (result.equals("SUCCESS")) {
build 'deploy'
} else {
echo "Cannot deploy without successful build" // it is important to have a deploy stage even here for the current visualization
}
}
Sounds like JENKINS-26522. Currently the best you can do is set an overall result:
if (result.equals("SUCCESS")) {
stage 'deploy'
build 'deploy'
} else {
currentBuild.result = e2e.result
// but continue
}
I recently tried to use vaza's answer
Show a Jenkins pipeline stage as failed without failing the whole job as template for writing a function that excutes a job in an own stage named like the job name. Surprisingly it worked, but maybe some groovy experts have a look at it :)
Here is how it looks like if one of the jobs is aborted:
def BuildJob(projectName) {
try {
stage(projectName) {
node {
def e2e = build job:projectName, propagate: false
result = e2e.result
if (result.equals("SUCCESS")) {
} else {
error 'FAIL' //sh "exit 1" // this fails the stage
}
}
}
} catch (e) {
currentBuild.result = 'UNSTABLE'
result = "FAIL" // make sure other exceptions are recorded as failure too
}
}
node {
BuildJob('job1')
BuildJob('job2')
}
In order to show a successful build with a failed stage when a downstream job fails AND support a user being able to cancel a build (including all subsequent stages), I had to use a combination of various solutions, specifically when, try/catch, throw and catchError().
env.GLOBAL_BUILD_ABORTED = false // Set if the user aborts the build
pipeline {
agent any
stages {
stage('First Stage') {
when { expression { env.GLOBAL_BUILD_ABORTED.toBoolean() == false } }
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
myLocalBuildMethod('Stage #1, build #1')
myLocalBuildMethod('Stage #1, build #2')
}
}
}
stage('Second Stage') {
when { expression { env.GLOBAL_BUILD_ABORTED.toBoolean() == false } }
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
myLocalBuildMethod('Stage #2, build #1')
myLocalBuildMethod('Stage #2, build #2')
myLocalBuildMethod('Stage #2, build #3')
}
}
}
}
}
def myLocalBuildMethod(myString) {
/* Dummy method to show User Aborts vs Build Failures */
echo "My Local Build Method: " + myString
try {
build (
job: "Dummy_Downstream_Job"
)
} catch (e) {
/* Build Aborted by user - Stop All Test Executions */
if (e.getMessage().contains("was cancelled") || e.getMessage().contains("ABORTED")) {
env.GLOBAL_BUILD_ABORTED = true
}
/* Throw the execiption to be caught by catchError() to mark the stage failed. */
throw (e)
}
// Do other stuff...
}
You could add a explicit fail task, such as 'sh "not exist command"' in the stage.
if (result.equals("SUCCESS")) {
stage 'deploy'
build 'deploy'
} else {
try {
sh "not exist command"
}catch(e) {
}
}
Solution steps
You must emit an error in a stage to mark it as an error
Outside the scope of the stage, handle the exception and choose the build status
This makes the effect desired by a couple of users here, including myself, #user3768904, #Sviatlana
Success with failed Step Example
node("node-name") {
try {
stage("Process") {
error("This will fail")
}
} catch(Exception error) {
currentBuild.result = 'SUCCESS'
return
}
stage("Skipped") {
// This stage will never run
}
}
Aborted with failure Step Example
node("node-name") {
try {
stage("Process") {
error("This will fail")
}
} catch(Exception error) {
currentBuild.result = 'ABORTED'
return
}
stage("Skipped") {
// This stage will never run
}
}
You can use the following code in your else statement:
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
error "some err msg"
}
This could be a general pattern showing how to customize the stage result with nice messages using the built-in functions and propagate the sub-job's result to the stage result. That the overall build is marked unstable if a sub-job is not successful is just a implementation choice for this example.
def run_sub_job() {
def jobBuild = build(job: 'foo', wait: true, propagate: false)
def result = jobBuild.getResult()
def msg = 'sub-job: ' + result
if ('SUCCESS' == result) {
println(msg)
} else if ('UNSTABLE' == result) {
unstable(msg) // will also set buildResult to UNSTABLE
} else { // anything else (FAILURE, ABORTED ...) is considered an error
catchError(
buildResult: 'UNSTABLE',
stageResult: result // propagate sub-job result
) {
error(msg)
}
}
}

Resources