Calling a declaratively defined closure from Jenkins Shared Library

Hi Everyone!
I have been working on creating a jenkins shared library, and it works perfectly for a generalized pipeline template. Recently I had the need to pass an extra user-defined stage to my generalized pipeline. I am using closure to do so, but facing some issues with it.
I would like for the closure to return a declarative stage which has its own agent, steps and other components as shown in the code samples below (refer the following images) :

Closure defined in .jenkinsfile | Call to closure from jenkins shared library | Error Message :

Please let me know how can I achieve the above functionality.

I am not that familiar with declarative stuff, but it is mostly because it doesn’t (or at least didn’t in the past) support this sort of a thing due to how declarative works (it needs to know the structure of the entire pipeline before executing, which is hard if your stage is being dynamically generated)

My suggestion is if you want this sort of functionality, just use scripted pipelines, esp in combination with Pipeline Libraries

1 Like

Noted, thanks for a quick response!

Hey I have multiple projects running with declarative pipeline, to change the code to scripted, I’ll have to redo all those project. Do you think there’s any other solution available to achieve this?

shortcuts tend to cause problems later, as witnessed by this.

script {} is a block of scripted pipeline. You don’t need your entire pipeline to be scripted, just the stuff you put into script {} tag. There’s no way to have declarative code inside script {} tag.

So that means I should not wrap the call to closure in script tag, right?

I am a bit confused, but any valid non-declarative groovy should work inside a script tag - but no declarative code. So you can call your closure and as long as the closure does not have declarative stuff in it, it should work

Looking at your example above, the point is that the declarative bits should be in your pipeline, but you can make your tests be a closure, (or better yet a method to avoid the complexity of closure context management)

so something like(I am too lazy to look up correct syntax or commands I do not remember offhand, so you may need to edit it, but close enough:

def hardwareBenchTest(){
  node('ABC'){ // allocate a node workspace
    def retcode = sh script: 'run some commands', returnStdout: true
    if (retcode) {
       echo 'do something when failed (set build to unstable?)'
    } else {
       echo 'do something when success (throw a party?)'
    echo 'This will always run'
  }  // deallocate a node workspace
  return retcode // dont need to return, but may be useful
1 Like