If you want to have a working, ready to use cookbook to setup Jenkins and use it as CI for Rails applications check out the next blog post. It describes how to use the cookbook and shows the highlights of the most interesting pieces of code.
This post weighs up the pros and cons of using Jenkins as a CI tool for a Rails project and briefly describes plugins used to get the best experience. If you are interested in our reasoning for using Jenkins this post is for you. If you just need a working solution skip to the next one.
This post series might be interesting for you if your requirements include at least one of the following:
- you need a free CI tool with support for Ruby/Rails applications and automatic pull request building
- you want to have CI set up with automation and have full control over the configuration
- the source code cannot be shared with third party services
- the setup for your applications or their environments is complicated and not supported by commercial CI tools.
There is no question that having a Continuous Integration server in every modern software house is a must. It allows you to build all the components of your system with each code push and gives you instant feedback on what went wrong and when.
There are plenty of third-party websites that integrate with Rails applications pretty well. SaaS saves your time by allowing you to focus on delivering software instead of maintaining infrastructure. For every major programming language and framework there are probably a couple of these services. But what if additional control over the environment is needed?
Control over the software installed on the host machine is extremely important if your software requires some custom setup or you’re working with a legacy framework which is no longer supported by major CI providers or, more simply, you’re working with your own custom framework.
Finally, a self-hosted open source solution is better when the build takes a lot of time and a whole farm of CI servers is needed. Then the cost of SaaS CI might be too high and setting up the CI becomes worth the effort.
Unfortunately, the number of open source solutions is quite limited. Actually, the only free CI which is stable, popular and fulfilled all our requirements in terms of running a Ruby on Rails project is Jenkins.
Jenkins is mostly known among the Java community as the default support for Java build tools is decent (e.g. both maven and gradle is supported). Sadly, there is no support for Ruby or Rails by default so either additional plugins are needed or the setup has to be done with simple bash scripts.
One additional feature is the lack of any default flow, though it’s up to you to decide whether that’s a pro or a con. On the one hand this gives you greater flexibility, on the other it might be difficult to start at all as you need to figure out what works best for you.
Jenkins is free and well known to a lot of developers.
It’s flexible. Multiple builders for job config are provided, e.g. maven, Gradle, shell. If you can’t set something up in the UI there is a high chance that it will be possible with a custom Groovy script. As a result nearly any setup can be achieved. It’s also easy to deploy: most Linux distributions provide packages for it.
The configuration of Jenkins and all jobs is stored in XML files. As long as the GUI is used it should not be a concern at all. But to automate things, we need to get familiar with its structure. Although this is inconvenient for manual changes, it is pretty easy to maintain it with external software.
GitHub provides ready-to-use web-hooks which make invoking third-party services very easy. The hooks can notify any website about new pushes, opened issues or finally pull requests.
It is easy to get lost with changes in the Jenkins configuration. If the configuration crashes because of an unstable plugin it might be difficult to restore the working config.
The simplest solution is to backup all the configs. Another approach is to create a git repository in the Jenkins home directory and keep track of all your changes. This is actually pretty useful when we want to analyse the structure of the configs.
As all the infrastructure in our project is described with Chef, I thought we could give a try and setup Jenkins using Chef as well.
However, Jenkins does also have a couple of drawbacks. One of the biggest is its terrible UI. Web 2.0 happened many years ago but somehow it bypassed Jenkins’s interface. The GUI is unintuitive, too many clicks are needed, and the job configuration form is a nightmare, with tens of wide fields with no validation.
In addition, Jenkins might be a little bit difficult at the beginning because it does not offer any reasonable default flow. This gives you the power to customize everything according to your needs but you have to rely more on convention rather than the software itself.
To get the best experience with Jenkins I used the following plugins:
Default views in Jenkins are quite limited. The plugin allows you to group jobs by regex which might be pretty useful if you follow some convention in naming your jobs.
Currently the approach I use is to put various branches of the same project together in one job group. So the convention for naming jobs is
This plugin links the CI with GitHub. It allows you to use GitHub Webhooks for triggering a job on git push.
This plugin is brilliant. It gives Jenkins what exactly it lacks when compared to a commercial solution. With it Jenkins builds each GitHub pull request and posts back the result of the build to GitHub.
Useful when testing a project against multiple configurations is required, e.g. test gem in multiple Ruby versions.
The workflow I used when creating the cookbook consisted of installing Jenkins manually, creating jobs with the CI GUI and then trying to describe what was done with a chef recipe.
The configuration of Jenkins is stored in multiple XML files, so to figure out the structure of the files it is best to first create them manually and then create chef templates out of them.
Creating a reliable cookbook for our CI host was quite time consuming. A simpler approach would be just to back up the server from time to time or to create Docker container.
Using Chef for provisioning Jenkins server has the following advantages:
- It is easy to create new branch jobs — each job has a separate build history and it is very easy to track what went wrong and when. Adding new jobs is as simple as editing node attributes.
- Easy to spin off a new machine — the only configuration required is to set up some node attributes. Everything else is handled by the cookbook.
- Job configuration is consistent — developer changes get overwritten for all jobs which are set up by Chef.
- Full history of job configuration changes — the cookbook and chef-repo is under git source control.
On the other hand:
- Time-consuming setup — figuring out the config format for each plugin was quite tedious.
- JobConfigHistory Plugin gives you the ability to control job configuration history.
So it’s up to you whether it’s worth the effort. If you want to manage your whole infrastructure consistently then go for it!