Use different Gemfile lockfile for multiple Ruby version development

03 May 2020

Recently, when I was doing a gem development, I tired to wait for Travis CI to run the tests under different Ruby versions. It is quite slow. I wanted to test them on my machine first, but switching Ruby version is pain. I have to re-run bundle install to refresh the Gemfile.lock for each Ruby version.

So I started to research if there is a way to use separate Gemfile.lock for each Ruby verion. Then, I found:

But the bootboot gem seems not very suit for my requirement. It only support two Gemfile lockfiles. I need more than that.

So I start to work on this by my own. And final comes up a solution for that with Docker and Make. Let’s start to do it.

Create a docker-compose.yml file.

version: "3"

services:
  ruby_2_5:
    image: ruby:2.5.8
    tmpfs:
      - /tmp
    volumes:
      - .:/app:cached
      - bundle_ruby_2_5:/bundle
      - ./Gemfile.lock.ruby_2_5:/app/Gemfile.lock
    environment:
      BUNDLE_PATH: /bundle
    working_dir: /app

  ruby_2_6:
    image: ruby:2.6.6
    tmpfs:
      - /tmp
    volumes:
      - .:/app:cached
      - bundle_ruby_2_6:/bundle
      - ./Gemfile.lock.ruby_2_6:/app/Gemfile.lock
    environment:
      BUNDLE_PATH: /bundle
    working_dir: /app

  ruby_2_7:
    image: ruby:2.7.1
    tmpfs:
      - /tmp
    volumes:
      - .:/app:cached
      - bundle_ruby_2_7:/bundle
      - ./Gemfile.lock.ruby_2_7:/app/Gemfile.lock
    environment:
      BUNDLE_PATH: /bundle
    working_dir: /app

  jruby_9_2:
    image: jruby:9.2.11.1
    tmpfs:
      - /tmp
    volumes:
      - .:/app:cached
      - bundle_jruby_9_2:/bundle
      - ./Gemfile.lock.jruby:/app/Gemfile.lock
    environment:
      BUNDLE_PATH: /bundle
    working_dir: /app

volumes:
  bundle_jruby_9_2:
  bundle_ruby_2_5:
  bundle_ruby_2_6:
  bundle_ruby_2_7:

See, I use volume mount, mount a Gemfile.lock.* to Gemfile.lock in docker. Then I get the feature I want. Each Ruby version has its own Gemfile lockfile.

To make the development and test process easier. We need to use Make. Create a Makefile

RUN = docker-compose run --rm

init:
  touch Gemfile.lock.ruby_2_5
  touch Gemfile.lock.ruby_2_6
  touch Gemfile.lock.ruby_2_7
  touch Gemfile.lock.jruby

bundle\:all:
  @${RUN} ruby_2_5 bundle install
  @${RUN} ruby_2_6 bundle install
  @${RUN} ruby_2_7 bundle install
  @${RUN} jruby_9_2 bundle install

bundle:
  @${RUN} ruby_2_6 bundle install
dev:
  @${RUN} ruby_2_6 bash
test:
  @${RUN} ruby_2_6 bundle exec rspec spec

dev\:ruby_2_5:
  @${RUN} ruby_2_5 bash
test\:ruby_2_5:
  @${RUN} ruby_2_5 bundle exec rspec spec
dev\:ruby_2_6:
  @${RUN} ruby_2_6 bash
test\:ruby_2_6:
  @${RUN} ruby_2_6 bundle exec rspec spec
dev\:ruby_2_7:
  @${RUN} ruby_2_7 bash
test\:ruby_2_7:
  @${RUN} ruby_2_7 bundle exec rspec spec
dev\:jruby_9_2:
  @${RUN} jruby_9_2 bash
test\:jruby_9_2:
  @${RUN} jruby_9_2 bundle exec rspec spec

test\:all:
  @${RUN} ruby_2_5 bundle exec rspec spec
  @${RUN} ruby_2_6 bundle exec rspec spec
  @${RUN} ruby_2_7 bundle exec rspec spec
  @${RUN} jruby_9_2 bundle exec rspec spec

Use make init to create all Gemfile lockfiles Use make bundle:all to run bundle install for all Ruby versions Use make test:all to run tests under all Ruby versions Use make test:ruby_x_x to run test for specific Ruby version Use make dev:ruby_x_x to do development under specific Ruby version

Now we can do development for multiple Ruby verions on local without affect each other.

Back to top