Use unsupported Jenkins plugins with Jenkins DSL

In a previous post I wrote about how to Automate Jenkins with the use of the plugin Job DSL Plugin. If you didn’t read it, I highly suggest you do that as it will help you understand better what I’ll be explaining here.

When you start using the Job DSL Plugin you’ll probably sooner or later need to configure your job with a plugin that is not yet supported. And by “not yet supported” I mean that there aren’t (yet) DSL commands that will generate a job for that specific plugin. Fortunately they provide you with a way to add them ‘manually’ through the Configure Block.

This part is a bit more complex than using simply the DSL commands, because you’ll have to understand how it works. Now you did notice I wrote “a bit” … that’s because it seems complex, but in fact it isn’t. The only thing you need to know is that the plugin will, with the DSL commands, generate the config.xml of your job containing the full configuration of the job.

To have an idea, this is the config.xml of an empty job

<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties />
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders/>
  <publishers/>
  <buildWrappers/>
</project>

Let’s see an example of a basic DSL command and the corresponding config.xml.

job {
    name 'Test Job'
    description 'A Test Job'
}
<project></project>
  ...
  <description>A Test Job</description>
  ...
</project>

So you see that every DSL command will generate some part in the config.xml.

Knowing this you’ll understand that we will have to study the config.xml of an existing job to see how the “unsupported” plugin is configured in the config.xml.

Let’s make it a bit more fun by integrating the HipChat Plugin. I created a simple job in jenkins and opened the config.xml file. (I assume you know how to install and configure the plugin in Jenkins)

<?xml version='1.0' encoding='UTF-8'?>
<project></project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty plugin="hipchat@0.1.4"></jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty>
      <room></room></room>
      <startNotification>false</startNotification>
    </jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty>
  </properties>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders/>
  <publishers>
    <jenkins.plugins.hipchat.HipChatNotifier plugin="hipchat@0.1.4"></jenkins.plugins.hipchat.HipChatNotifier>
      <jenkinsUrl></jenkinsUrl>http://jenkins/</jenkinsUrl>
      <authToken>ABCDEFGHIJKLMNOPQRSTUVWXYZ</authToken>
      <room>76124</room>
    </jenkins.plugins.hipchat.HipChatNotifier>
  </publishers>
  <buildWrappers/>
</project>

The values in the publisher section are being copied from the jenkins administration. That’s a bit annoying because it means you’ll have to expose that in the DSL scripting. At the moment of this writing, I didn’t find a way to configure that as variables.

Looking at the config.xml, we see that 2 nodes were modified, the properties and the publishers node. Both are children from the root project node. With the Configure Block we can obtain the XML Node to manipulate the DOM.

Get hold on the project node:

job {
  configure { project ->
    // project represents the node <project>
  }
}

Now that we can manipulate the project node, let’s add the properties node:

job {
  configure { project ->
      
    project / 'properties' << 'jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty' {
      room ''
      startNotification false
    }

  }
}

What we did here is tell the parser to append («) the block ‘jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty’ to the node project/properties. And finally in the block we simply enumerate the parameters as key[space]value as you can see them in the config.xml.

Hint 1: Do not specify the plugin version plugin=”hipchat@0.1.4″ otherwise it doesn’t work.
Hint 2: I append the properties (and below the publishers), because there will/can be others configured through other DSL blocks.

Let’s do the same now for the publishers part:

job {
  configure { project ->
      
    project / 'publishers' << 'jenkins.plugins.hipchat.HipChatNotifier' {
      jenkinsUrl 'http://jenkins/'
      authToken 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      room '76124'
    }
  }
}

As with the properties, we tell the parser to append («) ‘jenkins.plugins.hipchat.HipChatNotifier’ (without the plugin version) and enumerate the parameters.

Following is the full DSL for adding HipChat Plugin support:

job {
  name "Job with HipChat"
  
  configure { project ->
      
    project / 'properties' << 'jenkins.plugins.hipchat.HipChatNotifier_-HipChatJobProperty' {
      room ''
      startNotification false
    }

    project / 'publishers' << 'jenkins.plugins.hipchat.HipChatNotifier' {
      jenkinsUrl 'http://jenkins/'
      authToken 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      room '76124'
    }
  }
}

Once you grasp the Configure block, you’ll be able to generate any job you want. The example below uses the configure block to add a missing functionality in an existing predefined GIT DSL:

job {
  scm {
    git {
      remote { 
        url("")
      }
      branch("refs/heads/${branch}")
      configure { node -> //the GitSCM node is passed in
        
        // Add the CleanBeforeCheckout functionality
        node / 'extensions' << 'hudson.plugins.git.extensions.impl.CleanBeforeCheckout'  {
        }
        
        // Add the BitbucketWeb
        node / browser (class: 'hudson.plugins.git.browser.BitbucketWeb') {
          url 'https://bitbucket.org/my-account/my-project/'
        }
      }
    }
  }
}

A handy tool to play with (or test) the generation of your DSL is http://job-dsl.herokuapp.com/. It will prevent your from constantly running your DSL and open the config.xml from your jenkins to see if the xml is generated correctly!

Although the Configure block is really awesome, it doesn’t beat the predefined DSL commands, so if you have the time I suggest to contribute to the project by making it a predefined DSL :) https://github.com/jenkinsci/job-dsl-plugin/blob/master/CONTRIBUTING.md

If you have some other great Configure Block example, share them in the comments :)

By the way, if you found a typo, please fork and edit this post. Thank you so much! This post is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Comments

Fork me on GitHub