jenkins pipeline post actions not working - jenkins

I setup a post action like in the examples:
pipeline {
agent any
stages {
stage('Example1') {
steps {
bat 'return 1'
}
stage('Example2') {
steps {
echo 'Wont see this'
}
}
}
post {
always {
echo 'I will always say Hello'
}
}
}
So I do something in the first stage to make it fail. And I have a post action that always runs, but what happens when I run my pipeline in blueocean is it fails at the first stage and then just stops. Where do I see the post action that is always supposed to run??

I hade a similar problem when I used agent none at the beginning of the pipeline. Try using a node in your post action:
post {
always {
node('master') {
echo 'I will always say Hello'
}
}
}

Kind of late to the party but you have to use catchError before any steps that may fail. Something like this:
steps {
catchError {
bat 'return 1'
}
}

Related

Jenkins declarative pipeline conditional post action depending on stage (not pipeline) status

I have a Jenkins declarative pipeline in which some stages have a post action:
stage('1 unit tests') { ..... }
stage('2 other tests') {
steps { ..... }
post {
success { ...... }
}
}
It seems that if a unit test fails (build becomes unstable in stage 1), then the conditional post action of stage 2 is not performed.
How can I make sure the post action is only skipped if the build status changes
during the current stage?
There are some "options", I don't know what you might like or find acceptable. If a stage is skipped; it also skips all of its internals.
1: It's not exactly what you want but you can manipulate/mark a stage differently than other stages and continue the execution using something like the skipStagesAfterUnstable option or catchError. For more info see this post. But it may also be to heavy-handed and forcing you into a limited set of results or lead to unwanted execution.
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE')
2: You can move the stage to a separate pipeline/job, and trigger it via or after the run of this pipeline
3: Another option might be something like the following pseudo-code, but this feels more like a hack and adding ('global') 'state flags' adds clutter:
failedOne = false
failedTwo = false
pipeline {
agent any
stages {
stage('Test One') {
steps {...}
post {
failure {
failedOne=true
postFailureOne()
}
}
}
stage('Test Two') {
steps {...}
post {
failure {
failedTwo=true
postFailureTwo()
}
}
}
}
post {
success { .... }
failure {
if (!failedTwo) postFailureTwo()
}
}
}
void postFailureOne() { echo 'Oeps 1' }
void postFailureTwo() { echo 'Oeps 2' }

Conditional post section in Jenkins pipeline

Say I have a simple Jenkins pipeline file as below:
pipeline {
agent any
stages {
stage('Test') {
steps {
sh ...
}
}
stage('Build') {
steps {
sh ...
}
}
stage('Publish') {
when {
buildingTag()
}
steps {
sh ...
send_slack_message("Built tag")
}
}
}
post {
failure {
send_slack_message("Error building tag")
}
}
}
Since there's a lot non-tag builds everyday, I don't want to send any slack message about non-tag builds. But for the tag builds, I want to send either a success message or a failure message, despite of which stage it failed.
So for the above example, I want:
When it's a tag build, and stage 'Test' failed, I shall see a "Error building tag" message. (This is a yes in the example)
When it's a tag build, and all stages succeeded, I shall see a "Built tag" message. (This is also a yes in the example)
When it's not a tag build, no slack message will ever been sent. (This is not the case in the example, for example, when the 'Test' stage fails, there's will be a "Error building tag" message)
As far as I know, there's no such thing as "conditional post section" in Jenkins pipeline syntax, which could really help me out here. So my question is, is there any other way I can do this?
post {
failure {
script {
if (isTagBuild) {
send_slack_message("Error building tag")
}
}
}
}
where isTagBuild is whatever way you have to differentiate between a tag or no tag build.
You could also apply the same logic, and move send_slack_message("Built tag") down to a success post stage.
In the postbuild step you can also use script step inside and use if. And inside this if step you can add emailext plugin.
Well, for those who just want some copy-pastable code, here's what I ended-up with based on #eez0's answer.
pipeline {
agent any
environment {
BUILDING_TAG = 'no'
}
stages {
stage('Setup') {
when {
buildingTag()
}
steps {
script {
BUILDING_TAG = 'yes'
}
}
}
stage('Test') {
steps {
sh ...
}
}
stage('Build') {
steps {
sh ...
}
}
stage('Publish') {
when {
buildingTag()
}
steps {
sh ...
}
}
}
post {
failure {
script {
if (BUILDING_TAG == 'yes') {
slackSend(color: '#dc3545', message: "Error publishing")
}
}
}
success {
script {
if (BUILDING_TAG == 'yes') {
slackSend(color: '#28a745', message: "Published")
}
}
}
}
}
As you can see, I'm really relying on Jenkins built-in buidingTag() function to help me sort things out, by using an env-var as a "bridge". I'm really not good at Jenkins pipeline, so please leave comments if you have any suggestions.

Is there a way to programmatically inject post actions in declarative pipeline

