Loading...

How To Load A Shared Library From A Subfolder In Jenkins

Continuous Integration
January 22, 2020
3 minutes to read
Share this post:

I 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

Have you heard of Marcus' Backend Newsletter?

New ideas. Every week!
Top