Jenkins Pipeline still executes following stages even though current stage failed - jenkins

I'm implementing a try catch block on most of my stages inside my jenkins pipeline to skip all the following stages when the current stage fails however, one of my stages is returning an error but still continues to execute the next stages.
I've tried using sh 'exit 1', currentStage.result = 'FAILED', if else clause to check the stage result but to no avail.
pipeline {
agent none
stages {
stage ('one') {
steps {
echo 'Next stage should be skipped if this stage fails'
script {
try {
sh '''#!/bin/bash -l
source ~/.nvm/nvm.sh
nvm use node
node somefile.js'''
}
catch (e) {
currentBuild.result = 'FAILURE';
throw e
}
}
}
}
stage ('two') {
steps {
echo 'This stage should be skipped if prior stage throws an erorr'
}
}
}
}
I expect stage two to be skipped as my somefile.js is throwing an error.

You can use the when-clause that Jenkins provides (Source).
stage ('two') {
// Skip this stage if pipeline has already failed
when { not { equals expected: 'FAILURE', actual: currentBuild.result } }
steps {
echo 'This stage should be skipped if prior stage throws an erorr'
}
}

Related

How to mark whole Jenkins pipeline build as SUCCESS, after a stage fails and the remaining stages don't run?

My first stage runs a shell script. Exit 0 marks it as success and exit 1 marks it as fail. How can I read this result into the pipeline and get the desired behavior:
Run stage 1
If stage 1 fails, don't run the remaining stages, but mark the whole pipeline as a success
If stage 1 succeeds, run the remaining stages
If any of them fail, mark the pipeline as a fail
If they all succeed, mark the pipeline as a success
I am doing this in a declarative pipeline, how can I enforce this behavior?
You can use something like this, catch the error and then change the currentBuild result :
pipeline {
agent any
stages {
stage('Stage 1') {
steps {
script {
try {
// do something that fails
sh "exit 1"
} catch (Exception err) {
currentBuild.result = 'SUCCESS'
}
}
}
}
stage('Stage 2') {
steps {
echo "Stage 2"
}
}
stage('Stage 3') {
steps {
echo "Stage 3"
}
}
}
}
If you need to change a specific stage result, have a look to this link wich explain how to perform it.

Jenkins pipeline: Run all steps in stage, even if the first one fails

