How to append build jobs into a map? - groovy

I'm trying to implement the code inspired from here with << operator in the following way:
builder = { name, param1, param2 ->
[job: name, parameters: [string(name: 'Param1', value: param1), string(name: 'Param2', value: param2)], quietPeriod: 2, wait: false]
}
node {
stage('Tests') {
def testBuilds = [:]
testBuilds << build *builder('Test', 'Foo', 'Bar')
testBuilds << build *builder('Test', 'Foo2', 'Bar2')
parallel testBuilds
}
}
where I expect to append two jobs into testBuilds map in order to run them in parallel.
However when running the job, I've got the following exception error:
groovy.lang.MissingPropertyException: No such property: build for class: groovy.lang.Binding
at groovy.lang.Binding.getVariable(Binding.java:63)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:224)
at org.kohsuke.groovy.sandbox.impl.Checker$4.call(Checker.java:241)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:238)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:221)
at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:24)
...
What would be the correct syntax using the above approach?

You've got few problems in your code:
Branches description testBuild is a map (key: value) object, not a list. So you cannot left shift (<<) values into it the way you did, such operation is not supported for map
Jenkins pipeline parallel operation expects map with closure as its values.
build is part of pipeline DSL, not a common Groovy method. It appears it cannot accept arguments this way. While the expression build *builder(foo, bar) passes syntax check at first, omitting round brackets is just groovy syntactic sugar. Rewriting this line as build(*builder(foo, bar)) generates syntax error exception.
All in all, you can rewrite your code in a way like:
def builder(name, param1, param2) {
return build(job: name, parameters: [string(name: 'Param1', value: param1)], [string(name: 'Param2', value: param2)], quietPeriod: 2, wait: false)
}
node {
stage('Tests') {
def testBuilds = [:]
testBuilds['test1'] = { builder('Test', 'Foo', 'Bar') }
testBuilds['test2'] = { builder('Test', 'Foo2', 'Bar2') }
parallel testBuilds
}
}

Related

How to pass multiple optional parameters and one mandatory parameter in groovy? [duplicate]

I would like to write a wrapper method for a webservice, the service accepts 2 mandatory and 3 optional parameters.
To have a shorter example, I would like to get the following code working
def myMethod(pParm1='1', pParm2='2') {
println "${pParm1}${pParm2}"
}
myMethod();
myMethod('a')
myMethod(pParm2:'a') // doesn't work as expected
myMethod('b','c')
The output is:
12
a2
[pParm2:a]2
a2
bc
What I would like to achieve is to give one parameter and get 1a as the result.
Is this possible (in the laziest way)?
Can't be done as it stands... The code
def myMethod(pParm1='1', pParm2='2'){
println "${pParm1}${pParm2}"
}
Basically makes groovy create the following methods:
Object myMethod( pParm1, pParm2 ) {
println "$pParm1$pParm2"
}
Object myMethod( pParm1 ) {
this.myMethod( pParm1, '2' )
}
Object myMethod() {
this.myMethod( '1', '2' )
}
One alternative would be to have an optional Map as the first param:
def myMethod( Map map = [:], String mandatory1, String mandatory2 ){
println "${mandatory1} ${mandatory2} ${map.parm1 ?: '1'} ${map.parm2 ?: '2'}"
}
myMethod( 'a', 'b' ) // prints 'a b 1 2'
myMethod( 'a', 'b', parm1:'value' ) // prints 'a b value 2'
myMethod( 'a', 'b', parm2:'2nd') // prints 'a b 1 2nd'
Obviously, documenting this so other people know what goes in the magical map and what the defaults are is left to the reader ;-)
You can use arguments with default values.
def someMethod(def mandatory,def optional=null){}
if argument "optional" not exist, it turns to "null".
Just a simplification of the Tim's answer. The groovy way to do it is using a map, as already suggested, but then let's put the mandatory parameters also in the map. This will look like this:
def someMethod(def args) {
println "MANDATORY1=${args.mandatory1}"
println "MANDATORY2=${args.mandatory2}"
println "OPTIONAL1=${args?.optional1}"
println "OPTIONAL2=${args?.optional2}"
}
someMethod mandatory1:1, mandatory2:2, optional1:3
with the output:
MANDATORY1=1
MANDATORY2=2
OPTIONAL1=3
OPTIONAL2=null
This looks nicer and the advantage of this is that you can change the order of the parameters as you like.
We can Deal with Optional parameters in 2 ways
Creating the method parameter with null values:
def generateReview(def id, def createDate=null) {
return new Review(id, createDate ?: new Date()) // ?: short hand of ternary operator
}
generateReview(id) // createDate is not passed
generateReview(id, createDate) // createDate is passed
Using Java Optional.of()
def generateReview(def id, Optional<Date> createDate) {
return new Review(id, createDate.isPresent() ? createDate.get() : new Date())
}
generateReview(id, Optional.empty()) // createDate is not passed
generateReview(id, Optional.of(createDate)) // createDate is passed

