Upgrading Rails: The Dual-Boot Way at RailsConf 2021
This is a companion page to the Rails upgrade workshop at RailsConf 2021.
Here you will find a series of hands-on exercises to get started.
The main goal for this workshop is to define a roadmap to upgrade a Rails application. For all exercises you can either use our sample application, or upgrade your own application.
1. Pre-Requisites
Next StepPre-requisites depend on which path you want to take. You can either:
- - Upgrade your own application, or...
- - Upgrade our sample application
Here are the basic pre-requisites to participate with your own app:
- - Ruby 2.3 or higher
If your application does not meet our minimum requirements, you will need to use our sample app. These are the requirements for our sample app:
- - Git (Installation Steps)
- - Docker (Installation Steps)
- - Docker Compose (Installation Steps)
2. Sample Outdated Rails App
Previous Step Next Step Back to TopWe will work with this sample application: Pecas -- an open source application to show a time tracking leaderboard for https://nokotime.com.
If you don't have Docker, please install it. Instructions here: Docker Installation
To make sure it is installed:
docker --version
Docker version 20.10.5, build 55c4c88
If you don't have `docker-compose`, please install it. Instructions here: Docker Compose Installation
To make sure it is installed:
docker-compose --version
docker-compose version 1.28.5, build c4eb3a1f
Once you have installed `docker-compose` and `Docker`, you can clone the repository:
git clone git@github.com:fastruby/pecas.git
Follow these steps to install the application in your local environment:
cd path/to/pecas
docker-compose build
docker-compose run web /bin/bash
./bin/setup
3. How outdated is our application?
Previous Step Next Step Back to TopThe first step to create the upgrade roadmap is to get an idea of how outdated our application really is. To check that we'll use the next_rails gem.
group :development, :test do
gem 'next_rails'
# ...
end
Then we will need to install:
$ bundle install
Let's check it out! It should give us a good idea about the shape of our dependencies:
$ bundle_report outdated
tilt 1.4.1: released almost 8 years ago (latest version, 2.0.10, released over 1 year ago)
hike 1.2.3: released almost 8 years ago (latest version, 2.1.3, released almost 7 years ago)
coffee-rails 4.0.1: released over 7 years ago (latest version, 5.0.0, released almost 2 years ago)
sass 3.2.19: released about 7 years ago (latest version, 3.7.4, released about 2 years ago)
docile 1.1.5: released almost 7 years ago (latest version, 1.3.5, released 3 months ago)
coveralls 0.7.1: released over 6 years ago (latest version, 0.8.23, released almost 2 years ago)
rails-deprecated_sanitizer 1.0.3: released over 6 years ago (latest version, 1.0.4, released about 2 months ago)
(...)
0 gems are sourced from git
74 of the 108 gems are out-of-date (69%)
We now know that the dependencies are 69% out of date.
`next_rails` also provides an interesting command that we could run to find known compatibility issues:
$ bundle_report compatibility --rails-version=5.0.7
=> Incompatible with Rails 5.0.7 (with new versions that are compatible):
These gems will need to be upgraded before upgrading to Rails 5.0.7.
rails-dom-testing 1.0.9 - upgrade to 2.0.3
sass-rails 4.0.5 - upgrade to 6.0.0
=> Incompatible with Rails 5.0.7 (with no new compatible versions):
These gems will need to be removed or replaced before upgrading to Rails 5.0.7.
coffee-rails 4.0.1 - new version, 5.0.0, is not compatible with Rails 5.0.7
3 gems incompatible with Rails 5.0.7
Now we know there will be two gems that will cause problems. We can add these gems to our TODO list.
4. Dual Boot: Setup
Previous Step Next Step Back to TopWe are going to use a helper tool for dual booting: `next_rails`.
$ next --init
This command created a symlink called `Gemfile.next` and added a helper method to the top of the `Gemfile`.
Now we can open our `Gemfile` and verify that we have a `next?` method defined in it. In the odd chance it didn't work, we can try doing it manually like this:
$ ln -s Gemfile Gemfile.next
Then we can add this method to the top of your `Gemfile`:
def next?
File.basename(__FILE__) == Gemfile.next
end
Now we have all we need to start tweaking our `Gemfile` for the next version of Rails:
if next?
gem 'rails', '~> 5.0.7' # our target version
else
gem 'rails', '~> 4.2' # our current version
end
It's time to `bundle install` using the next version. We can use the `next` command like this:
$ next bundle install
If that doesn't work, you can try with:
$ BUNDLE_GEMFILE=Gemfile.next bundle install
5. Dual Boot: rails console
Previous Step Next Step Back to TopNow that we have bundled our project with the both versions of Rails, we can start the rails console to check what errors are we going to have.
next bundle exec rails console
Now is the time to handle with the issues you may find.
6. Dual Boot: bundle exec rspec
Previous Step Next Step Back to TopIn the same way we can start running the test suite to identify possible issues.
next bundle exec rspec
We will probably find a problem with `twitter-bootstrap-rails` gem that will look like this:
Failure/Error: =require twitter-bootstrap-static/fontawesome
ActionView::Template::Error:
couldn't find file 'twitter-bootstrap-static/fontawesome' with type 'text/css'
Checked in these paths:
/code/app/assets/images
/code/app/assets/javascripts
/code/app/assets/stylesheets
/code/vendor/assets/javascripts
/code/vendor/assets/stylesheets
/usr/local/bundle/gems/jquery-rails-4.4.0/vendor/assets/javascripts
/usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/app/assets/fonts
/usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/app/assets/javascripts
/usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/app/assets/stylesheets
/usr/local/bundle/gems/twitter-bootstrap-rails-4.0.0/vendor/assets/stylesheets
/usr/local/bundle/gems/coffee-rails-4.2.2/lib/assets/javascripts
/usr/local/bundle/gems/actioncable-5.0.7.2/lib/assets/compiled
/usr/local/bundle/gems/turbolinks-source-5.2.0/lib/assets/javascripts
# ./app/assets/stylesheets/bootstrap_and_overrides.css:6
This is because for bootstrap-sass gem now we need to replace require
for @import
.
So lets open the bootstrap_and_overrides.css file and replace this line:
=require twitter-bootstrap-static/fontawesome
with this one:
@import twitter-bootstrap-static/fontawesome
Now we should be finally ready to run all model specs.
next bundle exec rspec
............................
Finished in 4.04 seconds (files took 8.55 seconds to load)
37 examples, 0 failures
As we process the entire output from the test suite (like the sample here), we should consider a couple of things:
1. Every deprecation warning that we find should become a new story in our roadmap, like for example:
DEPRECATION WARNING: Using positional arguments in functional tests has been deprecated,
in favor of keyword arguments, and will be removed in Rails 5.1.
2. Every failure we find should become a new story. Be mindful to write down the potential root cause when creating the story.
Some of the failures we encounter might have a simple solution, some might take us hours or days to fix. That's why we won't fix all the issues we find today.
To find the root cause of a test failure, we will need to check the changes between Rails versions. You can find the official guides over here: https://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html
Or you can use the unofficial guides that we are maintaning over here: https://www.fastruby.io/blog/rails/upgrade/rails-upgrade-series.html
Another useful resource is RailsDiff, where we can see what changed between Rails 4.2 and Rails 5.0, for example.
7. Stay Current
Previous Step Next Step Back to TopDeprecation warnings in the latest versions of Rails have been quite helpful in guiding us to the next version of Rails. We should treat all new deprecation warnings as exceptions:
# config/environments/test.rb
Rails.application.configure do
# Raise on deprecation notices
config.active_support.deprecation = :raise
end
That way we can turn _noise_ into many _signals_. Deprecation warnings can easily be ignored when they're just a message in `test.log`. They can't be easily ignored if they break your test suite.
Another great resource is to add bundler-audit gem as a dependency and make sure to run a check every time you run your test suite. We can do this by tweaking your `Rakefile`:
# Rakefile
require File.expand_path('../config/application', __FILE__)
require 'rake'
Rails.application.load_tasks
task default: %i[
bundle:audit brakeman:check
spec spec:javascripts cucumber
]
Now everytime you run `bundle exec rake`, it will not only run your test suite but also run `bundle:audit` which does this:
namespace :bundle do
desc "Audit bundle for any known vulnerabilities"
task :audit do
unless system "bundle-audit check --update"
exit 1
end
end
end
end
Finally you can keep up with the latest version by using the Rails `main` branch:
# Gemfile
source 'https://rubygems.org'
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
"https://github.com/#{repo_name}.git"
end
if next?
gem 'rails', github: 'rails/rails'
else
gem 'rails', '~> 5.2'
end
You can find instructions on how to set this up in your CI server over here: https://fastruby.io/blog/upgrade-rails/dual-boot/dual-boot-with-rails-6-0-beta.html
At this point you should have a solid list of items in your roadmap. You can start addressing them one by one with many, tiny pull requests.
8. Questions?
Previous Step Next Step Back to Top
If you have any questions, you are welcome to reach out to us in Discord or sending us a message at FastRuby.io.
You can also find us on Twitter at:
@etagwerker,
@cleicar2 and
@lubc32
9. Thank you!
Previous Step Back to TopYou are wonderful! Thanks for participating in my workshop. I hope you can apply all of this in your next Rails upgrade project! :)