Jenkins pipeline skip stage if copying fails

Viewed 840

Let's say we have a simple pipeline setup like this:

pipeline {
   stages {
      stage('Stage1') {
         sh '''
           echo 'Copying files'
           cp ./file1 ./directory1
         '''
      }
      stage('Stage2') {
         sh '''
           echo 'This stage should still work and run'
           cp ./directory2/files ./directory2/subdirectory
         '''
      }
      stage('Stage3') { ... }
      ...
   }
}

Whenever I don't have the files in Stage1 or Stage2, it fails the build saying:

'cp cannot stat ./file1 ./directory1' or 'cp cannot stat ./directory2/files ./directory2/subdirectory'

Of course if the files exist, both stages work perfectly fine. The problem is that the build fails for the rest of the stages if a stage fails. So if Stage1 fails because there are no files, it fails every stage after and they don't even run, same goes for if Stage2 fails, then we know that Stage1 succeeded but then Stage3 and onwards fails and does not even run.

Is there a way to make it so that if the cp command fails and the cp cannot stat shows, to just skip the stage and proceed to the next one? Or at least make it so that only that stage fails and it can proceed to build the next stage(s)?

3 Answers

Here is an simple way of skipping the stage when a file does not exist, using the when directive:

pipeline {
   agent any   
   stages {
      stage('Stage1') {
         when { expression { fileExists './file1' } }
         steps {
            sh '''
            echo 'Copying files'
            cp ./file1 ./directory1
            '''
         }
      }
      stage('Stage2') {
         when { expression { fileExists './directory2/files' } }
         steps {
            sh '''
            echo 'This stage should still work and run'
            cp ./directory2/files ./directory2/subdirectory
            '''
         }
      }
      stage('Stage3') {
         steps {
            echo "stage 3"
         }
      }
   }
}

In above case you have to specify the path twice, in the when directive and in the sh step, it is better to handle it in a another way e.g. using variables or closures. Because of the restrictions in the declarative pipeline, I would recommend you to use the scripted pipeline instead.

This can be achieved using the catchError

pipeline {
    agent any
    stages {
       
        stage('1') {
            steps {
                script{
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    echo 'Copying files'
                    cp ./file1 ./directory1
                }
              }
            }
        }
        stage('2') {
            steps {
                script{
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    echo 'This stage should still work and run'
                    cp ./directory2/files ./directory2/subdirectory
                }
              }
            }
        }

        stage('3') {
            steps {
                sh 'exit 0'
            }
        }
    }
}

From above pipeline script, all stages will executed. If the cp command will not work for either of stage 1 or stage 2, it will show as failed for that particular stage but rest all stages will execute.

Similar to below screenshot:

enter image description here


Modified Answer

Following pipeline script include sh ''' ''', which need not have to be present inside the catchError block.

You can include only those commands inside catchError for which you want to catch the errors.


pipeline {
    agent any
    stages {

        stage('1') {
            steps {
                sh """
                
                echo 'Hello World!!!!!!!!'
                curl https://www.google.com/
                
                """ 

                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    echo 'Copying files'
                    cp ./file1 ./directory1
                }
            }
        }
        stage('2') {
            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    echo 'This stage should still work and run'
                    cp ./directory2/files ./directory2/subdirectory
                }
            }
        }

        stage('3') {
            steps {
                sh 'exit 0'
            }
        }
    }
}

You could just check if the file exists before you try to copy it using a conditional like this:

[ -f ./directory2/files ] && cp ./directory2/files ./directory2/subdirectory || echo "File does not exist"

Source and more info

Related