Monday, May 1, 2017

Death by Jenkins - Really difficult bits to find out about Declarative Pipeline DSL (Part 2)

As I said in my last post, I'm not a build engineer and I don't know groovy. I am currently working in a team that is creating a cross-platform system that integrates iOS and Android SDKs to be used as a shared library in C++. But somehow I've become the Jenkins Lady of the team...

So first thing I want to say is that I think there is a serious problem of not error-ing with Pipeline.

Say you have the following piece of code:

You have separate methods to load separate groovy files and run them with different parameters.
This seems fairly straightforward and valid to me.

But not to Jenkins Pipeline!

This will result in a successful Pipeline build but your external script, in this case neither build.groovy nor test.groovy will load. And it won't give you any output either.


So then I spend hours Googling and trying things out.
And well, it turns out, you cannot have the same method name in your external script.
Because the Jenkinsfile had two method calls to "run()", it could not figure out which it needed to call and failed silently.

So if I changed my code to the following, the external scripts are loaded as expected.


That was the first problem.

A few days later, I ran into the same exact problem. AGAIN! But now my scripts all had differently named methods so I had to go Googling and trying things all over again.

Now, my Jenkinsfile had some other methods that needed to go through a map and add the key to a separate array if the value was true. Like this:

While making this change, I first got an error saying:
"Calling public static java.util.Map org.codehaus.groovy.runtime.DefaultGroovyMethods.each(java.util.Map,groovy.lang.Closure) on a CPS-transformed closure is not yet supported (JENKINS-26481); encapsulate in a @NonCPS method, or use Java-style loops"

After confirming on various sites (like here) that you need to do this if you want to use .each in Pipeline, I put @NonCPS on prepareTests() and everything was all green (y)

But then of course I realised it wasn't loading the script.

Turns out you can't load scripts in a method that has or calls other methods with @NonCPS.

I have since found this document Pipeline Best Practices and it does say:
"Beware for (Foo f: foos) loops and Groovy closure-style operators like .each and the like. They will not work right in normal Scripted Pipeline contexts where Pipeline steps are involved directly."

So I wish I had known that earlier but the solution here is to use a for loop and give up on nicer .each syntax.
(also another link I have since found has other people complaining about Pipeline! yay friends! :P )

Well of course my misery doesn't end there and I could go on about how much Pipeline's been more of a headache than help but now our build process looks pretty so yay! x)

I feel like I've been rather grumpy writing these blogs and complaining about Pipeline but obviously there are benefits and I've been told by my team that it's been useful for them so all is good :) 

3 comments: