Access a groovy variable from within shell in Jenkins pipeline - jenkins

This answer did not help me
Below is the method in groovy:
def analyze(repoName){
result= sh (
script: '''
cd ${WORKSPACE}/${BUILD_NUMBER}
cat > sonar-project.properties << EOF_$$
sonar.projectKey=ABC-$repoName
sonar.projectName=ABC
sonar.projectBaseDir=${WORKSPACE}/${BUILD_NUMBER}
EOF_$$
''',
returnStatus: true
) == 0
print "Creating file - Return status: ${result}"
}
where below line gives error:
sonar.projectKey=ABC-$repoName
properties file gets created with entry sonar.projectKey=ABC-
How to use groovy variable in sh() step?

You should double quotes for string interpolation and escape $ by \$ in following places:
${WORKSPACE} and ${BUILD_NUMBER}, you intent to use them as bash environment variable, rather than groovy variable
EOF_$$, you intent to use it literal meaning
Changed code:
def analyze(repoName){
result= sh (
script: """
cd \${WORKSPACE}/\${BUILD_NUMBER}
cat > sonar-project.properties << EOF_\$\$
sonar.projectKey=ABC-$repoName
sonar.projectName=ABC
sonar.projectBaseDir=\${WORKSPACE}/\${BUILD_NUMBER}
EOF_\$\$
""",
returnStatus: true
) == 0
print "Creating file - Return status: ${result}"
}

