How to Upgrade Rails app
tl;dr
- Make sure all your tests pass. (
(rspec | minitest)
) - Install Ruby to appropriate version (`(rvm | rbenv) install
`) - Update Gemfile version of Ruby:
ruby '<appropriate ruby version>'
- Update Gemfile version of Rails:
gem 'rails', '~> <new version>'
- Run:
bundler update
- Run:
bin/rails app:update
and make appropriate choices. - Make sure all your tests pass. (
(rspec | minitest)
) - Test on pre-production environments, just in case. ;-)
- Deploy to production and test.
- On to the next problem!
I want to go over how to update an existing Ruby on Rails application. I will upgrade a Rails application from 5.2.2 to 6.0.3 with details of each step I took showing examples.
You will learn specific steps and what to expect when upgrading a Ruby on Rails application. This article will take you about eight minutes to read.
Introduction
I never had to “upgrade” a Rails app. The documentation sounds easy and you should refer to them. I felt I needed to get specific step-by-step instructions. Hopefully this will help you (or future me) when upgrading a Rails app.
Why??
I had upgrade a Rails project due to security issues from Github’s Dependabot
No time like the present, right?
Requirements
The steps I outline should work for any version of Rails. The most important thing is to have a working Rails application you want to upgrade.
If you want to use the one I have, it is available at: https://github.com/a-leung/tdd_indepth_callbacks/
You can also see all the code changes I made on this branch of the repository:
https://github.com/a-leung/tdd_indepth_callbacks/tree/fix/upgrade_rails_1
Get things in working order
Before upgrading, ensure the application is in a working order. This means:
- The app runs
- The automated test suite run and all pass
- If any, tests don’t pass, solve it.
In my case:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ rspec
....
Finished in 0.04772 seconds (files took 0.7398 seconds to load)
4 examples, 0 failures
This is a small app, larger apps benefit from automated tests when upgrading Rails because without tests, one won’t know the side effects up upgrading the app’s foundation until the worst possible time(s).
Install and Configure Ruby version
Each version of Rails requires a minimum version of Ruby. The documentation lists required Ruby versions for each version of Rails.
If the version of Ruby installed is equal or greater than the version required by Rails, move on to the next section. Otherwise, follow the next steps to install a new version of Ruby.
Install Ruby
To install Ruby 2.6.0, I used rvm
to install it:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ rvm install 2.6.0
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 18.1M 100 18.1M 0 0 3243k 0 0:00:05 0:00:05 --:--:-- 3394k
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.6.0 - #validate archive
ruby-2.6.0 - #extract
ruby-2.6.0 - #validate binary
ruby-2.6.0 - #setup
ruby-2.6.0 - #gemset created /home/vagrant/.rvm/gems/ruby-2.6.0@global
ruby-2.6.0 - #importing gemset /home/vagrant/.rvm/gemsets/global.gems..................................
ruby-2.6.0 - #generating global wrappers.......
ruby-2.6.0 - #gemset created /home/vagrant/.rvm/gems/ruby-2.6.0
ruby-2.6.0 - #importing gemsetfile /home/vagrant/.rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.6.0 - #generating default wrappers.......
Painless!
Configure Gemfile for new version
If you try to run or update your application now, this error may appear:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ bundle update
Your Ruby version is 2.6.0, but your Gemfile specified 2.4.5
This requires an update of the Ruby version in the Gemfile
.
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
-ruby '2.4.5'
+ruby '2.6.0'
Tricky, not too bad.
Upgrade Rails
When you started the project, do you remember installing Rails? In my previous articles, it was via:
$ gem install rails
...
$ rails new <application name>
Are the same steps taken?
Nope! This isn’t like other software where you just download a new version.
Update Rails’ entry in the Gemfile
Upgrading Rails is nothing like installing Rails. To upgrade Rails, on
an existing Rails project, update the rails
version in the
Gemfile
:
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
-gem 'rails', '~> 5.2.2'
+gem 'rails', '~> 6.0.2'
Easier than installing, right?!
Actually updating
The part where the actual updating of Rails happens is when running:
bundle update
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ bundle update
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies....
Using rake 13.0.1 (was 12.3.2)
Using concurrent-ruby 1.1.6 (was 1.1.4)
...
Using uglifier 4.2.0 (was 4.1.20)
Using web-console 4.0.3 (was 3.7.0)
Bundle updated!
If bundle returned an error, especially about compatibility, follow
the next steps, otherwise, you’re good to move onto the next section
to run bin/rails app:update
.
Compatibility Errors
Larger and longer lived projects will run into cases where not all gems are compatible with the version of Ruby or Rails. The error may look like:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ bundle update
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Bundler could not find compatible versions for gem "actionpack":
In snapshot (Gemfile.lock):
actionpack (= 5.2.2)
In Gemfile:
rails (~> 6.0.2) was resolved to 6.0.3.rc1, which depends on
actionpack (= 6.0.3.rc1)
sass-rails (~> 5.0) was resolved to 5.0.7, which depends on
railties (>= 4.0.0, < 6) was resolved to 5.2.2, which depends on
actionpack (= 5.2.2)
The solution would be to also update the associated gem’s version to the one compatible with Ruby and/or Rails.
Another option would be to remove the gem from the Gemfile
until
completing the Rails update process.
This may be the most complicated part of the process, especially if gems are incompatible or not updated.
If you have a lot of trouble, feel free to contact me.
Run: bin/rails app:update
This is the final step in upgrading Rails: getting latest
configurations for the application. Just run: bin/rails app:update
and follow the prompts.
Warning, there can be breaking changes to your application if you
accept all changes suggested. (i.e. getting a new clean routes.rb
file.)
Be careful with your choices!
This is what the process can look like:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ bin/rails app:update
DEPRECATION WARNING: Single arity template handlers are deprecated. Template handlers must
now accept two parameters, the view object and the source for the view object.
Change:
>> Coffee::Rails::TemplateHandler.call(template)
To:
>> Coffee::Rails::TemplateHandler.call(template, source)
(called from <main> at /home/vagrant/tdd_indepth_callbacks/Rakefile:6)
identical config/boot.rb
exist config
conflict config/routes.rb
Overwrite /home/vagrant/tdd_indepth_callbacks/config/routes.rb? (enter "h" for help) [Ynaqdhm] a
...
THOR_MERGE !?
If you want to merge changes, configure the THOR_MERGE
environment
variable with an appropriate editor before running bin/rails
app:update
.
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ THOR_MERGE=/usr/bin/vim bin/rails app:update
That way, you can incorporate changes into your file as merge instead of copying and pasting.
Test
Now, the moment of truth: did the rails update work?!
This is where tests come into play: use them! They will check if everything still behaves the way you expect it and tell you if Rails made a change that causes your app to misbehave.
I would advise performing the following:
- Run automated tests
- Deploy and test in pre-production environments
- Deploy and test in production environments
Past you may have hated writing all of those tests, present you definitely appreciates them! ;-)
Conclusion
Upgrading Rails is a smooth process when taking the proper steps:
- Making sure your application is in working order.
- Upgrade to appropriate version of Ruby.
- Upgrade Rails, associated gems, and configurations.
- Test and deploy.
Optional: Update Bundler
While you’re updating Ruby AND Rails, why not make sure you have the newest version of Bundler, the tool that glues everything together?!
The project’s Gemfile was still using Bundler 1.17.2.
You can find out which version of bundler your app is using by looking
at the bottom of the Gemfile.lock
file:
BUNDLED WITH
1.17.2
Update Bundler
Upgrading Bundler is like installing it the first time: gem install
bundler
. To specify a specific version, use the following format:
$ gem install bundler:<version>
For example, installing Bundler 2.1.4
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ gem install bundler:2.1.4
Fetching bundler-2.1.4.gem
Successfully installed bundler-2.1.4
Parsing documentation for bundler-2.1.4
Installing ri documentation for bundler-2.1.4
Done installing documentation for bundler after 5 seconds
1 gem installed
With a new version of Bundler, have your Rails project use it by running:
bundle update --bundler
For my project, the result:
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ bundle update --bundler
...
Using uglifier 4.1.20
Using web-console 3.7.0
Warning: the lockfile is being updated to Bundler 2, after which you will be unable to return to Bundler 1.
Bundle updated!
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ git diff
diff --git a/Gemfile.lock b/Gemfile.lock
index be7a8b4..76e6106 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -239,4 +239,4 @@ RUBY VERSION
ruby 2.4.5p335
BUNDLED WITH
- 1.17.2
+ 2.1.4
vagrant@ubuntu-xenial:~/tdd_indepth_callbacks$ bundle --version
Bundler version 2.1.4
Now your project is using another version of Bundler too.