Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jenkins Shared Library instantiate util class and use in workflowscript

I'm creating my own Jenkins shared library to reuse some code between pipelines. I know how to create steps (really easy indeed) but I have a problem when I want to make accessible my "utils class" in the jenkinsfile (or WorkflowScript as Jenkins say in logs). I call "Util class" a groovy class located in folder src/my/domain/... that have many methods, for example, i want to create a log class like this:

# src/my/domain/group

package my.domain.group

class Console implements Serializable {

    def log(msg) {
        spit(msg, '\033[34m')
    }

    def error(msg) {
        spit(msg, '\033[31m')
    }

    def warning(msg){
        spit(msg, '\033[33m')
    }

    def success(msg){
        spit(msg, '\033[32m')
    }

    private def spit(msg, color){
        ansiColor('xterm') {
            echo color + msg + '\033[0m'
        }
    }

}

And then I try to instantiate in a file in /vars/library.groovy so can be accessed from the Jenkinsfile with some code as for example:

node("mynode"){
   console.log("Hello World!")
} 

The idea is to avoid to instantiate the console in all the script and make it automatically, I have tried several things like

# vars/library.groovy

import my.domain.group.Console
import groovy.transform.Field

@Field String console = new Console() 

or

# vars/library.groovy

import my.domain.group.Console

def load(){
    this.console = new Console() 
    // And also
    console = new Console()
}

But when trying to access from jenkins file I always get the error: groovy.lang.MissingPropertyException: No such property: console for class: WorkflowScript

I understand that this means that the instance is not at the right scope so cannot be directly seen by the script but how can I do that?

Thanks!

like image 545
joliva Avatar asked Jul 06 '17 08:07

joliva


1 Answers

Looks like you are using your vars script incorrectly inside your Jenkinsfile, but I will try to solve some confusions :)

  1. Groovy scripts defined in the vars directory are called by their file name as "custom" step in your Jenkinsfile. Assuming your script name stays library.groovy, your Jenkinsfile should look like this

    node ("mynode") {
         library("Hello World!")
    } 
    

    or alternatively (since the parentheses are optional)

    node ("mynode") {
         library "Hello World!"
    }
    
  2. Additionally vars scripts should only contain a single function named call and necessary imports. Since you want to pass your log message as a string, the call function additionally needs a parameter. So your library.groovy script in vars could look like this

    # vars/library.groovy
    
    import my.domain.group.Console
    
    def call(msg){
        Console console = new Console(steps) 
        console.log(msg)
    }
    

    Inside the call function you can use all basic Jenkins steps (sh, bat, etc.) as you could in your Jenkinsfile, plus any Groovy/Java classes imported via import (like your Console class).

    As you may have noticed, I also added a steps variable (which does not have to declared or instantiated manually) to the constructor of your Console class. This is necessary since you cant use any Jenkins steps directly in any of your classes defined in the src directory. Resulting, the method

    private def spit(msg, color){           
        ansiColor('xterm') {
            echo color + msg + '\033[0m'
        }
    }
    

    would not work, because echo is a Jenkins step. In order to use echo inside your class, you can pass the steps variable to the constructor and store it in another variable inside your class. Then you can call all Jenkins steps like steps.echo "Hello World"

    # src/my/domain/group
    
    package my.domain.group
    
    class Console implements Serializable {
    
         def steps
    
         Console(steps) {
             this.steps = steps
         }
    
         def log(msg) {
             spit(msg, '\033[34m')
         }
    
         def error(msg) {
             spit(msg, '\033[31m')
         }
    
         def warning(msg){
             spit(msg, '\033[33m')
         }
    
         def success(msg){
             spit(msg, '\033[32m')
         }
    
         private def spit(msg, color){
             ansiColor('xterm') {
                 steps.echo color + msg + '\033[0m'
             }
         }
    }
    

That should make it work.

You should think about renaming your library.groovy inside vars though (for example myLog.groovy). Then your Jenkinsfile could look like this

node ("mynode") {
    myLog "Hello World!"
}

In order to use the other methods of your Console class, you can create additional scripts inside vars. Using them could look like this

node ("mynode") {
    myLog "Hello World!"
    myWarning "Hello World!"
    myError "Hello World!"
    mySuccess "Hello World!"
}

(im using the my prefix to avoid name collisions with already existing Jenkins steps. For example, the error step already exists and calling it in your Jenkinsfile will cancel your build)

For more info about shared libraries check out the Jenkins documentation at https://jenkins.io/doc/book/pipeline/shared-libraries/

You can find a complete list of all basic Jenkins steps at https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/

Getting Jenkins and pipelines to work can be a huge hassle, but in the end it's worth it. I hope this wall of text helps a bit. Good luck and happy coding :)

like image 188
Adrian Kuper Avatar answered Sep 18 '22 15:09

Adrian Kuper