Does Groovy support positional arguments?
I have a function defined in a Jenkins shared library name.groovy
def call(name, age) {
sh """
echo "My name is: ${name}"
echo "My age is: ${age}"
"""
}
And when I call it from the pipeline
stage ('Shared Library') {
steps {
name("Foo", "21")
}
}
It works perfectly and I get
My name is Foo
My age is 21
However, I would like to set the arguments positionally so that it prints correctly regardless of how they're placed.
But when I do
stage ('Shared Library') {
steps {
name(age: "21", name: "Foo")
}
}
I get
My name is null
My age is null
Is there a way to set this correctly?
What you are asking here is how to use a Map type as the input argument for your custom step method global variable. age: "21", name: "Foo" would be a Map in this situation. You can refactor the global variable method for your custom step like this:
def call(Map args) {
sh """
echo "My name is: ${args.name}"
echo "My age is: ${args.age}"
"""
}
and then you could invoke in your pipeline steps:
stage ('Shared Library') {
steps {
name(age: '21', name: 'Foo')
}
}
with the expected results.
We could also improve the method with some intrinsic Groovy methods and argument checks with the null coalescing operator:
def call(Map args) {
// provide defaults for arguments
args.name = args.name ?: 'no name'
args.age = args.age ?: 'unknown age'
// output name and age to stdout
print "My name is: ${args.name}"
print "My age is: ${args.age}"
}
Related
How can I convert all the parameters in a Jenkins pipeline to lowercase. Similar to trim, is there an attribute that one could add as part of the parameter declaration,
For trim, I have something like below,
parameters {
string defaultValue: '', description: 'Some dummy parameter', name: 'someparameter', trim: true
}
In my pipeline job, I have more than 10 string parameters and would like to convert them all to lowercase
Here's one approach:
pipeline {
agent any
parameters {
string ( name: 'testName', description: 'name of the test to run')
}
stages {
stage('only') {
environment {
TEST_NAME=params.testName.toLowerCase()
}
steps {
echo "the name of the test to run is: ${params.testName}"
sh 'echo "In Lower Case the test name is: ${TEST_NAME}"'
}
}
}
}
sh """ ${the_parameter.toLowerCase()} """
Need to use double quote so you have a GString
Put the toLowerCase() function call inside the brace for shell to refer back to groovy
Actually one can just do
VAR = "${VAR.toLowerCase()}"
Had to use this for my use case. It will not convert but it will prevent passing wrong value.
validatingString(name: "MYVAR",
defaultValue: "",
regex: /^[a-z0-9]+$/,
failedValidationMessage: "",
description: "")
I'm trying to convert my jenkins pipeline to a shared library since it can be reusable on most of the application. As part of that i have created groovy file in vars folder and kept pipeline in jenkins file in github and able to call that in jenkins successfully
As part of improving this i want to pass params, variables, node labels through a file so that we should not touch jenkins pipeline and if we want to modify any vars, params, we have to do that in git repo itself
pipeline {
agent
{
node
{
label 'jks_deployment'
}
}
environment{
ENV_CONFIG_ID = 'jenkins-prod'
ENV_CONFIG_FILE = 'test.groovy'
ENV_PLAYBOOK_NAME = 'test.tar.gz'
}
parameters {
string (
defaultValue: 'test.x86_64',
description: 'Enter app version',
name: 'app_version'
)
choice (
choices: ['10.0.0.1','10.0.0.2','10.0.0.3'],
description: 'Select a host to be delpoyed',
name: 'host'
)
}
stages {
stage("reading properties from properties file") {
steps {
// Use a script block to do custom scripting
script {
def props = readProperties file: 'extravars.properties'
env.var1 = props.var1
env.var2 = props.var2
}
echo "The variable 1 value is $var1"
echo "The variable 2 value is $var2"
}
In above code,i used pipeline utility steps plugin and able to read variables from extravars.properties file. Is it same way we can do for jenkins parameters also? Or do we have any suitable method to take care of passing this parameters via a file from git repo?
Also is it possible to pass variable for node label also?
=====================================================================
Below are the improvements which i have made in this project
Used node label plugin to pass the node name as variable
Below is my vars/sayHello.groovy file content
def call(body) {
// evaluate the body block, and collect configuration into the object
def pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
pipeline {
agent
{
node
{
label "${pipelineParams.slaveName}"
}
}
stages {
stage("reading properties from properties file") {
steps {
// Use a script block to do custom scripting
script {
// def props = readProperties file: 'extravars.properties'
// script {
readProperties(file: 'extravars.properties').each {key, value -> env[key] = value }
//}
// env.var1 = props.var1
// env.var2 = props.var2
}
echo "The variable 1 value is $var1"
echo "The variable 2 value is $var2"
}
}
stage ('stage2') {
steps {
sh "echo ${var1}"
sh "echo ${var2}"
sh "echo ${pipelineParams.appVersion}"
sh "echo ${pipelineParams.hostIp}"
}
}
}
}
}
Below is my vars/params.groovy file
properties( [
parameters([
choice(choices: ['10.80.66.171','10.80.67.6','10.80.67.200'], description: 'Select a host to be delpoyed', name: 'host')
,string(defaultValue: 'fxxxxx.x86_64', description: 'Enter app version', name: 'app_version')
])
] )
Below is my jenkinsfile
def _hostIp = params.host
def _appVersion = params.app_version
sayHello {
slaveName = 'master'
hostIp = _hostIp
appVersion = _appVersion
}
Now Is it till we can improve this?Any suggestions let me know.
I have the below Jenkins pipeline and I am trying to echo out SolutonName and TargetVersion value. I tried different approaches and it either gave me error or not the result I wanted.
If I use echo "Solution Name: $solution['SolutionName']", it gave a result of Solution Name: SolutionA={SolutionName=SolutionA, TargetVersion=1.0.0.0}['SolutionName'], which is the map itself with ['SolutionName'] at the end.
If I use echo "Solution Name: ${solution.SolutionName}", it throws an error of org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such field found: field java.util.AbstractMap$SimpleImmutableEntry SolutionName
def NodeLabel = 'windows'
// Solution
def SolutionMap = [
SolutionA: [
SolutionName: 'SolutionA',
TargetVersion: '1.0.0.0'
],
SolutionB: [
SolutionName: 'SolutionB',
TargetVersion: '2.1.0.0'
]
]
pipeline {
agent { node { label "${NodeLabel}" } }
stages {
stage('Test') {
steps {
script {
SolutionMap.each { solution ->
stage(solution.key) {
echo "Solution Name: ${solution['SolutionName']}"
echo "Solution Name: ${solution['TargetVersion']}"
}
}
}
}
}
}
}
I figured it out, apparently I need to call:
echo "Solution Name: $Solution.value.SolutionName"
So it seems like calling $Solution does not assume it wants its value so I need to call $Solution.value to get the value and from there call .SolutionName to get the child value.
I am set a string parameter in Jenkins pipeline(groovy script) like this:
def call(String type, Map map) {
if (type == "gradle") {
pipeline {
agent any
parameters {
string(name: 'k8sResourceType', defaultValue: "${map.k8sResourceType}", description: 'Kubernetes Resource Type')
}
}
}
is it possible to set a default value when ${map.k8sResourceType} is null? if the ${map.k8sResourceType} is null set the Kubernetes resource type to Deployment. Because 90% of apps are Deployment and only special apps are StatefulSet in Kubernetes. I am a newbie in groovy.
You better use environment instead of parameters to achieve what you want
pipeline {
agent any
environment {
k8sResourceType = getResourceType(map.k8sResourceType)
}
stages {
stage('Hello World') {
steps {
echo "Value: ${env.k8sResourceType}"
}
}
}
}
def getResourceType(value) {
return value == null ? "Deployment" : value
}
What is the best way that I can detect if a parameter in a parameterized build exists or not?
The closest solution I found was to do this in groovy:
node {
groovy.lang.Binding myBinding = getBinding()
boolean mybool = myBinding.hasVariable("STRING_PARAM1")
echo mybool.toString()
if (mybool) {
echo STRING_PARAM1
echo getProperty("STRING_PARAM1")
} else {
echo "STRING_PARAM1 is not defined"
}
mybool = myBinding.hasVariable("DID_NOT_DEFINE_THIS")
if (mybool) {
echo DID_NOT_DEFINE_THIS
echo getProperty("DID_NOT_DEFINE_THIS")
} else {
echo "DID_NOT_DEFINE_THIS is not defined"
}
}
Is using getBinding() the proper API to do this, or is there a better way?
You can use try-catch to check for the existence of a parameter:
try {
echo TEST1
echo 'TEST1 is defined'
} catch (err) {
echo 'TEST1 is not defined'
}
When you are using Pipelines then you have access to the object: params whichs is a Java map, then you can use: containsKey method, i.e:
if(params.containsKey("STRING_PARAM1")) {
echo "STRING_PARAM1 exists as parameter with value ${STRING_PARAM1}"
} else {
echo "STRING_PARAM1 is not defined"
}
When you're in Sandbox mode (or via a SCM), you're not allowed to use the getBinding(). At least, that's what I've run into so far.
What I've used so far is following method, in the workflow file, at the top I insert the following:
properties([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [[$class: 'StringParameterDefinition', defaultValue: 'default_value', description: '', name: 'your_parameter']]]])
This way your parameter will have a default value, which will be overridden when it is supplied as a build parameter.
You could use the params variable in the newer versions of Jenkins.
Here is how I read the PLATFORM parameter of a parametrized build. It is a String parameter.
def platform = params?.PLATFORM?.trim()
stage("checkPlatform") {
if (platform) {
echo "Going to build for platform: ${platform}"
// ...
} else {
echo "No platform given. Cancelling build"
error("No platform given")
}
}
stage("..."){
///...
}
There is a tutorial here: https://st-g.de/2016/12/parametrized-jenkins-pipelines
I wrote "newer versions of Jenkins" above. Here is the definition from that tutorial:
As of workflow-cps version 2.18, a new params global variable provides sane access also on the first run (by returning specified default values).