Reputation: 8276
I'm trying to dynamically define a variable I use later in a some shell commands of my Jenkins pipeline and it's throwing an exception. I even tried to predefine the variable from an environment section to no avail. Is this a prohibited operation? My other variable myVar
seems to work fine, but it's a constant through the pipeline.
pipeline {
agent any
environment {
py2Ana=""
myVar="ABCDE"
}
stages {
stage('Stage1') {
steps {
sh """
echo myVar=$myVar
echo Find Anaconda2 Python installation...
py2Ana=`which -a python | grep --max-count=1 anaconda2`
if [[ -z "$py2Ana" ]]; then
echo ERROR: must have a valid Anaconda 2 distribution installed and on the PATH for this job.
exit 1 # terminate and indicate error
fi
"""
}
}
}
groovy.lang.MissingPropertyException: No such property: py2Ana for class: groovy.lang.Binding
at groovy.lang.Binding.getVariable(Binding.java:63)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:242)
at org.kohsuke.groovy.sandbox.impl.Checker$6.call(Checker.java:288)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:292)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:268)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:268)
at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:29)
at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:20)
at WorkflowScript.run(WorkflowScript:21)
Upvotes: 20
Views: 50765
Reputation: 5149
The error itself seems really to be caused by the assignment of an empty string.
However: Do you really need that environment variable to be defined in the Jenkinsfile?
To me it looks like you just want to set and read the variable from within the shell script. But the way it's coded the if [[ -z "$py2Ana" ]]; then
would never pick up the value set by the shell script - it would always want to use a property from the Jenkinsfile - which didn't work.
You could use if [[ -z "${env.py2Ana}" ]]; then
for the if condition which would fix that error but it still would not pick up the value set by the previous line but always read the empty string set in the Jenkinsfile.
To solve this you could either enclose the string in single quotes for the whole string like (maybe you even want to get rid of the myVar
then)...:
pipeline {
agent any
stages {
stage('Stage1') {
steps {
sh '''
echo Find Anaconda2 Python installation...
py2Ana=`which -a python | grep --max-count=1 anaconda2`
if [[ -z "$py2Ana" ]]; then
echo ERROR: must have a valid Anaconda 2 distribution installed and on the PATH for this job.
exit 1 # terminate and indicate error
fi
'''
}
}
}
}
... or add a backslash right before $py2Ana
like:
pipeline {
agent any
stages {
stage('Stage1') {
steps {
sh """
echo Find Anaconda2 Python installation...
py2Ana=`which -a python | grep --max-count=1 anaconda2`
if [[ -z "\$py2Ana" ]]; then
echo ERROR: must have a valid Anaconda 2 distribution installed and on the PATH for this job.
exit 1 # terminate and indicate error
fi
"""
}
}
}
}
Either way without referencing env.py2Ana
in the code I doubt the environment
block in the Jenkinsfile still would make sense - that's why I removed it from the examples.
Upvotes: 5
Reputation: 8276
There appears to be a variable substitution precedence that Jenkins enforces in a preprocessing step if you will. In other words there's no delayed expansion as one would find in the Windows batch file behavior with setlocal ENABLEDELAYEDEXPANSION
. This explains what's going on, and here's the test pipeline I used to determine this:
pipeline {
agent any
environment {
py2Ana="DEFAULT"
}
stages {
stage('Stage1') {
steps {
sh """
echo py2Ana=$py2Ana
py2Ana=Initialized
echo py2Ana Initialized=$py2Ana
"""
}
}
}
}
This yields the following console output...
Started by user unknown or anonymous
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] node
[Pipeline] {
[Pipeline] withEnv
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Stage1)
[Pipeline] sh
[TestPipeline] Running shell script
+ echo py2Ana=DEFAULT
py2Ana=DEFAULT
+ py2Ana=Initialized
+ echo py2Ana Initialized=DEFAULT
py2Ana Initialized=DEFAULT
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
Another restriction that this poses is that you truly cannot use dynamic variables in the sh
portion of the Jenkins declarative pipeline script since Jenkins will first attempt to resolve all variables before execution. Thus the following will always yield an error
sh """
for filename in /tmp/**; do
echo filename=$filename
done
"""
The error being...
groovy.lang.MissingPropertyException: No such property: filename for class: groovy.lang.Binding
One would need to define a script dynamically (after figuring out a way to escape the $
to write to file), or already have it in the source, to be executed.
Upvotes: 10
Reputation: 922
As @jxramos stated, Jenkins is trying to resolve the variables in the script. It interprets any $string as a variable that needs substitution. The solution is to escape the $ of the in-script variables, as follows:
pipeline {
agent any
stages {
stage('test stage'){
steps {
sh """#!/bin/bash
myvar=somevalue
echo "The value is \$myvar"
"""
}
}
}
}
Upvotes: 36
Reputation: 2322
Just add a value to py2Ana
environment {
py2Ana="1234"
myVar="ABCDE"
}
It doesn't create the variable in environment if you pass a empty string :)
Upvotes: -1