Post

Final-keyword doesn't work properly in Jenkins pipeline

Unexpected, but that’s the reality of the Pipeline

Final-keyword doesn't work properly in Jenkins 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() { ... }
This post is licensed under CC BY 4.0 by the author.

Trending Tags