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

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.
}

Related

Accessing parameters in Jenkins

How do I access this variable in a shell script.
I have tried
echo params.$STATES;
echo $STATES;
Output for the first one is params. . Output for the second one is an empty string. The output I am expecting is the string I am passing when I build that job with parameters.
If you are using the 'Execute Shell' block in a Freestyle Project, you will need a $ (dollar sign) before the variable. Since you already tried this, there could be an issue not wrapping variable within {}
try echo ${STATES}
More info on curly braces around variables,
See: codeforester answer on usage of curly braces around shell variables
If you are using a Jenkinsfile without a shell block (valid in Scripted and Declarative)
Use dollar sign with double quotes
node(){
echo "$STATES"
}
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo "$STATES"
}
}
}
}
or without Double quotes and dollar sign
node(){
echo STATES
}
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo STATES
}
}
}
}
If you are using a Shell block in the Jenkinsfile pipeline, use
sh "echo $STATES" or sh "echo ${STATES}"
(as in the Freestyle Execute Shell block, Double quotes are needed for interpolation)
See
String interpolation in Jenkinsfiles
Handling parameters in Jenkinsfiles

Bad substitution when passing parameter to shell script in Jenkinsfile

In a Jenkinsfile I'm attempting to set an environment variable by setting the stdOut of a shell script. The script contains an AWS command that returns an InstanceID:
stage('Set InstanceID') {
steps {
script {
env.IID = sh (script: 'scripts/get-node-id.sh "${params.ENVIRONMENT}" "${params.NODE}"', returnStdout: true).trim()
}
}
}
No matter what I do or how many backslashes I use to escape the quotes, nothing works. I get a bad substitution error. I've also tried without double quotes.
If I hardcode in the shell script arguments, it runs fine.
How do I get this working if I want to use the parameter values here?
Groovy (the language of the Jenkinsfile) and Bash share the same substitution syntax. As you're using single quotes in your example code, the Groovy substitution does not work (see https://groovy-lang.org/syntax.html#_single_quoted_string). So Bash will try to do the substitution, but does not know these variables as they are Jenkins parameter values.
So solve this you need to use double quotes for your script and escape the double quotes in it (or use singe quotes):
stage('Set InstanceID') {
steps {
script {
env.IID = sh (script: "scripts/get-node-id.sh \"${params.ENVIRONMENT}\" \"${params.NODE}\"", returnStdout: true).trim()
}
}
}
I managed to resolve this, the comment from #yong above was almost what I needed. I needed three lots of double quotes and to unquote the variables:
stage('Set InstanceID') {
steps {
script {
env.IID = sh (script: """scripts/get-node-id.sh ${params.ENVIRONMENT} ${params.NODE}""", returnStdout: true).trim()
}
}
}

How to access groovy variable from pipeline into shell script?

I've a global variable in pipeline say BACKUP_DIR_NAME and in shell script which is inside pipeline, I want to build path using it hence have following code -
BACKUP_DIR_NAME="10-04-2020"
pipeline {
agent any
stages {
stage('First') {
steps {
script {
sh '''
BACKUP_DIR_PATH="/home/oracle/SeleniumFramework/SeleniumResultsBackup/"$BACKUP_DIR_NAME"/"
echo "Directory path is "$BACKUP_DIR_PATH
'''
}
}
}
}
}
When executed this, I can see value of BACKUP_DIR_NAME is evaluated as empty. Could you please help me to correct above code?
You mix two types of variables in your sh step. In the first line, you are trying to access the Groovy variable and interpolate its value to construct shell variable. In the second line, you expect to access this shell variable.
To satisfy the first part, you need to use double quotes to construct a Groovy string that supports variables interpolation. To satisfy the second part, you need to escape \$ to prevent $BACKUP_DIR_PATH from being interpolated.
BACKUP_DIR_NAME="10-04-2020"
pipeline {
agent any
stages {
stage('First') {
steps {
script {
sh """
BACKUP_DIR_PATH="/home/oracle/SeleniumFramework/SeleniumResultsBackup/"$BACKUP_DIR_NAME"/"
echo "Directory path is "\$BACKUP_DIR_PATH
"""
}
}
}
}
}

iterate over environment variables in Jenkins Pipeline Groovy [duplicate]

