No such DSL method 'getVar' while using shared libraries

Viewed 479

I have created a shared library to hold my declarative pipeline and then calling it in Jenkinsfile. In Jenkins file there are definitions for for methods that are invoked in the pipeline scripts in the shared library. I don't understand why this error occur. What is a solution for this?

Jenkinsfile:

@Library('jenkinsLib')_

def getVar(){
   echo "${env.myVar}"
}

mymaster()

Under var in jenkinsLib shared library a script mymaster.groovy:

def call(){

  pipeline {
      agent any
      stages {
        stage('one') {
          steps {
            echo "This is Pipeline one"
            getVar()
          }
        }
      }
    }

}

It can work when the functions are defined with the pipeline definition which opposes DRY and make pipelines look big and repeated. Below shows this case:

def call(){

  pipeline {
      agent any
      stages {
        stage('one') {
          steps {
            echo "This is Pipeline one"
            getVar()
          }
        }
      }
    }

} 

def getVar(){
   echo "${env.myVar}"
}
2 Answers

I don't know if it's optimal solution, I'm a Jenkins Pipeline and Groovy newbie, but this worked for me.

Here's Jenkinsfile:

@Library('jenkinsLib') _

def getVar() {
  echo "${env.myVar}"
}

mymaster(this.&getVar)

And here's mymaster.groovy:

def call(Closure getVar) {
  pipeline {
    agent any
    stages {
      stage('one') {
        steps {
          echo "This is Pipeline one"
          script {
            getVar()
          }
        }
      }
    }
  }
}

Summary: you may pass method (closure) as a parameter to make it visible inside of call() method. To get closure you need to use .& operator. Also, Jenkins complains about calling methods outside of script clause, so you may need to add one.

As a side note I'd like to mention that such approach is very clear (explicit) - inside of library you are using only what you know and code doesn't have external dependencies.

If you want to pass many methods as arguments you may consider using Map to catch many arguments and to allow more explicit calling.

Modified Jenkinsfile with more methods and new call style:

@Library('jenkinsLib') _

def getVar(){
  echo "${env.myVar}"
}

def getVar2(){
  echo "${env.myVar2}"
}

mymaster([
  getVar: this.&getVar,
  getVar2: this.&getVar2
])

And mymaster.groovy using Map in place of fixed arguments:

def call(Map args) {
  pipeline {
    agent any
    stages {
      stage('one') {
        steps {
          echo "This is Pipeline one"
          script {
            args.getVar()
            args.getVar2()
          }
        }
      }
    }
  }
}

Unfortunately, there's a drawback in this solution - you don't have as much control over passed arguments, you need to check manually what is in args map (or don't make mistakes in arguments while calling).

Any Groovy code must be used within a script section. Only Jenkins' methods can be used without script (i.e. just with steps).

In your case,

def getVar(){
   echo "${env.myVar}"
}

is a Groovy function, so you need to use it inside a script section.

Related