I'm writing my own shared library. Now I want to use global variable in my code. How can I make that happen ?
I.E. I write a class.
class MyWork {
build() {
// here I want to use global docker(which is docker-plugin)
docker.doSomething {
}
}
}
enable the library in 'Global Pipeline Libraries' (i add the library 'pipeline-shared-lib')
create a shared-library (with the necessary structure)
src/net/kukinet/Utils.groovy
package net.kukinet;
def myVar = 1
def sayHello() {
print ('hello')
}
create a pipeline job and create the object
JenkinsJob.groovy
#!groovy
// this need to be enabled in jenkins configuration ( in: manage jenkins)
#Library('pipeline-shared-lib') import net.kukinet.*
node (){
u = new net.kukinet.Utils();
stage('preperations') {
print(u.myVar)
u.sayHello()
}
}
Related
Is something like this possible, i.e. using the JobDSL API from a class outside the main DSL script?
//main_jobdsl_script.groovy:
new JobCreator().createJob()
//JobCreator.groovy:
job("new-job") {
steps {
batchFile("Hello World")
}
}
When running it I get the error
13:03:18 ERROR: No signature of method: JobCreator.job() is applicable for argument types:
(org.codehaus.groovy.runtime.GStringImpl, StartJobCreator$_createJob_closure1)
values: ["new-job", de.dbh.jobcreation.StartJobCreator$_createStartJob_closure1#374d293]
I want to avoid that the main-script gets too big and cluttered and rather divide the code into several scripts / classes.
Yes, it is possible. The current script has access to all API methods, so you need to pass it to the custom class.
//main_jobdsl_script.groovy:
new JobCreator(this).createJob()
//JobCreator.groovy:
class JobCreator {
private final Object context
JobCreator(Object context) {
this.context = context
}
void createJob() {
context.job('new-job') {
steps {
batchFile('Hello World')
}
}
}
}
I have a groovy class which I am attempting to import from a folder-level shared library.
Here is the groovy class -
package abc.esmm
#Singleton
class JiraCommands implements Serializable
{
def steps
def jiraCommandsTool
def Initialize(steps)
{
this.steps = steps
jiraCommandsTool = "${steps.WORKSPACE}/JenkinsPipeline/UtilityScripts/bin/JiraCommands"
}
def AddFixVersionToJiraIssues(jiraIssues, fixVersion, overwriteFixVersionParam=false)
{
def overwriteFixVersion = "False"
if(overwriteFixVersionParam)
{
overwriteFixVersion = "True"
}
steps.sh(returnStdout: true, script: "${jiraCommandsTool} -command addFixVersionToJira -jiraIssues \"${jiraIssues}\" -fixVersion ${fixVersion} -overwriteFixVersion ${overwriteFixVersion}").trim()
}
}
I try to create an instance of this class with this pipeline code :
#Library('LotteryFolderPipelineLibs')
import abc.esmm.JiraCommands
node('All_LinuxBuildPool')
{
JiraCommands.instance.Initialize(this)
}
This works ok when called from a Jenkins Global shared library, but not when called from a folder-level shared library. The global shared library and the folder-level shared library point to the same code. When called from a folder-level shared library I receive this error :
CpsCallableInvocation{methodName=getInstance, call=null, receiver=class abc.esmm.JiraCommands, arguments=[]}
Finished: FAILURE
Does anyone know why this is happening ?
I have a Jenkins pipeline, it imports a shared library which in turn "grabs" a dependency. I want to call a method on the class which takes a File parameter.
But the file doesn't exist when I try to access it in the shared library.
The Pipeline
#Library('my shared library')
pipeline {
// ...
stages {
stage {
steps {
script {
f = new File("foo.txt")
libFn(myFile: f)
}
}
}
}
}
The Jenkins Shared Library
// ./vars/libFn.groovy
#Grab("3rd party java library")
import LibraryClass
def call(Map params) {
LibraryClass libraryClass = new LibraryClass();
libraryClass.processFile(params.myFile);
}
The 3rd Party Java Library
public class LibraryClass {
public void processFile(File file) {
// do something with a file
}
}
Currently coding a lot of groovy for very specific jenkins scenarios.
The problem is that I have to keep track of the current CpsScript-instance for the context (getting properties, the environment and so on) and its invokeMethod (workflow steps and the likes).
Currently this means I pass this in the pipeline groovy script onto my entry class and from there it's passed on to every class separately, which is very annoying.
The script instance is created by the CpsFlowExecution and stored within the Continuable-instance and the CpsThreadGroup, neither of which allow you to retrieve it.
Seems that GlobalVariable derived extensions receive it so that they have a context but I'm currently not knowledgeable enough to write my own extension to leverage that.
So the question is:
Does anyone know of a way to keep track of the CpsScript-instance that doesn't require me to pass it on to every new class I create? (Or alternatively: obtain it from anywhere - does this really need to be so hard?)
Continued looking into ways to accomplish this. Even wrote a jenkins plugin that provides an cpsScript global variable. Unfortunately you need the instance to provide a context for that call, so it's useless.
So as the "least bad solution"(tm) I created a class I called ScriptContext that I can use as a base class for my pipeline classes (It implements Serializable).
When you write your pipeline script you either pass it the CpsScript statically once:
ScriptContext.script = this
Or, if you derived from it (make sure to call super()):
new MyPipeline(this)
If your class is derived from the ScriptContext your work is done. Everything will work as though you didn't create a class but just used the automagic conversion. If you use any CpsScript-level functions besides println, you might want to add these in here as well.
Anywhere else you can just call ScriptContext.script to get the script instance.
The class code (removed most of the comments to keep it as short as possible):
package ...
import org.jenkinsci.plugins.workflow.cps.*
class ScriptContext implements Serializable {
protected static CpsScript _script = null
ScriptContext(CpsScript script = null) {
if (!_script && script) {
_script = script
}
}
ScriptContext withScript(CpsScript script) {
setScript(script)
this
}
static void setScript(CpsScript script) {
if (!_script && script) {
_script = script
}
}
static CpsScript getScript()
{
_script
}
// functions defined in CpsScript itself are not automatically found
void println(what) {
_script.println(what)
}
/**
* For derived classes we provide missing method functionality by trying to
* invoke the method in script context
*/
def methodMissing(String name, args) {
if (!_script) {
throw new GroovyRuntimeException('ScriptContext: No script instance available.')
}
return _script.invokeMethod(name, args)
}
/**
* For derived classes we provide missing property functionality.
* Note: Since it's sometimes unclear whether a property is an actual property or
* just a function name without brackets, use evaluate for this instead of getProperty.
* #param name
* #param args
* #return
*/
def propertyMissing(String name) {
if (!_script) {
throw new GroovyRuntimeException('ScriptContext: No script instance available.')
}
_script.evaluate(name)
}
/**
* Wrap in node if needed
* #param body
* #return
*/
protected <V> V node(Closure<V> body) {
if (_script.env.NODE_NAME != null) {
// Already inside a node block.
body()
} else {
_script.node {
body()
}
}
}
}
I recently started with Jenkins shared libraries in Jenkins pipeline.
I created a "func.groov" class and located it under "src/org/prj/func.groovy" :
package org.prj
import jenkins.model.
class func implements Serializable {
def steps
func(steps) {
this.steps = steps
}
def sh(args) {
steps.sh "echo ${args}"
}
def setLBL(CurrentNodeName,newLabelName){
jenkins.model.Jenkins.instance.slaves.each{ slave ->
if (slave.getNodeName() == CurrentNodeName){
slave.setLabelString(newLabelName)
}
}
}
Jenkinsfile (scripted) looks like:
#Library('prj') import org.prj.func
def utils = new func(steps)
node(lbl)
{
stage("A"){
Build_node_lbl = env.NODE_NAME+System.currentTimeMillis()
utils.setLBL(env.NODE_NAME,Build_node_lbl)
}
}
so currently it works. my question is how to create a full stage (like "A") as a function in func.groovy shared lib which will include, for example:
GIT checkout step
sh compilation step
Artifactory deploy step
Im actually looking to create a "building blocks" (a "Build" in my example) with Jenkins pipeline and shard libraries.
1. With Class Instantiation
You can create a class like you would do in Java. Then in your Jenkinsfile you instantiate the class and call its function.
src/org/prj/MyPipeline.groovy:
package org.prj
class MyPipeline {
def steps
MyPipeline(steps) {this.steps = steps}
public def build() {
steps.node('lbl') {
steps.stage('A') {
// Do build stuff
// steps.sh(..)
}
}
}
}
Jenkinsfile:
import org.prj.MyPipeline
def pipeline = new MyPipeline(this)
pipeline.build()
2. With Static Functions
You may also work with static contexts, without instantiation. However, this would require to hand over the caller context to the pipeline:
src/org/prj/MyPipeline.groovy:
package org.prj
class MyPipeline {
public static def build(caller) {
caller.node('lbl') {
caller.stage('A') {
// Do build stuff
caller.sh(..)
}
}
}
}
Jenkinsfile:
import org.prj.MyPipeline
MyPipeline.build(this)