Occasionally when working with Jenkinsfiles, you’ll run into a NoSuchMethodError
because of calling a function that Jenkins’s doesn’t know about:
java.lang.NoSuchMethodError: **No such DSL method 'someFunc'** found among [...]
There are number of reasons that this can happen, such as a required plugin not being installed, or the shared library that defines the function isn’t loaded.
Lately I have been working a lot with shared libraries and building up functionality that is designed for multiple people to use, without necessarily knowing the technical details of each product. I occasionally want to check if a function is available before running some code to avoid the NoSuchMethodError
, but I don’t want to block everything if it isn’t available. This allows making libraries more robust by not failing when an optional dependency is missing.
So, how do we check if a DSL method exists before calling it?
Based on the stacktrace that came with the error, I ended up poking around the source code for the CpsScript class to see how it detected this problem. It turns out that in a pipeline script we can use the this
object to get the bindings available on the script itself, and check whether a given method exists. This is all you need:
this.getBinding().hasVariable('foobar')
That expression will evaluate to true
if foobar
exists, and false
if it doesn’t.
For convenience, if we’re doing this multiple times, we can write a simple function in the pipeline itself:
boolean dslMethodExists(String name) {
return this.getBinding().hasVariable(name)
}
if (dslMethodExists('foobar')) {
foobar()
}
If you wanted to take advantage of this across multiple Jenkinsfiles, the function can go into a shared library function. However, in that context this
will no longer point to the pipeline script being executed. We can still do the check by passing a reference to the current script directly:
// vars/dslMethodExists.groovy in a shared library
import org.jenkinsci.plugins.workflow.cps.CpsScript
boolean call(CpsScript script, String name) {
return script.getBinding().hasVariable(name)
}
I’m importing the CpsScript
class just so I can declare its type in the function signature; it’s good practice to avoid def
, and it helps future developers know what class they’re working with explicitly.
Then, as long as that shared library is loaded, you can call that function in your pipelines, or even in other shared library functions:
if (dslMethodExists('foobar')) {
foobar()
}
And that’s all there is to it. I don’t guarantee that this is the best way to check for a DSL method—as I said I went poking around in the source code to find this—but it works. I googled this enough times, without finding an answer, that when I finally figured it out I thought I should put it up here in the hopes it’ll help someone else.
Top comments (0)