Given a jenkins build pipeline, jenkins injects a variable env into the node{}. Variable env holds environment variables and values.
I want to print all env properties within the jenkins pipeline. However, I do no not know all env properties ahead of time.
For example, environment variable BRANCH_NAME can be printed with code
node {
echo ${env.BRANCH_NAME}
...
But again, I don't know all variables ahead of time. I want code that handles that, something like
node {
for(e in env){
echo e + " is " + ${e}
}
...
which would echo something like
BRANCH_NAME is myBranch2
CHANGE_ID is 44
...
I used Jenkins 2.1 for this example.
According to Jenkins documentation for declarative pipeline:
sh 'printenv'
For Jenkins scripted pipeline:
echo sh(script: 'env|sort', returnStdout: true)
The above also sorts your env vars for convenience.
Another, more concise way:
node {
echo sh(returnStdout: true, script: 'env')
// ...
}
cf. https://jenkins.io/doc/pipeline/steps/workflow-durable-task-step/#code-sh-code-shell-script
The following works:
#NonCPS
def printParams() {
env.getEnvironment().each { name, value -> println "Name: $name -> Value $value" }
}
printParams()
Note that it will most probably fail on first execution and require you approve various groovy methods to run in jenkins sandbox. This is done in "manage jenkins/in-process script approval"
The list I got included:
BUILD_DISPLAY_NAME
BUILD_ID
BUILD_NUMBER
BUILD_TAG
BUILD_URL
CLASSPATH
HUDSON_HOME
HUDSON_SERVER_COOKIE
HUDSON_URL
JENKINS_HOME
JENKINS_SERVER_COOKIE
JENKINS_URL
JOB_BASE_NAME
JOB_NAME
JOB_URL
You can accomplish the result using sh/bat step and readFile:
node {
sh 'env > env.txt'
readFile('env.txt').split("\r?\n").each {
println it
}
}
Unfortunately env.getEnvironment() returns very limited map of environment variables.
Why all this complicatedness?
sh 'env'
does what you need (under *nix)
Cross-platform way of listing all environment variables:
if (isUnix()) {
sh env
}
else {
bat set
}
Here's a quick script you can add as a pipeline job to list all environment variables:
node {
echo(env.getEnvironment().collect({environmentVariable -> "${environmentVariable.key} = ${environmentVariable.value}"}).join("\n"))
echo(System.getenv().collect({environmentVariable -> "${environmentVariable.key} = ${environmentVariable.value}"}).join("\n"))
}
This will list both system and Jenkins variables.
I use Blue Ocean plugin and did not like each environment entry getting its own block. I want one block with all the lines.
Prints poorly:
sh 'echo `env`'
Prints poorly:
sh 'env > env.txt'
for (String i : readFile('env.txt').split("\r?\n")) {
println i
}
Prints well:
sh 'env > env.txt'
sh 'cat env.txt'
Prints well: (as mentioned by #mjfroehlich)
echo sh(script: 'env', returnStdout: true)
The pure Groovy solutions that read the global env variable don't print all environment variables (e. g. they are missing variables from the environment block, from withEnv context and most of the machine-specific variables from the OS). Using shell steps it is possible to get a more complete set, but that requires a node context, which is not always wanted.
Here is a solution that uses the getContext step to retrieve and print the complete set of environment variables, including pipeline parameters, for the current context.
Caveat: Doesn't work in Groovy sandbox. You can use it from a trusted shared library though.
def envAll = getContext( hudson.EnvVars )
echo envAll.collect{ k, v -> "$k = $v" }.join('\n')
Show all variable in Windows system and Unix system is different, you can define a function to call it every time.
def showSystemVariables(){
if(isUnix()){
sh 'env'
} else {
bat 'set'
}
}
I will call this function first to show all variables in all pipline script
stage('1. Show all variables'){
steps {
script{
showSystemVariables()
}
}
}
The easiest and quickest way is to use following url to print all environment variables
http://localhost:8080/env-vars.html/
The answers above, are now antiquated due to new pipeline syntax. Below prints out the environment variables.
script {
sh 'env > env.txt'
String[] envs = readFile('env.txt').split("\r?\n")
for(String vars: envs){
println(vars)
}
}
Includes both system and build environment vars:
sh script: "printenv", label: 'print environment variables'
if you really want to loop over the env list just do:
def envs = sh(returnStdout: true, script: 'env').split('\n')
envs.each { name ->
println "Name: $name"
}
I found this is the most easiest way:
pipeline {
agent {
node {
label 'master'
}
}
stages {
stage('hello world') {
steps {
sh 'env'
}
}
}
}
You can get all variables from your jenkins instance. Just visit:
${jenkins_host}/env-vars.html
${jenkins_host}/pipeline-syntax/globals
ref: https://www.jenkins.io/doc/pipeline/tour/environment/
node {
sh 'printenv'
}
You can use sh 'printenv'
stage('1') {
sh "printenv"
}
another way to get exactly the output mentioned in the question:
envtext= "printenv".execute().text
envtext.split('\n').each
{ envvar=it.split("=")
println envvar[0]+" is "+envvar[1]
}
This can easily be extended to build a map with a subset of env vars matching a criteria:
envdict=[:]
envtext= "printenv".execute().text
envtext.split('\n').each
{ envvar=it.split("=")
if (envvar[0].startsWith("GERRIT_"))
envdict.put(envvar[0],envvar[1])
}
envdict.each{println it.key+" is "+it.value}
I suppose that you needed that in form of a script, but if someone else just want to have a look through the Jenkins GUI, that list can be found by selecting the "Environment Variables" section in contextual left menu of every build
Select project => Select build => Environment Variables

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