In buying a new machine, I had to re-setup my development environment and tools. This is always a good opportunity to reflect on what is working, what to keep, and what to discard in favor of new tools and systems. But above all, with a new system usually comes new versions of software. And sometimes new versions break old things.
My deployment setup using Capistrano broke horribly.
I must admit, I have not been the best when it comes to upgrading my software packages. Even though I’ve been using homebrew, and upgrading is as easy as
brew update x, I have rarely done it. Same with my rubygems. Basically I’ve only done it at times when I hear about a security update, or I run into a specific bug that is fixed in a newer version.
Capistrano 3 incompatible with Capistrano 2 scripts
But technology never stands still. While I was busy getting things done elsewhere, the Capistrano team had rolled out a major release and went from version 2 to version 3. If you’re not familiar with semantic versioning, a major release means changes that breaks backwards compatibility. This happened in 2013, which in the technology world is a long time ago, but I didn’t know. I didn’t keep up with development news from the Ruby world.
I happily installed the latest version of Capistrano on my new shiny machine and watched in dismay when the script only spat out a bunch of errors and I no longer could deploy to my site.
I found out about the change during my google travels to solve the errors. Since the release had been so far back in time, it took me a while to track down it as the cause. In the end I solved it the easy way:
In order to make the original deployment script work with Capistrano 3, it would need a rewrite. Rewrite means time. Time to study docs, what has changed, and how to accomplish the same task with new code. The developers of Capistrano provided an upgrade guide as most developers do when shipping significant changes. But I did not have this time for studying and rewriting.
Keep using Capistrano 2 with the help of bundler
Instead I used bundler to handle the dependency of my script. If you come from the php world, bundler does similar things for ruby that composer does for php. If you are not familiar with dependency management, I highly encourage you to read about it. Andrey “Rarst” Savchenko has put together a good resource for using composer in the context of WordPress.
This is how to make the Capistrano deployment work again:
- Install bundler. It is a ruby gem and installed via
$ gem install bundler
- Create a Gemfile in your project, in the same directory as the Capfile. It is simply a text file called “Gemfile” without any extension. You can do this manually or use
$ bundler init
- Edit your Gemfile and specify which gems and versions your project depends on. The entire contents of my Gemfile looks like this:
source 'https://rubygems.org' gem 'capistrano', '~> 2.12.0' gem 'railsless-deploy', '~> 1.0.2'
- Set up the project with
$ bundle install
Bundler will check if the required gems are installed and download and install any missing gems, including dependencies, for you. So even though the Gemfile only specifies two gems above, the number of gems installed by bundler will be many more. Exactly how many depends on which gems that already are installed on the system and which are missing. Bundler will then create a file called
Gemfile.lockthat includes a snapshot of the exact version of the gems required to run the project.
Both the Gemfile and the Gemfile.lock files should be checked into source control if you use that (and you do use source control don’t you?). This is similar to the composer.json and composer.lock files.
Same deploy script, new deploy command
The final piece to the puzzle is that the command to deploy has changed. Deploy with
$ bundle exec cap deploy
and continue on with your business! I have both Capistrano gems 2.12 and 3.3.5 installed in parallell but bundler makes sure the right one gets used so the script can execute without errors.
I might revisit my Capfile in the future and rewrite it for Capistrano 3. It would be good for me to learn a bit more of Ruby. But I am already thinking of other improvements to the workflow.
When I first set things up, I didn’t know about composer so at the moment all plugins and themes are checked in together with WordPress core into one big git repo for deployment. A couple of themes and plugins are managed using git submodules.
This is an inelegant and clunky solution. If I revisit my setup I would rather use composer to manage themes and plugin dependencies. I could then let capistrano execute the composer update on the server instead of me checking in each plugin’s code into my repository. This would require a bit of restructuring of the directory setup and also some research from my part. That project goes on my someday-maybe list.
As of now, I got things working and I can update my production site from my local environment again. If I decide to rewrite my script, I will definitely do a writeup of it here. But in the end, programming is about solving problems. This problem is now solved and I can get on with my work.