Pass parameters to Jenkins shared library - jenkins

I just simply want to pass the repo name cloud-nates in shared pipeline, so i've passed the parameter deployName from jenkinsfile to shared library. below is the Jenkinsfile
#Library("minePipelines#auto") _
if (env.BRANCH_NAME in ["auto", "stage"]) {
reloadDeploy {
deployName = "cloud-nates"
}
}
And below is the shared-pipeline reloadDeploy.groovy code :
def call(body) {
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
body()
properties([disableConcurrentBuilds()])
node("ops") {
timeout(unit: 'SECONDS', time: 60) {
stage("Reload Deployment") {
echo params.deployName
}
}
}
}
This prints null in console o/p. i've googled it but no luck :(
please feel free to ask any doubts.

Related

Jenkins custom DSL Cannot invoke method image() on null object

I'm trying to define custom DSL refer https://www.jenkins.io/doc/book/pipeline/shared-libraries/#defining-custom-steps
it seems work if just define simple command in {}
but failed when use complicated command
(root)
+- vars
| +- shareL.groovy
| +- xxx.groovy
| +- monitorStep.groovy
shareL.groovy
def install(){
print "test install"
}
def checkout(){
print "test checkout"
}
monitorStep.groovy
def call(body) {
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
// This is where the magic happens - put your pipeline snippets in here, get variables from config.
script {
def status
def failed_cause=''
try{
body()
} catch(e){
status = 'fail'
failed_cause = "${e}"
throw e
}finally {
def myDataMap = [:]
myDataMap['stage'] = STAGE_NAME
myDataMap['status'] = status
myDataMap['failed_cause'] = failed_cause
influxDbPublisher selectedTarget: 'myTest', measurementName: 'myTestTable',customData: myDataMap
}
}
}
Jenkinsfile
#!groovy
#Library('myShareLibray#') _
pipeline {
stages{
stage('Checkout') {
steps {
script {
monitorStep{
shareL.checkout()
}
}
}
}
stage('Install') {
steps {
script {
monitorStep{
docker.image("node").inside(){
shareL.install()
}
}
}
}
}
}
}
first stage failed with
java.lang.NullPointerException: Cannot invoke method checkout() on null object
second stage failed with
java.lang.NullPointerException: Cannot invoke method image() on null object
The problem is that the closure cannot find the name shareL which should be accessible in the closure's delegate, which is in our case the map config.
You need to redeclare the map to expose the name shareL and additionally a second name install which must be invokable.
The solution is to rewrite the map like this:
def config = [ shareL : [install: { println "Inside the map" }] ]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
body()
Then when you call body() it will find shareL.install() but this will not point to the call() method in the shareL.groovy but to the property in the map.

Passing environment variable as a pipeline parameter to Jenkins shared library

I have a shared Jenkins library that has my pipeline for Jenkinsfile. The library is structured as follows:
myPipeline.groovy file
def call(body) {
def params= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = params
body()
pipeline {
// My entire pipeline is here
// Demo stage
stage("Something"){
steps{
script{
projectName = params.name
}
}
}
}
}
And my Jenkinsfile is as follows:
Jenkinsfile
#Library("some-shared-lib") _
myPipeline{
name = "Some name"
}
Now, I would like to replace "Some name" string with "env.JOB_NAME" command. Normally in Jenkinsfile, I would use name = "${env.JOB_NAME}" to get the info, but because I am using my shared library instead, it failed to work. Error message is as follows:
java.lang.NullPointerException: Cannot get property 'JOB_NAME' on null object
I tried to play around with brackets and other notation but never got it to work. I think that I incorrectly pass a parameter. I would like Jenkinsfile to assign "${env.JOB_NAME}" to projectName variable, once library runs the pipeline that I am calling (via myPipeline{} command)
You can do like this in myPipeline.groovy:
def call(body) {
def params= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = params
body()
pipeline {
// My entire pipeline is here
// Demo stage
stage("Something"){
steps{
script{
projectName = "${env.JOB_NAME}"
}
}
}
}
}

How to pass and invoke a method utility to Jenkins template?

I have this template:
def call(body) {
def pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
pipeline {
agent any
....
stages {
stage('My stages') {
steps {
script {
pipelineParams.stagesParams.each { k, v ->
stage("$k") {
$v
}
}
}
}
}
}
post { ... }
}
}
Then I use the template in a pipeline:
#Library('pipeline-library') _
pipelineTemplateBasic {
stagesParams = [
'First stage': sh "do something...",
'Second stage': myCustomCommand("foo","bar")
]
}
In the stagesParams I pass the instances of my command (sh and myCustomCommand) and they land in the template as $v. How can I then execute them? Some sort of InvokeMethod($v)?
At the moment I am getting this error:
org.jenkinsci.plugins.workflow.steps.MissingContextVariableException: Required context class hudson.FilePath is missing
Perhaps you forgot to surround the code with a step that provides this, such as: node
The problem of using node is that it doesn't work in situations like parallel:
parallelStages = [:]
v.each { k2, v2 ->
parallelStages["$k2"] = {
// node {
stage("$k2") {
notifySlackStartStage()
$v2
checkLog()
}
// }
}
}
If you want to execute sh step provided with a map, you need to store map values as closures, e.g.
#Library('pipeline-library') _
pipelineTemplateBasic {
stagesParams = [
'First stage': {
sh "do something..."
}
'Second stage': {
myCustomCommand("foo","bar")
}
]
}
Then in the script part of your pipeline stage you will need to execute the closure, but also set the delegate and delegation strategy to the workflow script, e.g.
script {
pipelineParams.stagesParams.each { k, v ->
stage("$k") {
v.resolveStrategy = Closure.DELEGATE_FIRST
v.delegate = this
v.call()
}
}
}

Groovy Nested Closures - how to pass a hash?

I'd like to load a closure for my Jenkins build, but pass it some variables that are generic to any type of build (Go, Java, Docker) that are going on on our system. Since I'm loading the specific closure from a separate groovy file, it doesn't see those variables. For the purposes of making a simpler example, I've commented out the load and included that closure.
I'm a little unsure about how to do this - how do I pass the config from buildProject to buildSpecificProject? Am I referring to it wrong?
#!/usr/bin/groovy
//def buildSpecificProject = load 'buildSpecificProject.groovy'
def buildSpecificProject = { body->
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
body()
println config.name
println config.builddirectory
}
def buildProject = { projbody ->
def config = [:]
projbody.resolveStrategy = Closure.DELEGATE_FIRST
projbody.delegate = config
projbody()
config.builddirectory = "/bar"
return config
}
try {
def newProjectVersion = buildSpecificProject { body ->
buildProject { projbody ->
name = 'projectname'
versionPrefix = "4.2.0"
fetchFromURL = 'git#github.com:myorg/myproject.git'
}
}
println "New Project Version = ${newProjectVersion}\n"
} catch (err) {
println err
}
I have no experience with build scripts in Jenkins really, so maybe my answer is not applicable, but from a Groovy only perspective the situation is the following:
The config variable in buildSpecificProject is a local variable, to which itself you have no access, unless you expose it or its value. Right now you do that through the setting the delegate actually.
If we say buildProject is only called from within a block given to buildSpecificProject, then the block given to buildProject is a Closure nested into the Closure given to buildSpecificProject. This Closure object will have a property owner, which will refer to the enclosing Closure instance in this case (see http://groovy-lang.org/closures.html#_owner_of_a_closure). Of this we know it has set the configuration as delegate, thus you can do projbody.owner.delegate to access the configuration set by buildSpecificProject.
But actually I would consider doing something like this:
def buildSpecificProject = { body ->
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = [config: config] // expose config
body()
println config.name
println config.builddirectory
return config
}
def buildProject = { config, projbody ->
projbody()
config.builddirectory = "/bar"
}
try {
def newProjectVersion = buildSpecificProject { body ->
// make config accessible to buildProject by providing it as parameter
buildProject(config) { projbody ->
config.name = 'projectname'
config.versionPrefix = "4.2.0"
config.fetchFromURL = 'git#github.com:myorg/myproject.git'
}
}
println "New Project Version = ${newProjectVersion}\n"
} catch (err) {
println err
}
As you can see it is actually enough for buildSpecificProject to set the delegate, which I set to a map with one key named config, containing the actual config. The disadvantage is of course, that you now have to do config.name. Also note the call "buildProject(config) { projbody ->", which gives the config to buildProject. And of course we can combine both ideas:
def buildSpecificProject = { body ->
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
body()
println config.name
println config.builddirectory
return config
}
def buildProject = { config, projbody ->
projbody()
config.builddirectory = "/bar"
}
try {
def newProjectVersion = buildSpecificProject {
buildProject(delegate) { projbody ->
name = 'projectname'
versionPrefix = "4.2.0"
fetchFromURL = 'git#github.com:myorg/myproject.git'
}
}
println "New Project Version = ${newProjectVersion}\n"
} catch (err) {
println err
}
But I would recommend this not so much, as I do not like to depend on the delegate in this manner. I can break too easily.

How can I define a custom pipeline directive?

For example something like:
pipeline {
customDirective {
sh "env"
..
}
}
This is currently not possible. You can only define custom pipeline steps via a Shared Library and use them within the stage/steps section and in the condition block of the post section. If you need for any reasons more customization you would have to have a look into the Scripted pipeline syntax. It allows to use most functionality of Groovy and is therefore very flexible.
This works:
customDirective.groovy (Shared Library)
def call(Closure body) {
def config = [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = config
body()
config.script()
}
Jenkinsfile
customDirective {
url="http://something.another.com"
title="The Title"
script = {
sh "env"
}
}

Resources