[shell] How to mark a build unstable in Jenkins when running shell scripts

In a project I'm working on, we are using shell scripts to execute different tasks. Some are sh/bash scripts that run rsync, and some are PHP scripts. One of the PHP scripts is running some integration tests that output to JUnit XML, code coverage reports, and similar.

Jenkins is able to mark the jobs as successful / failed based on exit status. In PHP, the script exits with 1 if it has detected that the tests failed during the run. The other shell scripts run commands and use the exit codes from those to mark a build as failed.

// :: End of PHP script:
// If any tests have failed, fail the build
if ($build_error) exit(1);

In Jenkins Terminology, an unstable build is defined as:

A build is unstable if it was built successfully and one or more publishers report it unstable. For example if the JUnit publisher is configured and a test fails then the build will be marked unstable.

How can I get Jenkins to mark a build as unstable instead of only success / failed when running shell scripts?

This question is related to shell jenkins build status

The answer is


You can just call "exit 1", and the build will fail at that point and not continue. I wound up making a passthrough make function to handle it for me, and call safemake instead of make for building:

function safemake {
  make "$@"
  if [ "$?" -ne 0 ]; then
    echo "ERROR: BUILD FAILED"
    exit 1
  else
    echo "BUILD SUCCEEDED"
  fi
}

In my job script, I have the following statements (this job only runs on the Jenkins master):

# This is the condition test I use to set the build status as UNSTABLE
if [ ${PERCENTAGE} -gt 80 -a ${PERCENTAGE} -lt 90 ]; then
  echo WARNING: disc usage percentage above 80%

  # Download the Jenkins CLI JAR:
  curl -o jenkins-cli.jar ${JENKINS_URL}/jnlpJars/jenkins-cli.jar

  # Set build status to unstable
  java -jar jenkins-cli.jar -s ${JENKINS_URL}/ set-build-result unstable

fi

You can see this and a lot more information about setting build statuses on the Jenkins wiki: https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI


One easy way to set a build as unstable, is in your "execute shell" block, run exit 13


  1. Configure PHP build to produce xml junit report

    <phpunit bootstrap="tests/bootstrap.php" colors="true" >
       <logging>
           <log type="junit" target="build/junit.xml" 
               logIncompleteSkipped="false" title="Test Results"/>
       </logging>
    
       ....
    
     </phpunit>
    
  2. Finish build script with status 0

    ...
    exit 0;
    
  3. Add post-build action Publish JUnit test result report for Test report XMLs. This plugin will change Stable build to Unstable when test are failing.

    **/build/junit.xml
    
  4. Add Jenkins Text Finder plugin with console output scanning and unchecked options. This plugin fail whole build on fatal error.

    PHP Fatal error:
    

you should also be able to use groovy and do what textfinder did

marking a build as un-stable with groovy post-build plugin

if(manager.logContains("Could not login to FTP server")) {
    manager.addWarningBadge("FTP Login Failure")
    manager.createSummary("warning.gif").appendText("<h1>Failed to login to remote FTP Server!</h1>", false, false, false, "red")
    manager.buildUnstable()
}

Also see Groovy Postbuild Plugin


Modern Jenkins versions (since 2.26, October 2016) solved this: it's just an advanced option for the Execute shell build step!

exit code for build

You can just choose and set an arbitrary exit value; if it matches, the build will be unstable. Just pick a value which is unlikely to be launched by a real process in your build.


Duplicating my answer from here because I spent some time looking for this:

This is now possible in newer versions of Jenkins, you can do something like this:

#!/usr/bin/env groovy

properties([
  parameters([string(name: 'foo', defaultValue: 'bar', description: 'Fails job if not bar (unstable if bar)')]),
])


stage('Stage 1') {
  node('parent'){
    def ret = sh(
      returnStatus: true, // This is the key bit!
      script: '''if [ "$foo" = bar ]; then exit 2; else exit 1; fi'''
    )
    // ret can be any number/range, does not have to be 2.
    if (ret == 2) {
      currentBuild.result = 'UNSTABLE'
    } else if (ret != 0) {
      currentBuild.result = 'FAILURE'
      // If you do not manually error the status will be set to "failed", but the
      // pipeline will still run the next stage.
      error("Stage 1 failed with exit code ${ret}")
    }
  }
}

The Pipeline Syntax generator shows you this in the advanced tab:

Pipeline Syntax Example


I thought I would post another answer for people that might be looking for something similar.

In our build job we have cases where we would want the build to continue, but be marked as unstable. For ours it's relating to version numbers.

So, I wanted to set a condition on the build and set the build to unstable if that condition is met.

I used the Conditional step (single) option as a build step.

Then I used Execute system Groovy script as the build step that would run when that condition is met.

I used Groovy Command and set the script the following

import hudson.model.*

def build = Thread.currentThread().executable
build.@result = hudson.model.Result.UNSTABLE

return

That seems to work quite well.

I stumbled upon the solution here

http://tech.akom.net/archives/112-Marking-Jenkins-build-UNSTABLE-from-environment-inject-groovy-script.html


I find the most flexible way to do this is by reading a file in the groovy post build plugin. enter image description here