I have a series of steps in a stage that I want to run even if the first one fails. I want the stage result to fail and the build to get aborted, but only after all steps have run. For example,
pipeline {
agent any
stages {
stage('Run Test') {
steps {
sh "echo running unit-tests"
sh "echo running linting && false" // failure
sh "echo generating report" // This should still run (It currently doesn't)
publishCoverage adapters: [coberturaAdapter("coverage.xml")] // This should still run (It currently doesn't)
junit 'unit-test.xml' // This should still run (It currently doesn't)
}
}
stage('Deploy') {
steps {
echo "deploying" // This should NOT run
}
}
}
}
The result should be a failed build where the "Run Test" stage failed and the "Deploy" stage did not run. Is this possible?
P.S.
I am NOT asking for the same behavior as in Continue Jenkins pipeline past failed stage. I want to run the steps following the failure, but not any of the stages afterwards. I tried to enclose each of the test steps with catchError (buildResult: 'FAILURE', stageResult: 'FAILURE'), but the "Deploy" stage still runs.
EDIT:
I cannot combine all the steps into one big sh step and capture its return code because some of the steps are not shell commands, but instead jenkins steps like junit and publishCoverage.
A script witha non-zero exit code will always cause a jenkins step to fail. You can use returnStatus as true so that jenkins does not fails the step.
Additionally considering your use case, you could use a post always execution, so that the steps are always carried out.
Please see below reference example:
stage('Run Test') {
steps {
def unit_test_result= sh returnStatus: true, script: 'echo "running unit-tests"'
def lint_result= sh returnStatus: true, script: 'echo "running linting"'
if (unit_test_result!=0 || lint_result!=0 ) {
// If the unit_test_result or lint_result status is not 0 then mark this stage as unstable to continue ahead
// and all later stages will be executed
unstable ('Testing failed')
// You can also mark as failed as below and it will not conintue other stages:
// error ('Testing failed')
}
}
post {
always {
// This block would always be executed inspite of failure
sh "echo generating report"
publishCoverage adapters: [coberturaAdapter("coverage.xml")]
junit 'unit-test.xml'
}
}
}
I found a slightly hacky way to get the behavior I want. The other answers didn't work for me, either because they need all the steps to be sh steps, or they don't stop the deploy stage from running. I used catchError to set the build and stage result. But to prevent the next stage from running, I needed to an explicit call to error if the stage failed.
pipeline {
agent any
stages {
stage('Run Test') {
steps {
script {
// catchError sets the stageResult to FAILED, but does not stop next stages from running
catchError (buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh "echo running unit-tests"
}
catchError (buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh "echo running linting && false" // failure
}
catchError (buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh "echo generating report" // This still runs
}
publishCoverage adapters: [coberturaAdapter("coverage.xml")] // This still runs
junit 'unit-test.xml' // This still runs
if (currentBuild.result == "FAILURE") { // This is needed to stop the next stage from running
error("Stage Failed")
}
}
}
}
stage('Deploy') {
steps {
echo "deploying" // This should NOT run
}
}
}
}
Theoretically you should be able to use sh "<command>||true" It would ignore the error on command and continue. However, Jenkins will not fail as it would ignore the error.
If you don't want Jenkins to ignore the error and want it to stop at the end of the stage, you can do something like: sh "<command>||$error=true" then fail the build based on the $error variable. (sh "$error" might be enough but I am not sure, may require an if statement at the end.) It will be only set to true iff command fails.
Another option is to wrap your build steps in a try-catch block! if there's an exception, i.e. return code of build is not 0 you can catch it, mark the build as unstable and then the rest of the pipeline continues on.
here's an example `
pipeline {
agent {
node {
label 'linux'
}
}
options {
timestamps()
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '3'))
}
tools {
maven 'Maven 3.6.3'
jdk 'jdk11'
}
stages {
stage('CleanWS') {
steps {
cleanWs()
}
}
stage('Build') {
steps {
withMaven(options: [artifactsPublisher(disabled: true)]) {
sh "export NLS_LANG=GERMAN_GERMANY.WE8ISO8859P1 && mvn -f pom.xml clean install -DskipTests -Pregression-test -Dmaven.javadoc.skip=true"
}
}
}
stage('Test') {
steps {
script {
try {
withMaven(options: [artifactsPublisher(disabled: true)]) {
sh "export MAVEN_OPTS=\"-Xmx2048m\" && export NLS_LANG=GERMAN_GERMANY.WE8ISO8859P1 && mvn -B verify -Dmaven.source.skip=true -Dmaven.javadoc.skip=true"
}
} catch (exc) {
currentBuild.result = 'UNSTABLE'
}
}
}
post {
always {
script {
junit "**/surefire-reports/*.xml"
}
}
}
}
stage('Sonar Analyse') {
steps {
script {
withMaven(options: [artifactsPublisher(disabled: true)]) {
withSonarQubeEnv("SonarQube") {
sh "export MAVEN_OPTS=\"-Xmx2048m\" && export NLS_LANG=GERMAN_GERMANY.WE8ISO8859P1 && mvn sonar:sonar"
}
}
}
}
}
stage('Deploy to Nexus') {
steps {
sh "export NLS_LANG=GERMAN_GERMANY.WE8ISO8859P1 && mvn -f pom.xml -B clean deploy -DdeployAtEnd=true -DskipTests"
}
}
}
post {
failure {
script {
emailext(
body: "Please go to ${env.BUILD_URL}/console for more details.",
to: emailextrecipients([developers(), requestor()]),
subject: "Nightly-Build-Pipeline Status is ${currentBuild.result}. ${env.BUILD_URL}"
)
}
}
unstable {
script {
emailext(
body: "Please go to ${env.BUILD_URL}/console for more details.",
to: emailextrecipients([developers(), requestor()]),
subject: "Nightly-Build-Pipeline Build Status is ${currentBuild.result}. ${env.BUILD_URL}"
)
}
}
}
}`

How to execute next stage in sequential stages inspite of previous stage failure in Jenkins pipeline

At the moment I have two stages defined in Jenkins because they both need different agents.
Here is my Proof of concept code
stages {
stage("Run Tests"){
agent docker
stage("Do setup"){}
stage("Testing") {}
post{
always{
echo "always"
}
failure {
echo "failure"
}
}
}
stage("Generate Reports"){
agent node-label
stage("Generate"){
}
}
}
I need "Generate reports" on different agent since certain binaries are on the node and not inside docker container. Tests run inside docker share volume on node so I get all artifacts that are needed to generate report on the node
(I have tried to run "Generate reports" in post stage, but it seems to run inside docker container somehow.)
Now if "Run Tests" fails, "Generate reports" is skipped due to previous stage failure. Any idea how I can force "Generate reports" stage to be run even on failure of previous stage.
Below is the pipeline.
pipeline {
agent none
stages {
stage("Run Tests"){
agent { label "agent1" }
stages {
stage("Do setup"){
steps{
sh 'exit 0'
}
}
stage("Testing") {
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh "exit 1"
} //catchError
} //steps
post{
always{
echo "always"
}
failure {
echo "failure"
}
} // post
} // Testing
} // stages
} // Run Test
stage("Generate Reports"){
agent { label "agent2" }
steps {
sh 'exit 0'
}
} // Reports
}
}
The pipeline is successful, but stage Testing is showed as failed, you can choose the status of buildResult and stageResult in case you want it to be unstable or fail:
If you want to run "Generate Reports" stage always then you could mark your earlier stages as unstable if there are failures. By this way Jenkins will execute all stages and will not stop on errors at a particular stage.
Example:
stages {
stage("Run Tests"){
agent docker
stage("Do setup"){}
stage("Testing") {
steps {
script {
// To show an example, Execute "set 1" which will return failure
def result = bat label: 'Check bat......', returnStatus: true, script: "set 1"
if (result !=0) {
// If the result status is not 0 then mark this stage as unstable to continue ahead
// and all later stages will be executed
unstable ('Testing failed')
}
}
}
}
}
}
stage("Generate Reports"){
agent node-label
stage("Generate"){
}
}
}
Option 2 , if you dont want to handle via return status , you can use try and catch block
stage("Testing") {
steps {
script {
try {
// To show an example, Execute "set 1" which will return failure
bat "set 1"
}
catch (e){
unstable('Testing failed!')
}
}
}
}
Option 3: You can change the return status as success of complete build irrespective of stage failure.
stage("Testing") {
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE'){
script {
// To show an example, Execute "set 1" which will return failure
bat "set 1"
}
}
}
NOTE: Option 3 has a drawback it will not execute further steps in the same stage if at all there were any errors.
Example:
In this example, the print message "Testing stage" will not be executed as there were issues in bat set 1 command
stage("Testing") {
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE'){
script {
// To show an example, Execute "set 1" which will return failure
bat "set 1"
// Note : Below step will not be executed since there was failure in prev step
println "Testing stage"
}
}
}
Option 4 : which you already tried out to keep Generate Reports stage in the Post always section of the build so that is executed always irrespective of any failure.

Execute Parallel Jenkins steps and move to next stage independent of the second step state of execution

I am trying to execute Jenkins Pipeline build from legacy Upstream-Downstream jobs.
node () {
stage('Checkout') {
<Code for checkout>
}
stage ('Support') {
<Restore dependencies>
<Restore build environment>
}
stages{
parallel{
stage ('Build and Archive'){
stages{
stage ('BuildSolution') {
<Build Solution>
}
stage ('Signing') {
<Sign deliverables>
}
stage ('InstallerCreation') {
<Create deployment package>
}
stage ('CreateNgPkg') {
<Create SDK package>
}
}
}
stage ('SecurityScan'){
<Execute scan on the complete source code>
}
}
}
}
}
I want to run stage ('Build') and stage ('Scan') in parallel and after stage ('Build') is executed it should start stage ('CreateNgPkg') without checking or waiting for stage ('Scan')
UPDATE:
I tried using the above nested Stages to achieve what i needed but it's giving error "No such DSL method 'stages' found among steps" upon execution. -No syntax error, just this at run time.
What you want to do is to execute the set of stages Build, then CreateNgPkg in parallel with the stage Scan
It translates to the following Jenkins DSL:
node () {
stage ('Checkout')
{
echo 'Checkout'
}
stage ('Support')
{
echo 'Support'
}
parallel Build: {
stage ('Build')
{
echo 'build'
}
stage ('CreateNgPkg')
{
echo 'CreateNgPkg'
}
}, secondStage: {
stage ('Scan')
{
echo 'scan'
}
}
}
I personally prefer the declarative dsl which is way better documented and can be linted
https://jenkins.io/blog/2017/09/25/declarative-1/

How to detect which parallel stage failed in a Jenkins declarative pipeline?

My Jenkins pipeline runs several tasks in parallel. It appears that if one stage fails, all subsequent stages will run their failure post block (whether they actually failed or not). I don't know if this is by design or if I'm doing something wrong.
Note: This pipeline runs on a Windows node, hence the bat('exit /b 1')
pipeline {
agent any
stages {
stage('Parallel steps') {
parallel {
stage('Successful stage') {
steps {
script { sleep 10 }
}
post {
failure {
echo('detected failure: Successful stage')
}
}
}
stage('Failure stage') {
steps {
script { bat('exit /b 1') }
}
post {
failure {
echo('detected failure: Failure stage')
}
}
}
}
}
}
}
In the above pipeline, only 'Failure stage' fails, yet in the output I see this, indicating the failure conditional executed for both steps!
Started by user Doe, John
Running on WINDOWS_NODE in D:\workspace
[Successful stage] Sleeping for 10 sec
[Failure stage] [test] Running batch script
[Failure stage] D:\workspace>exit /b 1
Post stage
[Pipeline] [Failure stage] echo
[Failure stage] detected failure: Failure stage
[Failure stage] Failed in branch Failure stage
Post stage
[Pipeline] [Successful stage] echo
[Successful stage] detected failure: Successful stage
ERROR: script returned exit code 1
Finished: FAILURE
What's the best way for me to detect which parallel stage failed and report it to the overall pipeline?
It looks like this is a known bug with Declarative Pipelines. I had to give up using the built-in post->failure block and use try/catch instead, which has its own problems:
You have to catch and then re-throw the error in order to make the stage fail appropriately.
The UI can get a little confusing, as the step that failed is no longer highlighted in red (but the error message is still in the log).
The code is slightly less readable.
This code works correctly. Only the failing stage echoes "detected failure" instead of both.
pipeline {
agent any
stages {
stage('Parallel steps') {
parallel {
stage('Successful stage') {
steps {
script {
try {
sleep 10
} catch (e) {
echo('detected failure: Successful stage')
throw(e)
}
}
}
}
stage('Failure stage') {
steps {
script {
try {
bat('exit /b 1')
} catch (e) {
echo('detected failure: Failure stage')
throw(e)
}
}
}
}
}
}
}
}

Resources