In a jenkins shared library I can do something like this:
Jenkinsfile
#Library(value="my-shared-lib", changelog=false) _
jobGenerator {
notifier = [notifyEveryUnstableBuild: true]
}
sharedLibary/vars/jobGenerator.groovy
def call(body) {
println 'hi!'
}
To better understand the flow of whats goes on I have created two groovy files locally (with no reference to jenkins at all):
samples/launcher.groovy
jobGenerator {
s = 's'
}
samples/jobGenerator.groovy
def call(body) {
println 'inside jobGenerator '
}
But when I run that with:
groovy "/home/user/samples/launcher.groovy"
I get:
Caught: groovy.lang.MissingMethodException: No signature of method: launcher.jobGenerator() is applicable for argument types: (launcher$_run_closure1) values: [launcher$_run_closure1#61019f59]
groovy.lang.MissingMethodException: No signature of method: launcher.jobGenerator() is applicable for argument types: (launcher$_run_closure1) values: [launcher$_run_closure1#61019f59]
at launcher.run(launcher.groovy:2)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
So how much of the above code is jenkins/shared library specific? And is it even possible to write something like the above in plain groovy?
Or put in another way. How do I convert the above jenkins code to plain groovy?
IMHO following is close to what jenkins is doing
launcher.groovy
// load library scripts/functions
def binding = this.getBinding()
def gshell = new GroovyShell(this.getClass().getClassLoader(),binding)
new File("./my-lib").traverse(nameFilter: ~/.*\.groovy$/){f-> binding[f.name[0..-8]] = gshell.parse(f) }
// main
bar{
foo(name:"world")
}
./my-lib/foo.groovy
def call (Map m){
return "hello $m.name"
}
./my-lib/bar.groovy
def call (Closure c){
println ( "BAR: "+c() )
}
#> groovy launcher.groovy
BAR: hello world
Related
I have created a pipeline that takes an array of JSON objects and will call a shared library which will iterate over the JSON objects
When trying to run the Jenkins job to test that I can forward the objects but I'm seeing the following error:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: imageBuild.call() is applicable for argument types: (java.lang.String) values: [[{"dockerConfig":"home/pipelines/conf/journeys/dockerbuild.yaml","aquaConfig":"home/pipelines/conf/journeys/aquascan.yaml","gtag": "v1.0.1","preFixName": "journey1"},{"dockerConfig":"home/pipelines/conf/if.com/dockerbuild.yaml","aquaConfig":"home/pipelines/conf/if.com/aquascan.yaml","gtag": "v2.0.2","preFixName": "journey2"},{"dockerConfig":"home/pipelines/conf/colleague/dockerbuild.yaml","aquaConfig":"home/pipelines/conf/colleague/aquascan.yaml","gtag": "v3.0.3","preFixName": "journey2"}]]
Possible solutions: call(), call(java.util.Map), wait(), any(), wait(long), main([Ljava.lang.String;)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
Code:
library identifier: 'jenkins-sharedlib-cooking#BB-3611', retriever: modernSCM([$class: 'GitSCMSource',
remote: 'https://github.com/lbg-gcp-foundation/jenkins-sharedlib-cooking-lifecycle.git',
credentialsId: 'jenkinsPAT'])
def configList = '[{"dockerConfig":"home/pipelines/conf/journeys/dockerbuild.yaml","aquaConfig":"home/pipelines/conf/journeys/aquascan.yaml","gtag": "v1.0.1","preFixName": "journey1"},{"dockerConfig":"home/pipelines/conf/if.com/dockerbuild.yaml","aquaConfig":"home/pipelines/conf/if.com/aquascan.yaml","gtag": "v2.0.2","preFixName": "journey2"},{"dockerConfig":"home/pipelines/conf/colleague/dockerbuild.yaml","aquaConfig":"home/pipelines/conf/colleague/aquascan.yaml","gtag": "v3.0.3","preFixName": "journey2"}]'
pipeline {
environment {
def config =
brand =
environmentName =
CLUSTER_NAME =
CLUSTER_PROJECT =
VERSION = '1.0.0'
}
options {
ansiColor('xterm')
timeout(time: 150, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '100'))
}
agent {
kubernetes {
label "jrn-${UUID.randomUUID().toString()}"
yamlFile "pipelines/conf/podTemplate.yaml"
}
}
stages {
stage('Stage 6 - Docker Image ') {
parallel {
stage ('Docker Image - Journeys') {
steps {
echo "*********************** Docker Journeys ***********************************"
container('docker') {
echo "Building the docker image..."
imageBuild(configList)
}
archiveArtifacts artifacts: "*.html", allowEmptyArchive: true
}
}
}enter code here
}
}
Looks like the groovy method or global variable imageBuild in the shared library Jenkins-shared lib-cooking#BB-3611 does not expecting any parameter but you are trying to pass a string, that's the reason you are getting MissingMethodException.
with the details
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: imageBuild.call() is applicable for argument types: (java.lang.String) values
To fix the issue, you have to change the shared library method or global variable imageBuild from imageBuild() to imageBuild(String param) or imageBuild(def param)
I will try to illustrate with an example similar to your example for your reference.
Assuming you have a shared library in a git repository named - jenkins-sharedlib-cooking-lifecycle and you are following the folder structure suggested by Jenkins shared library documentation
// vars/imageBuild.groovy
import groovy.json.JsonSlurper
def call(def imagebuildParameter) {
def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText(imagebuildParameter)
// use loop on object to retrive the value like below
println object[0].name
}
//Jenkinsfile
node {
stage('stageName') {
def x = '[{"name": "foo"},{"name": "bar"}]'
imageBuild(x)
}
}
I'm new to Groovy and am trying to invoke a Groovy script as a Jenkins Post-build Action, but whenever I run it, I'm getting "ERROR: Failed to evaluate groovy script":
groovy.lang.MissingMethodException: No signature of method: Script1.stage() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl, Script1$_run_closure1) values: [branch_1, Script1$_run_closure1#7e39737b]
Possible solutions: wait(), any(), isCase(java.lang.Object) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
Here is my code :
def warList1= ["one.war", "two.war", "three.war" ]
def branches = [:]
for (int i = 0; i < 10 ; i++) {
int index=i, branch = i+1
stage ("branch_${branch}"){
branches["branch_${branch}"] = {
node {
sshagent(credentials : ['someuser-SSH']){
sh "scp ${WORKSPACE}/${warList1[index]} someuser#<somefqdn>:/tmp/pscp/dev"
}
}
}
}
}
}
I think that your issue come from the fact that you can not use stage method in a Groovy Post Build Action. This method is only available in a pipeline script.
As a part of our Jenkins solutions, we use Groovy in our pipelines.
In one of our groovy file I want to update a docker-stack.yaml.
To do so I'm using readYaml():
stage("Write docker-stack.yaml") {
def dockerStackYamlToWrite = readFile 'docker-stack.yaml'
def dockerStackYaml = readYaml file: "docker-stack.yaml"
def imageOrigin = dockerStackYaml.services[domain].image
def versionSource = imageOrigin.substring(imageOrigin.lastIndexOf(":") + 1, imageOrigin.length())
def imageWithNewVersion = imageOrigin.replace(versionSource, imageTag)
dockerStackYamlToWrite = dockerStackYamlToWrite.replace(imageOrigin, imageWithNewVersion)
sh "rm docker-stack.yaml"
writeFile file: "docker-stack.yaml", text: dockerStackYamlToWrite
sh "git add docker-stack.yaml"
sh "git commit -m 'promote dockerStack to ${envname}'"
sh "git push origin ${envname}"
}
I am using test to validate my code:
import org.junit.Before
import org.junit.Test
class TestUpdateVersionInDockerStack extends JenkinsfileBaseTest {
#Before
void setUp() throws Exception {
helper.registerAllowedMethod("build", [Map.class], null)
helper.registerAllowedMethod("steps", [Object.class], null)
super.setUp()
}
#Test void success() throws Exception {
def script = loadScript("src/test/jenkins/updateVersionInDockerStack/success.jenkins")
script.execute()
}
}
Here is the success.jenkins:
def execute() {
node() {
stage("Build") {
def version = buildVersion()
updateVersionInDockerStack([
DOMAIN : "security-package",
IMAGE_TAG : version,
GITHUB_ORGA : "Bla",
TARGET_ENV : "int"
])
}
}
}
return this
When I run my test I get this message:
groovy.lang.MissingMethodException: No signature of method: updateVersionInDockerStack.readYaml() is applicable for argument types: (java.util.LinkedHashMap) values: [[file:docker-stack.yaml]]
At this point I'm lost. For what I understand from the documentation readYaml() can I a file as an argument.
Can you help to understand why it is expecting a LinkedHashMap? Do you have to convert my value in a LinkedHashMap?
Thank you
Your pipeline unit test fails, because there is no readYaml method registered in pipeline's allowed methods. In your TestUpdateVersionInDockerStack test class simply add to the setUp method following line:
helper.registerAllowedMethod("readYaml", [Map.class], null)
This will instruct Jenkins pipeline unit environment that the method readYaml that accepts a single argument of type Map is allowed to use in the pipeline and invocation of this method will be registered in the unit test result stack. You can add a method printCallStack() call to your test method to see the stack of all executed steps during the test:
#Test void success() throws Exception {
def script = loadScript("src/test/jenkins/updateVersionInDockerStack/success.jenkins")
script.execute()
printCallStack()
}
I can't figure how to define a dynamic shared library, and use it into my pipeline:
myLib=library (identifier: 'lib#master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: 'https://mygit.orga.com/git/ORGA/Jenkins-libs.git',
credentialsId: 'aaaaaaa-8f3f-4e3c-vvvvvvv-6c77351e7872',
includes: '*',
excludes: 'test'
]))
pipeline {
agent {
node(){
label("linux&&!master")
}
}
tools {
jdk "JDK1.8.0_45"
maven "MVN339"
}
stages{
stage("test lib"){
steps {
script {
myLib.a.b.c.Utils.sayHelloTo("Guillaume")
log.info("test lib")
}
}
}
}
At runtime it fails with :
java.lang.ClassNotFoundException: Utils
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
Any ideas of what is wrong? I can't find any info on dynamic shared lib tutorial on the net.
Edit : adding the library tree :
src
--a
--b
--c
Utils.groovy
Utils.groovy content:
package a.b.c
class Utils {
def sayHelloTo(String name) {
script.echo("Hello there $name")
}
}
Thanks guys,
Guillaume
I can't explain the CNFE you are getting, but there is definitely a couple of issues with this code.
The sayHelloTo method is not declared as static and yet, it is being called as one.
The script.echo doesn't seem valid, as I can't see script being a valid name here.
If you want to use this as a static function, then change the function like this
< def sayHelloTo(String name) {
> static sayHelloTo(def steps, String name) {
The only change to the call is to pass this (which should refer to pipeline steps):
< myLib.a.b.c.Utils.sayHelloTo("Guillaume")
> myLib.a.b.c.Utils.sayHelloTo(this, "Guillaume")
If you instead want to keep it as an instance method, then you still need to accept steps:
< def sayHelloTo(String name) {
> def sayHelloTo(def steps, String name) {
However, the call would change like this:
< myLib.a.b.c.Utils.sayHelloTo("Guillaume")
> myLib.a.b.c.Utils.new().sayHelloTo(this, "Guillaume")
In all the cases, the echo call would change as:
< script.echo("Hello there $name")
> steps.echo("Hello there $name")
When running a large Jenkins pipeline script, it can give the error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: General error during class generation: Method code too large!
java.lang.RuntimeException: Method code too large!
What is the reason for this error and how can it be fixed?
This is due to a limit between Java and Groovy, requiring that method bytecode be no larger than 64kb. It is not due to the Jenkins Pipeline DSL.
To solve this, instead of using a single monolithic pipeline script, break it up into methods and call the methods.
For example, instead of having:
stage foo
parallel([
... giant list of maps ...
])
Instead do:
stage foo
def build_foo() {
parallel([
...giant list of maps...
])}
build_foo()
If you are using declarative pipeline with shared library, you may need to refactor and externalize your global variables in the new methods. Here is a full example:
Jenkinsfile:
#Library("my-shared-library") _
myPipeline()
myPipeline.groovy:
def call() {
String SOME_GLOBAL_VARIABLE
String SOME_GLOBAL_FILENAME
pipeline {
stages() {
stage('stage 1') {
steps {
script {
SOME_GLOBAL_VARIABLE = 'hello'
SOME_GLOBAL_FILENAME = 'hello.txt'
...
}
}
}
stage('stage 2') {
steps {
script {
doSomething(fileContent: SOME_GLOBAL_VARIABLE, filename: SOME_GLOBAL_FILENAME)
sh "cat $SOME_GLOBAL_FILENAME"
}
}
}
}
}
}
def doSomething(Map params) {
String fileContent = params.fileContent
String filename = params.filename
sh "echo $fileContent > $filename"
}