30

Is there any way in a scripted pipeline to mark a stage as unstable but only show that stage as unstable without marking every stage as unstable in the output?

I can do something like this:

node() { stage("Stage1") { // do work (passes) } stage("Stage2") { // something went wrong, but it isn't catastrophic... currentBuild.result = 'UNSTABLE' } stage("Stage3") { // keep going... } } 

But when I run this, Jenkins marks everything as unstable... but I'd like the first and last stages to show green if possible and just the stage that had an issue to go yellow.

It's ok if the whole pipeline gets flagged unstable, but it might also be nice to have a later stage over ride that and set the final-result to pass if possible too.

4 Answers 4

39

This is now possible:

pipeline { agent any stages { stage('1') { steps { sh 'exit 0' } } stage('2') { steps { catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { 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 unstable. I use a declarative pipeline in the example, but it should work the same in a scripted pipeline.

As you might have guessed, you can freely change the buildResult and stageResult to any combination. 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. Upgrade any plugins affected by bug JENKINS-39203, such as:

Sign up to request clarification or add additional context in comments.

5 Comments

This seems to only be available in the declarative part of the script. Is it possible to this in the imperative part as well?
@Moberg It works in a "script" block of a declarative pipeline too.
@erik-b Thanks! This works. Out of curiosity, do you know any way to check the status of the current stage? catchError can set stageResult but so far I can't find any way to check that value later. For example, I can access currentBuild.getCurrentResult() to get the overall pipeline status but so far I can't find the analog for a stage.
The options of catchError are only available since the version 2.16 of workflow-basic-steps plugin (May 14, 2019).
this answer will set the buildResult to SUCCESS, which might not be what you want. What if the buildResult was set to UNSTABLE in a previous stage? You would be overriding that... You do not want to change the buildResult - you want to keep it to what it was. See my answer instead: stackoverflow.com/a/65687211/1326411
15

an elegant way I found to set only the stage result but not change the build result is this:

catchError(stageResult: 'UNSTABLE', buildResult: currentBuild.result) { error 'example of throwing an error' } 

1 Comment

catchError(stageResult: 'UNSTABLE', buildResult: null) can do exactly same
14

Also worth mentioning are the warnError and unstable steps, released on Jul/2019, as part of the "Jenkins Pipeline Stage Result Visualization Improvements".

They are meant to allow you to mark a stage as unstable (this appears as an amber warning icon, and not a red error icon), while the rest of the build is still marked as successful.

Examples (lifted from the link above):

warnError acts like catchError, and changes the stage to have a warning if any part of the block fails:

warnError('Script failed!') { sh('false') } 

unstable is a directive-style step, which you can use to mark the current stage as unstable:

try { sh('false') } catch (ex) { unstable('Script failed!') } 

1 Comment

unstable marks the build as unstable as well, not just the stage. Same with warnError.
3

I know this question is a few years old but would like to offer an alternative for people who are coming across this problem. The accepted answer is close to what I needed, but the issue is that catchError does not allow for an alternate set of code to execute when an error occurs in its block like a try/catch does. I got around this in the following way:

stage('Stage 2') { steps { echo 'In stage 2' script { try { echo 'In stage 2: try block' sh 'python3 ./python/script.py' } catch(Exception e) { echo 'In stage 2: catch block' catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') { echo 'In stage 2: catchError block' sh 'exit 1' } } } } } 

When my Python script raises an exception, whatever is in the catch block will execute per usual try/catch logic. Putting the catchError there as well and raising a bogus exception in it guarantees that my build and stage statuses will come out how I want (e.g. all stages with status = SUCCESS except for Stage 2, and a build status = UNSTABLE).

There's no question that this is an awkward workaround. However, the Jenkins developers insist (see here) that "To ensure consistency the FAILED state should always fail the pipeline."

My code snippet above is just simple proof of concept, but in my production code I have a legitimate need to fail a stage, yet mark the entire build as UNSTABLE. The noble intentions behind designing Jenkins declarative pipelines with so much rigor are admirable, but really inconvenient and unnecessary in my opinion.

Hopefully this workaround helps someone. If I'm missing something then I'm open to looking at a cleaner way of doing this.

3 Comments

so does 'exit 1' exit complete at that point, or do subsequent stages still execute?
As I've written it here (and as the accepted answer shows), subsequent stages still execute, which happens to be what my business logic calls for. One could prevent that and fail the build entirely by simply doing throw new Exception( ) inside the catch block and forgetting about catchError( ) altogether. That automatically fails the entire pipeline, without giving one the ability to set both the pipeline and stage statuses like catchError( ) does.
It's also worth mentioning that if you catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') { throw new Exception( ) }, the entire pipeline will still go UNSTABLE and the next stage will still execute. In fact, I don't see a way right now of preventing subsequent stages from executing when catchError( ) is being used.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.