import hudson.FilePath
import java.io.InputStream

def build = Thread.currentThread().executable

String unstable = null
if(build.workspace.isRemote()) {
    channel = build.workspace.channel;
    fp = new FilePath(channel, build.workspace.toString() + "/build.properties")
    InputStream is = fp.read()
    unstable = is.text.trim()
} else {
    fp = new FilePath(new File(build.workspace.toString() + "/build.properties"))
    InputStream is = fp.read()
    unstable = is.text.trim()
}

manager.listener.logger.println("Build status file: " + unstable)
if (unstable.equalsIgnoreCase('true')) {
    manager.listener.logger.println('setting build to unstable')
    manager.buildUnstable()
}

If the file contents are 'true' the build will be set to unstable. This will work on the local master and on any slaves you run the job on, and for any kind of scripts that can write to disk.


You should use Jenkinsfile to wrap your build script and simply mark the current build as UNSTABLE by using currentBuild.result = "UNSTABLE".

   stage {
      status = /* your build command goes here */
      if (status === "MARK-AS-UNSTABLE") {
        currentBuild.result = "UNSTABLE"
      }
   }

As a lighter alternative to the existing answers, you can set the build result with a simple HTTP POST to access the Groovy script console REST API:

    curl -X POST \
     --silent \
     --user "$YOUR_CREDENTIALS" \
     --data-urlencode "script=Jenkins.instance.getItemByFullName( '$JOB_NAME' ).getBuildByNumber( $BUILD_NUMBER ).setResult( hudson.model.Result.UNSTABLE )" $JENKINS_URL/scriptText

Advantages:

  • no need to download and run a huge jar file
  • no kludges for setting and reading some global state (console text, files in workspace)
  • no plugins required (besides Groovy)
  • no need to configure an extra build step that is superfluous in the PASSED or FAILURE cases.

For this solution, your environment must meet these conditions:

  • Jenkins REST API can be accessed from slave
  • Slave must have access to credentials that allows to access the Jenkins Groovy script REST API.

It can be done without printing magic strings and using TextFinder. Here's some info on it.

Basically you need a .jar file from http://yourserver.com/cli available in shell scripts, then you can use the following command to mark a build unstable:

java -jar jenkins-cli.jar set-build-result unstable

To mark build unstable on error, you can use:

failing_cmd cmd_args || java -jar jenkins-cli.jar set-build-result unstable

The problem is that jenkins-cli.jar has to be available from shell script. You can either put it in easy-to-access path, or download in via job's shell script:

wget ${JENKINS_URL}jnlpJars/jenkins-cli.jar

The TextFinder is good only if the job status hasn't been changed from SUCCESS to FAILED or ABORTED. For such cases, use a groovy script in the PostBuild step:

errpattern = ~/TEXT-TO-LOOK-FOR-IN-JENKINS-BUILD-OUTPUT.*/;
manager.build.logFile.eachLine{ line ->
    errmatcher=errpattern.matcher(line)
    if (errmatcher.find()) {
        manager.build.@result = hudson.model.Result.NEW-STATUS-TO-SET
    }
 }

See more details in a post I've wrote about it: http://www.tikalk.com/devops/JenkinsJobStatusChange/


Examples related to shell

Comparing a variable with a string python not working when redirecting from bash script Get first line of a shell command's output How to run shell script file using nodejs? Run bash command on jenkins pipeline Way to create multiline comments in Bash? How to do multiline shell script in Ansible How to check if a file exists in a shell script How to check if an environment variable exists and get its value? Curl to return http status code along with the response docker entrypoint running bash script gets "permission denied"

Examples related to jenkins

Maven dependencies are failing with a 501 error Jenkins pipeline how to change to another folder Docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock groovy.lang.MissingPropertyException: No such property: jenkins for class: groovy.lang.Binding How to solve npm install throwing fsevents warning on non-MAC OS? Run bash command on jenkins pipeline Try-catch block in Jenkins pipeline script How to print a Groovy variable in Jenkins? Jenkins pipeline if else not working Error "The input device is not a TTY"

Examples related to build

error: This is probably not a problem with npm. There is likely additional logging output above Module not found: Error: Can't resolve 'core-js/es6' WARNING in budgets, maximum exceeded for initial How can I change the app display name build with Flutter? Error - Android resource linking failed (AAPT2 27.0.3 Daemon #0) Still getting warning : Configuration 'compile' is obsolete and has been replaced with 'implementation' Could not find com.android.tools.build:gradle:3.0.0-alpha1 in circle ci Error:Execution failed for task ':app:compileDebugKotlin'. > Compilation error. See log for more details Component is part of the declaration of 2 modules Maven build Compilation error : Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project Maven

Examples related to status

How to set bot's status How to change status bar color to match app in Lollipop? [Android] Git status ignore line endings / identical files / windows & linux environment / dropbox / mled Android : change button text and background color How to mark a build unstable in Jenkins when running shell scripts How to find whether MySQL is installed in Red Hat? Should a 502 HTTP status code be used if a proxy receives no response at all? How to get current CPU and RAM usage in Python?