I am running a SonarQube - Jenkins integration.
I need to achieve the following:
Link each pipeline build with that respective SonarQube analysis.
i.e
Build A - Passes Quality Gate
Build B - Fails Quality Gate
If I click on the SonarQube link associated with build A - It directs to the SonarQube dashboard showing a failure.
If I click on the SonarQube link associated with build B - It directs to the SonarQube dashboard showing a success.
I have tried the following:
sonar.projectVersion = ${env.BUILD_NUMBER}
This just tells with which analysis version to compare the latest analysis.
How can I achieve a direct link with the SonarQube dashboard and that particular build number in a Jenkins script pipeline?
I understand you want read Sonar Quality Gate in your Jenkins job ?
Sonar Quality gate will generate a "report-task.txt" file once analysis completed.
Depending on your project size, it might take a while.
Then, using Sonar rest api, you can get json status and use it.
Here is a copy/paste sample I'm using :
myEcho('DEBUG', "I want to wait for SONAR completion")
if (fileExists('target/sonar/report-task.txt')) {
//echo sh(returnStdout: true, script: 'cat target/sonar/report-task.txt')
def reportTak = readProperties(file: "target/sonar/report-task.txt")
def countLoop = 0
def countMax = 10
def sonarGateIsDone = false
// wait at least 1 second before asking for Sonar feedback
sleep(time: 3000, unit: 'MILLISECONDS')
while (!sonarGateIsDone && (countLoop <= countMax)) {
countLoop++
// loop while status is over OR timeout...
echo sh(returnStdout: true, script: "curl ${SONAR_URL}api/ce/task?id=${reportTak.ceTaskId} -o target/sonar/output.json")
if (fileExists('target/sonar/output.json')) {
def outputJson = readJSON(file: 'target/sonar/output.json')
if ("${outputJson.task.status}" == 'SUCCESS' || "${outputJson.task.status}" == 'CANCELED' || "${outputJson.task.status}" == 'FAILED') {
myEcho('INFO', "Sonar Gate internal analysis finished [${outputJson.task.status}]")
if ("${outputJson.task.status}" == 'SUCCESS') {
// internal process done, lets check Gate status
echo sh(returnStdout: true, script: "curl ${SONAR_URL}api/qualitygates/project_status?analysisId=${outputJson.task.analysisId} -o target/sonar/sonarGate.json")
if (fileExists('target/sonar/sonarGate.json')) {
def sonarGateJson = readJSON(file: 'target/sonar/sonarGate.json')
if ("${sonarGateJson.projectStatus.status}" == 'OK') {
// Gate is OK
myEcho('INFO', "Sonar gate is OK : status=[${sonarGateJson.projectStatus.status}]")
sonarGateIsDone = true
} else {
// Gate is NOK
myEcho('WARN', "Sonar gate is NOK : status=[${sonarGateJson.projectStatus.status}]")
sonarGateIsDone = true
}
} else {
// cannot find SonarGate.json ?!
myEcho('FAIL', 'Sonar gate check failed : cannot find [target/sonar/sonarGate.json]')
}
} else {
myEcho('WARN', "Sonar gate check is [${outputJson.task.status}]...")
}
} else {
// Sonar internal analysis isnt over, keep on going
myEcho('INFO', "Sonar Gate internal analysis still ongoing, wait a little... [${outputJson.task.status}]")
}
} else {
myEcho('FAIL', 'Sonar gate check failed : cannot find [target/sonar/output.json]')
}
// reaching here might probably be for Sonar to get time to make it...
if (!sonarGateIsDone) {
sleep(time: 1000, unit: 'MILLISECONDS')
}
} // while loop
if (!sonarGateIsDone) {
myEcho('WARN', "Waiting too long for completion... gave up !")
}
} else {
myEcho('INFO', "target/sonar/report-task.txt DOES NOT EXISTS")
}
Hope this helps.
Regards
Related
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')
}
}
I am using latest Jenkins in my Linux Box. I am trying to create a pipline with script block like below;
pipeline {
agent any
stages {
stage('TestStage') {
steps {
script {
sh "testfile.sh"
}
}
}
}
}
testfile.sh will return a json text like below;
{
"Worker": [
{
"Status": "running"
}
]
}
The Status can be either running or success or failure. If it is running, the code has to call testfile.sh again and check the status. If its success, pipeline has to continue to next step and if it is failure, pipeline has to terminate. Is it possible to achieve this?
Thanks.
You can achieve by creating a while loop that executes your script, reads the output and checks its value until a given timeout, when the status is no longer running you can check the result and fail the build according to the final status.
Something like:
pipeline {
agent any
stages {
stage('TestStage') {
steps {
script {
timeout(time: 1, unit: 'HOURS') { // timeout for the 'running' period
def status = sh script: 'testfile.sh', returnStdout: true
while (status == 'running') {
sleep time: 10, unit: 'SECONDS' // sleep between iterations
def output = sh script: 'testfile.sh', returnStdout: true
def dict = readJSON text: output
status = dict.Worker.Status
}
}
if(status == 'failure'){
error "Operation has ended with status 'failure'"
}
}
}
}
}
}
You can find more info on the relevant steps: sleep, timeout, error and readJSON.
I configure Jenkins to work with sonarqube scanner. The scan are working fine. The jenkins pipeline is working and I don't have any isssue in the jenkins log.
SonarQube Scanner 3.0.3.778
Jenkins: 2.70
SonarQube Scanner for Jenkins plugin: 2.6.1
I use this code:
stage('SonarQube analysis') {
sh 'sed -ie "s|_PROJECT_|${PROJECT_CODE}|g" $WORKSPACE/_pipeline/sonar-project.properties'
// requires SonarQube Scanner 3.0+
def scannerHome = '/opt/sonar/bin/sonar-scanner';
withSonarQubeEnv('mscodeanalysis') {
sh "${scannerHome}/bin/sonar-scanner -Dproject.settings=$WORKSPACE/_pipeline/sonar-project.properties"
}
}
}
}
}
}
// No need to occupy a node
stage("Quality Gate"){
timeout(time: 15, unit: 'MINUTES') { // Just in case something goes wrong, pipeline will be killed after a timeout
def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
My problem come from Quality Gate. It never POST the json payload to jenkins. I don't see a json entry inside jenkins log. But I know the connection between jenkins and sonarqube server is working because I was able to send a POST using curl from the sonarqube VM.
Here the jenkins job output:
Timeout set to expire in 15 min
[Pipeline] {
[Pipeline] waitForQualityGate
Checking status of SonarQube task 'AV3irVJXpvBxXXNJYZkd' on server 'mscodeanalysis'
SonarQube task 'AV3irVJXpvBxXXNJYZkd' status is 'PENDING'
Cancelling nested steps due to timeout
Here is my payload that never reach jenkins pipeline:
url: http://sonar-server:9000/api/ce/task?id=AV3irVJXpvBxXXNJYZkd
{"task":{"organization":"default-organization","id":"AV3irVJXpvBxXXNJYZkd","type":"REPORT","componentId":"AV3hrJeCfL_nrF2072FH","componentKey":"POOL-003","componentName":"POOL-003","componentQualifier":"TRK","analysisId":"AV3irVkZszLEB6PsCK9X","status":"SUCCESS","submittedAt":"2017-08-14T21:36:35+0000","submitterLogin":"jenkins","startedAt":"2017-08-14T21:36:37+0000","executedAt":"2017-08-14T21:36:38+0000","executionTimeMs":650,"logs":false,"hasScannerContext":true}}
I can't insert image but the Quality gate is Pass and the analysis task is success.
Let me know if I need to include more information.
Thank you
The issue could be that Jenkins is using https with self-signed certificate. Then solution is:
Generate truststore for SonarQube:
keytool -import -trustcacerts -alias jenkins-host-name -file cert.crt -keystore sonarqube.jks
keystore passw: password
Where cert.crt - is certificate used for ssl for jenkins, jenkins-host-name - is a hostname of jenkins in the docker network (which is used in webhook)
Add truststore to SonarQube Dockerfile:
FROM sonarqube
COPY sonarqube.jks /var/sonar_cert/
COPY sonar.properties /opt/sonarqube/conf/sonar.properties
Update sonar.properties
sonar.ce.javaAdditionalOpts=-Djavax.net.ssl.trustStore=/var/sonar_cert/sonarqube.jks -Djavax.net.ssl.trustStorePassword=password
Then if you have a correct user and password for Jenkins provided in webhook URL everything should work.
Tried: Jenkins 2.107.2, SonarQube 7.1
Here is a quick example of what we did to resolve this issue:
SonarQube randomly hangs at "pending" state. Telling it to retry refreshes it. We set it to 10 seconds in this example
maxRetry = 200
forloop (i=0; i<maxRetry; i++){
try {
timeout(time: 10, unit: 'SECONDS') {
waitForQualityGate()
}
} catch(Exception e) {
if (i == maxRetry-1) {
throw e
}
}
}
Was surprised to find that #Katone Vi's answer worked so well. Based on their answer we added a quick exit on success and used the DSL for the original request:
stage('SonarQube') {
steps {
withSonarQubeEnv('SonarQube') {
sh """
${scannerHome}/bin/sonar-scanner -Dsonar.projectKey=XXX_${env.STAGE}_lambda
"""
}
script {
Integer waitSeconds = 10
Integer timeOutMinutes = 10
Integer maxRetry = (timeOutMinutes * 60) / waitSeconds as Integer
for (Integer i = 0; i < maxRetry; i++) {
try {
timeout(time: waitSeconds, unit: 'SECONDS') {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
error "Sonar quality gate status: ${qg.status}"
} else {
i = maxRetry
}
}
} catch (Throwable e) {
if (i == maxRetry - 1) {
throw e
}
}
}
}
}
}
If you have configured SonarQube to use a HTTP(S) proxy, make sure that your jenkins is either reachable through the proxy or is configured as a "non-proxy host". This can be done with the http.nonProxyHosts property or HTTP_NONPROXYHOSTS environment variable. See also the documentation for further information and syntax.
If you are using Jenkinsfile, this is workaround:
define creadentials:
environment {
CRED = credentials('jenkins_user_pass')
}
then use:
stage("Quality Gate") {
steps {
script {
while(true){
sh "sleep 2"
def url="http://jenkinsURL/job/${env.JOB_NAME.replaceAll('/','/job/')}/lastBuild/consoleText";
def sonarId = sh script: "wget -qO- --content-on-error --no-proxy --auth-no-challenge --http-user=${CRED_USR} --http-password=${CRED_PSW} '${url}' | grep 'More about the report processing' | head -n1 ",returnStdout:true
sonarId = sonarId.substring(sonarId.indexOf("=")+1)
echo "sonarId ${sonarId}"
def sonarUrl = "http://jenkinsURL/sonar/api/ce/task?id=${sonarId}"
def sonarStatus = sh script: "wget -qO- '${sonarUrl}' --no-proxy --content-on-error | jq -r '.task' | jq -r '.status' ",returnStdout:true
echo "Sonar status ... ${sonarStatus}"
if(sonarStatus.trim() == "SUCCESS"){
echo "BREAK";
break;
}
if(sonarStatus.trim() == "FAILED "){
echo "FAILED"
currentBuild.result = 'FAILED'
break;
}
}
}
}
}
I have faced similar issue, while the quality Gate back-end activities in Sonar server takes less than 20 sec to complete its analysis.But quality gate fail/success response from sonar-webhook in jenkins job take lot of time and stuck.
stage('Sonar:QG') {
steps {
**sleep(10) /* Added 10 sec sleep that was suggested in few places*/**
script{
timeout(time: 10, unit: 'MINUTES') {
def qg = waitForQualityGate abortPipeline: true
if (qg.status != 'OK') {
echo "Status: ${qg.status}"
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
}
}
Essentially check below thing:-
Webhook is configured in sonar or not :- SonarQube -> Administration -> Webhooks
http://:/sonarqube-webhook/
or Use localhost in place of IP in http://locahlhost:port/sonarqube-webhook/ solves issue in my case.
I'm made more simple decision but it work a same
stage("Quality gate") {
steps {
retry(3){
waitForQualityGate abortPipeline: true
}
}
}
Adding a sh 'sleep 10' between stage('SonarQube analysis') AND stage("Quality Gate") fix the issue. Now the jenkins job receive
Checking status of SonarQube task 'AV3rHxhp3io6giaQF_OA' on server 'sonarserver'
SonarQube task 'AV3rHxhp3io6giaQF_OA' status is 'SUCCESS'
SonarQube task 'AV3rHxhp3io6giaQF_OA' completed. Quality gate is 'OK'
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.
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.