I've been using Parametrized-pipelines in Jenkins and notice that while using parameters, the value is both useable from script scope as well as via params.variable.
PARAMETER == true
params.PARAMETER == true
In groovy, is it possible to add a variable to script scope from within a method? I would like to get similar functionality as the following...
// I don't want to have to declare value here
def function1(){
value = 1
}
def function2(){
assert value == 1
}
function1()
function2()
Is there a way to access value from within function2 without doing something like...
value = 0
def function1() {
value = 1
...
Could also do somthing like :
def f1() {
env.aaa = "hello"
}
def f2() {
assert aaa=="hello"
}
node{
f1()
f2()
}
Essentially setting it as an environment variable.
this pipeline works fine:
def f1(){
aaa = "hello"
}
def f2(){
assert aaa=="hello"
}
node{
f1()
f2()
}
the pipeline definition as actually an instance of org.jenkinsci.plugins.workflow.cps.CpsScript that extends groovy.lang.Script
so groovy script properties should work here.
You can use scope variable in script
import groovy.transform.Field
#Field List awe = [1, 2, 3]
def awesum() { awe.sum() }
assert awesum() == 6
http://docs.groovy-lang.org/2.4.9/html/gapi/groovy/transform/Field.html
Related
I premise that I am not very familiar with Jenkins and Groovy.
I am trying to set a timeout with a time value that can change based on a specific condition. I was wondering if it is possible to do this and how.
This is a shortened example for simplicity, of the pipeline I'm dealing with:
/**
* prepare tests for parallel
* #param filename Name of the file that contains the testsuites list
*/
def doDynamicParallelSteps(filename){
tests = [:]
echo "doDynamicParallelSteps"
def w = pwd()
def path = "$w/$filename"
// read all the lines into a list, each line is an element in the list
def fh = new File(path)
def lines = fh.readLines()
for (line in lines) {
def values = line.split(":")
def testsuite_name = values[0].trim()
def test_path = values[1].trim()
def test_filename_or_directory = test_path.split("/").getAt(-1);
def is_file = test_filename_or_directory.matches("(.*).php")
def is_custom_mycondition = test_filename_or_directory.matches("MyMatchCondition")
if (is_custom_mycondition){ // large timeout
def time_val = 10
} else { // default timeout
def time_val = 5
}
tests["${test_filename_or_directory}"] = {
stage("UnitTest ${test_filename_or_directory}") {
timeout(time: time_val, unit: 'MINUTES') { // scripted syntax
// other stuff here
} // end timeout
} // end stage
} // end MAP
}
parallel tests
}
If I run this pipeline I got the following output:
hudson.remoting.ProxyException: groovy.lang.MissingPropertyException: No such property: time_val for class: mycustomJob
Then I've tried to set it as global value and it worked but not as expected, because its value doesn't seems to changed, it outputs "5" ignoring my condition.
What am I doing wrong? Can anyone show me the right way or a better approach?
What you are doing looks more pythonic than groovy. You must define the variable 'time_val' on a higher level to make it visible in your scope, like:
def time_val = 5
if (is_custom_mycondition){ // large timeout
time_val = 10
}
Instead of your else part.
You can also define it in a single line, like:
def time_val = is_custom_mycondition ? 10 : 5
Your 'timeout' usage looks correct to me. Just define the variable properly.
What is the best way of chaining multiple custom methods together? I want to put the output of my method directly into the next method in an elegant way. The first one below is what I have now.
verified = search_verified(#providers)
matching = search_matching(verified)
deactivated = search_deactivated(matching)
networks = search_networks(deactivated)
designations = search_designations(networks)
disorders = search_disorders(designations)
age_groups = search_age_groups(disorders)
governing = search_governing(age_groups)
search_availabilities(governing)
maybe something more along the lines of:
search_verified(#providers)
>> search_matching
>> search_deactivated
>> search_networks
>> ....
You might want to use then to chain your methods and numbers parameter to simplify the blocks:
search_verified(#providers)
.then { search_matching(_1) }
.then { search_deactivated(_1) }
.then { search_networks(_1) }
.then { search_designations(_1) }
.then { search_disorders(_1) }
.then { search_age_groups(_1) }
.then { search_governing(_1) }
.then { search_availabilities(_1) }
You can do something along those lines. Ruby 2.6.0 introduces then, and the function composition operators << and >>.
You do have to select your methods with method(:method_name) because method_name by itself invokes the method and does not return it.
#providers.then(&
method(:search_verified) >>
method(:search_matching) >>
method(:search_deactivated) >>
method(:search_networks) >>
method(:search_designations) >>
method(:search_disorders) >>
method(:search_age_groups) >>
method(:search_governing) >>
method(:search_availabilities)
)
If you don't like the character "overhead". You could shrink the amount of characters by storing the method method in a shorter variable first:
fn = method(:method)
#providers.then(&
fn[:search_verified] >>
# ...
)
You could write that as follows.
METHODS = [:verified, :matching, :deactivated, :networks, :designations,
:disorders, :age_groups, :governing, :search_availabilities]
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| send(meth, arg) }
end
This would be called
doit(#providers)
For example:
def a(arg)
arg * 2
end
def b(arg)
arg + 1
end
def c(arg)
arg/5.0
end
METHODS = [:a, :b, :c]
doit(3)
#=> 1.4
One could alternatively write
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| method(meth).call(arg) }
end
doit(3)
#=> 1.4
One advantage of this approach is that if methods are added, removed or renamed is is only necessary to change the constant METHODS; the method doit is unaffected.
I've not actually tried this other than a quick attempt in the console, but from looking at: https://andersmurphy.com/2019/12/07/ruby-functional-programming.html it looks like something like the following should be possible:
pipe = -> *fns {fns.reverse.reduce {|f, g| -> x {f.(g.(x))}}}
add_one = -> x {x + 1}
times_two = -> x {x * 2}
add_one_and_times_two = pipe.(add_one, times_two)
add_one_and_times_two.(2)
=> 6
pipe.(add_one, times_two).(3)
=> 8
If you want to use this with methods you can possibly (this seems to work in the console) do something like:
def divide_by_three(x); x / 3.0 end
pipe.(
add_one,
times_two,
method(:divide_by_three)
).(4)
=> 3.3333333333333335
using the method function as shown in #3limin4t0r's answer.
If all the methods are in one class you can chain these methods by returning self in each method.
For the sake of clarity I take your example but the providers are just numbers.
class MyClass
def initialize
##providers = [2, 6, 4, 8, 7]
end
def search_matching
# do stuff
##providers.select!{ |n| n > 3 }
self
end
def search_deactivated
# do other stuff
##providers.select!{ |n| n < 8 }
self
end
def providers
##providers
end
end
MyClass.new.search_matching.search_deactivated.providers # [6, 4, 7]
it depends on what data do you really need and how you define the architecture of your code, i usually make a Service object.
if you only want to return only the last method output. return search_availabilities(governing).
If you need all variables, you can make it return an array with all the variables or the ones that you need. return [verified, matching, deactivated, ...].
I have a task where my Jenkins job needs two parameters for build. The first specifies the application name and can be either QA, Dev, Prod etc and the second is a server which is dependent on the first one.
Example: If I chose the app name as QA, the second parameter should display values like QAServer1, QAServer2, QAServer3.
I'm using Active Choices Plugin (https://wiki.jenkins.io/display/JENKINS/Active+Choices+Plugin) to get this done but facing an problem in fetching the second parameter contents.
Snapshots:
For obtaining the second parameter, I've written a Groovy code which reads the respective files of the selected first parameter and gets the details.
code:
#!/usr/bin/env groovy
import hudson.model.*
def Appliname = System.getenv("APPNAME")
//println Appliname
def list1 = []
def directoryName = "C:/Users/Dev/Desktop/JSONSTest"
def fileSubStr = Appliname
def filePattern = ~/${fileSubStr}/
def directory = new File(directoryName)
def findFilenameClosure =
{
if (filePattern.matcher(it.name).find())
{
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
String listAsString = "[\'${list1.join("', '")}\']"
println "return"+listAsString
}
}
directory.eachFileRecurse(findFilenameClosure)
The above code will print the output as return['QAServer1', 'QAServer2'] which i want to use it as input for the second parameter.
Snapshot of Second parameter:
Somehow the Groovy script is not being executed and second parameter value remains empty. How can i get this done dynamically. Am i following the right away to it. Kindly help me figure out. TIA
Would you like to try below change
From:
def findFilenameClosure =
{
if (filePattern.matcher(it.name).find())
{
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
String listAsString = "[\'${list1.join("', '")}\']"
println "return"+listAsString
}
}
directory.eachFileRecurse(findFilenameClosure)
To:
directory.eachFileRecurse {
if (filePattern.matcher(it.name).find()) {
def jsoname = it.name
def jsoname1 = jsoname.reverse().take(9).reverse()
list1.add(jsoname1.substring(1,4))
}
}
return list1
I've been trying to take advantage of operator overloading in Groovy by defining a custom putAt method in my POGO like this:
class Book {
Map additionalInfo = [:]
def putAt(key, value) {
additionalInfo[key] = value
}
}
So that I can do something like, book['notes'] = 'I like this one.' (let's say this makes sense). However, I've been getting:
groovy.lang.MissingPropertyException: No such property: notes for class: Book
at BookSpec.Set property using putAt(BookSpec.groovy:40)
My class is part of a Grails application so I'm not sure if Grails has something to do with the problem. Can anyone enlighten me on this?
The signature should be
def putAt(String key, value)
Instead of doing putAt and then override the operator, there is an easy/better way by adding the propertyMissing methods.
Here is the link for more explanation.
class Foo {
def storage = [:]
def propertyMissing(String name, value) { storage[name] = value }
def propertyMissing(String name) { storage[name] }
}
def f = new Foo()
f.foo = "bar"
assertEquals "bar", f.foo
I have recently seen some code that I do not completely understand. There is an array named foo that contains instances of Proc objects. Then, an env object is used to setup an environment to work in:
env = Object.new
foo.each do |f|
env.instance_eval &f # what happens here?
end
What exactly happens when you open the object with instance_eval and pass &f as an argument? What happens to env at this point and the Proc itself?
The scope is changed for the proc, which is then evaluated in that context. Internally, all procs are stored in memory as a C struct which includes the self of the proc (the scope in which the proc was made). When you call instance_eval, the self value is manually changed in memory to the object you are calling instance_eval on. If you explore the ruby source code, you will find that it boils down to this function:
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)
{
rb_thread_t *th = GET_THREAD();
rb_block_t block, *blockptr;
NODE *cref;
if ((blockptr = GC_GUARDED_PTR_REF(th->cfp->lfp[0])) != 0) {
block = *blockptr;
block.self = self; // <- This is where the scope changes!
th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
}
cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);
cref->flags |= NODE_FL_CREF_PUSHED_BY_EVAL;
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
}
else {
return vm_yield_with_cref(th, RARRAY_LENINT(values), RARRAY_PTR(values), cref);
}
}
Note the line containing // <- This is where the scope changes!.
The Proc gets executed in the context of env. It is as if you are calling a method on env: the block has access to its instance variables and public and private methods.
env = Object.new
env.instance_variable_set :#test, "test"
class << env
private
def test
#test
end
end
env.instance_eval { #test } #=> "test"
env.instance_eval { test } #=> "test"