StoneThrow
StoneThrow

Reputation: 6285

What doesn't a println() in a Groovy shared library class member function appear in the Jenkins console log?

This question follows from Why is this Groovy class static function not executed from its calling Jenkins pipeline?.

I have a Jenkins/groovy shared library set up like this:

+- src                       # Groovy source files
|   +- org
|       +- company
|           +- Stage.groovy  # for org.company.Stage class
+- vars
    +- func.groovy           # for global var func()

The shared library is set up as a Global Pipeline Library named 'foo': enter image description here

The shared library implements a global variable:

// vars/func.groovy
def func(d) {
  println("func.groovy:func()")
}

...and class Stage with member function func:

// src/org/company/Stage.groovy
package org.company;

class Stage  implements Serializable {
  def func(d) {
    println("org.company.Stage.func()")
    return 'hello, world!`
  }
}

Note that both the global variable and the class member function do the same thing: println of some static string.

My Jenkins pipeline calls both the global variable and the class member function:

// Jenkinsfile
@Library('foo@dev')
import org.company.Stage

pipeline {
  agent any

    stages {
      stage('1') {
        steps {
          testLoadLib()
        }
      }
    }
}

void testLoadLib() {
  loadLibrary('foo@dev')
}

void loadLibrary(String libraryName) {
  def lib = library(libraryName)
  def d = [:]
  func.func(d)

  def myvar = new Stage()
  def ret = myvar.func()
  println(ret)
}

The output of this Jenkins pipeline is:

Only using first definition of library foo
[Pipeline] echo
func.groovy:func()
[Pipeline] echo
hello, world!
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline

Question: why is the println() in org.company.Stage.func() not reflected in the Jenkins console log whereas the println() in the global variable func.func() is reflected in the Jenkins console log?
By using the return-value from org.company.Stage.func(), it's clear that the function is being executed...but its functionality is "crippled" -- I don't know how else to describe it...the println is muted...what other functionality is compromised?
Why would the behavior of this println() statement be different between invoking a global variable versus invoking a class function?

I have tried various combinations of class Stage vs. public class Stage, def func(d) vs. public def func(d), removing/keeping implements Serializable, and the @NonCPS annotation -- none of that appears to affect the behavior described above.

Upvotes: 1

Views: 555

Answers (1)

StoneThrow
StoneThrow

Reputation: 6285

I discovered this by throwing spaghetti at the wall and seeing what stuck.

Starting with a Jenkinsfile like this:

// Jenkinsfile
@Library('foo@dev')
import org.company.Stage
...
def myvar = new Stage(steps)
myvar.func()

...this works:

// src/org/company/Stage.groovy
package org.company;

class Stage {
  def steps
  Stage(steps) {this.steps = steps}
  def func(d) {
    steps.echo("org.company.Stage.func()")
  }
}

This does not work:

// src/org/company/Stage.groovy
package org.company;

class Stage {
  def steps
  Stage(steps) {this.steps = steps}
  def func(d) {
    steps.println("org.company.Stage.func()")
  }
}

I.e. two things appear necessary as an answer to the question asked:

  1. The shared library class must be given, and use, a handle to steps.
  2. The shared library class must use steps.echo, and not steps.println.

Jenkins/Groovy continues to slowly drive me insane.

Upvotes: 0

Related Questions