Jenkinsfile declarative syntax for conditional post-build step - jenkins

I have a Jenkinsfile for a multibranch pipeline like this:
pipeline {
agent any
stages {
// ...
}
post {
failure {
mail to: 'team#example.com',
subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
body: "Something is wrong with ${env.BUILD_URL}"
}
}
}
I want to only send email for failures on the master branch. Is there a way to make the mail step conditional? Based on the documentation a when directive may only be used inside a stage.
https://jenkins.io/doc/book/pipeline/syntax/#when
https://jenkins.io/doc/pipeline/tour/post/

like you've noted when only works inside a stage. And only valid steps can be used inside the post conditions.
You can still use scripted syntax inside of a script block, and script blocks are a valid step. So you should be able to use if inside a script block to get the desired behavior.
...
post {
failure {
script {
if (env.BRANCH_NAME == 'master') {
... # your code here
}
}
}
}
}
see JENKINS-52689

Related

How to use conditional post action in Jenkins pipeline?

I am using Jenkins declarative pipeline and want to perform some post build actions depending on the build status.
To be more precise, I want these conditions to be true:
beforeAgent true &&
jobName == 'Cypress Test'
Here's my code:
post {
always {
script {
passwordIDs.each{ pw ->
credentialFetch.deleteTemporaryCredential(env.BUILD, pw, expireTime)
}
}
}
}
Any idea where can I use my conditions? Also, how to use them since Post doesn't support when condition
You can use normal if condition within the script block in the post conditions as you would do with a normal stage. For example, I have used this in one of my jobs:
post {
failure {
script {
def response = httpRequest '${env.BUILD_URL}/consoleText'
if (response.content.contains("Automatic merge failed; fix conflicts")){
env.BUILD_FAILURE_MESSAGE = "Checkout Failing! Make sure that there are no merge conflicts...
} else {
env.BUILD_FAILURE_MESSAGE = "Checkout Failing! Check the build log and re-run the build if the issue seems unrelated to your commit...
}
}
}
}
As you can see, it is a normal if condition that checks if the string contains text. You should be able to use your conditions in a similar manner:
if (beforeAgent && jobName == 'Cypress Test')

jenkins question - Adding post section to script based & difference between methods

I have 2 questions regarding jenkins
1. What is the difference between the 2 methods?
Method 1
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
Method 2
node ("jenkins-nodes") {
stage("git clone"){
echo 'Hello World' }
}
As I understand in the first method I can add Post section that will be running regardless of the result of the job. I wish to add the same post section for the second method, but it is not working. Any ideas?
What is the difference between the 2 methods?
As Noam Helmer wrote, pipeline{} is declarative syntax and and node{} is scripted syntax.
In general I recommend to always use pipeline{} as it makes common tasks easier to write and visualization using Blue Ocean plugin works best with declarative pipeline.
When declarative pipeline becomes too unflexible, you can insert scripted blocks using the script{} step in a declarative pipeline:
pipeline {
agent any
stages {
stage('Example') {
steps {
script {
echo 'Hello World'
}
}
}
}
}
A cleaner approach is to define a function, which is scripted by definition and use that as a custom step in declarative pipeline. Note this works even without script{}!
pipeline {
agent any
stages {
stage('Example') {
steps {
myStep 42
}
}
}
}
void myStep( def x ) {
echo "Hello World $x" // prints "Hello World 42"
}
In complex pipelines that use lots of custom code, I usually have one function per stage. This keeps the pipeline{} clean and makes it easy to see the overall structure of the pipeline, without script{} clutter all over the place.
As I understand in the first method I can add Post section that will
be running regardless of the result of the job. I wish to add the same
post section for the second method, but it is not working. Any ideas?
post{} is only available in declarative pipeline, but not within a scripted pipeline or a scripted section of a declarative pipeline. You can use try{} catch{} finally{} instead. catch{} runs only when an error occurs, finally{} always runs. You can use both or either of catch{} and finally{}.
node ("jenkins-nodes") {
stage("git clone"){
echo 'Hello World'
try {
// some steps that may fail
}
catch( Exception e ) {
echo "An error happened: $e"
// Rethrow exception, to let the build fail.
// If you remove "throw", the error would be ignored by Jenkins.
throw
}
finally {
echo "Cleaning up some stuff"
}
}
}

How to use post build action in a dynamically generated jenkins stage

I'm using Jenkins and I want to add a post-build action to my dynamically generated stages.
I dynamically generate stages by looping over a configuration file I have by doing this:
stage('run list'){
steps {
script {
...
def stages = stagelist.collectEntries {
["${it}": generateStage(it)]
}
...
}
}
}
Where generateStage is defined as:
def generateStage(job){
return {
stage("${job}"){
catchError(){
....
}
post {
...
}
}
}
}
Which works fine for the stages I have currently. However, when I try to add a post-action to my generate stage function I get NoSuchMethod error.
If I could get some help on this it would be greatly appreciated!
When you use static parallel steps it is possible to use the post directive as part of the pipeline syntax, but when you are creating the stages dynamically (in the generateStage) you are doing so by using the scripted pipeline syntax which Does not support the post directive.
What you can do instead is use the scripted pipeline version of the post section using the try catch finally syntax to achieve a similar result to that you would achieve using the post section in a declarative pipeline.
Try something like:
def generateStage(job){
return {
stage("${job}"){
try {
// Your Execution steps
...
// This is equivalent to the 'success' section in the post directive
}
catch(Exception ex) {
// This is equivalent to the 'Failure' section in the post directive
}
finally {
// This is equivalent to the 'always' section in the post directive
}
}
}
}
It is not hundred precent equal, as the try catch will only detect exception and wont detect direct build result change - but it should work for the majority of steps

