DEV Community


Rails Dual-boot + Dependabot = 💔?

David Wessman
Originally published at Updated on ・4 min read

There are two concepts which greatly affected how I work with Rails development during the last couple of years, specifically with dependency management and preparing for upgrading to the next Rails version.

The first one is Dependabot which opens pull-requests to update one dependency at a time. This way of working felt natural at once and allowed me to be on top of dependency updates instead of being scared of it 👻.

The second one is dual-booting my Rails applications using separate lock-files, I was introduced to this concept by and their blog post Getting Ready for Rails 6.0: How to Dual Boot.

Unfortunately the two do not work well together out of the box 💔.

When using dual-booting of Rails, we specify the dependencies in the Gemfile like this:

def next?
  File.basename(__FILE__) == ""

if next?
  # Specific override for next
  gem 'rails', '~> 6.0'
  gem 'rubyzip', '~> 3' # Allow freer version
  gem 'rails', '~> 5.2'
  gem 'rubyzip', '> 2', '< 3' # Lock this to specific version compatible with Rails 5.2

gem 'webpacker', '~> 5.1.0' # Same version for Rails 5.2 and 6.0.
Enter fullscreen mode Exit fullscreen mode

This allows us to prepare working with higher versions of specific gems if needed.
The next?-block is used if the Gemfile we use is called
In order to use this we create a symbolic link, so our gemfiles in the project folder look like this:

Dec 13 13:07 Gemfile
Dec 13 12:57 Gemfile.lock
Dec 13 12:57 -> Gemfile
Dec 13 13:08
Enter fullscreen mode Exit fullscreen mode

This means will refer to our usual Gemfile but next? will be true.


When Dependabot updates our dependencies it is configured to find Gemfile and Gemfile.lock and see if they need updating.

Lets say that webpacker releases a new version and needs to be updated to version 5.2, if we have a version requirement in the Gemfile that does not allow to update to the latest version, then Dependabot will update both Gemfile and Gemfile.lock.

Since Dependabot does not update it will get out of sync with our Gemfile:

# Gemfile - updated by Dependabot
gem 'webpacker', '~> 5.2.0'

# Gemfile.lock - updated by Dependabot
webpacker (5.2.0)

# - not updated
webpacker (5.1.0)
Enter fullscreen mode Exit fullscreen mode

This works in development, but in deployment using BUNDLE_DEPLOYMENT=1 we get an error:

You are trying to install in deployment mode after changing
your Gemfile. Run `bundle install` elsewhere and add the
updated to version control.

If this is a development machine, remove the <project-path>/ freeze
by running `bundle install --no-deployment`.

The dependencies in your gemfile changed

You have added to the Gemfile:
* webpacker (~> 5.2)

You have deleted from the Gemfile:
* webpacker (~> 5.1)
Enter fullscreen mode Exit fullscreen mode

There have been discussions about using multiple Gemfiles on Dependabot, but the proposed solution did not work well with symbolic links in the Github API:


Instead of trying to configure Dependabot differently I wrote a Github Action to update the anytime Gemfile.lock is updated.

The steps in the Action are:

  1. Trigger on every pull-request.
  2. Check if Gemfile.lock was updated.
  3. Generate an access token using a Github App, based on this article.
  4. Checkout the code using the generated access token.
  5. Install ruby.
  6. Update dependencies using Bundler.
  7. Commit if there are any changes using EndBug/add-and-commit.

Here is a gist and it looks like this:

name: Update next

      - "Gemfile.lock"

    runs-on: ubuntu-20.04


      - name: Generate token
        id: generate_token
        uses: tibdex/github-app-token@v1
          app_id: ${{ secrets.APP_ID }}
          private_key: ${{ secrets.PRIVATE_KEY }}

      - uses: actions/checkout@v2
          token: ${{ steps.generate_token.outputs.token }}

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1

      - name: Gems Cache
        id: gem-cache
        uses: actions/cache@v2
          path: vendor/bundle
          key: ${{ runner.os }}-gem-${{ hashFiles('') }}
          restore-keys: |
            ${{ runner.os }}-gem-

      - name: Update
        run: |
          bundle update --minor --conservative

      - uses: EndBug/add-and-commit@v6
          message: 'Updated'
Enter fullscreen mode Exit fullscreen mode

The above action should be saved as .github/workflows/<workflow-name>.yml to get automatic updates.

Future work

  • The bundle strategy can probably be tweaked to something other than bundle update --minor --conservative.

Please get in touch with me on twitter @davidwessman if you have any questions or suggestions!



  • When using a GITHUB_TOKEN, the added commit cannot trigger Github Actions again. This can be solved by using a Personal access token (PAT) as I learned from Github user airtower-luna (Thank you!) in this discussion.


  • The initial setup used the default GITHUB_TOKEN from the Github Action, and it cannot be used to trigger another Github Action job.
  • Added a setup using an access token from a Github App, allowing new jobs to be triggered.

Discussion (0)