I need to share some code between several stages, which would also need to add post actions. To do so, I thought about putting everything in a method, which will be called from
pipeline {
stages {
stage('Some') {
steps {
script { commonCode() }
}
}
}
}
However, I'm not sure how could I install post actions in from commonCode. Documentation does not mention a thing. Looking at the code, implies that this DSL is basically just playing with a hash map, but I don't know would it be possible to access it from the method and modify on the fly.
Basically I would like to do something like this in commonCode:
if (something) {
attachPostAction('always', { ... })
} else {
attachPostAction('failure', { ... })
}
The only thing that works so far is that in commonCode I do:
try {
...
onSuccess()
} catch (e) {
onError()
} finally {
onAlways()
}
But was wondering if there is a more elegant way...
Now that I better understand the question (I hope)...
This is a pretty interesting idea--generate your post actions on the fly in previous stages.
It turns out to be really easy. I tried one option (success) that stored various closures in a list, then iterate through the list and run all the closures in the post action. Then I did another (failure) where I just saved a single closure as a variable and ran that. Both work well.
Below is the code that does this. Uncomment the error line to simluate a failed build.
def postSuccess = []
def postFailure
pipeline {
agent any
stages {
stage('Success'){
steps {
script {
println "Configure Success Post Steps"
postSuccess[0] = {echo "This is a successful build"}
postSuccess[1] = {
echo "Running multiple steps"
sh "ls -latr"
}
}
}
}
stage('Failure'){
steps {
script {
println "Configure Failure Post Steps"
postFailure = {
echo "This build failed"
echo "Running multiple steps for failure"
sh """
whoami
pwd
"""
}
}
// error "Simulate a failed build" //uncomment this line to make the build fail
}
}
} // stages
post {
success {
echo "SUCCESS"
script {
for (def my_closure in postSuccess) {
my_closure()
}
}
}
failure {
echo "FAILURE!"
script {
postFailure()
}
}
}
} // pipeline
You can use regular groovy scripting outside of the pipeline block. While I haven't tried it, you should be able to define a method outside of there and then call it from inside the pipeline. But method calls can't be called as steps. You would need to wrap it in a script step. But post actions take the same steps as steps{} blocks, so if you can use it insteps, you can use it in the post sections. You will need to watch scoping carefully or you will end up trying to sort out why things are null in some places.
You can also used a shared library. You could define a step in the shared library and then use it like any other step in a steps{} block or one of the post blocks.

How to catch manual UI cancel of job in Jenkinsfile

I've tried to find documentation about how in a Jenkinsfile pipeline catching the error that occurs when a user cancels a job in jenkins web UI.
I haven't got the postor try/catch/finally approaches to work, they only work when something fails within the build.
This causes resources not to be free'd up when someone cancels a job.
What I have today, is a script within a declarative pipeline, like so:
pipeline {
stage("test") {
steps {
parallell (
unit: {
node("main-builder") {
script {
try { sh "<build stuff>" } catch (ex) { report } finally { cleanup }
}
}
}
)
}
}
}
So, everything within catch(ex) and finally blocks is ignored when a job is manually cancelled from the UI.
Non-declarative approach:
When you abort pipeline script build, exception of type org.jenkinsci.plugins.workflow.steps.FlowInterruptedException is thrown. Release resources in catch block and re-throw the exception.
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
def releaseResources() {
echo "Releasing resources"
sleep 10
}
node {
try {
echo "Doing steps..."
} catch (FlowInterruptedException interruptEx) {
releaseResources()
throw interruptEx
}
}
Declarative approach (UPDATED 11/2019):
According to Jenkins Declarative Pipeline docs, under post section:
cleanup
Run the steps in this post condition after every other post condition has been evaluated, regardless of the Pipeline or stage’s status.
So that should be good place to free resources, no matter whether the pipeline was aborted or not.
def releaseResources() {
echo "Releasing resources"
sleep 10
}
pipeline {
agent none
stages {
stage("test") {
steps {
parallel (
unit: {
node("main-builder") {
script {
echo "Doing steps..."
sleep 20
}
}
}
)
}
post {
cleanup {
releaseResources()
}
}
}
}
}
You can add a post trigger "cleanup" to the stage:
post {
cleanup {
script { ... }
sh "remove lock"
}
}

How can I catch any pipeline error in Jenkins?

I have a Jenkins pipeline script that for the most part works fine and I surround most things that will fire a fatal error with try catches. However from time to time really unexpected things happen and I'd like to be able to have a safe catch-all available to do some final reporting before failing the build.
Is there no final default 'stage' I can define that runs whenever an error isn't caught?
Although already been answered for a scripted pipeline I would like to point out that for a declarative pipeline this is done with a post section:
pipeline {
agent any
stages {
stage('No-op') {
steps {
sh 'ls'
}
}
}
post {
always {
echo 'One way or another, I have finished'
deleteDir() /* clean up our workspace */
}
success {
echo 'I succeeeded!'
}
unstable {
echo 'I am unstable :/'
}
failure {
echo 'I failed :('
}
changed {
echo 'Things were different before...'
}
}
}
Each stage can also have it's own section when required.
You can do it by wrapping all your build stages in a big try/catch/finally {} block, for example:
node('yournode') {
try {
stage('stage1') {
// build steps here...
}
stage('stage2') {
// ....
}
} catch (e) {
// error handling, if needed
// throw the exception to jenkins
throw e
} finally {
// some common final reporting in all cases (success or failure)
}
}

Resources