👊 RailsBump.org is now supported & maintained by FastRuby.io ❤️

👊 RailsBump.org is now supported & maintained by FastRuby.io ❤️

Today I’m happy to share that RailsBump is now being supported and maintained by our team. I wanted to take a moment to explain why this project is so important to our mission of reducing technical debt in our internal, open source, and clients’ applications.

What is RailsBump.org?

RailsBump is a free & open source web application that allows anyone to check their Ruby gems (or Gemfiles) for compatibility with all major Rails versions.

The app is live at RailsBump.org opens a new window , where you can check an individual gem or a whole Bundler lockfile (Gemfile.lock).

RailsBump used to be called Ready4Rails until December 2019, when Manuel Meurer opens a new window took over from Florent Guilleux opens a new window to automate the service that Ready4Rails had been doing more or less manually until then.

The relaunch took longer than expected, mainly because of the Coronavirus pandemic, and the first usable version of RailsBump was finally launched in August 2020.

This is an example of what the gem’s compatibility page looks like. You can see the compatibility status for every single version of Rails (from Rails 2.3 to 7.2):


Appreciation

Before even launching FastRuby.io as a productized service, I remember using Ready4Rails in one of our early upgrade projects. It had really useful information for determining compatibility between our client’s Gemfile and the Rails release we were upgrading to.

Before I dive into technical details, I wanted to take a moment to say thanks to Florent for starting this project and Manuel for continuing and improving this project. I’m very grateful for Manuel’s trust in FastRuby.io, and support and patience in the migration project.

We hope we can steward this project for years to come so that it can continue to be a valuable asset in everyone’s upgrade journey.

Why Maintaining It Is Key to FastRuby.io & The Ruby & Rails Communities

It’s human nature to be afraid of the unknown. If we can shed some light in the future state of your Gemfile’s dependencies, we can help you be less afraid and more confident in your upgrade project.

An upgrade project is no easy feat and most of the problems come when you go down the rabbit hole of gem dependencies. There are a few strategies you can use to figure out whether your set of dependencies (and versions) are going to be a problem or not.

RailsBump implements these strategies so you don’t have to. If we can support and maintain a tool that saves our community time, then that aligns very well with our values opens a new window .

Architecture

RailsBump itself is a Ruby on Rails application (so meta!) that heavily relies on Bundler and Rubygems (not just the tool, but also their API) to determine compatibility status.

The core components of this application are:

  • Rails Release: Representing a minor Rails release. At the moment of writing this article, the application was tracking 15 releases (from Rails 2.3 to 7.2)

  • Lockfile: A database record for every Gemfile.lock that is submitted to the application. At the moment of writing this article, the application had checked compatibility for 12,000 Gemfiles.

  • Gemmies: It has to be called that to avoid clashes with the actual Gem class. A lockfile has n gemmies. At the moment of writing this article, the application had checked compatibility for 18,300 gemmies.

  • Compats: A combination of dependencies and a Rails Release. At the moment of writing this article, the application had over 1,000,000 compatibilities checked.

Each compatibility record can have these statuses:

  • Pending: Waiting to be checked.
  • Compatible
  • Incompatible
  • Inconclusive

If you are interested in reading more about the compatibility checking strategies, you can find them all over here opens a new window .

The simplest one is the EmptyDependenciesCheck:

# This method checks for the simplest case: if the compat has no dependencies,
# it's marked as compatible.
class EmptyDependenciesCheck < Base
  def call
    return unless @compat.pending?

    check!
  end

  def check!
    if @compat.dependencies.blank?
      @compat.status               = :compatible
      @compat.status_determined_by = "empty_dependencies"
      @compat.checked!
    end
  end
end

I wanted to share that one because it is quite self explanatory. Thanks, Manuel opens a new window ! ❤️

Migration

Trigger warning: You’re probably not going to agree with some of these decisions, but at the end of the day the FastRuby.io team has to support and maintain the project, so we decided to get closer to our most familiar stack.

At FastRuby.io we are quite familiar with Heroku and we use it for most of our internal and public applications, so we decided to migrate from Hetzner + SQLite to Heroku + Postgres. In the process I learned that in the past RailsBump had migrated from Postgres to SQLite a few years ago.

Most of the hiccups I encountered were related to the JSON columns that are present in the database. For example:

class Compat < ApplicationRecord
  # ...
  def unique_dependencies_for_rails_release
    if Compat.where(rails_release: rails_release)
             .where("dependencies::jsonb = ?", dependencies.to_json)
             .exists?
      errors.add(:dependencies, "must be unique for the given Rails release")
    end
  end

If there is a better way to do that, please submit a pull request! 🤓

While working on the migration, Manuel and I collaborated on a checking strategy that worked with GitHub Actions. That meant creating a new GitHub Action:

# Create a temporary Gemfile with the specified dependencies
def gemfile_content
  result = <<~GEMFILE
    source 'https://rubygems.org'
    gem 'rails', '#{@rails_version}'
  GEMFILE

  @dependencies.each do |gem_name, gem_version|
    result += "gem '#{gem_name}', '#{gem_version}'\n" unless gem_name == 'rails'
  end

  result
end

# Tries to bundle nstall
def try_bundle_install
  File.write('tmp/Gemfile', gemfile_content)

  puts "Checking with temporary Gemfile: \n\n#{gemfile_content}\n\n"

  # Build the definition from the temporary Gemfile
  definition = Bundler::Definition.build('tmp/Gemfile', 'tmp/Gemfile.lock', nil)

  original_stdout = $stdout
  $stdout = StringIO.new
  begin
    Bundler::Installer.install(Bundler.root, definition)
  ensure
    @captured_output = $stdout.string
    $stdout = original_stdout
  end

  @captured_output
end

Source opens a new window

And also adding an API method for the callback from within the GitHub Action:

class ResultsController < BaseController
  before_action :authenticate_api_key!

  def create
    @rails_release = RailsRelease.find_by(version: params[:rails_version])
    @compat = @rails_release.compats.find_by_id!(params[:compat_id])

    if @compat.dependencies == params.require(:dependencies).permit!.to_h
      if @compat.process_result(params[:result])
        logger.info "Compat #{@compat.id} processed successfully"
        head :ok
      else
        logger.info "Compat #{@compat.id} process_result failed: #{@compat.errors.full_messages}"
        head :unprocessable_entity
      end
    else
      logger.info "Compat #{@compat.id} dependencies do not match"
      head :unprocessable_entity
    end
  end
  # ...
end

Source opens a new window

If you’re interested in seeing the action, all the source code is over here: RailsBump Checker on GitHub opens a new window

Next Steps

At FastRuby.io we believe that part of being dependable open source maintainers is actively trying to find co-maintainers in the community, so we’re open to suggestions, ideas, pull requests, questions, or anything like that.

Opening an issue or a pull request is a great first step to eventually becoming a co-maintainer, so if you’re interested in what we are doing with RailsBump, please take a moment to get in touch via GitHub opens a new window , email, Mastodon opens a new window , or Twitter opens a new window !

Some of the key goals we have for the near future:

  1. Adding more strategies to be more efficient checking Ruby gem + Rails compatibility. One idea is to add a check that uses one of our other open source projects: next_rails opens a new window

  2. Improving documentation. During the migration, I noticed that the documentation is not totally up to date. So if you see anything that is inaccurate, confusing, or plain wrong, documentation pull requests will be greatly appreciated and promptly reviewed.

  3. Improving discoverability. Wouldn’t it be great if you searched for “compatibility between Rails 4.2 and administrate” and RailsBump showed up with a real answer? I think so.

  4. Checking compatibility between a Ruby version and a Ruby gem. More often than not, a Rails upgrade will require a Ruby upgrade, so wouldn’t it be nice to see this information in RailsBump?

We’re also very much interested in your ideas, questions, and concerns.

Need a new feature in RailsBump.org? Something not working the way you expect it? Submit an Issue to RailsBump.org on GitHub! opens a new window

Get the book