Final-keyword doesn't work properly in Jenkins pipeline
Unexpected, but that’s the reality of the Pipeline
Self-sufficient example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pipeline {
agent any
stages {
stage('Hello') {
steps {
script {
final String str = "someVal"
str = "yetAnotherVal" // Exception expected
echo str
}
}
}
}
}
As a result of executing this Pipeline, we get the following output in stdout:
1
yetAnotherVal
Now let’s just run the same code outside of the Pipeline DSL:
1
2
3
final String str = "someVal"
str = "yetAnotherVal" // Exception expected
println str
Main-Pipeline script can execute simple Groovy code
But the result is the same.
What about Shared library
Variables with local scope can still be overwritten, both in class methods and in scripts from the vars/
However, properties are protected. Attempting to change a final-property results in an exception:
1
2
hudson.remoting.ProxyException: groovy.lang.ReadOnlyPropertyException:
Cannot set readonly property: myProperty for class: mainpackage.MyClass
Why?
I asked this question1 in a discussion on community.jenkins.io and received the following answer from one of the Jenkins developers:
Pipeline script is run through a Jenkins-specific transformer which winds up essentially running an independent interpreter, … nobody bothered to check for the final modifier in this context…
What does it mean?
Until this issue2 is resolved, you should not rely on the final keyword. For example, when creating a global variable in a Pipeline, the interpreter does not enforce immutability:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final SOME_GLOBAL_VAR
pipeline {
agent any
stages {
stage('Prepare') {
steps {
script {
SOME_GLOBAL_VAR = calculate()
}
}
}
stage('Global var may be overriden here') {
steps {
script {
...
}
}
}
}
}
def calculate() { ... }