This week I finally got the time to check out Hubot. Which really is a cool bot for your IRC/Campfire-Channel.

As I dislike solutions like “just extract this tarball and run …” for production deployment, I needed a way to deploy and configure this with puppet. The solution should also be very easy, so we can create a jenkins task to build the package.

I came out with a solution where you just need to run two commands to create a new package from upstream:

rake prepare[2.3.2]
rake build

My solution was to use fpm to create a debian package from a folder which contained all the stuff to run Hubot.

As there is a huge repository of Hubot-scripts and I also wanted some plugins out of it, I also needed a configuration file to define which scripts I wanted to have packaged into.

To automate the process of downloading Hubot and the needed scripts, afterwards installing the npm-modules and packing it with fpm, I decided to use rake.

There is a configfile, config.yml.

package_url: 'https://github.com/downloads/github/hubot'
scripts_url: 'https://raw.github.com/github/hubot-scripts/master/src/scripts'
scripts:
 - jenkins
 - moarcatsme
 - redmine

The Rakefile contains all the rake-tasks which are used to build the package.

require 'yaml'

desc "Prepare the package"
task :prepare, [:version] do |task,args|
  config = YAML.load_file('config.yml')

  sh "echo #{args.version} > VERSION"

  Dir.mkdir 'workdir'
  Dir.mkdir 'packages'

  Dir.chdir 'workdir'

  sh "curl -L #{config['package_url']}/hubot-#{args.version}.tar.gz | tar xzf -"

  config['scripts'].each do |script|
    sh "curl -L #{config['scripts_url']}/#{script}.coffee > scripts/#{script}.coffee"
  end

  sh 'npm install coffee-script'
  sh 'npm install hubot-irc --save'
  sh 'npm install'
end

desc 'Build the package'
task :build do
  Dir.chdir 'workdir'
  sh 'fpm -s dir -t deb -n hubot --prefix /opt -v $(cat ../VERSION) -d nodejs --after-install ../postinst --before-install ../preinst hubot'
  Dir.chdir '..'
  sh 'cp workdir/hubot_$(cat VERSION)_amd64.deb packages/'

  sh 'fpm -s dir -t deb -n hubot-init --prefix / -v $(cat VERSION) -d hubot,upstart --package packages/hubot-init-$(cat VERSION)_amd64.deb etc'
end

The prepare-task takes one argument which should be the version (e.g 2.3.2). It downloads the tarball of this version and extracts it in a subfolder. Then it downloads all the specified scripts and puts them into the correct folder of Hubot. Afterwards it installs the npm-modules to another subfolder of hubot.

The most interesting part are the fpm-lines:

sh 'fpm -s dir -t deb -n hubot --prefix /opt -v $(cat ../VERSION) -d nodejs --after-install ../postinst --before-install ../preinst hubot'

This command creates a debian package (-t deb) from a directory (-s dir) and throws all the stuff of the given directory (last argument, hubot) in /opt (--prefix /opt). It uses the version we gave the prepare-task and adds a preinst and a postinst file. With -d nodejs we set a dependency on nodejs. We need to change the current workingdir, otherwhise fpm would deploy the stuff in /opt/workdir/hubot.

The second fpm command packages the init script and the example config file from the etc-folder.

sh 'fpm -s dir -t deb -n hubot-init --prefix / -v $(cat VERSION) -d hubot,upstart --package packages/hubot-init-$(cat VERSION)_amd64.deb etc'

The preinst and postinst scripts just add a hubot user and set the rights on /opt/hubot as fpm does not support right management for debian packages, yet.

I tried several times to get all these stuff working. For an easier cleanup between the tries, I added another task:

require 'fileutils'

desc "Cleanup the workdir"
task :cleanup do
  FileUtils.rm_r 'workdir' if File.exists? 'workdir'
  FileUtils.rm_r 'packages' if File.exists? 'packages'
end

Now we just need to modify the config.yml, push the changes and call Hubot in our IRC-Channel to trigger the jenkins build:

hubot jenkins build hubot-build-deb, version=2.3.2