Import class from shared library in Jenkinsfile
Asked Answered
L

1

5

I have a shared library repo with this structure:

(root)
+- src             
|   +- com
|       +- company
|           +- DeploySteps.groovy
+- vars
|   +- MainDeploySteps.groovy

This library importing to job via Jenkinsfile as follows:

library identifier: 'jenkinslib@master', retriever: modernSCM(
     [$class       : 'GitSCMSource',
      remote       : '[email protected]:jenkinslib.git',
      credentialsId: 'jenkins-credentials'])

Class in repo in src/com/company/DeploySteps.groovy has a method (for example CheckoutSCM) which I want include in Jenkinsfile.

DeploySteps.groovy:

def CheckoutSCM() {
    useful steps here
}

Is there a possible include this particular method in Jenkinsfile like

import com.company.DeploySteps

And then use it like:

CheckoutSCM('repo-here')

In Jenkinsfile later? I read documentation many times but no found answer is there possible import something from src folder, not only from vars.

Why I am asking because now when import: import com.company.DeployUtils and then try invoke method CheckoutSCM() see the error in Jenkins console output:

java.lang.NoSuchMethodError: No such DSL method 'CheckoutSCM' found among steps

with list of available methods below, where no mine CheckoutSCM for sure)

So, is there possible import class from src folder to Jenkinsfile?

P.S. I can access in Jenkinsfile MainDeploySteps as

MainDeploySteps {}

with no problems however.

Locris answered 26/3, 2020 at 6:58 Comment(3)
I barely remember to use library just as you have written. If you there is some time, you can try to ask some colleague to write some hello world library from scratch and check if it would work (author does not see ist own mistakes). Perhaps you can have a look at this repo for any inspiration github.com/wcm-io-devops/jenkins-pipeline-libraryUnderdeveloped
While you can do this, it is awkward, and the reason it is being difficult for you is because this is not normal usage/architecture. Your pipeline typically interfaces with global var methods, which then interface with your library's classes. The method you have listed above seems like a much better fit for a global var method (as it is not a member, nor is it pure Groovy and/or Java), where it would be easier to invoke within your Jenkinsfile.Kish
@MattSchuchard am I understand you correctly that shared library expose ONLY global vars from 'vars' directory, to include that vars to Jenkinsfile? While trying include anything else to Jenkinsfile directly from 'src' folder is not how it was designed, because 'src' classes intended to be include to vars from 'vars' directory.Locris
P
7

I just wanted to add an answer to address a couple of questions from the OP and the subsequent comments.

am I understand you correctly that shared library expose ONLY global vars from 'vars' directory, to include that vars to Jenkinsfile? While trying include anything else to Jenkinsfile directly from 'src' folder is not how it was designed, because 'src' classes intended to be include to vars from 'vars' directory.

First let me give some background on shared libraries. There are 2 types referred to as dynamic shared library (defined using library step) and global shared library (defined in Jenkins global config and accessed using @Library annotation). Two important differences between these two are:

  • Sandboxing restrictions do not apply to code in global shared library as the code in such libraries is treated as trusted (after all, it is configured globally by the administrator). This is not the case for dynamic shared libraries, as these are defined by users and so are not trustable.
  • Classes from global shared libraries can be statically referenced in Jenkinsfile and get resolved as part of the compilation of Jenkinsfile, but this is not possible for dynamic shared libraries as these libraries get defined at runtime and classes can't be cross checked at the time of compilation.

Coming to the OP, it is using the dynamic shared library concept, which means, classes can't be referenced statically. However there are some features put into dynamic shared library that can be taken advantage of. These details can be found in the documentation, but here is a summary:

  • library step returns a representation of the library that can be assigned to a variable for further use, something like def myLib = library identifier: ...
  • Classes can be referenced using their fully qualified name on the library object, something like def myCls = myLib.com.mycom.mypkg.MyClass
  • What you receive is not the actual Groovy class, but a proxy like object that only permits limited operations on the underlying class.
  • You can instantiate a new instance using new function and it works very similar to the new operator. You pass the same arguments that you would pass to a constructor, e.g., myCls.new(arg1, arg2)
  • You can get a static variable (though you can't set it) or call a static method using the proxy object just like it is the real class. If you are instantiating an object using new anyway, then you can access static members through the instance as well (normal Groovy semantics apply).

In both types of libraries, var files are exposed using the same mechanism (referred to also as global vars and custom steps), so there is no difference in usage. Also, the var file executes in the context of the library so with in the var file, you do not need the special syntax to access the classes, whether they are in global or dynamic shared libraries.

Now to answer the real question in the OP, it is possible to call CheckoutSCM from the dynamic shared library. If it is defined as a static function, it can be invoked using myLib.com.company.DeploySteps.CheckoutSCM('repo-here') and if it is defined as a non-static method, it can be invoked using myLib.com.company.DeploySteps.new(...).CheckoutSCM('repo-here'). However in both cases the DeploySteps class will not be able to access the steps API so even a simple echo will not work. One traditional workaround is to provide the this instance of Jenkinsfile as a parameter (e.g., CheckoutSCM(this, 'repo-here')) which then gets assigned to a steps parameter (can be named anything) inside the function. You would then invoke all step calls on the steps parameter, e.g., steps.echo '...'.

Patience answered 1/6, 2021 at 7:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.