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

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')

Related

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.

Import groovy class in a pipeline Jenkinsfile

I need to be able to create classes and use them within a Jenkins pipeline.
Let's say I have a very simple groovy class, declared in a groovy script, looking as this:
class MyClass {
#Override
public String toString() {
return "toto";
}
}
return MyClass();
This class is located in the folder: Project\Buildfiles\Jenkins\com\external
Then in my Jenkinsfile I would do:
node('mynode') {
toto = load 'Project\Buildfiles\Jenkins\com\external\MyClass.groovy'
echo toto.toString()
}
And this actually works
However this do pose a certain numbers of issues with my IDE which does not understand what is happening. Also, this prevents me to have several constructor in my custom class.
What I have been trying to do, and for which I need help, is the following. In a file named ExternalClasses.groovy:
class Toto{
#Override
public String toString() {
return "toto";
}
}
class Tata{
#Override
public String toString() {
return "tata";
}
}
return this;
In the JenkinsFile:
node('mynode') {
external= load 'Project\Buildfiles\Jenkins\com\external\ExternalClasses.groovy'
toto = new Toto();
tata = new Tata();
}
And this fails
I have tried several approaches, used packages names, used the Toto.new() syntax, but none worked.
Any ideas ?
Edit about Shared Libraries:
I actually have a Shared library, it is used by several teams and contains very specific data which should be own by the teams and not by the library.
We need to be able to put out of the library things which does not belong to it. The purpose of this work is to alleviate the said library of non generic code.
You could use the Shared Library Feature. Upload your scripts into a VCS like Github/Bitbucket and use Jenkins-Jobs to execute them. They are available for all projects/jobs.

Cannot import package in unit tests for a Jenkins Shared Library

I'm attempting to create unit tests for a JenkinsShared library using Gradle in order to run the test tasks.
I've followed this tutorial which upon conclusion one has a working test suite for a shared library for functions within the vars folder (with the unit tests in src/test/groovy/*Test.groovy).
However, in our internal shared jenkins library we followed a more object oriented style and isolated functionality into a package of classes in the format: src/org/company/*.groovy.
The problem arises when attempting to import said package into a unit test class. In the tutorial, the functions are imported using the loadScript method this method fails when loading a class which is dependent on another file.
Take the class:
package tests
import org.junit.*
import com.lesfurets.jenkins.unit.*
import static groovy.test.GroovyAssert.*
import org.company.UtilFactory
class UtilFactoryTest extends BasePipelineTest {
#Test
void testCall() {
def util = UtilFactory.getUtil("hello")
assertEquals true, true
}
}
src/org/company/UtilFactory.groovy
package org.company
class UtilFactory implements Serializable {
static Util instance
static Util getUtil(script=null) {
if (!(UtilFactory.instance)) {
if (!script) {
// Throws an exception if on the first call to getUtil the
// script parameter is null.
throw new ScriptUndefinedException("script parameter null on initial call to getUtil")
}
UtilFactory.instance = new Util(script)
}
return UtilFactory.instance
}
}
class ScriptUndefinedException extends Exception {
// Parameterless Constructor
public ScriptUndefinedException() {}
// Constructor that accepts a message
public ScriptUndefinedException(String message)
{
super(message);
}
}
Which gives me the exception:
jenkins-utilities/src/test/groovy/UtilFactoryTest.groovy: 7:
unable to resolve class org.company.UtilFactory
# line 7, column 1.
import org.company.UtilFactory
This may be more of a Gradle issue than a JenkinsShared Library. I've just spent a good portion of my day trying to figure out exactly what I'm doing wrong to no avail.
I would really appreciate any help to guide me in the right direction.
This library may be helpful getting your shared libraries to work in the unit test https://github.com/stchar/pipeline-sharedlib-testharness

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).

What is the `using` keyword in Haxe?

I often see people use the keyword using in their Haxe code. It seem to go after the import statements.
For example, I found this is a code snippet:
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;
using Lambda;
What does it do and how does it work?
The "using" mixin feature of Haxe is also referred as "static extension". It's a great syntactic sugar feature of Haxe; they can have a positive effect on code readability.
A static extension allows pseudo-extending existing types without modifying their source. In Haxe this is achieved by declaring a static method with a first argument of the extending type and then bringing the defining class into context through the using keyword.
Take a look at this example:
using Test.StringUtil;
class Test {
static public function main() {
// now possible with because of the `using`
trace("Haxe is great".getWordCount());
// otherwise you had to type
// trace(StringUtil.getWordCount("Haxe is great"));
}
}
class StringUtil {
public static inline function getWordCount(value:String) {
return value.split(" ").length;
}
}
Run this example here: http://try.haxe.org/#C96B7
More in the Haxe Documentation:
Haxe static extensions in the Haxe Manual
Haxe static extensions tagged articles in the Haxe Code Cookbook

Resources