Getting Ready for Rails 6.0: How to Dual Boot
In this article I will explain how you can dual boot your application in your local environment and your continuous integration (CI) service. I hope that this will help you get ready for the next stable release of Rails.
Even though my example assumes you are running Rails 5.2 and want to migrate to Rails 6.0 , these tips work for any two versions of Rails.
Create a Gemfile.next File
At RailsConf 2018, Jordan Raine talked about Clio’s process to upgrade Rails over the years. If you missed his talk, you can watch it over here: Ten Years of Rails Upgrades
In his talk he mentioned quite a handy companion gem: ten_years_rails
. At FastRuby.io we decided to fork
it and call it next_rails
. I’m going
to use that gem to get my project ready for dual booting. First, I need to
install it in my local environment.
$ gem install next_rails
Successfully installed next_rails-1.0.2
Parsing documentation for next_rails-1.0.2
Installing ri documentation for next_rails-1.0.2
Done installing documentation for next_rails after 0 seconds
1 gem installed
Warning: next_rails
requires Ruby 2.3 or higher. You can find a manual
workaround below.
Assuming I can use that gem, I will initialize my Gemfile.next
file like this:
$ next --init
Created Gemfile.next (a symlink to your Gemfile). Your Gemfile has been modified
to support dual-booting!
There's just one more step: modify your Gemfile to use a newer version of Rails
using the `next?` helper method.
For example, here's how to go from 5.2.3 to 6.0:
if next?
gem "rails", "6.0.0"
else
gem "rails", "5.2.3"
end
That command creates a Gemfile.next
symlink
to my Gemfile
and adds a handy method called next?
to my Gemfile
:
Why to use a symlink
I see three main benefits to using a symlink:
-
The way Bundler works it will generate one
.lock
file per Gemfile. If you manage all your dependencies logic in your Gemfile (withoutGemfile.next
) and yourGemfile.lock
is checked in to your Git repository, then you will have to constantly resolve conflicts between your long running upgrade branch andmaster
. This will become tedious if you have a really activemaster
branch and your upgrade project lasts months (not weeks) -
By making it a symlink to
Gemfile
, you can keep all your logic inside one file. That means that you can quickly see what are the main difference between your current version of Rails and the next version. -
You can use Bundler’s
BUNDLE_GEMFILE
environment variable. Because a symlink is transparent to Bundler, it assumes that you have two physical files. You can later switch between one version of Rails or the other by just adding one environment variable to your command line.
# Gemfile
def next?
File.basename(__FILE__) == "Gemfile.next"
end
source 'https://rubygems.org'
# ...
If you have any problems installing next_rails
, you can manually add the
next?
method to your Gemfile
and create a symlink like this:
$ cd path/to/project
$ ln -s Gemfile Gemfile.next
Bump Rails (Gemfile.next)
In this simple example, I only need to upgrade rails
(from Rails 5.2 to Rails
6.0). It’s very likely that you will have to upgrade more dependencies. The
first step is to get my Gemfile
to look like this:
def next?
File.basename(__FILE__) == "Gemfile.next"
end
source 'https://rubygems.org'
if next?
gem 'rails', '~> 6.0.0'
else
gem 'rails', '~> 5.2.3'
end
# ...
Now I can install my current dependencies with bundle install
and my future
dependencies with next bundle update
. If next bundle update
doesn’t work
for you, you can just run BUNDLE_GEMFILE=Gemfile.next bundle install
. As a
general rule, if next <command>
doesn’t work in your environment you can
replace it with BUNDLE_GEMFILE=Gemfile.next <command>
.
next bundle <command>
might not work because you are using an old version of
Bundler . So, you should try using Bundler 2.0 or higher.
Run Tests
After running next bundle update
, I have a brand new Gemfile.next.lock
file.
That means that my dependencies are ready to run my test suite. So I can run
them like this:
$ next bundle exec rake
There are many advantages to using dual booting in your Rails application. In no particular order:
- You can run your test suite with two different versions of Rails. Running
bundle exec rake
still works thanks to the conditionals in yourGemfile
. - You can run your application in development with two different versions of
Rails. Simply prepend
next
tobundle exec rails server
. - You can even run your application in staging using the next version of Rails.
Simply make sure that you set this environment variable:
BUNDLE_GEMFILE
- You can quickly debug issues between your current version of Rails and the
next one. Dual booting plus
debugger
is a powerful combo for finding bugs between versions.
Setup Continuous Integration
Depending on the type of project, we like to use Travis CI for open source projects and Circle CI for client projects. Below you will find a couple of sample configuration files that you could use.
Both samples will require you to commit and push Gemfile.next
to your repository;
will run a build matrix; and might need some tweaking.
Circle CI
For all of our client projects we prefer this continuous integration service. Here is the configuration that you could use for dual booting in Circle CI:
A few notes about this configuration:
- It’s using Ruby 2.6 and a Postgres database
- It’s using Circle CI’s API version 2.0
- It has a lot of duplication (there is probably a way to DRY it)
- The key is to configure a workflow with two jobs (one for Rails 5.2 and another one for Rails 6.0) like this:
workflows:
version: 2
build:
jobs:
- "build-rails-5-2"
- "build-rails-6-0"
Travis CI
For all of our open source projects we prefer this continuous integration service. Here is the configuration that you could use for dual booting in Travis CI:
A few notes about this configuration:
- It’s for an open source application that is using Postgres
- You might not need some of the things in this sample
- It’s certainly simpler than the Circle CI configuration. I love how simple it is to configure a build matrix in Travis CI:
rvm:
- 2.2.6
gemfile:
- Gemfile
- Gemfile.next
Start Fixing The Rails 6.0 Test Suite
Now that you have your test suite running in both Rails 5.2 and Rails 6.0, you can start tweaking your code and dependencies to work with both gemfiles. There will be two big hurdles:
- Getting Bundler to bundle your dependencies
- Getting your test suite to pass
Summary
This has been quite a useful technique for us at FastRuby.io . We have used it with client projects, internal projects, and open source applications.
I hope that you will find it useful in getting ready for the next version of Rails!