I want to load variably-named shared libraries in my Jenkinsfiles sequentially, and run a global variable method of the same name in each one.
I.e. as pseudocode, what I want to do is:
for lib in in [foo, bar]:
load shared library(lib)
run the shared library's global variable method named 'func()'
I tried implementing this as follows:
// Jenkinsfile
pipeline {
agent any
stages {
stage('1') {
steps {
testLoadLib()
}
}
}
}
void testLoadLib() {
runGlobalVariableMethod('foo@dev')
runGlobalVariableMethod('bar@dev')
}
void runGlobalVariableMethod(String libraryName) {
def lib = library(libraryName)
def d = [:]
func.func(d)
}
// shared_lib_foo
// vars/func.groovy
def func(d) {
println("foo func.groovy:func()")
}
// shared_lib_bar
// vars/func.groovy
def func(d) {
println("bar func.groovy:func()")
}
The shared libraries have been configured as Jenkins Global Pipeline Libraries as follows:

On running the Jenkins pipeline, func.func() from the foo@dev shared library is run twice, instead of from foo@dev once, then bar@dev next.
I.e.:
actual output (edited for brevity):
foo func.groovy:func()
foo func.groovy:func()
desired output:
foo func.groovy:func()
bar func.groovy:func()
Question: between the Jenkinsfile and the Jenkins shared libraries, how can I implement the desired behavior?
The desired behavior being: I want to load multiple shared libraries, iteratively, and run each one's global variable method (that has the same name).
One might describe this as a plugin architecture: load a variable/specifiable shared library, and run an expected function within it.
The unedited Jenkins pipeline output (for better context) shows that it appears to be checking out the correct shared libraries -- I see correct references to the respective shared libraries' git repository and commit-ids:
[Pipeline] // stage
[Pipeline] withEnv
[Pipeline] {
[Pipeline] stage
[Pipeline] { (1)
[Pipeline] library
Loading library foo@dev
Attempting to resolve dev from remote references...
> /usr/bin/git --version # timeout=10
> git --version # 'git version 2.20.1'
using GIT_ASKPASS to set credentials
> /usr/bin/git ls-remote -h -- ssh://[email protected]:8999/prj/foo.git # timeout=10
Found match: refs/heads/dev revision 12419a423b1da2827215bca89aaa0f1fdba7e6ae
The recommended git tool is: NONE
using credential f3d14e3a-851b-4837-bbfd-31292f16e310
> /usr/bin/git rev-parse --resolve-git-dir /home/user/workspace/wip@libs/006019fd942f0d37b69f2cd051be759efa34f738b3937627946801f48dd03a7c/.git # timeout=10
Fetching changes from the remote Git repository
> /usr/bin/git config remote.origin.url ssh://[email protected]:8999/prj/foo.git # timeout=10
Fetching without tags
Fetching upstream changes from ssh://[email protected]:8999/prj/foo.git
> /usr/bin/git --version # timeout=10
> git --version # 'git version 2.20.1'
using GIT_ASKPASS to set credentials
> /usr/bin/git fetch --no-tags --force --progress -- ssh://[email protected]:8999/prj/foo.git +refs/heads/*:refs/remotes/origin/* # timeout=10
Checking out Revision 12419a423b1da2827215bca89aaa0f1fdba7e6ae (dev)
> /usr/bin/git config core.sparsecheckout # timeout=10
> /usr/bin/git checkout -f 12419a423b1da2827215bca89aaa0f1fdba7e6ae # timeout=10
Commit message: "wip"
> /usr/bin/git rev-list --no-walk 12419a423b1da2827215bca89aaa0f1fdba7e6ae # timeout=10
[Pipeline] echo
foo func.groovy:func()
[Pipeline] library
Loading library bar@dev
Attempting to resolve dev from remote references...
> /usr/bin/git --version # timeout=10
> git --version # 'git version 2.20.1'
using GIT_ASKPASS to set credentials
> /usr/bin/git ls-remote -h -- ssh://[email protected]:8999/prj/bar.git # timeout=10
Found match: refs/heads/dev revision 52bc665d148fd2109831f484e604f1e3f82e895b
The recommended git tool is: NONE
using credential f3d14e3a-851b-4837-bbfd-31292f16e310
> /usr/bin/git rev-parse --resolve-git-dir /home/user/workspace/wip@libs/87e71759ef01c569f052b9dc73ff630019f50862832bd0583331e87472373144/.git # timeout=10
Fetching changes from the remote Git repository
> /usr/bin/git config remote.origin.url ssh://[email protected]:8999/prj/bar.git # timeout=10
Fetching without tags
Fetching upstream changes from ssh://[email protected]:8999/prj/bar.git
> /usr/bin/git --version # timeout=10
> git --version # 'git version 2.20.1'
using GIT_ASKPASS to set credentials
> /usr/bin/git fetch --no-tags --force --progress -- ssh://[email protected]:8999/prj/bar.git +refs/heads/*:refs/remotes/origin/* # timeout=10
Checking out Revision 52bc665d148fd2109831f484e604f1e3f82e895b (dev)
> /usr/bin/git config core.sparsecheckout # timeout=10
> /usr/bin/git checkout -f 52bc665d148fd2109831f484e604f1e3f82e895b # timeout=10
Commit message: "wip"
> /usr/bin/git rev-list --no-walk 52bc665d148fd2109831f484e604f1e3f82e895b # timeout=10
[Pipeline] echo
foo func.groovy:func()
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline

tl;dr: it appears that:
library(loads a shared library) isn't an object on which you can invoke the shared library's global variable methods.load(load a "local" Groovy script) is an object on which you can invoke the Groovy script's global variable methods.Use
loadto load Groovy scripts from the local filesystem instead oflibraryto load from source control.The ugly details: the closest I've been able to achieve to an answer to the question asked is to use the
loadfunction, instead oflibrary.libraryloads groovy scripts directly from SCM, which may or may not be in the same repo as the Jenkinsfile itself.loadseems specifically only able to load from the local filesystem, wherever theJenkinsfileexists.Critical to this question, though,
libraryseems unwilling to override global variable functions if newly-loaded shared libraries have groovy scripts with the same filename and function names. Also,libraryseems to not return any kind of object to which its functions are scoped.This shortcoming appears to be absent with the
loadfunction: it returns an object to which the loaded groovy script's functions are scoped. This behavior is in line with lazy-loading shared libraries, e.g. with C'sdlopenanddlsymfunctions.This is perhaps better illustrated by example:
The relevant output of running this Jenkins pipeline, as noted in the original post, is:
This may be compared with equivalent-intent use of the
loadfunction:Running this Jenkins pipeline reveals the desired output:
In terms of implementation, the relevant differences are:
load, the groovy script filenames need to be different, since you'reloading by filename..groovyfiles in the same folder and git repo as theJenkinsfile.An arguable demerit of this approach is that the loaded groovy script isn't as "decoupled" from the Jenkins pipeline as a true shared library: e.g. the groovy scripts don't exist in a separate git repo, which could be modified and maintained independent from the Jenkinsfile's git repo.
But I think with the right structure and workflow, the detriments can be minimized.
And this nicely achieves the desired goal: I can successively load multiple groovy scripts and execute each one's same-named global variable function.