seg
seg

Reputation: 1520

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.

Upvotes: 93

Views: 182700

Answers (11)

John Michelau
John Michelau

Reputation: 1301

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 see 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.

Upvotes: 4

Barel elbaz
Barel elbaz

Reputation: 636

The most elegant way I found: Notice, it requires to approve a method signature in the Manage Jenkins -> In process script approval page

currentBuild.result = "ABORTED"
throw new org.jenkinsci.plugins.workflow.steps.FlowInterruptedException(hudson.model.Result.ABORTED)

enter image description here

If you just want to mark the build/stage result but continue to the next steps & stages you can use:

steps{
    script{
        echo "Hello"
        catchError(buildResult: 'ABORTED', stageResult: 'ABORTED') {
            error("Your abortion reason goes here")
        }
    }
}

Upvotes: 7

Ben Amos
Ben Amos

Reputation: 1900

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.NOT_BUILT)
    sleep(1)   // Interrupt is not blocking and does not take effect immediately.
}

You can use any Result constant for this, but based on your desire to "cancel" because "there is no actual work to be done," I feel the NOT_BUILT result is the best fit. This has the added advantage of signaling to connected integrations (e.g. Bitbucket) that the build should be ignored and not counted as a failure.

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.

Upvotes: 2

Hana Hasanah
Hana Hasanah

Reputation: 223

This is the way to abort the currently running build pipeline in Jenkins UI(in Build History there is a cancel button), for capture:

enter image description here

Upvotes: 0

stawiu
stawiu

Reputation: 21

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:

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

Upvotes: 2

Shree Prakash
Shree Prakash

Reputation: 2314

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'
      }
    }
  }
}

Upvotes: 2

AKS
AKS

Reputation: 17306

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"));

Upvotes: 1

Wim Rutgeerts
Wim Rutgeerts

Reputation: 127

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.

Upvotes: 3

CSchulz
CSchulz

Reputation: 11020

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:

enter image description here

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:

enter image description here

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
}

Upvotes: 22

Christopher Orr
Christopher Orr

Reputation: 111555

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):

Pipeline Stage View

Upvotes: 163

segalaj
segalaj

Reputation: 1281

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.

Upvotes: 3

Related Questions