Jenkins+Groovy: Libraries - jenkins

Situation is simple - IN A GLOBAL LIBRARY (OUTSIDE THE SANDBOX):
in src - a.b.c.Utils.groovy
in vars - Defaults.groovy
How do I call Defaults.groovy from within Utils.groovy?
In src:
#!groovy
package a.b.c
public class Utils implements Serializable {
def script
public def run() {
println(Defaults.text)
//groovy.lang.MissingPropertyException: No such property: Defaults for class: a.b.c.Utils
}
}
in vars:
#!groovy
public class Defaults {
public static def text = "hello world"
}
in Jenkinsfile:
#Library("ItLoads")
utils = new a.b.c.Utils(script:this)
...
utils.run()
so I tried to load the library explicitly
#!groovy
package a.b.c
public class Utils implements Serializable {
def script
public def run() {
println(script.library("ItLoads").Defaults.text)
//Only using first definition of library ItLoads
//java.lang.IllegalAccessException: Defaults was defined in file:///apps/opt/.../vars/Ansible.groovy which was not inside file:///apps/opt/.../src/
}
}
So, Defaults is defined somewhere, but I have no idea how to get to it...
If I try to use Defaults in the Jenkinsfile, it works.
HELP

Looking at your code I can see that you're not using the script object to access Defaults:
Instead of Defaults.text you should use something like script.Defaults.text.
Adding some links as a reference for future readers:
https://jenkins.io/doc/book/pipeline/shared-libraries/
How to access pipeline DSL in groovy classes(and not in Jenkinsfile)?

Related

Do i have to use "call{}" in a shared library when using it in a Jenkins pipeline?

I'm working on transforming a whole Jenkins from normal jobs to DSL/Pipelines. Had to implement a shared library for all the imports. In the said library there is a whole load of scripts.
Currently, all of them are set up like this:
package common
class Foo {
static String bar(String text) { stuff }
static String bar2(String text) { stuff }
static String bar3(String text) { stuff }
}
And in the pipeline:
#!/usr/bin/env groovy
#Library('jenkins-shared-libs') _
import common.*
There are many scripts and many methods. How, where, and can I not use call() to actually, "call" them?
If you want to invoke the bar method in the Foo class you could do any of these:
Option 1:
common.Foo.bar('some argument')
Option 2:
import common.*
Foo.bar('some argument')
Option 3:
import static common.Foo.*
bar('some argument')

Closure as argument in Jenkins shared library function

