How To Load A Shared Library From A Subfolder In Jenkins
Author
Marcus HeldI work with Jenkins Pipeline for three years now and one pain point is proper isolation of shared functionality between pipelines but even steps. In our repository we defined multiple pipelines and some are that large that we share functionality within it. Jenkins offers the possibility to create shared libraries for that purpose. But unfortunately it’s not possible to load it from the same repository. Since many of the changes in the pipeline are related to a change of the shared library it was tedious to match the branches and versions to be backward compatible. What we actually wanted is having the shared library in our main repository so the states of the pipeline is pinned to the state of the main repository. And we finally figured out how to do (hack) that.
Currently, we have the limitation that we can’t define a subfolder from the main repository to use as a shared library (There’s a PR open for 2.5 years ). So after searching the internet for a while I stumbled upon this creative idea (hack). The idea is simple. You initialize a new git repository in the subfolder where your shared library lies and give the path to the library step that loads it.
cd ./path/to/library && \ # We change to the library subfolder
(rm -rf .git || true) && \ # To make sure that we have a clean state we remove any previous created repository
git init && \ # Now initialize the "fake" repository
git add --all && \ # And add all files
git commit -m init # And commit it
After setting up the repository we can use the library
step to load it from the repository we just created.
library identifier: 'local-lib@master', // The identifier doesn't matter
retriever: modernSCM([$class: 'GitSCMSource', remote: "/path/to/library"]), // Here we load the library from disk
changelog: false // We don't want to see the changelog of the fake repository
And this is essentially it. In the last step we combine both and execute it on the master
node.
node("master") {
echo "Loading local shared library"
checkout scm
// Create new git repo inside jenkins subdirectory
sh("""cd ./$libraryPath && \
(rm -rf .git || true) && \
git init && \
git add --all && \
git commit -m init
""")
def repoPath = sh(returnStdout: true, script: 'pwd').trim() + "/$libraryPath"
library identifier: 'local-lib@master',
retriever: modernSCM([$class: 'GitSCMSource', remote: "$repoPath"]),
changelog: false
deleteDir() // After loading the library we can clean the workspace
echo "Done loading shared library"
}
And this code - that loads the local library - can be a shared library itself so we don’t need to repeat ourselves for different projects or pipelines.
I implemented a simple example
to demonstrate how to use the loader. The project just has a Jenkinsfile which loads the shared library in the library
subfolder.
.
├── Jenkinsfile
└── library
└── vars
└── localTest.groovy
And this is the Jenkinsfile that loads and uses the shared library:
@Library('subfolder-library@1.0') _
loadLocalLibrary scm, "library"
pipeline {
agent any
stages {
stage("Test") {
steps {
localTest()
}
}
}
}
To make this work you need to configure the subfolder-library
shared library under Manage Jenkins > Configure System > Global Pipeline Libraries
jenkins-library-subfolder-loader
directly in the job configuration. This has the neat effect that it runs in the sandbox and can be modified when replaying a run.