Groovy: Usage of 'it' with closures

I wonder how I can pass the implicit variable 'it' to the closure.
See the following code:
def myfunc(String name, Closure cl)
{
println("name: ${name}")
cl.call()
}
list = ['a', 'b']
list.each
{
println("it1: ${it}")
}
list.each
{
myfunc("f1")
{
println("it2: ${it}")
}
}
list.each
{
p ->
myfunc("f2")
{
println("p: ${p}")
}
}
The resulting output is:
it1: a
it1: b
name: f1
it2: null
name: f1
it2: null
name: f2
p: a
name: f2
p: b
How can I achieve that in the second version (f1/it2) the implicit variable it is passed to the closure? Currently it is "null".
I want to achieve to have 'a', 'b' within 'it' in the second version.
The background of the question is, that within a Jenkins Pipeline, the following code
mylist = ['a1', 'a2']
mylist.each {
dir ("xxxx") {
echo "it in xxxx is ${it}"
}
}
prints out the values 'a1' and 'a2' for it.
This does not fit to the understanding that it is bound to the outermost closure.
In case of Jenkins the behaviour seems to be different.
I want to achieve the very same behaviour and I am wondering how to do this.
Thanks for all feedback!
Best regards
Mathias
dir from jenkins is not a function - it's a jenkins Step and your groovy function example is not applicable.
you have to check if it's achievable from groovy-defined custom steps https://rubix.nl/jenkins-creating-a-custom-pipeline-step-in-your-library/
or think about custom plugin creation.
btw, here is a source of dir jenkins step: https://github.com/jenkinsci/workflow-basic-steps-plugin/blob/master/src/main/java/org/jenkinsci/plugins/workflow/steps/PushdStep.java

Call stage from function in Jenkinsfile

I have in my Jenkinsfile:
def foo = ["1", "2", "3"]
def parallelStagesFromMap = foo.collectEntries {
["Build ${it}" : generateStage(it)]
}
def generateStage(bar) {
return {
stage("Build ${bar}") {
echo "Building for ${bar}"
}
}
}
I can then use them with parallel parallel parallelStagesFromMap but now I'm trying to call one in particular, for example:
generateStage("a") and it is just skipped... Am I missing anything?
You are missing closure invocation. Your generateStage(name) method returns a closure, and this closure is not invoked implicitly. (It works with parallel stages, because parallel method expects a map where each entry value is a closure, so it iterates over all map entries and invokes collected closures).
Here is what your example should look like to add a non-parallel stage to the pipeline using generateStage(name) method:
def foo = ["1", "2", "3"]
def parallelStagesFromMap = foo.collectEntries {
["Build ${it}" : generateStage(it)]
}
def generateStage(bar) {
return {
stage("Build ${bar}") {
echo "Building for ${bar}"
}
}
}
node {
parallel parallelStagesFromMap
generateStage("skipped") // no invocation, stage is skipped
generateStage("nonparallel").call()
}
And here is what the Blue Ocean UI looks like after running this exemplary pipeline:

