This isn't a question, but rather a cautionary tale: I tried to save some space and declared my variables in Jenkins Declarative pipeline like so:
int a, b, c
Then, I initialized them as:
a = b = c = 0
In my code, I use these integers as counters in a for-loop. My script kept failing over and over, some of the exceptions thrown:
java.lang.NullPointerException: Cannot invoke method next() on null object
and I knew for sure that my list is valid since it was hard-coded. So, I started wondering what's going on with these counters and when I called getClass() on them, Jenkins happily told me that they weren't integers, but rather
org.codehaus.groovy.runtime.NullObject
After changing code to
int a = 0
int b = 0
int c = 0
everything worked like a charm. Just wanted to share this. Maybe it'll help someone to save some frustration.
They start with two forward slashes (//). Any text between // and the end of the line is commented and ignored in Jenkinsfile.
What is the Scripted Pipeline in Jenkins? A Jenkins Scripted Pipeline is a sequence of stages to perform CI/CD-related tasks that can be specified as code, enabling you to develop a pipeline script and add it to your code repository so you can version it.
Using a shell script within Jenkins Pipeline can help simplify builds by combining multiple steps into a single stage. The shell script also allows users to add or update commands without having to modify each step or stage separately.
In contrast to freestyle jobs, pipelines enable you to define the whole application lifecycle. Pipeline functionality helps Jenkins to support continuous delivery (CD). The Pipeline plugin was built with requirements for a flexible, extensible, and script-based CD workflow capability in mind.
Jenkins pipelines execute Groovy code in the continuation-passing style using groovy-cps interpreter. This is not vanilla Groovy you can execute directly in the IDE or in Groovy Shell.
Groovy CPS transforms your code to support the continuation-passing style and the correct Groovy expression like:
a = b = c = 0
gets transformed to something that looks more like:
eval(
var("a"),
assign(
eval(
var("b"),
assign(
eval(
var("c"),
assign(0)
)
)
)
)
)
The problem with this expression in the CPS interpreter is that the assignment does not return any value, and thus the null
value gets assigned to the variable b
, and the same thing happens to the variable a
.
If you want to dig deeper in the CPS invocations block, you can clone groovy-cps project and write a simple test case in the com.cloudbees.groovy.cps.CpsTransformerTest
class.
@Test
void testMultiVariablesInlineCPS() {
def cps = parseCps('''
int a, b, c
a = b = c = 0
''')
println cps
}
Then you can put a breakpoint at the println cps
and run the debugger. When you open the inspection window, you will see the picture similar to this one:
As a side note, keep in mind that the Groovy compiler also transforms your single line assignments when compiled the code to the bytecode. If you compile a simple Groovy script like:
int a, b, c
a = b = c = 0
println "$a $b $c"
and then you open its class file in the IDE to decompile the bytecode to the Java equivalent, you will see something like this:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.GStringImpl;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class test extends Script {
public test() {
CallSite[] var1 = $getCallSiteArray();
}
public test(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, test.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
int a = 0;
int b = 0;
int c = 0;
byte var5 = 0;
return var1[1].callCurrent(this, new GStringImpl(new Object[]{Integer.valueOf(var5), Integer.valueOf(var5), Integer.valueOf(var5)}, new String[]{"", " ", " ", ""}));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With