let's say I have a Configuration class in a Jenkins shared library written like this
class Configuration {
String param1, param2
Closure closure1
}
There's also a helper class like this
class Helper {
String helperMethod(String arg1, Closure closure1) {
// some invocation to closure 1
}
}
Within the var folder there's a dynamic pipeline in a pipeline.groovy file like this:
def call(Configuration config) {
node {
stage {
def helper = new Helper()
helper.helperMethod('foo') { config.closure1 it }
}
}
}
Finally I'm trying to use the shared library in another repo like this,
#Library('my-library')
import com.mylibrary.configuration.Configuration
def baz = { it.toUpperCase() }
def config = new Configuration(
param1: 'foo',
param2: 'bar',
closure1: baz
)
pipeline(config)
The problem is that baz get transformed to org.jenkinsci.plugins.workflow.cps.CpsClosure2 and helperMethod throws a MissingMethodException because of the type mismatch between CpsClosure2 and the expected groovy.lang.Closure
I've tried:
Using the #NonCPS annotation in baz
Strong typing the closure through a functional interface and trying to pass it to the config like closure1: baz as MyStronglyTypedClosure
Removing the closure typing in the helperMethod definition
Using helper.helperMethod('foo', config.closure1) instead of helper.helperMethod('foo') { config.closure1 it }
to no avail :(
Is there any workaround to receive the closure in the configuration so it can be used correctly in the helper class? Thanks in advance
MissingMethodException is usually thrown when you are invoking functions incorrectly or inexistent function, it rarely has to do with CPS closures, which indeed as dagget mentioned, extends standard java closures. I strongly recommend you to use testing frameworks like Spock (with Jenkins extension) to be able to detect errors beforehand.

Using a shared library class from a custom step with Jenkins pipeline shared libraries

I am setting up a shared library for Jenkins pipelines and am trying to figure out how to import a class in the shared library into a custom step that I am writing.
Here's what the directory structure looks like:
src
--jenny
----util
------Versioning.groovy
vars
--calculateVersion.groovy
The Versioning.groovy file defines some static helper methods that do some stuff.
package jenny.util
class Versioner implements Serializable {
static bool checkForValidVersion(version) {
return true
}
}
I would like to call this method from the calculateVersion.groovy something like this:
def call(version) {
return jenny.util.Versioner.checkForValidVersion(version)
}
So that my declarative pipeline can call:
def valid = calculateVersion "1.0.0"
But I receive this error No such property: jenny for class: calculateReleaseVersions
Is it possible to reference the classes in the shared library from files in the vars to define custom steps and how is this done?
Yes it is possible. At least for us:
Just like in plain java (or groovy) we put an import statement into the groovy script in vars. In your case that would be something like:
import jenny.util.Versioner
def call(version) {
return Versioner.checkForValidVersion(version)
}
Another thing I just found: It looks like the file name of the class Versioner doesn't match the class name: Versioning.groovy. Could that be the issue?
If that doesn't work you propably want to upgrade your pipeline plugin version(s).

Getting application config in doWithSpring closure with a Grails 3 application

Grails 3 allows authors to use startup hooks similar to the ones provided to Grails 2 plugins. I'm looking at defining beans in the doWithSpring closure, and I'd like to pass values into a new bean based on some configuration values. I can't figure out, however, how to get the grailsApplication instance or the application configuration. How do you do this with Grails 3?
Your plugin should extend grails.plugins.Plugin which defines the getConfig() method. See https://github.com/grails/grails-core/blob/9f78cdf17e140de37cfb5de6671131df3606f2fe/grails-core/src/main/groovy/grails/plugins/Plugin.groovy#L65.
You should be able to just refer to the config property.
Likewise you can refer to the inherited grailsApplication property which is defined at https://github.com/grails/grails-core/blob/9f78cdf17e140de37cfb5de6671131df3606f2fe/grails-core/src/main/groovy/grails/plugins/Plugin.groovy#L47.
I hope that helps.
Under Grails 3, I took Jeff Scott Brown's advice and used GrailsApplicationAware instead:
This is how you go about setting up a configuration bean:
So in your new plugin descriptor you need to change grails 2 style def doWithSpring to a ClosureDoWithSpring as below:
Notice in Grails 2 we injected grailsApplication, in grails 3 all we do is declare the bean:
/*
def doWithSpring = {
sshConfig(SshConfig) {
grailsApplication = ref('grailsApplication')
}
}
*/
Closure doWithSpring() { {->
sshConfig(SshConfig)
}
}
Now to get your plugin configuration:
src/main/groovy/grails/plugin/remotessh/SshConfigSshConfig.groovy
package grails.plugin.remotessh
import grails.core.GrailsApplication
import grails.core.support.GrailsApplicationAware
class SshConfig implements GrailsApplicationAware {
GrailsApplication grailsApplication
public ConfigObject getConfig() {
return grailsApplication.config.remotessh ?: ''
}
}
grails.plugin.remotessh.RemoteSsh.groovy:
String Result(SshConfig ac) throws InterruptedException {
Object sshuser = ac.config.USER ?: ''
Object sshpass = ac.config.PASS ?: ''
...
This is now your configuration object being passed into your src groovy classes. The end user application would pass in the sshConfig bean like this:
class TestController {
def sshConfig
def index() {
RemoteSSH rsh = new RemoteSSH()
....
def g = rsh.Result(sshConfig)
}
Edited to add, just found this :) which is relevant or duplicate question:
http://grails.1312388.n4.nabble.com/Getting-application-config-in-doWithSpring-closure-with-a-Grails-3-application-td4659165.html

access configuration/property files from src/groovy

I have a file under src/groovy and I have some properties that are in my Config.groovy and in external property file too. Normally if one want access properties its possible to use grailsApplication .configuration.property.name expression. I want to be able to access all those properties from this file that is under src/groovy directory. What I've tried so far
import grails.util.Holders
class ForkedTomcatCustomizer {
def application
void customize(Tomcat tomcat) {
println Holders.grailsApplication.config.property.name
}
}
gave me NPE saying that grailsAppliction is null
import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class ForkedTomcatCustomizer {
def application
void customize(Tomcat tomcat) {
def ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
def grailsAppliction = ctx.grailsApplication.getObject()
println grailsAppliction.config.property.name
}
}
the same - NPE because grailsAppliction is null
Is it possible to handle this situation somehow? Thank you!
Use the below and see if it works
println Holders.config.property.name
You don't need grailsApplication when using Holders.
The examples below are probably a little more complex than what you need, but they show how to get a configuration property at build time. I use them to merge two configuration files, but you might not need to do that.
This method returns a config property when called here at the CompileEnd event.
You could define a similar method in your app's _Events.groovy file that calls your own configuration holder class.
import org.codehaus.groovy.grails.commons.ConfigurationHolder;
class KeyAndSecret{
public static String consumerKey = ConfigurationHolder.config.consumerKey;
public static String consumerSecret = ConfigurationHolder.config.consumerSecret;
}
Try like this

Resources