Explicitly access global vars in jenkins libraries

We are refactoring our jenkins library from using a single vars/foo.groovy file to multiple files and we want to maintain backward compatibility while we transition our jenkinsfiles.

Something we ran into is that if the new files have the same name as the old functions, then we do not have a way to resolve the external name scoping.

For example in your jenkins files we call foo.bar() with bar defined in vars/foo.groovy. If we move bar() to its own file so we can just call bar(), but want foo.bar() to keep on working we end up with an infinite loop

// vars/foo.groovy
def bar() {
    bar()  // we want to call vars/bar.groovy, but this calls itself.
}

A workaround we found is to have a shims.groovy abstraction:

// vars/shims.groovy
def barShim() {
    bar()   // calls vars/bar.groovy
}

And in the old file for backward compatibility:

// vars/foo.groovy
def bar() {
    shims.barShim()  // calls barShim() in vars/shims.groovy, which calls vars/bar.groovy
}

Ideally I would want to skip the need for vars/shims.groovy and have something like:

// vars/foo.groovy
def bar() {
   vars.bar()  // call the new vars/bar.groovy
}

From what I’ve ascertained from the testing that I’ve done on this topic, the way that global variables in vars/ get resolved by other vars is that the global vars are script objects with a propertyMissing method which then finds and instantiates (if necessary) the appropriate singleton from vars/.

So when you do, e.g.:

// vars/foo.groovy
def call() {
  bar()
}

What this does is it resolves bar via a propertyMissing method to find the vars/bar.groovy object. Which means that you actually don’t need to do any of what you’ve done up above - you can omit def bar(){} from your foo.groovy file entirely, and foo.bar will then actually refer to the vars/bar.groovy object. Spooky magic!

This also means that foo.foo == foo. Neat, huh?

Worst case ontario, you can just make an empty vars/vars.groovy file, and then use that as a shim to refer to other vars/ classes, e.g. vars.foo, vars.bar, et cetera.

Thanks! Spooky magic indeed.

I’m not sure this would work in our case where we already have a def call() { in our foo.groovy file and we need to migrate multiple methods. For now we will probably use the shims.groovy trick which is explicit and easier to understand to most folks. The shims are temporary anyways.