You should use double quotes for string interpolation, so just replace '''with """
And change EOF_$$ to EOF_\$\$

Related

Script in Jenkins file giving exit code 1

I want to get a line from a file in my workspace. I am using this script :
stage('Test') {
steps {
script {
outputJenkins = 'output-jenkins.log'
sh "cd invoker && mvn clean install && mvn exec:java -Dexec.mainClass=\"com.JenkinsRunner\" -Dexec.args=\"qal ${GIT_COMMIT_HASH}\" > ../${outputJenkins}"
logFile = readFile(outputJenkins)
echo logFile
adminRepoLogLine = sh "echo logFile | grep \"Admin repo url is :::\""
echo adminRepoLogLine
}
}
}
But I am getting this error:
+ echo logFile
+ grep Admin repo url is :::
script returned exit code 1
The script works fine in my shell when I try it locally. Are there any contains around doing it in a JenkinsFile?
If we apply various fixes and improvements to the code in the question to achieve the desired functionality, then it will succeed:
stage('Test') {
steps {
script {
dir('invoker') {
sh(label: 'Maven Clean Install', script: 'mvn clean install')
// assign maven output to variable
String output = sh(label: 'Maven Git Log', script: "mvn exec:java -Dexec.mainClass=\"com.JenkinsRunner\" -Dexec.args=\"qal ${GIT_COMMIT_HASH}\"", returnStdout: true)
}
// assign regex return to variable
def adminRepoLogLine = output =~ /(.*Admin repo url is :::.*)/
// print extracted string from return
print adminRepoLogLine[0][1]
}
}
}
Note that GIT_COMMIT_HASH is neither an intrinsic Jenkins environment variable, nor defined in the pipeline code in the question, so it will need to be defined at Pipeline scope elsewhere in your code.
This is because the string literal logFile does not contain the string Admin repo url is :::. If there's no such match, then grep will exit with status 1.
You probably want to use
cat logFile | grep \"Admin repo url is :::\"
instead, or, even simpler:
grep \"Admin repo url is :::\" logFile
Append || true (or ||:) to the command if you want to avoid the errors when the log line does not appear.

How to use declared variables in when conditional step?

I am declaring a variable outside the pipeline tags and initializing in one of the stages inside script using readFile. When I try to use this declared variable inside when condition, the when condition always skips.
When I initialize the variable with var1='myValue' and check for
when{ expression{var1=='myValue'} } works .
I tried making the varible into environment variable but not else works
def test1=''
pipeline{
agent { label 'local'}
stages{
stage('initialization'){
steps{
sh """echo "myValue" > user_file
pwd
"""
script{
test1=readFile file: 'user_file
//env.TEST=readFile file: 'user_file', encoding: 'UTF-8' doesn't work
//test1='myValue' only this works
}
}
}
stage('test'){
when {
expression { return test1 == 'myValue'}
//environment name: 'TEST', value: 'myValue'
}
steps{
echo " user :${env.TEST}"
//this step always gets skipped because of when condition above
}
}
}
}
I expect the steps under when to run, but it gets skips every time.
As Matt commented on the question, I had a new line character in the file. The new line character was introduced by echo by default. I had to suppress it using the -n option from echo.
echo -n "myValue" > user_file
The above command only puts the value without the newline in the file
Sorry that my shell-foo is bad and didn't realize that echo puts in a new line by default.

Jenkinsfile ${steps.env.BUILD_NUMBER}: bad substitution

I am trying to print a variable in Jenkins. But I am getting an error saying "bad substitution". I am using Jenkinsfile to achieve that. This is what I am doing.
static def printbn() {
sh '''
#!/usr/bin/env bash
echo \"${env.BUILD_NUMBER}\"
'''
}
pipeline {
agent any
stages {
stage('Print Build Number') {
steps {
printbn()
}
}
}
}
Error that I am getting
/var/lib/jenkins/workspace/groovymethod#tmp/durable-7d9ef0b0/script.sh: line 4: ${steps.env.BUILD_NUMBER}: bad substitution
NOTE: I am using Jenkins version Jenkins ver. 2.163
In Shell, variable name is not allow use ., that's why you get following error: bad substitution
In Groovy, there are 4 ways to represent a string:
single quote: ' a string '
tripe single quote: ''' a string '''
double quote: " a string "
tripe double quote: """ a string """
And Groovy only execute string interpolation on double and triple double quote string.
For example:
def name = 'Tom'
print "Hello ${name}"
print """Hello ${name}"""
// do interpolation before print, thus get Hello Tom printed out
print 'Hello ${name}'
print '''Hello ${name}'''
//no interpolation thus, print Hello ${name} out directly.
BUILD_NUMBER is Jenkins job's build-in environment variable. You can directly access it in shell/bat.
static def printbn() {
sh '''
#!/usr/bin/env bash
echo ${BUILD_NUMBER}
// directly access any Jenkins build-in environment variable,
// no need to use pattern `env.xxxx` which only works in groovy not in shell/bat
'''
}
If you want use env.xxxx pattern, you can archive that via groovy string interpolation.
static def printbn() {
// use pipeline step: echo
echo "${env.BUILD_NUMBER}" // env.BUILD_NUMBER is groovy variable
// or use pipeline step: sh
sh """#!/usr/bin/env bash
echo ${env.BUILD_NUMBER}
"""
// will do interpolation firstly, to replace ${env.BUILD_NUMBER} with real value
// then execute the whole shell script.
}

Is it possible to capture the stdout from the sh DSL command in the pipeline

For example:
var output=sh "echo foo";
echo "output=$output";
I will get:
output=0
So, apparently I get the exit code rather than the stdout. Is it possible to capture the stdout into a pipeline variable, such that I could get:
output=foo
as my result?
Now, the sh step supports returning stdout by supplying the parameter returnStdout.
// These should all be performed at the point where you've
// checked out your sources on the slave. A 'git' executable
// must be available.
// Most typical, if you're not cloning into a sub directory
gitCommit = sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
// short SHA, possibly better for chat notifications, etc.
shortCommit = gitCommit.take(6)
See this example.
Note: The linked Jenkins issue has since been solved.
As mention in JENKINS-26133 it was not possible to get shell output as a variable. As a workaround suggested using of writ-read from temporary file. So, your example would have looked like:
sh "echo foo > result";
def output=readFile('result').trim()
echo "output=$output";
Try this:
def get_git_sha(git_dir='') {
dir(git_dir) {
return sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
}
}
node(BUILD_NODE) {
...
repo_SHA = get_git_sha('src/FooBar.git')
echo repo_SHA
...
}
Tested on:
Jenkins ver. 2.19.1
Pipeline 2.4
You can try to use as well this functions to capture StdErr StdOut and return code.
def runShell(String command){
def responseCode = sh returnStatus: true, script: "${command} &> tmp.txt"
def output = readFile(file: "tmp.txt")
if (responseCode != 0){
println "[ERROR] ${output}"
throw new Exception("${output}")
}else{
return "${output}"
}
}
Notice:
&>name means 1>name 2>name -- redirect stdout and stderr to the file name
I had the same issue and tried almost everything then found after I came to know I was trying it in the wrong block. I was trying it in steps block whereas it needs to be in the environment block.
stage('Release') {
environment {
my_var = sh(script: "/bin/bash ${assign_version} || ls ", , returnStdout: true).trim()
}
steps {
println my_var
}
}
A short version would be:
echo sh(script: 'ls -al', returnStdout: true).result
def listing = sh script: 'ls -la /', returnStdout:true
Reference : http://shop.oreilly.com/product/0636920064602.do Page 433

How to access parameters in a Parameterized Build?

How do you access parameters set in the "This build is parameterized" section of a "Workflow" Jenkins job?
TEST CASE
Create a WORKFLOW job.
Enable "This build is parameterized".
Add a STRING PARAMETER foo with default value bar text.
Add the code below to Workflow Script:
node()
{
print "DEBUG: parameter foo = ${env.foo}"
}
Run job.
RESULT
DEBUG: parameter foo = null
I think the variable is available directly, rather than through env, when using Workflow plugin.
Try:
node()
{
print "DEBUG: parameter foo = ${foo}"
}
I tried a few of the solutions from this thread. It seemed to work, but my values were always true and I also encountered the following issue:
JENKINS-40235
I managed to use parameters in groovy jenkinsfile using the following syntax: params.myVariable
Here's a working example:
Solution
print 'DEBUG: parameter isFoo = ' + params.isFoo
print "DEBUG: parameter isFoo = ${params.isFoo}"
A more detailed (and working) example:
node() {
// adds job parameters within jenkinsfile
properties([
parameters([
booleanParam(
defaultValue: false,
description: 'isFoo should be false',
name: 'isFoo'
),
booleanParam(
defaultValue: true,
description: 'isBar should be true',
name: 'isBar'
),
])
])
// test the false value
print 'DEBUG: parameter isFoo = ' + params.isFoo
print "DEBUG: parameter isFoo = ${params.isFoo}"
sh "echo sh isFoo is ${params.isFoo}"
if (params.isFoo) { print "THIS SHOULD NOT DISPLAY" }
// test the true value
print 'DEBUG: parameter isBar = ' + params.isBar
print "DEBUG: parameter isBar = ${params.isBar}"
sh "echo sh isBar is ${params.isBar}"
if (params.isBar) { print "this should display" }
}
Output
[Pipeline] {
[Pipeline] properties
WARNING: The properties step will remove all JobPropertys currently configured in this job, either from the UI or from an earlier properties step.
This includes configuration for discarding old builds, parameters, concurrent builds and build triggers.
WARNING: Removing existing job property 'This project is parameterized'
WARNING: Removing existing job property 'Build triggers'
[Pipeline] echo
DEBUG: parameter isFoo = false
[Pipeline] echo
DEBUG: parameter isFoo = false
[Pipeline] sh
[wegotrade-test-job] Running shell script
+ echo sh isFoo is false
sh isFoo is false
[Pipeline] echo
DEBUG: parameter isBar = true
[Pipeline] echo
DEBUG: parameter isBar = true
[Pipeline] sh
[wegotrade-test-job] Running shell script
+ echo sh isBar is true
sh isBar is true
[Pipeline] echo
this should display
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
I sent a Pull Request to update the misleading pipeline tutorial#build-parameters quote that says "they are accessible as Groovy variables of the same name.". ;)
Edit: As Jesse Glick pointed out:
Release notes go into more details
You should also update the Pipeline Job Plugin to 2.7 or later, so that build parameters are defined as environment variables and thus accessible as if they were global Groovy variables.
When you add a build parameter, foo,
it gets converted to something which acts like a "bare variable",
so in your script you would do:
node {
echo foo
}
If you look at the implementation of the workflow script, you will see that when a script is executed, a class called WorkflowScript is
dynamically generated. All statements in the script are executed in the context of this class. All build parameters passed down to this script are converted to properties which are accessible from this class.
For example, you can do:
node {
getProperty("foo")
}
If you are curious, here is a workflow script I wrote which attempts to print out the build parameters, environment variables, and methods on the WorkflowScript class.
node {
echo "I am a "+getClass().getName()
echo "PARAMETERS"
echo "=========="
echo getBinding().getVariables().getClass().getName()
def myvariables = getBinding().getVariables()
for (v in myvariables) {
echo "${v} " + myvariables.get(v)
}
echo STRING_PARAM1.getClass().getName()
echo "METHODS"
echo "======="
def methods = getMetaClass().getMethods()
for (method in methods) {
echo method.getName()
}
echo "PROPERTIES"
echo "=========="
properties.each{ k, v ->
println "${k} ${v}"
}
echo properties
echo properties["class"].getName()
echo "ENVIRONMENT VARIABLES"
echo "======================"
echo "env is " + env.getClass().getName()
def envvars = env.getEnvironment()
envvars.each{ k, v ->
println "${k} ${v}"
}
}
Here is another code example I tried, where I wanted to test to see
if a build parameter was set or not.
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"
}
}
Use double quotes instead of single quotes
e.g. echo "$foo" as opposed to echo '$foo'
If you configured your pipeline to accept parameters using the Build with Parameters option, those parameters are accessible as Groovy variables of the same name. See Here.
You can drop the semicolon (;), drop the parentheses (( and )), and use single quotes (') instead of double (") if you do not need to perform variable substitutions. See Here. This clued me into my problem, though I've found that only the double (") is required to make it work.
To parameter variable add prefix "params."
For example:
params.myParam
Don't forget: if you use some method of myParam, may be you should approve it in "Script approval".
You can also try using parameters directive for making your build parameterized and accessing parameters:
Doc:
Pipeline syntax: Parameters
Example:
pipeline{
agent { node { label 'test' } }
options { skipDefaultCheckout() }
parameters {
string(name: 'suiteFile', defaultValue: '', description: 'Suite File')
}
stages{
stage('Initialize'){
steps{
echo "${params.suiteFile}"
}
}
}
Hope the following piece of code works for you:
def item = hudson.model.Hudson.instance.getItem('MyJob')
def value = item.lastBuild.getEnvironment(null).get('foo')
The following snippet gives you access to all Job params
def myparams = currentBuild.rawBuild.getAction(ParametersAction)
for( p in myparams ) {
pMap[p.name.toString()] = p.value.toString()
}
Please note, the way that build parameters are accessed inside pipeline scripts (pipeline plugin) has changed. This approach:
getBinding().hasVariable("MY_PARAM")
Is not working anymore. Please try this instead:
def myBool = env.getEnvironment().containsKey("MY_BOOL") ? Boolean.parseBoolean("$env.MY_BOOL") : false
As per Pipeline plugin tutorial:
If you have configured your pipeline to accept parameters when it is built — Build with Parameters — they are accessible as Groovy variables of the same name.
So try to access the variable directly, e.g.:
node()
{
print "DEBUG: parameter foo = " + foo
print "DEBUG: parameter bar = ${bar}"
}

Resources