Introducing Variability into Jenkins Plugins

Written by: Stephen Connolly
5 min read

All at the same time, this question has popped up from multiple sources. I think it is an indication that a new wave of plugin developers are seeking to do more complex things in their Jenkins plugins.

So this post aims to help explain for Jenkins Plugin developers how to introduce variability and develop better modelling of configuration within your Jenkins Plugin.

If you want a hint as to how powerful this can give you for your UI, I suggest you take a look at the “Deploy Now” page in the CloudBees Deployer plugin (unfortunately the code base is still closed source… Note to self check up on the status of getting some of the required dependencies public so that I can make the source code public again)

We have a multiple list of different types of "Host service" (which is also an extension point, so other plugins can provide their own "Host service", e.g. App Engine and Cloud Foundry being two such implementations.

Then each "Host service" has multiple "Applications" (all the one type, but a different type for each "Host service").

Finally, each "Application" has a single selection for the application source ("first match", "maven artifact selection", etc)

Each layer provides its own set of details that are specific to that layer. So for example the Host service asks for one set of options that make sense at the host service level, e.g. a CloudBees RUN@cloud service asks for the account on the CloudBees service to deploy into, on the other hand the App Engine host service asks a different set of questions.

So that is complex… what about if you just want to start off on that road...

Lets say your question is something like:

Is there a way to have 2 different config.jelly files and control which is displayed based on what I choose from a combo box above the content of those jelly files?

The way I normally handle this type of thing is to use a Describable object tree to map the different types of object that you want to allow the user to configure from.

Normally you do this by extending AbstractDescribableImpl , but you can just add the Describable interface to the root of your existing object tree and add the required methods if otherwise modifying the object tree is too difficult (typically you have a class that must be a superclass of the base of your object tree)

So in your case you will have something like

And then for each type of product you will then do something like

It is important that each concrete Product subclass has a @DataBoundConstructor annotated constructor and aDescriptorImpl (though the class name can be anything you like, convention is DescriptorImpl ) annotated with @Extension

The methods you expose in each class is up to you. Typically you will put the getters for each specific product fields in the specific class, so for example you would have getManufacturer() , getFuelType() etc with corresponding backing fields inCar the field validation would go in Car.DescriptorImpl e.g. thedoCheckManufacturer (@QueryParameter String value) , doFillFuelTypeItems() , etc methods.

Where there are fields common for all Product instances, you put their getters in the base abstract class and their field validation / autocomplete in the base descriptor class.

For each class you then implement your config.jelly and optional global.jelly .

If it were me, I would have a Product/config.jelly that then included the optional bits from the concrete child class, e.g.

Then, only if the child class actually has any relevant configuration would I define the child classes config-detail.jelly , e.g.Car/config-detail.jelly would look something like

Now at this point we hit the actual trick...

In the class that needs these Product instances, which is also ultimately being instantiated by a @DataBoundConstructor or using the stapler binding against an already constructed object (which means you would need getters and setters for the form fields)

So lets say you have something like

In other words this is a property of a job that stores the type of products that the job is testing or something like that (this works for any class, just trying to show how to get it with a simple example)

And finally, to get the flexible list of product types in ProductsJobProperty/config.jelly you have something like

You may have to play slightly with that tag. And you will need to provide the actual fully qualified class name (or provide a getter for that class from your ProductsJobProperty.DescriptorImpl

Another option to look at (if you only want one product) is

If you want a drop-down list in place of a radio list, that would be

I hope this all helps you on the road to enlightenment

—Stephen Connolly
CloudBees
cloudbees.com

Stephen Connolly has over 20 years experience in software development. He is involved in a number of open source projects, including Jenkins . Stephen was one of the first non-Sun committers to the Jenkins project and developed the weather icons. Stephen lives in Dublin, Ireland - where the weather icons are particularly useful. Follow Stephen on Twitter and on his blog .

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.