How to write a Jenkins Plugin

Jenkins plugins allow you to extend the Jenkins build system to do almost anything. Jenkins in Italian? . Replace Jenkins with Chuck Norris? You bet. Almost every feature used in building code on Jenkins is a plugin – in fact, even things like LDAP logins can be handled through the Jenkins plugin system. However, Jenkin’s near-limitless customization is also its drawback: with so much possibility, there is also an inherent complexity.

Jenkins plugins work by creating or extending an extension point, which hooks into a specific part of the build process. The catch: these extension points can differ significantly in what they do and how they work. In this post I specifically talk about implementing a Notifier plugin, as it showcases how you can use multiple extension points in a singular plugin while not being significantly more complex than the HelloWorldBuilder plugin described in the official Jenkins plugin tutorial.

Jenkins Plugins

Writing a Notifier Plugin for Jenkins

1. Set Up Your Environment

This part is taken directly from Jenkin’s official plugin tutorial. As it turns out, you need to setup Jenkins to be able to use it.

Jenkins works using Java 6 (or higher) and Maven 3 (if you’re using Nexus Maven Repository Manager, follow these instructions to set it up). If you’ve used Java before, you’ll be somewhat familiar with it (as well as Maven’s tendency to download all of the internet every time). Once you have both installed you’ll want to put this xml in your ~/.m2/settings.xml (or %USERPROFILE%.m2\settings.xml for Windows):

2. Create the Jenkins Plugin Template

It may seem weird that I always start off with the official Jenkin’s steps, but it makes everything to do with configs much easier (as you’ll see in step 5).

This step is made straightforward by Jenkins:

  1. Open your console of choice
  2. Navigate to the directory where you want to put the plugin source in
  3. Run
    1
    mvn -U org.jenkins-ci.tools:maven-hpi-plugin:create
  4. Answer the questions it will ask you (I’ll be naming the plugin HelloWorldNotifier for reasons discussed in Step 3)
  5. Open up the created folder with the plugin template in your preferred IDE

3. Destroy the Jenkins Plugin Template

Okay, just one file.

The template is great. It is one of the singularly most useful pieces of documentation that Jenkins offers. Unfortunately, the code file itself is made with Builder plugins in mind. Since we’re going to make a Notifier plugin, most of the generated code is not applicable .. so we’re going to write the class from scratch.

Open up

1
HelloWorldBuilder.java

and remove all the code from the doc (leaving imports intact). This is also a good time to rename the file to whatever you’ve decided to name your plugin. In this post I use HelloWorldNotifier.java since it’s the same concept as the HelloWorldBuilder tutorial but with a notifier.

4. Create the Outline of the New Plugin

Incidentally, our plugin is going to be mostly outline.

First, we need to define the class itself. In this case, it should be:

This means we’re going to have to implement a couple of required methods, but we’ll get to that later.

In our outline we’re also going to be adding in the configs, as they aren’t part of the logic of our plugin.

There are two kinds of configs: global and local. Global configs are values which will always be available to your plugin, whilst local configs will only be set per plugin use (any field that is job-specific and not Jenkins-wide is local).

To load per-project configurations ,we must mark our class’s constructor as a

1
@DataBoundConstructor

. The constructor’s inputs must match the names of the fields in config.jelly, as that is how Jenkins knows what to pass in where. In this case, the only local config values we’ll have is a String called local, so our constructor looks like this:

Of course, we also have to change our config.jelly to match:

Now that we have our local configs, let’s make it so we load an Integer from our global config.

Loading global configs is somewhat more complicated. To load a global config we must define a

1
getDescriptor

method and a

1
DescriptorImpl

class.

Jenkins has a standard plugin model called the Descriptor/Descriptable model which allows you to have global configs for your plugin. The first step is to write the

1
DescriptorImpl

. The DescriptorImpl is a class that Jenkins will pass on to every instance of your plugin class it creates, so anything we put in it will be accessible to every instance of our plugin. The DescriptorImpl must be marked with

1
@Extension

to tell Jenkins that it is a plugin’s Descriptor.

Of course we now have to implement all the classes required by

1
BuildStepDescriptor

:

Then comes the data loading: we override configure and have it load the data from the form just as we would any other. Here we can also throw an exception if the user put invalid values in our fields.

Finally, for the constructor we have to call super and pass it our plugin’s class name. This is what lets Jenkins know that this descriptor is for our class. Additionally, we load() in order to get the data from the configure step.

Now that we have a descriptor for our class, we need to create a

1
getDescriptor

method which we will use our new class. Oddly enough, you will almost always write this the same way, as it contains none of the logic for loading the global config info: https://gist.github.com/andres-rama-hs/c094434887f5bf5d7f471820ff8d1fda

5. Write the Rest of the Plugin

It’s plugins all the way down.

The plugin right now does … nothing. It loads the configs, and does nothing with them. To fix this we’re going to create a whole other class. This class is a kind of plugin unto itself; a BuildListener. All it is is:

And we just make it call the fund we want in our notifier! In this case, we’ll only trigger our notification plugin on build completion, and we’ll check that our step is in the build so we don’t send notifications about all of them.

6. Write Tests

Jenkins has final methods, and therefore cannot be mocked.

While Jenkins calls them “unit tests”, the only tests you can write that will interact with any form of Jenkins will involve spinning up a fake instance of Jenkins and running tests on it (you can’t mock final methods, and Jenkins has some). Thankfully, that’s fairly straightforward. To get a test Jenkins, just add the following rule in your test class:

Then you can interact with it more or less the same way you would the actual Jenkins, just using method calls and not clicking buttons. You can create a fake project by simply writing:

And finally getting and parsing the logs for our plugin’s printing of the passed in String 3 times is just as easy:

Summary

Jenkins plugins are easy to learn but difficult to master, so if there’s already one that almost does what you need, you should try to do that. The community behind Jenkins has made it easy to get started with a variety of plugin tutorials covering everything from build steps to colonizing terminal output, making it easy to customize your learning to the task you want to achieve. Here are my favourite resources online to learn more about Jenkins plugin development:

Jenkins plugins also work on many other build systems, including Jenkins 2.0.

About the Author

Andres RamaAndres was a co-op Software Developer on Hootsuite’s Production Delivery team, working to build and maintain the tools we use to ship code out to customers multiple times a day. He also previously worked in Hootsuite on the Platform team, which used these same build pipelines. When he’s not writing code, he likes to play Counter Strike: Global Offensive, Wargame: Red Dragon, and Rocket League.