How can I retrieve the build parameters from a queued job?

I would like to write a system groovy script which inspects the queued jobs in Jenkins, and extracts the build parameters (and build cause as a bonus) supplied as the job was scheduled. Ideas?
Specifically:
def q = Jenkins.instance.queue
q.items.each { println it.task.name }
retrieves the queued items. I can't for the life of me figure out where the build parameters live.
The closest I am getting is this:
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
it.task.properties.each { key, val ->
println(" ${key}=${val}")
}
}
This gets me this:
4.1.next-build-launcher:
com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty$ScannerJobPropertyDescriptor#b299407=com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty#5e04bfd7
com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty$DescriptorImpl#40d04eaa=com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty#16b308db
hudson.model.ParametersDefinitionProperty$DescriptorImpl#b744c43=hudson.mod el.ParametersDefinitionProperty#440a6d81
...
The params property of the queue element itself contains a string with the parameters in a property file format -- key=value with multiple parameters separated by newlines.
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
println("Parameters: ${it.params}")
}
yields:
dbacher params:
Parameters:
MyParameter=Hello world
BoolParameter=true
I'm no Groovy expert, but when exploring the Jenkins scripting interface, I've found the following functions to be very helpful:
def showProps(inst, prefix="Properties:") {
println prefix
for (prop in inst.properties) {
def pc = ""
if (prop.value != null) {
pc = prop.value.class
}
println(" $prop.key : $prop.value ($pc)")
}
}
def showMethods(inst, prefix="Methods:") {
println prefix
inst.metaClass.methods.name.unique().each {
println " $it"
}
}
The showProps function reveals that the queue element has another property named causes that you'll need to do some more decoding on:
causes : [hudson.model.Cause$UserIdCause#56af8f1c] (class java.util.Collections$UnmodifiableRandomAccessList)

how to create xml document with special node names with groovy markupbuilder

I am building an ant script with groovy markupbuilder. Unfortunately markupbuilder doesn't allow me to create nodes with name 'target' (no problem with targetee),
becauase it throws me
Caught: groovy.lang.MissingMethodException: No signature of method: java.lang.String.call() is applicable for argument types: (java.util.LinkedHashMap, BuildGen$_main_closure1_closure5) values: [[name:clean], BuildGen$_main_closure1_closure5#18efaea]
Possible solutions: wait(), any(), trim(), split(), dump(), next()
so inside my markupbuilder this snippet works:
targete(name: 'clean') {
delete(dir: rootProj.compilerOutput)
}
but I would like to achieve the same with a 'target' node..
I managed to create an empty 'target' node this way:
builder.invokeMethod('target', [name: 'clean'])
but how can I go on and put more nodes inside this 'target' node?
Example of working code:
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.project(name: projectName, basedir:'.') {
// works with 'target2' but not with 'target'
'target2'(name: 'build-subprojects') {
rootProj.getAllDependentProjects().each { p->
echo(message: "Compiling project: ${p.projectName}")
// some real stuff
}
}
If I guess right, your problem is you want to create nodes with names that are Groovy keywords?
If so, then you can simply put the name in quotes, like so:
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder( writer )
builder.project {
'for'(name: 'clean') {
delete(dir: '.')
}
}
println writer
That snippet will print out:
<project>
<for name='clean'>
<delete dir='.' />
</for>
</project>
For me, this works:
def projects = [ 'pro_one', 'pro_two' ]
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.project( name: 'test', basedir:'.' ) {
'target'( name: 'build-subprojects' ) {
projects.each { p ->
echo( message: "Compiling project: ${p}" )
}
}
}
println writer.toString()
Have you got target set to anything in your code before calling this?
You could try:
builder.target( name: 'build-subprojects' ) {
That might work better?
I've tried Groovy 1.7.5, and 1.8 beta 2 and can't get it to fail :-/

Resources