DevelopmentWeb

Adventures with Boxen

By January 12, 2014 No Comments

Setting up a new laptop can be an arduous process. Here I try Boxen, an automated setup tool for OS X.

So I’ve started working at Cookies HQ and now have a brand new MacBook Pro running OS X Mavericks with 8GB and a blistering fast SSD.

Setting up a new shiny (Apple) laptop by hand can be a arduous process. What dependencies does my app have again? How do I transfer all my beloved settings and config? Add to that the process of going to 10 different sites, downloading your favourite apps and labouriously installing them all. Maybe there’s a better way?

Zac has already covered his experiences with Thoughbot’s laptop script (which is great and definitely worth a look), but I’d been reading recently about a project to come out of Github called Boxen, and wanted to give a go.

What’s Boxen?

Boxen is basically a customised-for-Mac wrapper round the provisioning tool Puppet, which allows you to specify the desired state of your computer in code and have Puppet do all the hard work of working out how to achieve that state. The big draw for Puppet is that all its changes are idempotent, and so unlike a shell script, stuff shouldn’t break if you re-run it! If you want to install or upgdate a program, you simply update your Puppet manifest and re-execute Boxen.

For example, simply adding the following to your Boxen project

include chrome

and running Boxen will download and install Google Chrome for you. No more drag-and-dropping or lengthy installers!

First steps

For getting up and running with Boxen, the setup is surprisingly simple. The full steps are detailed in the Boxen README, but basically involve cloning the boxen/our-boxen repo to your machine, and overriding the origin with a new Git repo of your own.

Once cloned (this may take a little while as the repo contains cached tarballs of common modules), simply run script/boxen from inside your boxen repo and watch as Boxen magically sets up your laptop!

Of course, you’ll want to change the defaults that vanilla Boxen gives you. Consult manifests/site.pp, and remove (or add, more on this later) anything you don’t want – I personally removed the nodejs versions, for example, as we don’t currently work with Node.js at Cookies HQ.

Adding modules

Adding a new module is generally a two-step process: firstly, find the module on Github and add it to your Puppetfile (the version will be the git tag), then add include <module_name> to your manifest.

Pitfalls

Setting up all the gems we use at Cookies HQ was a relatively pain-free experience (and lightning-quick with the MacBook Pro’s SSD), but I did have problems with gems that use C extensions. Most of these problems have been fixed upstream, so if you can, a swift bundle update <gem name> will solve you a lot of headaches.

libv8 and therubyracer

Older versions of libv8 will not compile with the new compilers that ship with Mavericks’ XCode tools, so you’ll need to install an older gcc package.

In your manifests somewhere, add:

package { 'apple-gcc42': }

Next, you’ll need to add some shims so that RubyGems uses the older gcc-4.2. The easiest way is to create a folder somewhere:

mkdir gcc42_shims
cd gcc42_shims

then create your shims:

ln -s `which c++-4.2` c++
ln -s `which g++-4.2` g++
ln -s `which gcc-4.2` cc
ln -s `which gcc-4.2` gcc

finally, in your project directory, add the shims to the front of your path and install the troublesome gem:

PATH=/path/to/gcc42_shims:$PATH bundle install

Qt

We use capybara for integration tests, which requires Qt. I had problems with the Boxen puppet-qt module, which uses a custom Homebrew formula. Fortunately the normal Homebrew formula seemed to work. Simply replace

include qt

in your manifest with

package { 'qt': }

and all should be well.

Puppet

You can get pretty far by just using the Puppet modules on the Boxen Github organisation (and there are plenty of others elsewhere on Github), but at some point you may find yourself wanting to write some Puppet. The syntax is relatively straightforward once you’ve looked at enough manifests (reading the source of the various Boxen puppet modules helped me a great deal) – just remember that the language is like Make or Rake, in that you need to declare dependencies for each unit of work, rather than expecting Puppet to execute your manifests in the order you wrote them (often it won’t!).

Customising modules (or: resetting to defaults)

By default, Boxen runs its services (like databases) on non-standard ports, typically port + 1000. This is to avoid clashes with already-installed services – but if, like me, you’re running on a new laptop, you may want to use the standard settings to conform with other developers on your team.

Puppet has a component called Hiera that allows us to do just that. The feature is pretty poorly documented within Boxen, so here’s how I got it working:

Firstly, Boxen expects a Hiera config file at config/hiera.yaml within your Boxen repo.

---
:backends: yaml
:yaml:
  :datadir: "%{::boxen_repodir}/hieradata"
:hierarchy: common
:logger: console

The %{::boxen_repodir} refers to a Puppet fact that Boxen gives us, which is the location of your Boxen repo. Read the Puppetlabs docs for further details.

Now we have Hiera configured, Boxen will look within the hieradata directory for your module overrides. I added a common.yaml file here:

---
# Use default ports for databases
mysql::config::port: 3306
postgresql::port: 5432

Running script/boxen again, you will see Puppet changing the necessary settings files so that Postgres and MySQL use the default ports.

I found the following links invaluable whilst getting Boxen set up: