Upgrade Ruby using dual boot

Vijay Vinod

By Vijay Vinod

on September 4, 2024

Recently, we upgraded all neeto product's Ruby version from 3.2.2 to 3.3.0 using "dual-booting".

Dual-booting is a process that allows you to run your application with different sets of dependencies, making it easy to switch between them. This approach enables you to quickly test your code with both the current and the newer version of what you are upgrading, ensuring that everything works smoothly before fully committing to the upgrade.

How to dual-boot Ruby?

The dual-boot technique involves maintaining two separate Gemfile.lock files within your Rails project: one for the current version of Ruby and another for the next version you're upgrading to.

To get started, first create a symbolic link for Gemfile.next with the following command:

1ln -s Gemfile Gemfile.next

This command creates Gemfile.next file, which you'll use for the new Ruby version. This file isn't a separate copy but rather a pointer to your original Gemfile in the Rails project directory.

Next, add the following snippet at the top of your Gemfile:

1def next?
2  File.basename(__FILE__) == "Gemfile.next"
3end

This code snippet helps determine which Gemfile is in use, whether it's the standard one or the Gemfile.next for the new Ruby version.

Now, let's utilize the next? method to dynamically set the Ruby version in the Gemfile using a conditional operator:

1ruby_version = next? ? "3.3.0" : "3.2.2"
2ruby ruby_version

This code snippet helps determine the Ruby version based on the next? method. If next? returns true, meaning you're operating within Gemfile.next, it sets the Ruby version to "3.3.0". Otherwise, if the standard Gemfile is being processed, it defaults to "3.2.2".

How to install dependencies?

With the dual-boot setup in place, you can effectively manage dependencies for both your current and next versions of the application.

Installing current dependencies: To install dependencies for your current version, simply run:

1bundle install

This command uses the standard Gemfile to resolve and install dependencies for your current application.

Installing next dependencies: To install dependencies for the next version of your application, use the following command:

1BUNDLE_GEMFILE=Gemfile.next bundle install

This command specifies Gemfile.next as the Gemfile, allowing you to install dependencies specifically for the next version.

Managing commands for the next version: To perform various operations for the next version, you can use the syntax BUNDLE_GEMFILE=Gemfile.next <command>. For example, to start the Rails server with the next version's dependencies, you would use:

1BUNDLE_GEMFILE=Gemfile.next rails s

This approach ensures that Gemfile.next is explicitly used for the specified command, allowing you to work with the next version of your application while keeping dependencies and configurations correctly managed.

Considerations for dual-boot setup

When implementing a dual-boot setup, it's crucial to consider where both Ruby and gem versions are defined. While gem versions are usually specified only in the Gemfile, the Ruby version can be defined in several locations. The most common are:

  • .ruby-version File: Used by version managers like rbenv to specify the Ruby version.
  • Gemfile: This is where Bundler, RVM, Heroku, and similar tools reference the Ruby version to manage the Ruby environment.
  • Gemfile.lock: Although the Ruby version in this file is mostly informative, it's worth noting.
  • Dockerfile: Defines the Ruby version for Docker containers if you're using Docker.
  • CI setup steps and configurations.
  • Other less common locations within applications.

We need to make sure that dual-boot setup is compatible with all of them to ensure a smooth transition and consistency across different environments.

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.