jenkinsgroovyjenkins-shared-libraries

Feeding parameters to Jenkins shared library


I have a Jenkins shared library that I use for my Jenkinsfile. My library has an entire pipeline and it has a function (lets call it examFun()) that is used within my pipeline.

My Jenkinsfile:

@Library('some-shared-lib') _

jenkinsPipeline{
    exampleArg = "yes"
}

My shared library file (called jenkinsPipeline.groovy):

def examFun(){
  return [
     new randClass(name: 'Creative name no 1', place: 'London')
     new randClass(name: 'Creating name no 2', place: 'Berlin')
  ]
}

def call(body) {
    def params= [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = params
    body()

    pipeline {
        // My entire pipeline is here
        // Demo stage
        stage("Something"){
          steps{
            script{
              projectName = params.exampleArg
            }
          }
        }

    }
}

class randClass {
    String name
    String place
}

It is working fine, but the values of examFun() are hardcoded and may need to be changed in the future. As such, I would like to be able to change them from my Jenkinsfile (just like I can change exampleArg). Following this article, I tried the following:

I changed my examFun() to

def examFun(String[] val){
  return [
     new randClass(name: val[0], place: 'London')
     new randClass(name: val[1], place: 'Berlin')
  ]
}

And call it from my Jenkinsfile as follows

@Library('some-shared-lib') _

jenkinsPipeline.examFun(['Name 1','Name 2'])
jenkinsPipeline{
    exampleArg = "yes"
}

But that did not work (probably because my library is structured via def call(body){}). After, I tried to split jenkinsPipeline.groovy into 2 files: first file having my pipeline part, and second file having examFun() with randClass in it. I modified my Jenkinsfile to be as follows:

@Library('some-shared-lib') _
def config = new projectConfig() // New groovy filed is called 'projectConfig.groovy'
config.examFun(["Name 1","Name 2"])

jenkinsPipeline{
    exampleArg = "yes"
}

But again did not succeed. The error always says

No such DSL method 'examFun()' found among steps

Update - New error

Thanks to zett42 , I have successfully resolved this error. However, I run into another error mentioned below:

java.lang.NullPointerException: Cannot invoke method getAt() on null object

To solve it, in my case, I needed to assign examFun() to a variable in my Jenkinsfile and pass it as a parameter in jenkinsPipeline{}. The functioning code is as follows: Jenkinsfile:

@Library('some-shared-lib') _

def list = jenkinsPipeline.examFun(['Name 1','Name 2'])
def names = jenkinsPipeline.someFoo(list)
jenkinsPipeline{
    exampleArg = "yes"
    choiceArg = names
}

jenkinsPipeline.groovy:

def examFun(List<String> val){
  return [
     new randClass(name: val[0], place: 'London')
     new randClass(name: val[1], place: 'Berlin')
  ]
}
def call(body) {
    def params= [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = params
    body()

    pipeline {
        // My entire pipeline is here
        parameters{
          choice(name: "Some name", choices: params.choiceArg, description:"Something")
        }
        // Demo stage
        stage("Something"){
          steps{
            script{
              projectName = params.exampleArg
            }
          }
        }

    }
}

def someFoo(list) {
    def result = []
    list.each{
        result.add(it.name)
    }
    return result
}

class randClass {
    String name
    String place
}

Solution

  • When you get an error "no such DSL method..." it often just means that the types of parameters and actually passed arguments are incompatible, which is the case here.

    The function examFun() expects an array, but you are actually passing an ArrayList. As explained in this answer, low level arrays are not really idiomatic in Groovy.

    Try this instead:

    def examFun(List<String> val){
      return [
         new randClass(name: val[0], place: 'London')
         new randClass(name: val[1], place: 'Berlin')
      ]
    }
    

    List is the more general interface, which ArrayList implements.

    Now, your first jenkinsfile example should work:

    jenkinsPipeline.examFun(['Name 1','Name 2'])
    

    You actually can have additional named methods, even when you already have a call method. I'm using this all the time.