Jenkins Shared Library Recursive Function Calls - jenkins

I have declarative pipeline and uses jenkins shared library. I am trying to make recursive function call within jenkins shared library.
My shared lib structure is something similar to below :
vars/xyz.groovy
Inside xyz.groovy I have method foo, to whom I call xyz.foo from my pipeline which works.
However recursive call from
foo(){
foo() // says No such DSL method
xyz.foo() //says no signature of method: java.lang.class.foo
}
I am trying to understand how to calls functions within jenkins shared library.

this.methodName is rightway to call function recursively inside shared library. All though method is not a part of class. But using this.foo() worked for me.

I was trying something similar to invoke a recursive function with a method declared in the same file .groovy
At the end, the option #Sagar gave didn't work for me
As a workaround, I created the recursive function in a new file (b.groovy) and invoke it from the original file (a.groovy). it worked like a charm :)
example.
b.groovy
/**
* recursive function to compare semantic version form package.json and tag version, only evaluate x.x.x version
* 0 same version
* 1 package.json version is GREATER than repo tag version
* -1 package.json version is SMALLER than repo tag version
*/
int call(ArrayList versionPackageJsonSplit, ArrayList versionTagRepoSplit, Integer iteration) {
if (versionPackageJsonSplit[iteration].toInteger() == versionTagRepoSplit[iteration].toInteger()) {
if (iteration == 2) {
println 'return 0'
return 0
}
return utility_cdkCompareVersions (versionPackageJsonSplit, versionTagRepoSplit, iteration+1) // --> recursive invocation
} else if (versionPackageJsonSplit[iteration].toInteger() > versionTagRepoSplit[iteration].toInteger()) {
println 'return 1'
return 1
} else if (versionPackageJsonSplit[iteration].toInteger() < versionTagRepoSplit[iteration].toInteger()) {
println 'return -1'
return -1
}
}
a.groovy
.
.
.
compareVersionsResult = b (versionPackageJsonSplit, versionTagRepoSplit, 0)
println compareVersionsResult
.
.
.
.
```

So far the least awful way I've found to do this reliably is to create a top-level wrapper method with a recursive closure inside it:
def recursiveMethod(args) {
Closure rec
rec = { arg ->
if (recursiveCase) {
return rec(…)
}
else {
return baseCase
}
}
return rec(…)
}
The upside is that you can reference method args or method-scoped state variables from within the closure without having to pass them around the recursive stack, so recursive calls may look a little cleaner. The downside is a little extra boilerplate to do the wrapping, and also that the closure args have to be named differently than the method args or Groovy raises an error.

Related

Use curried lua function as custom complete list in neovim

I'm trying to provide a curried lua function as method to call for autocomplete a command. However, it sometimes does nothing and sometimes fails with a memory segmentation error.
This is a minimal reproduction example:
Given this init.lua
vim.cmd(string.format([[set rtp=%s]], vim.fn.getcwd()))
local custom_complete = [[customlist,v:lua.require'split_later'.split_later('a b')]]
local options = { complete = custom_complete, desc = "Test to break", nargs = 1, force = true }
vim.api.nvim_create_user_command("Test", "echo <args>", options)
And this file called split_later under the correct lua/ subfolder:
local M = {}
function M.split_later(str)
print("called split")
return function()
return vim.fn.split(str)
end
end
return M
The idea is to have a generic utility that is able to produce auto-complete functions from lua, but it is not working as expected.
I found a workaround using a lookup table that stores the command name and then uses it, but I don't like it that much.

java.lang.IllegalArgumentException: Expected named arguments while executing jobs in parallel

I have a monorepo and i am trying to make them run in parallel
def abc = findJenkinsfileToRun(modifiedFiles)
parallel {
for (file in abc) {
println("Building ${file.toString()}")
load "${file.toString()}/Jenkinsfile"
}
}
This results in the following
java.lang.IllegalArgumentException: Expected named arguments but got org.jenkinsci.plugins.workflow.cps.CpsClosure2#b7ccdc
can anyone help how to resolve this?
You are not using the parallel keyword correctly, it should receive a map of branch names as keys and execution code (closure) as values. See the Documentation.
So in your case you should use something like:
def abc = findJenkinsfileToRun(modifiedFiles)
parallel abc.collectEntries { file ->
["Building ${file.toString()}" : {
// The code to run in parallel
println("Building ${file.toString()}")
load "${file.toString()}/Jenkinsfile"
}]
}

How to pass variable name, but not its value into /var function as argument piece?

I'm trying to divide a pipeline. Most of the parameters passed successful, but those containing variables are resolved before i need.
Jenkins ver. 2.164.1
Jenkins.file content:
stage ('prebuild') {
steps {
script {
VERSION="temprorary-value"
POSTBUILDACTION="make.exe \\some\\path\\file_${VERSION}"
}
}
}
stage ('build') {
steps {
script {
build (POSTBUILDACTION)
}
}
}
build.groovy content:
def call (String POSTBUILDACTION) {
...
checkout somefile
VERSION=readFile(somefile)
bat "${POSTBUILDACTION}"
}
here i expected that version will be taken from redefined VERSION variable but POSTBUILDACTION passed into the function as a string. In result it's called as is ("make.exe \some\path\file_temprorary-value"). In fact command i'd like to get is (somefile contains only one number, for example "5")
make.exe \some\path\file_5
But now i have
make.exe \some\path\file_temprorary-value
Or if i trying to pass \${VERSION} like:
POSTBUILDACTION="make.exe \\some\\path\\file_\${VERSION}"
- it's transfer as is:
make.exe \some\path\file_${VERSION}
I've tried to view a class of POSTBUILDACTION in prebuild stage - it's equal "class org.codehaus.groovy.runtime.GStringImpl" and same on build stage after passing throw - it become a string: "class java.lang.String"
So how to pass into a function argument contained a variable, but not it's value ?
OR
to "breathe life" into a dry string like
'make.exe \\some\\path\\file_${VERSION}'
so the variables could be resolved?
Option 1 - lazy evaluation (#NonCPS)
You can use a GString with lazy evaluation, but since Jenkins doesn't serialize lazy GStrings you'll have to return it from a #NonCPS method like so:
#NonCPS
def getPostBuildAction() {
"make.exe \\some\\path\\file_${ -> VERSION }"
}
stage ('prebuild') {
...
}
Then you set POSTBUILDACTION=getPostBuildAction() and you can use POSTBUILDACTION as you wanted, but be aware that the object you have here is a groovy.lang.GString and not a String, so you'll want to change your parameter class (or simply use def.)
Option 2 - use a closure for every call
You can use an eager GString inside a closure:
def String getPostBuildAction() {
"make.exe \\some\\path\\file_$VERSION"
}
But here you'll have to call getPostBuildAction() every time you want a different reading of VERSION, so you'll have to replace POSTBUILDACTION with this closure.

Jenkins pipeline script for passing variables from one function to another function

I am new to Jenkins pipeline scripting. I am developing a Jenkins pipeline in which the Jenkins code is as follows. The logic looks like this:
node{
a=xyz
b=abc
//defined some global variables
stage('verify'){
verify("${a}","${b}")
abc("${a}","${b}")
echo "changed values of a and b are ${a} ${b}"
}}
def verify(String a, String b)
{ //SOme logic where the initial value of a and b gets changed at the end of this function}
def verify(String a, String b){
//I need to get the changed value from verify function and manipulate that value in this function}
I need to pass the initial a and b(multiple) values to the verify function and pass the changed value on to the other function. I then need to manipulate the changed value, and pass it to the stage in the pipeline where echo will display the changed values. How can I accomplish all this?
Ok, here's what I meant:
def String verify_a(String a) { /* stuff */ }
def String verify_b(String b) { /* stuff */ }
node {
String a = 'xyz'
String b = 'abc'
stage('verify') {
a = verify_a(a)
b = verify_b(b)
echo "changed values of a and b are $a $b"
}
stage('next stage') {
echo "a and b retain their changed values: $a $b"
}
}
The easiest way I have found to pass variables between stages is to just use Environment Variables. The one - admittedly major - restriction is that they can only be Strings. But I haven't found that to be a huge issue, especially with liberal use of the toBoolean() and toInteger() functions. If you need to be passing maps or more complex objects between stages, you might need to build something with external scripts or writing things to temporary files (make sure to stash what you need if there's a chance you'll switch agents). But env vars have served me well for almost all cases.
This article is, as its title implies, the definitive guide on environment variables in Jenkins. You'll see a comment there from me that it's really helped me grok the intricacies of Jenkins env vars.

Comparing 2 parameters in Jenkins pipeline in a single command

what is wrong with below code, comparing 2 strings in groovy
I am trying do the comparison between the 2 parameters in a single line to make it look tidier
if (params.dirname == ((params.path =~ ~/${params.dirname}/).with { matches() ? it[0] : null })) {
print success
}
Throwing Exception -
java.lang.NoSuchMethodError: No such DSL method 'matches' found among steps
There is no need to over-complicate your use case. According to:
params.dirname = hde, params.path = /usr/tmp/jenkins/hde/filename.txt or /usr/hde/jenkins/ing/filename.txt or any random path which has hde in it
you are trying to find if given string a contains substring b. It can be done using Java's method String.contains(String substring). Alternatively you can use regular expression for that, but String.contains() just looks a few times simpler to understand what is your intention. Consider following Groovy script:
def params = [
dirname: 'hde',
path: '/usr/tmp/jenkins/hde/filename.txt'
]
// Using String.contains()
if (params.path.contains(params.dirname)) {
println "Path '${params.path}' contains '${params.dirname}'"
}
// Using regular expression
if (params.path ==~ /(.*)${params.dirname}(.*)/) {
println "Path '${params.path}' contains '${params.dirname}'"
}
When you run it both if statements evaluates to true:
Path '/usr/tmp/jenkins/hde/filename.txt' contains 'hde'
Path '/usr/tmp/jenkins/hde/filename.txt' contains 'hde'

Resources