How can I define a global variable or constant using jenkins pipeline shared library?

Viewed 15343

While I was able to define methods, defining variables using Jenkins Shared library seems to an unresolved quest so far.

I added /vars/True.groovy with this body

def call() {
    return true
}

And now inside Jenkinsfile, I tried to test if it works as expected:

println "evaluation ${ true == True }"

But surprise, this fails as it considers true != class True. My impression is that true == True() may work but that's not the point, I need a real variable because that whole purpose was to avoid some errors caused by people using wrong case.

So what is the magic trick?

3 Answers

I've found a way to achieve this, but with a slight caveat: the variables must be defined/wrapped within a class. However, this does have the upside of providing better organization, in order not to pollute the global space too much.

For example, we often reuse four standard "magic strings" for build statuses, which I wanted to save as global constants to facilitate interoperability. So I created a global status class, defined in vars/status.groovy:

class status {
  final String STARTED = "STARTED"
  final String SUCCESS = "SUCCESS"
  final String FAILURE = "FAILURE"
  final String ABORTED = "ABORTED"
}

The constants can then be used by referring to their parent class:

echo status.STARTED
echo status.SUCCESS
echo status.FAILURE
echo status.ABORTED

Specials thanks to @mkobit for pointing me in the right direction!

It looks like Global Variables defined in the vars directory must be lower/camel/maybe some other special casing. This isn't stated anywhere in on the Defining global variables section, but there is this note at the top:

The vars directory hosts scripts that define global variables accessible from Pipeline. The basename of each *.groovy file should be a Groovy (~ Java) identifier, conventionally camelCased. The matching *.txt, if present, can contain documentation, processed through the system’s configured markup formatter (so may really be HTML, Markdown, etc., though the txt extension is required).

Here is what I tried:

  • vars/MyTrue.groovy

    class MyTrue implements Serializable {
    }
    
  • vars/myTrue.groovy

    class myTrue implements Serializable {
    }
    
  • vars/mytrue.groovy

    class mytrue implements Serializable {
    }
    
  • vars/doesCasingMatter.groovy

    class DoesCasingMatter implements Serializable {
    }
    

And in my pipeline script to test if they are instances or Class types (no script security enabled here):

echo("MyTrue: ${Class.isInstance(MyTrue)}")
echo("myTrue: ${Class.isInstance(myTrue)}")
echo("mytrue: ${Class.isInstance(mytrue)}")
echo("What bindings are there?: ${binding.variables}")

This prints out:

[Pipeline] echo
MyTrue: true
[Pipeline] echo
myTrue: false
[Pipeline] echo
mytrue: false
[Pipeline] echo
What bindings are there?: [steps:org.jenkinsci.plugins.workflow.cps.DSL@e96256, myTrue:myTrue@8a1ddc5, mytrue:mytrue@392ff649]

Which seems to indicate that something about the class name determines how it gets compiled and created. The first example, which is similar to your vars/True.groovy, is only imported as a class and not instantiated. The other two are compiled and instantiated and are bound to the script with their defined class names. I think you will have to define your classes differently if you want them to be global variables.

Variable True is definitely a variable but it holds a reference to object of type True(that you defined in /vars/True.groovy). You have two options
The good one: Use it this way

println "evaluation ${ true == True() }"

The strange one: You can override equals() method in /vars/True.groovy

public boolean equals(obj) {
    return obj == true;
}

Then this should work

println "evaluation ${ true == True }"

But it would be really strange and can lead to misunderstandings.

Related