Code execution failure for post pipeline failure - jenkins

I'm building a pipeline using scripted pipeline and as I can see from https://jenkins.io/doc/pipeline/tour/post/, for declarative pipeline, we can use post actions.
Similarly I tried in declarative pipeline -
node {
stage("Stage 1") {
script {
FAILED_STAGE=env.STAGE_NAME
echo "stage 1"
}
}
stage("Stage 2") {
script {
FAILED_STAGE=env.STAGE_NAME
echo "stage 2"
error "failed for some reason."
}
}
stage("Stage 3") {
script {
FAILED_STAGE=env.STAGE_NAME
echo "stage 3"
}
}
post {
failure {
echo "Failed stage name: ${FAILED_STAGE}"
}
}
}
But I do not see failure block of post executed even if pipeline failed. Can you please help to understand if it has some different syntax for scripted pipeline?

You are using a scripted pipeline and not a declarative pipeline (see the difference here). The syntax is completely different so you can't use a post-stage (existing for declarative pipeline) in a scripted pipeline (like your pipeline).
You have two options here.
You convert your scripted pipeline to a declarative pipeline (see syntax here) and simply use the post-stage like you have it used in your questoin. (I would prefer this option)
or
Implement the post-stage for your scripted pipeline by yourself in a finally-block (see this SO question for help)

Related

What is differences between either of using def and without using def in Jenkinsfile in script block?

I have two Jenkinsfile for sample:
The content of A_Jenkinsfile is:
pipeline {
agent any
stages {
stage("first") {
steps {
script {
foo = "bar"
}
sh "echo ${foo}"
}
}
stage("two") {
steps {
sh "echo ${foo}"
}
}
}
}
The other one is B_Jenkinsfile and its content is:
pipeline {
agent any
stages {
stage("first") {
steps {
script {
def foo = "bar"
}
sh "echo ${foo}"
}
}
stage("two") {
steps {
sh "echo ${foo}"
}
}
}
}
When I build them, B_Jenkinsfile is failed and A_Jenkinsfile is success.
What is differences between either of using def and without using def in Jenkinsfile in script block?
There are two types of Pipeline syntax. Declarative Pipeline and Scripted Pipeline. A declarative pipeline starts with a pipeline {} wrapper and will have Stages and Steps. Declarative pipeline limits what is available to the user with a more strict and pre-defined structure. Where in scripted Pipeline it's more closer to groovy, and users will have more flexibility on what they can do. When you run something in a Script block in a declarative Pipeline, The script step takes a block of the Scripted Pipeline and executes that in the Declarative Pipeline. Basically, it runs a Groovy script for you. So your question can be rephrased as what def means in a Groovy script.
Simply in a Groovy script, if you omit adding the def keyword the variable will be added to the current script's binding. So it will be considered as a Global variable. If you use def the variable will be scoped, and you will only be able to use it in the current script block. There are multiple detailed answers for this here, so I'm not going to repeat them.

How to mark Jenkins' job stage as skipped, for scripted pipeline

