How "vars" in Jenkins Shared Libraries work?

7k views Asked by At

I'm experiencing some behaviors in Jenkins Shared Libraries, and it'll be great if someone can explain this to me:

First issue

Let's say i have a file in the vars directory:

// MultiMethod.groovy

def Foo() { ... }

def Bar() { ... }

Now if i want to use those functions from the pipeline, what i did was:

// Jenkinsfile
@Library('LibName') _

pipeline {
  ...
  steps {
    script { 
        // Method (1): this will work
        def var = new MultiMethod()
        var.Foo()
        var.Bar()

        // Method (2) this will not work
          MultiMethod.Foo()
       }
  }

}

(The (1) and (2) methods are methods of calling the methods in the groovy script. Don't be confused by these 2 uses of the word "Method" please.)

So it works only if I instantiate this MultiMethod with the new operator.

But, if I name the file multiMethod (camelCased) instead of MultiMethod, i can use method (2) to call the methods in the script. Can someone explain this behavior?

That seems to be working fine.

Second issue

Based on the example above. If I have the groovy file named MultiMethod, (We saw earlier that i can use its methods if I instantiate with with new), I can't seem to instantiate an object of MultiMethod when loading the library dynamically, like this:

// Jenkinsfile

pipeline {
  ...
  steps {
    script { 
        // Method (1): this will not work
        library 'LibName'
        def var = new MultiMethod()
        var.Foo()
        var.Bar()

       }
  }

}

If i try to do so, i get this:

Running in Durability level: MAX_SURVIVABILITY
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 11: unable to resolve class multiMethod 
 @ line 11, column 32.
                       def mult = new multiMethod()
                                  ^

1 error

    at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
    at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:958)
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUni  
...

EDIT

I notice that if I do this:

// Jenkinsfile

pipeline {
  ...
  steps {
    script { 
        library 'LibName'
        MultiMethod.Foo()
        MultiMethod.Bar()
       }
  }

}

It does work !!

Last Question

Another question if you may. I noticed that people use to write

return this

In the end of their scripts in the vars directory. Can someone explain what is it good for? I'd be happy if someone could explain this in the context of how does the mechanism of this works, like why are those scripts turns into Global Variables?

Thanks

1

There are 1 answers

0
Samit Kumar Patel On

First Question Answer

It's because Jenkins has defined that standard for the shared library. To clear your doubt, there is really a good explanation in Jenkins official documentation and it will work if you do it by following the standards. like below example:

make sure you are following this folder structure

shared-library
├── src
│   └── org
│       └── any
│           └── MultiMethod.groovy
└── vars
    └── multiMethod.groovy

multiMethod.groovy

def foo() {
  echo "Hello foo from vars/multiMethod.groovy"
}
def bar() {
  echo "Hello bar from vars/multiMethod.groovy"
}

Once you have this and you are configuring your shared library like this way, then you can make use of multiMethod.groovy on your Jenkins file like below:

Jenkinsfile

@Library('jenkins-shared-library') _
pipeline {
    agent any;
    stages {
        stage('log') {
            steps {
                script {
                    multiMethod.foo()
                    multiMethod.bar()
                }
            }
        }
    }
}

why does it work this way? - it explains here

But to make use of src/org/any/MultiMethod.groovy available in the src folder, you have to instantiate the class and call the method. Below is my example

MultiMethod.groovy

package org.any

class MultiMethod {
    def steps;
    MultiMethod(steps) {
        this.steps = steps
    }
    def foo() {
        steps.echo "Hello foo from src/org/any/MultiMethod.groovy"
    }
    def bar() {
        steps.echo "Hello bar from src/org/any/MultiMethod.groovy"
    }
}

Jenkinsfile

@Library('jenkins-shared-library') _
import org.any.MultiMethod

pipeline {
    agent any;
    stages {
        stage('log') {
            steps {
                script { 
                    def x= new MultiMethod(this);
                    x.foo()
                    x.bar()
                }
                
            }
        }
    }
}

Second Question Answer

Your second question is duplicate to this post. I have tried to explain and given an example. Please take a look.

Last Question Answer

It's not necessary to return this from the Jenkins global variable defined in vars if you do

vars/returnThisTest.groovy

def helloWorld() {
    echo "Hello EveryOne"
}

or

def helloWorld() {
    echo "Hello EveryOne"
}

return this;

both are the same and from Jenkinsfile you can just call like returnThisTest.helloWorld(), but return this can be more useful when a scenario will be like this - a good example from Jenkins documentation