Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jenkins shared library, CpsCallableInvocation

I am trying to create a shared library for Jenkins.

I tried to create a script to send notifications through Teams, this is the code:

def call(String buildStatus = null, String webook) {
    def status
    def color

    switch(buildStatus) {
        case { null || "SUCCESS" }:
            status = "Success"
            color = "Green"
            break
        case "FAILED":
            status = "Failed"
            color = "Red"
            break
        default:
            status = null
            color = null
    }

    println(status + " " + color)
}

The problem is that if I try to run it in my pipeline I get this exception:

Error when executing always post condition:
CpsCallableInvocation{methodName=call, call=com.cloudbees.groovy.cps.impl.CpsClosureDef@1ba818f1, receiver=org.jenkinsci.plugins.workflow.cps.CpsClosure2@22438c5c, arguments=[SUCCESS]}

This is the pipeline I am testing:

pipeline{
    agent{
        label 'linux'
    } 

    stages{
        stage('Build') {   
            steps{    
                ...snip...
            }
        }

    }
    post {
        always {
            notifyTeams(currentBuild.result, "XXXXX")
        }
    }    

}

What am I doing wrong? Thanks!

like image 523
Marco Cotrufo Avatar asked Jan 30 '26 22:01

Marco Cotrufo


1 Answers

The problem is caused by the closure used in the switch statement. Jenkins transforms every closure into CPS-transformed closures, which are different than the regular Groovy closures. There are multiple use cases where using CPS-transformed closures causes unexpected errors. I guess this use case was not documented yet, and the closest one to yours is using closure inside the GString one.

There are two ways to solve it. Firstly, you can replace a closure in the switch statement with regular values, e.g.:

def call(String buildStatus = null, String webook) {
    def status
    def color

    switch(buildStatus) {
        case null:
            status = "Success"
            color = "Green"
            break
        case "SUCCESS":
            status = "Success"
            color = "Green"
            break
        case "FAILED":
            status = "Failed"
            color = "Red"
            break
        default:
            status = null
            color = null
    }

    println(status + " " + color)
}

Alternatively, if you want to use closure inside the switch statement, you will need to extract the switch block to a separate method annotated with @NonCPS. This is the instruction for the groovy-cps to not transform the code inside this method. (You could annotate the call method with @NonCPS, however, if you plan to use pipeline steps in it, it won't work. Pipeline steps have to be executed in the cps mode.)

def call(String buildStatus = null, String webook) {
    def (status, color) = switchBuildStatusAndColor(buildStatus)

    println(status + " " + color)
}

@NonCPS
private static switchBuildStatusAndColor(String buildStatus) {
    switch (buildStatus) {
        case { null || "SUCCESS" }:
            return ["Success", "Green"]

        case "FAILED":
            return ["Failed", "Red"]
    }

    return [null, null]
}
like image 195
Szymon Stepniak Avatar answered Feb 02 '26 23:02

Szymon Stepniak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!