Required context class hudson.FilePath is missing Perhaps you forgot to surround the code with a step that provides this, such as: node

When i load another groovy file in Jenkinsfile it show me following error.
"Required context class hudson.FilePath is missing
Perhaps you forgot to surround the code with a step that provides this, such as: node"
I made a groovy file which contains a function and i want to call it in my Declarative Jenkinsfile. but it shows an error.
My Jenkinsfile--->
def myfun = load 'testfun.groovy'
pipeline{
agent any
environment{
REPO_PATH='/home/manish/Desktop'
APP_NAME='test'
}
stages{
stage('calling function'){
steps{
script{
myfun('${REPO_PATH}','${APP_NAME}')
}
}
}
}
}
Result--
org.jenkinsci.plugins.workflow.steps.MissingContextVariableException: Required context class hudson.FilePath is missing
Perhaps you forgot to surround the code with a step that provides this, such as: node
Suggest me what is the right way to do it.
You either need to use a scripted pipeline and put "load" instruction inside the node section (see this question) or if you are already using a declarative pipeline (which seems to be the case), you can include it in "environment" section:
environment {
REPO_PATH='/home/manish/Desktop'
APP_NAME='test'
MY_FUN = load 'testfun.groovy'
}
We have to wrap with node {}, so that jenkins executors will execute on node, Incase if we would like to execute on any specific agent node, we can mention like node('agent name'){}
example here :
node {
def myfun = load 'testfun.groovy'
pipeline{
agent any
environment{
REPO_PATH='/home/manish/Desktop'
APP_NAME='test'
}
stages{
stage('calling function'){
steps{
script{
myfun('${REPO_PATH}','${APP_NAME}')
}
}
}
}
}
}
Loading the function in an initial script block inside the pipeline worked for me. Something like below:
def myfun
pipeline {
agent any
environment {
REPO_PATH='/home/manish/Desktop'
APP_NAME='test'
}
stages {
stage('load function') {
steps {
script {
myfun = load 'testfun.groovy'
}
}
}
stage('calling function') {
steps {
script {
myfun("${REPO_PATH}","${APP_NAME}")
}
}
}
}
}
I got this error message when I was calling a sh script that does not not exist in the repository / file system. Please look in the stack trace the following line:
at WorkflowScript.run(WorkflowScript:135)
The 135 marks the line in Jenkinsfile, on which the missing script or error is happening.
Another possibility is that due to earlier/underlying errors, the context has been removed for example by multiple executor machines. This happens if you are missing the node (e.g. script) -block, but especially at the post always -block. You can use the if -check also in other places. After this you get another error, that was causing this error message.
post {
always {
script {
//skip the step if context is missing
if (getContext(hudson.FilePath)) {
echo "It works"
}
}
}
}
See https://docs.cloudbees.com/docs/cloudbees-ci-kb/latest/troubleshooting-guides/how-to-troubleshoot-hudson-filepath-is-missing-in-pipeline-run

Trigger an action within Jenkins declarative pipeline right after a stage ends or just before a stage begins?

I have the following Jenkinsfile:
pipeline {
agent any
environment { }
stages {
stage('stageA') {
steps {
... Do something with arg1, arg2 or arg3
}
}
stage('stageB') {
steps {
... Do something with arg1, arg2 or arg3
}
}
...
}
}
Is there anywhere I can specify a universal "pre-stage" or "post-stage" set of actions to perform? A use-case would be sending logging information at the end of a stage to a log manager, but it would be preferable to not copy and paste those invocations at the end of each and every stage.
As far as I know there is no generic post- or pre-stage hook in Jenkins pipelines. You can define post steps in a post section but you need one per stage.
However, if you don't want to repeat yourself, you have some options.
Use a shared lib
The place to put repeating code to it a shared library. That way allows you to declare your own steps using Groovy.
You need another repository to define a shared lib, but apart from that it is a pretty strait forward way and you can reuse the code in all of your Jenkins' pipelines.
Use a function
If you declare a function outside of the pipeline, you can call it from any stage. This is not really documented and might be prevented in the future. As far as I understand it messes with the coordination between master and agents. However, it works:
pipeline {
agent any
stages {
stage ("First") {
steps {
writeFile file: "resultFirst.txt", text: "all good"
}
post {
always {
cleanup "first"
}
}
}
stage ("Second") {
steps {
writeFile file: "resultSecond.txt", text: "all good as well"
}
post {
always {
cleanup "second"
}
}
}
post {
always {
cleanup "global" // this is only triggered after all stages, not after every
}
}
}
}
void cleanup(String stage) {
echo "cleanup ${stage}"
archiveArtifacts artifacts: "result*"
}

Resources