I am having an issue extending a utilizing a jobdsl command from a groovy script.
I have created a helper class and i am passing "this". It works for jobdsl commands that do not require closures. But fails with invalid property for job dsl command that accepts closures. Example is the freestylejob("inputstring"){}
Helper Code
static freeJob(def fJ, def dslFactory){
def txt = dslFactory.freeStyleJob(fJ){
}
}
Caller Code
def testjob = DS.freeJob("inputstring", this)
I expect no errors. Instead i am getting the error below from jenkins.
ERROR: (filename.groovy, line 119) No signature of method: .filename.freeStyleJob() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl, classname$_classname_closure3) values: [inputstring, classname$_project_closure3#5c7bc682]
The code was calling the method from a class deep. Issue resolved.
Related
Let me preface this by saying that I don't yet fully understand how jenkins DSL / groovy handles namespace, scope, variables etc.
In order to keep my code DRY I put repeated command sequences into variables.
It turns out the variable script below is not readable by the code in doParallelStuff. Why is that? Is there a way to share global variables defined in the script (or elsewhere) among both the main pipleine steps and the doParallelStuff code?
def script = """\
#/bin/bash
python xyz.py
"""
def doParallelStuff() {
tests["1"] = {
node {
stage('ps1') {
sh script
}
}
}
tests["2"] = {
node {
stage('ps2') {
sh script
}
}
}
parallel tests
}
pipeline {
stages {
stage("myStage") {
steps {
script {
sh script
doParallelStuff()
}
}
}
}
}
The actual steps are a bit more complicated, but this causes an error like the following to be thrown:
hudson.remoting.ProxyException: groovy.lang.MissingPropertyException: No such property: script for class: WorkflowScript
When you define a variable outside of the pipeline directive scope using the def keyword you are defining it in the local scope of the main script, because the pipeline keyword is actually a method that is executed in the main script it can access the variable is they are defined and executed in the same scope (they are actually transformed into a separated class).
When you define a function outside of the pipeline directive, that function has its own scope for variables which is separated from the scope of the main script and therefore it cannot access the defined variable in the top level.
To solve it you can define the variable without the def keyword which will affect the scope in which this variable is created, as without the def (in a groovy script, not class) the variable is added to the global variables of the script (The Binding) which makes it accessible from any function or code within the groovy script. You can read more on the following question: What is the difference between defining variables using def and without?
So in your case you want a variable that is available for both the pipeline code itself and for the defined functions - so it needs to be available anywhere in the script as a global variable and therefore just define it without the def keyword, and it should do the trick:
script = """\
#/bin/bash
python xyz.py
"""
Noob in Shared library,
I am puzzled with Jenkins document section 'Loading Libraries dynamically'.
Followed the Stackoverflow_answer, but I have some different needs, just wanted to call a function from library to pipeline with an argument.
Note: Jenkins library configuration is correct and library access is already checked with another example with call method
vars/foo.groovy contains function
//{Root}/vars/foo.groovy
def Foo_Func(Body){
Body= Body + "This is a Message from Shared Lib."
return Body
}
Body Variable is already defined into main Pipeline 'bar.jenkinsfile'
My real problem is how to call the function from foo.groovy without using call method,
I have tried following -
//somefolder_in_scm/bar.jenkinsfile
#Library('jenkins-shared-libs') _
def Body_Main=""
deg SUBJECT="Title 1"
def NativeReceivers = "abc#xyz.com"
pipeline{
node any
stage{
script {
/*Some script*/
}
}
post {
always {
script {
foo.Foo_Func(Body_Main)
// send email
emailext attachLog: true,
mimeType: 'text/html',
subject: SUBJECT,
body: Body_Main,
to: NativeReceivers
}
}
}
}
Since I have used _, I expect that no import needed.
Error which is occurred after triggering pipeline,
groovy.lang.MissingMethodException: No signature of method: java.lang.Class.Foo_Func() is applicable for argument types:
In the error, why function Foo_Func is treated as a class? It might possible that the argument need to be given in different way.
Any help is appreciated.
Have you tried declaring a Field ?
#groovy.transform.Field
def myVar = "something"
script.myScript.myVar
Assuming your file is myScript.groovy.
I am writing an shared lib too.
I think the problem is in the:
def Foo_Func(Body)
what works for me is:
def Foo_Func(Map Body)
so if you try:
def Foo_Func(String Body)
it should work. I think it can't find the function with the right signature.
My question is very related to How to access list of Jenkins job parameters from within a JobDSL script?
With the diference: How can I access one specific parameter within the DSL script?
I tried to figure it out from the answers in the mentioned question but couldn't figure it out.
Let's say the parameter is named REPOSITORY_NAME.
I tried to use the code from the accepted answer and do something like
import hudson.model.*
Build build = Executor.currentExecutor().currentExecutable
ParametersAction parametersAction = build.getAction(ParametersAction)
def newname = parametersAction.parameters['REPOSITORY_NAME'].ParameterValue
println newname
but I only got
ERROR: (script, line 5) Exception evaluating property 'REPOSITORY_NAME' for java.util.Collections$UnmodifiableRandomAccessList, Reason: groovy.lang.MissingPropertyException: No such property: REPOSITORY_NAME for class: hudson.model.StringParameterValue
I also tried
def newname = parametersAction.parameters.getParameter('REPOSITORY_NAME').ParameterValue
instead but it gave me
ERROR: (script, line 5) No signature of method: java.util.Collections$UnmodifiableRandomAccessList.getParameter() is applicable for argument types: (java.lang.String) values: [REPOSITORY_NAME]
What do I have to change to make this work?
Okey just figured it out now using the second answer on the mentioned question and if-else like
def reponame = ''
binding.variables.each {
println "${it.key} = ${it.value}"
if(it.key == 'REPOSITORY_NAME'){
reponame = it.value
}
}
probably not the most eficient way but it works.
I've Pipeline job in Jenkins (v2.7.1) where I'd like to print each element of Multi-line String parameter (Params) with 3 strings in each line: Foo, Bar, Baz as an input.
So I've tried the following syntax (using split and each):
Params.split("\\r?\\n").each { param ->
println "Param: ${param}"
}
but it fails with:
java.lang.UnsupportedOperationException: Calling public static java.lang.Object
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(java.lang.Object,groovy.lang.Closure) on a CPS-transformed closure is not yet supported (JENKINS-26481); encapsulate in a #NonCPS method, or use Java-style loops
at org.jenkinsci.plugins.workflow.cps.GroovyClassLoaderWhitelist.checkJenkins26481(GroovyClassLoaderWhitelist.java:90)
which suggest to encapsulate in a #NonCPS method, or use Java-style loops.
So I've tried to encapsulate in a #NonCPS method like:
#NonCPS
def printParams() {
Params.split("\\r?\\n").each { param ->
println "Param: ${param}"
}
}
printParams()
but it fails with:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods println groovy.lang.Closure java.lang.Object
Without the function (as per first example), adding #NonCPS at the beginning it complains about unexpected token.
I also tried Java-style syntax as suggested by using for operator (similar as here):
String[] params = Params.split("\\r?\\n")
for (String param: params) {
println "Param: ${param}"
}
which seems to work in plain Groovy, but it fails in Jenkins with:
java.io.NotSerializableException: java.util.AbstractList$Itr
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860)
Which syntax I should use to make it work?
The code works fine when disabling a Use Groovy Sandbox option and adding #NonCPS helper method. Alternatively, as suggested by #agg3l, proceed to Jenkins management to permit this method access.
So the working code is (same as the 2nd example):
#NonCPS
def printParams() {
Params.split("\\r?\\n").each { param ->
println "Param: ${param}"
}
}
printParams()
I know it's an old post but this is my way to do it, hopefully help anyone else
params.readLines().each {
println it
if (it) {
// if you want to avoid make operation with empty lines
}
}
I have a very long workflow for building and testing our application. So long, in fact, that when we try to load the main workflow script, we get this exception:
java.lang.ClassFormatError: Invalid method Code length 67768 in class file WorkflowScript
I am not proud of this. I'm tying to split the workflow into smaller scripts that we load from the main workflow script, but are running into an issue with variable scoping. For example:
def a = 'foo' //some variable referenced in multiple workflow stages
node {
echo a
}
//... and then a whole bunch of other stages
might become
def a = 'foo' //some variable referenced in multiple workflow stages
node {
git: ...
load 'flowPartA.groovy'
}()
where flowPartA.groovy looks like:
{ ->
node {
echo a
}
}
Based on my understanding of the documentation, where flowPartA.groovy is interpreted as a closure, I expect the variable 'a' would remain in scope, but instead, I get an exception to the contrary.
groovy.lang.MissingPropertyException: No such property: a for class: groovy.lang.Binding
Am I missing something about the way workflow interprets the flow scripts? Is there a good way to take a huge workflow that uses many, many parameters and split it into smaller chunks?
You have to define a function in the external groovy and call it passing all required parameters:
def a = 'foo'
node('slave') {
git '…'
def flow = load 'flowPartA.groovy'
flow.echoFromA(a)
}
And flowPartA.groovy contains:
def echoFromA(String a) {
echo a
}
return this
See the documentation for more information.