jenkinsgroovyjenkins-pipelinejenkins-groovy

Switch in Jenkins pipeline returns null, same switch in normal Groovy returns expected output


I have this switch in my code (context: Jenkins pipeline library):

def installOptions(filePath) {
    switch (filePath) {
        case ~/.*\.pom/:
            "-Dpackaging=pom -DpomFile=$filePath"
            break
        case ~/.*\.jar/:
            '-Dpackaging=jar'
            break
        case ~/.*-exe-archive.zip/:
            filePath = filePath.replace '-exe-archive.zip', '.pom'
            "-Dpackaging=zip -DpomFile=$filePath"
            break
        default:
            ''
            break
    }
}

Value of filePath: 'temp_downloads/merge/QA-9344/itextcore/java/main.pom'

Expected value:

-Dpackaging=pom -DpomFile=temp_downloads/merge/QA-9344/itextcore/java/main.pom

Actual value:

null

When I use exactly the same switch in "normal" Groovy, I do get the expected output:

import org.codehaus.groovy.runtime.InvokerHelper

class InstallJavaBranchArtifacts extends Script {

    static void main(String[] args) {
        InvokerHelper.runScript(InstallJavaBranchArtifacts, args)
    }

    def run() {
        println installOptions('temp_downloads/merge/QA-9344/itextcore/java/main.pom')
        println installOptions('temp_downloads/merge/QA-9344/itextcore/java/barcodes.pom')
        println installOptions('temp_downloads/merge/QA-9344/itextcore/java/itext7-barcodes-7.1.12-SNAPSHOT.jar')
    }

    def installOptions(filePath) {
        switch (filePath) {
            case ~/.*\.pom/:
                "-Dpackaging=pom -DpomFile=$filePath"
                break
            case ~/.*\.jar/:
                '-Dpackaging=jar'
                break
            case ~/.*-exe-archive.zip/:
                filePath = filePath.replace '-exe-archive.zip', '.pom'
                "-Dpackaging=zip -DpomFile=$filePath"
                break
            default:
                ''
                break
        }
    }
}

then this is my output:

/usr/lib/jvm/java-1.11.0-openjdk-amd64/bin/java ...
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/usr/share/java/groovy-2.4.17.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
-Dpackaging=pom -DpomFile=temp_downloads/merge/QA-9344/itextcore/java/main.pom
-Dpackaging=pom -DpomFile=temp_downloads/merge/QA-9344/itextcore/java/barcodes.pom
-Dpackaging=jar

Process finished with exit code 0

Which is (apart from the illegal reflective access mumbo jumbo) the expected output.

So what do I need to do, to make a switch behave in the same way in a Jenkins pipeline library as in "normal" Groovy?


Solution

  • Jenkins pipeline runs your Groovy code using Groovy CPS interpreter, which deconstructs your code into parts and evaluates them in the continuous-passing style. In the case of your example, your switch statement misses the explicit return, and that is why evaluating a body of your function returns null.

    There are two options to fix it.

    1. You can add @NonCPS annotation to installOptions function implementation, and this way you will run the body of this function using regular Groovy Shell interpreter. This may work for you, because in the body of this function you are not calling any Jenkins Pipeline workflow steps, but only plain Groovy code.

          @NonCPS
          def installOptions(filePath) {
               switch (filePath) {
                   case ~/.*\.pom/:
                       "-Dpackaging=pom -DpomFile=$filePath"
                       break
                   case ~/.*\.jar/:
                       '-Dpackaging=jar'
                       break
                   case ~/.*-exe-archive.zip/:
                       filePath = filePath.replace '-exe-archive.zip', '.pom'
                       "-Dpackaging=zip -DpomFile=$filePath"
                       break
                   default:
                       ''
                       break
               }
           }
      
    2. If you want to keep the execution of this code in the Groovy CPS mode, remove break from each case and add explicit return before every string you return from the switch case.

          def installOptions(filePath) {
               switch (filePath) {
                   case ~/.*\.pom/:
                       return "-Dpackaging=pom -DpomFile=$filePath"                        
                   case ~/.*\.jar/:
                       return '-Dpackaging=jar'
                   case ~/.*-exe-archive.zip/:
                       filePath = filePath.replace '-exe-archive.zip', '.pom'
                       return "-Dpackaging=zip -DpomFile=$filePath"
                   default:
                       return ''
               }
           }
      

    If you want to read more about Groovy CPS, go and check the docs here https://github.com/cloudbees/groovy-cps
    https://github.com/jenkinsci/workflow-cps-plugin