Creating a Custom Jenkins Plugin with JRuby

13. January 2015 00:27 by Jay Grossman in   //  Tags: , , , ,   //   Comments (0)
I have seen scenarios where I would like to not have Jenkins not execute builds under certain circumstances. One common example is with the Maven Project Plugin, it will often update the .pom file and check it back into source control. When you have Jenkins jobs that poll source code repos for new commits, your builds will enter an endless cycle of triggering more builds.
It would be nice to have the ability to stop builds from executing when the commit contained a specified phrase. I searched for such a plugin with no luck, so I have created my own plugin (that I use in production):
Since there wasn't much detail for creating custom plugins in Ruby that I could find, this blog post will walk through the process.
Options for making Jenkins Plugins

1) Maven (default)
2) JRuby 
 Since setting up .pom files always seems painful to me, I wanted to try the Ruby option.
I found this very light post from 2013 that showed a few examples and the jpi gem with not much explanation:
Setting up a JRuby Plugin Development Environment
I like to do all my development in reproducible environments when possible, so I set up a vagrant environment for building and testing Jenkins Plugin development & testing.
My Vagrantfile installs the following dependencies on Centos 6.5:
  • Java 1.6
  • Maven 
  • Jenkins
  • rbenv & jruby 1.7.9
  • jpi gem
Setting up a JRuby Plugin Development Project
1) Create a directory with the name of your project (jenkins-ignore-commit-plugin)
2) Create a pluginspec file (jenkins-ignore-commit-plugin.pluginspec):
Jenkins::Plugin::Specification.newdo |plugin| = "jenkins-ignore-commit-plugin"
 plugin.display_name = "Ignore Commit Plugin"
 plugin.version = '0.0.1'
 plugin.description = 'Commits that contain a supplied phrase within the
commit messages will be skipped.'
# You should create a wiki-page for your plugin when you publish it, see
# This line makes sure it's listed in your POM.
 plugin.url = ''
# The first argument is your user name for
 plugin.developed_by "jaygrossman", "Jay Grossman <jay.grossman@org>"
 plugin.uses_repository :github => "jaygrossman/jenkins-ignore-commit-plugin"
# This is a required dependency for every ruby plugin.
 plugin.depends_on 'ruby-runtime', '0.12'
3) You'll need these gems: jenkins-plugin-runtime, jpi, jruby-openssl. Here is my Gemfile:
gem "jenkins-plugin-runtime", "~> 0.2.3"
group :development do
gem "jpi", "~> 0.3.8"
gem "jruby-openssl", "~> 0.8.8"
gem "rake", "~> 10.0.4"
gem "pry"
gem 'coveralls', require: false
gem 'rubyzip', "~> 0.9.9"
4) If your plugin requires user interface elements (such as checkbox, textbox, textarea, password) such as the items shown below, you'll want to create a View.
To set up the View:
    a) Create views sub-directory.
    b) Create a plugin sub-directory with views (I called my directory views/ignore_commit). 
    c) Create a file named config.erb in that directory.
    d) The entry() function can define the form element, title, field id, and description as shown below:
f = taglib("/lib/form")
f.entry(:title => 'phrase', :field => 'ignore_commit_phrase', :description => "Commits containing this phrase will be considered NOT_BUILT") do

5) Next you'll want to create a model to do the actual work for the plugin.
    a) create a models sub-directory
    b) create a file plugin.rb file (mine is called ignore_commit.rb):
# The name of the plugin shown in Jenkins
display_name "Ignore Commits with Phrase"
# assign parameter associated with field from view
attr_accessor :ignore_commit_phrase
# set parameter to a variable
def initialize(attrs)
@ignore_commit_phrase = attrs['ignore_commit_phrase']
# Here we test if any of the changes warrant a build
def setup(build, launcher, listener)
  changeset = build.native.getChangeSet()
  # XXX: Can there be files in the changeset if it's manually triggered?
  # If so, how do we check for manual trigger?
  if changeset.isEmptySet() "Empty changeset, running build."
  logs = changeset.getLogs()
  latest_commit = logs.get(logs.size -1)
  comment = latest_commit.getComment()
 # This is the key part of the model.
 # We Test if comment includes the specified phrase and
 # will exit the build as "NOT_BUILT" when found. 
 if comment.include? ignore_commit_phrase "Build is skipped through commit message." "Commit: #{latest_commit.getCommitId()}" "Message: #{comment}"
   build.halt("Build is skipped by Ignore Commit Plugin.")
   listener.error "Encountered exception when scanning for filtered paths: #{$!}"
   listener.error "Allowing build by default."
  listener.error "Encountered exception when looking commit message: #{$!}"
  listener.error "Allowing build by default."
Building the code and generating the Jenkins Plugin

From within the project directory, run:
jpi build
The JPI gem executes a build via maven and will compile a .hpi file (plugin binary file that can be uploaded into Jenkins) in a pkg sub-directory:
A Screenshot
You can then manually install the plugin from the Plugin Manager upload page in Jenkins.
If you have a local  local Jenkins environment (like in our vagrant set up), you can run the following commands to upload the plugin to it:
bundle update rake
jpi server
A Screenshot
Running the Build and Testing Vagrant image
1) Download this Vagrantfile and put it in the root of your plugin project directory:
2) Run this command to build the plugin and load it into a local Jenkins instance:
vagrant up
3) Once the vagrant up execution is complete, paste the following link into a web browser on the host machine to view the Jenkins instance running in the vagrant:

About the author

Jay Grossman

techie / entrepreneur that enjoys:
 1) my kids + awesome wife
 2) building software projects/products
 3) digging for gold in data sets
 4) my various day jobs
 5) rooting for my Boston sports teams:
    New England PatriotsBoston Red SoxBoston CelticsBoston Bruins

Month List