How do I mark a stage as skipped when using scripted pipeline.
I have no problem skipping a stage in declarative pipeline. I just set
when {
expression {<some boolean expression>}
}
And if the expression is evaluate to false that stage is skipped.
The problem is that if you try to do this with scrippted pipeline you get:
java.lang.NoSuchMethodError: No such DSL method 'when' found among steps
error message. This is because the DSL of declarative pipeline is not the same as scripted pipeline
So, how can it be done?
Solving this issue takes a little bit of hacking... (don't worry, nothing fancy)
The way to do this is by using Jenkins' module that can be found here.
So to mark a stage as skipped you need to call static method markStageSkippedForConditional passing the name of the stage you are skipping.
Lets say you have a stage named "mystage". and you want to skip it and mark it as "skipped".
Your code should look something like:
import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
node() {
stage('a'){
echo 'stage 1'
}
stage('mystage'){
if(true){
echo 'skipping stage...'
Utils.markStageSkippedForConditional('mystage')
}else{
echo 'This stage may be skipped'
}
}
stage('b'){
echo 'stage 2'
}
}
If you're using a version of Jenkins older than mid 2019, you must uncheck Use Groovy Sandbox checkbox, since the Utils method was not yet whitelisted for external use.
You can find an implementation (in the form of a shared pipeline step) in comquent/imperative-when on GitHub.
This allows to access the method Utils.markStageSkippedForConditional that you found yourself in a nice way, like the following:
stage('Zero') {
when(BRANCH_NAME != 'master') {
echo 'Performing steps of stage Zero'
}
}
In declarative pipeline you can use:
stage('deploy') {
when { <some boolean expression> }
......
}
In Scripted pipeline you can use:
if(<some boolean expression>) {
stage('deploy') {
......
}
}
both above works fine. i tested

Can I use a Closure to define a stage in a Jenkins Declarative Pipeline?

I'm trying to do something like this:
def makeStage = {
stage('a') {
steps {
echo 'Hello World'
}
}
}
pipeline {
agent none
stages {
makeStage()
}
}
But it gives me this exception:
WorkflowScript: 11: Expected a stage # line 11, column 5.
makeStage()
^
Is it possible to define a stage as a external closure and if so - how?
Super late, but in case anyone runs into this issue, a possible solution would be to wrap your generated stage around a script declarative and invoke .call on the generated stage.
So for you:
def makeStage = {
return {
stage('a') {
echo 'Hello World'
}
}
}
pipeline {
agent none
stages {
stage ('hello world') {
steps {
script {
makeStage().call()
}
}
}
}
}
Whoops. edited, I had "steps" inside on my stage('a') in the makeStage declaration. "steps" is a declarative pipeline directive so it was throwing an error inside the script block.
You can't define stages outside the declarative pipeline. The main purpose of declarative pipeline is to provide simplified and opinionated syntax so you can focus on what should be done (by using some of the available steps) and not how to do it.
If you are interested in more flexible way of implementing pipeline, you may choose Scripted Pipeline approach which is not that strict if it comes to the syntax - it's only limited by Groovy and CPS execution module.
Working (scripted) pipeline from your example would look like this:
#!groovy
def makeStage = {
stage('a') {
echo 'Hello World'
}
}
node {
makeStage()
}
Attention: There is no steps method inside stage in a scripted pipeline. If you leave it there you will get
java.lang.NoSuchMethodError: No such DSL method 'steps' found among
steps [archive, bat, build, catchError, checkout, deleteDir, dir,
dockerFingerprintFrom, ...
Scripts in declarative pipeline
Declarative pipeline defines a script step that allows you to put a block of scripted pipeline. However it still does not allow you to define stage dynamically or/and extract stage definition to a function or closure. script step gets executed inside the stage so you can't control inside this block if stage is executed or not. In some cases however this step might be very useful if you want to do something more complex than just calling pre-defined step of a declarative pipeline.

Mark a stage in Jenkins Pipeline as eg "UNSTABLE" but proceed with future stages?

I'm going to use Jenkins pipeline plugin to test several binaries A B C on several nodes 1 2 3.
In the end of my test I would like to have every single result of all possible combinations. So my Pipe may not abort when a single stage fails. It should proceed.
eg: A1 green, A2 green, A3 red, B1 green, B2 red, ..., C3 green
But when the first binary returns with an value unequal zero ("Binary not working on the system") it's stage is marked as FAILURE and any other stages are skipped.
Is there a possibility in Jenkins Pipeline to mark a stage as "UNSTABLE" but proceed with running the other tests?
According to Continue Jenkins job after failed stage while marking stage as failed can't mark this step as failed. The solution of this in running tasks in parallel is not working for my setup. So is it possible to safely mark it as something else? Is it possible to manipulate the result of a stage?
This question How to continue past a failing stage in Jenkins declarative pipeline syntax intents to use a scripted pipeline. I would like to avoid that if it is possible to do it in an other way.
pipeline {
agent {label 'master'}
stages {
stage('A1') {
agent {label 'Node1'}
steps {
sh 'binA'
}
}
stage('A2') {
agent {label 'Node1'}
steps {
sh 'binB' // If this bin fails, all following stages are skipped
}
}
// ...
stage('C3'){
agent {label 'Node3'}
steps {
sh 'binC'
}
}
}
}
Declarative Pipeline: Though using currentBuild.result = 'UNSTABLE' works in declarative pipelines too, Blue Ocean displays all stages as unstable irrespective of which stage fails.
To mark only specific stages as unstable, use the step unstable(message: String) as described here within your stage and install/update the following plugins:
Pipeline: Basic Steps to 2.16 or newer
Pipeline: API Plugin to 2.34 or newer
Pipeline: Groovy to 2.70 or newer
Pipeline Graph Analysis to 1.10 or newer
Sample pipeline stage:
stage('Sign Code') {
steps {
script {
try {
pwd()
sh "<YOUR SCRIPT HERE>"
}
catch (err) {
unstable(message: "${STAGE_NAME} is unstable")
}
}
}
}
Note: This also marks the overall build status as unstable.
There is now a more elegant solution, that not only allows you to set a stage and the job result to unstable. Using catchError, you can set any combination of stage and build result:
pipeline {
agent any
stages {
stage('1') {
steps {
sh 'exit 0'
}
}
stage('2') {
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
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 failed:
As mentioned above, you can freely choose the buildResult and stageResult. 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. (Pipeline: Basic Steps needs to be 2.18 or newer)
For scripted pipeline, you can use try .. catch blocks inside the stages and then set currentBuild.result = 'UNSTABLE'
in the exception handler.

Limiting Jenkins pipeline to running only on specific nodes

I'm building jobs that will be using Jenkins piplines extensively. Our nodes are designated per project by their tags, but unlike regular jobs the pipeline build does not seem to have the "Restrict where this project can be run" checkbox. How can I specify on which node the pipeline will run the way I do for regular jobs?
You specify the desired node or tag when you do the node step:
node('specialSlave') {
// Will run on the slave with name or tag specialSlave
}
See https://jenkins.io/doc/pipeline/steps/workflow-durable-task-step/#node-allocate-node for an extended explanation of the arguments to node.
Edit 2019: This answer (and question) was made back in 2017, back then there only was one flavor of Jenkins pipeline, scripted pipeline, since then declarative pipeline has been added. So above answer is true for scripted pipeline, for answers regarding declarative pipeline please see other answers below.
Choosing a node which has the label X in a declarative pipeline with json format:
pipeline {
agent { label 'X' }
...
...
}
You also can apply multiple labels with or (||) or with and (&&) operator.
Running the job on any of the nodes which has label X or label Y:
agent { label 'X || Y' }
Running the job only on nodes which have both label:
agent { label 'X && Y' }
More in the Jenkins Pipeline reference guide.
ps: if you are reading this you probably have just started using Jenkins pipeline and you are not sure if you should use declarative or scripted pipeline. Short answer: it's better to start with declarative. From jenkins.io:
Declarative and Scripted Pipelines are constructed fundamentally
differently. Declarative Pipeline is a more recent feature of Jenkins
Pipeline which:
provides richer syntactical features over Scripted Pipeline syntax, and
is designed to make writing and reading Pipeline code easier.
To be clear, because Pipeline has two Syntax, there are two ways to achieve that.
Declarative
pipeline {
agent none
stages {
stage('Build') {
agent { label 'slave-node​' }
steps {
echo 'Building..'
sh '''
'''
}
}
}
post {
success {
echo 'This will run only if successful'
}
}
}
Scripted
node('your-node') {
try {
stage 'Build'
node('build-run-on-this-node') {
sh ""
}
} catch(Exception e) {
throw e
}
}
Agent or Node where we should not execute the jenkins job :
This is the negation of the problem statment i.e. node where not to run
It was most weird solution to me but issue has been already raised to jenkins community
agent { label '!build-agent-name' }
If you need to run entire jenkins pipeline to run on single node, use the following format
pipeline {
agent {
label 'test1'
}
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
if you need to execute each stage in different nodes, use the below format,
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
node("test1"){
echo 'Testing..'
}
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}

Resources