Jenkins: Using Gradle to build your Shared Library

Being able to test your Jenkins pipeline Shared Library code, and therefore minimising the risk of pushing new pipelines or functionality to your Production instance, is vital for organisations using Jenkins at scale. Having your teams impeded by a ‘simple’ change in your Jenkins Shared Library is simply unacceptable in many cases, and even more so if it can be avoided altogether.

There is always a certain risk attached to pushing code to Production, but any effort to minimize this risk is welcomed as it will save the organisation real money in the long run.

This post is part of a Series in which I elaborate on my best practices for running Jenkins at scale which might benefit Agile teams and CI/CD efforts.

The first part of being able to run tests on your Shared Library is being able to ‘run’ the code, and with the Library being Groovy, code Gradle is the logical choice.

 

Obtaining Gradle

Download Gradle, install it, and make it accessible from your path. You can follow the guide at gradle.org to do so.
I’m assuming you’re using Gradle version 5+ for the remainder of this post.

Initializing your project

Shamelessly stolen from the official manual, starting a Gradle project boils down to running this command:

gradle init

That will give you these files:

├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

Now you’re all set!

Make Gradle understand your Jenkins Library

As Gradle understands default Groovy, Java and many other projects, it follows the standards set for those technologies. Jenkins differs from this, and you’ll need to make Gradle understand where it would be able to find the correct sources.

For instance, where the application code can be found at /src/java/your/package/path/ in a regular Java project, Jenkins expects it at /var/ or /src/your/package/path/.

You can configure your Gradle build in build.gradle with sourceSets to instruct it to search for the code of a specified type in certain locations.
To have Gradle find the Jenkins Groovy code as per the Shared Library standard, you need to use the snippet below:

sourceSets {
  main {
    groovy {
      srcDirs = ['src','vars']
    }
  }
}

Other source files

A Jenkins Shared Library holds three distinct types of source files:

  • Application code
  • Resources
  • Test code

The previous chapter shows you how to instruct Gradle how to find application code, and this method can also be used to locate the other types of sources.
Resources can be configured by utilising the resources instruction, and test code with test.
Together, you’ll get something like the snippet below:

sourceSets {
    main {
        groovy {
            srcDirs = ['src','vars']
        }
        resources {
            srcDirs = ['resources']
        }
    }
    test {
        groovy {
            srcDirs = ['test']
        }
    }
}

This will instruct Gradle to look for application code (main / groovy) in two locations, to look for resources (main / resources) in another folder and the test code (test / groovy) in the ‘test’ folder.

Dependencies

The set of dependencies for a Jenkins Shared Library is quite different from a plain Java project.
I have chosen to write these down using the Maven-style ‘GAV’ notation. All libraries are the most recent versions as of the time of writing.
At a minimum, you’ll need these dependencies:

Groovy & the @Grab annotation:

  • org.codehaus.groovy:groovy-all:2.5.6
  • org.apache.ivy:ivy:2.4.0

Jenkins:

  • org.jenkins-ci.main:jenkins-core:2.164.1
  • org.kohsuke.stapler:stapler:1.255
  • org.jenkins-ci.plugins.workflow:workflow-step-api:2.19@jar

(Optional) plugins:

  • org.jenkins-ci.plugins:pipeline-utility-steps:2.2.0@jar
compile 'org.codehaus.groovy:groovy-all:2.5.6'
compile 'org.apache.ivy:ivy:2.4.0'
compile 'com.lesfurets:jenkins-pipeline-unit:1.1'
compile 'org.jenkins-ci.main:jenkins-core:2.164.1'
// Defining it twice for Gradle 5 as some annotation processors live here as well
def staplerGAV = 'org.kohsuke.stapler:stapler:1.255'
compile staplerGAV
annotationProcessor staplerGAV
compile 'org.jenkins-ci.plugins.workflow:workflow-step-api:2.19@jar'
compile 'org.jenkins-ci.plugins:pipeline-utility-steps:2.2.0@jar'

Repositories

Not all dependencies listed in the previous chapter are available in Maven Central or Bintray’s JCenter, so you’ll need to instruct Gradle where to find these.
Jenkins has it’s own publicly accessible Maven repository, so we’ll use that to obtain the ‘org.jenkins-ci.plugins’ dependencies.

This also needs to be configured in build.gradle.
As this configuration completely overwrites the Gradle default settings, we need to still point to JCenter as well.

repositories {
    jcenter()
    maven {
        url "https://repo.jenkins-ci.org/releases/"
    }
}

Final product

By combining all these pieces of information, the following build.gradle is established.
This will enable you to ‘run’ your Gradle build and compile the source files in it.

apply plugin: 'groovy'

// follow the structure as dictated by Jenkins:
sourceSets {
    main {
        groovy {
            srcDirs = ['src','vars']
        }
        resources {
            srcDirs = ['resources']
        }
    }
}

repositories {
    jcenter()
    maven {
        url "https://repo.jenkins-ci.org/releases/"
    }
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.5.6'
    compile 'org.apache.ivy:ivy:2.4.0'
    compile 'org.jenkins-ci.main:jenkins-core:2.164.1'
    def staplerGAV = 'org.kohsuke.stapler:stapler:1.255'
    compile staplerGAV
    annotationProcessor staplerGAV
    compile 'org.jenkins-ci.plugins.workflow:workflow-step-api:2.19@jar'
    compile 'org.jenkins-ci.plugins:pipeline-utility-steps:2.2.0@jar'
}

Next steps

Now that we can build our project, the next step is to introduce some code and the necessary tests to it. You’ll be able to find how to do this in the next post!