spring icon indicating copy to clipboard operation
spring copied to clipboard

Support dual/multi-booting Rails apps with different Gemfiles

Open eliotsykes opened this issue 6 years ago • 0 comments

Out-of-the box, spring needs custom configuration of the SPRING_APPLICATION_ID env var to support dual-booting different Gemfiles. This is because the generated application_id only takes into account the RUBY_VERSION and the project_root path.

Would it be a welcome PR to change the current application_id generation to use the Gemfile lockfile path instead of the project_root path? This would allow Spring to support multiple gemfiles without custom config, as it will start a spring server for each Gemfile:

def application_id
- ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
+ ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + Bundler.default_lockfile.to_s)
end

For developers wanting to support this today, you can set SPRING_APPLICATION_ID in bin/spring by adding the lines shown below.

File: bin/spring

#!/usr/bin/env ruby

# This file loads Spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.

unless defined?(Spring)
  require 'rubygems'
  require 'bundler'

+ # The default application_id generated by Spring::Env#application_id
+ # does not support booting with different Gemfiles. The default
+ # value is `Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)`
+ # Here, we set the SPRING_APPLICATION_ID env var to customize the
+ # application_id so it takes the current gemfile into account, to
+ # support booting with different Gemfiles.
+ def configure_spring_for_multiple_gemfiles
+   require 'digest/md5'
+   ENV["SPRING_APPLICATION_ID"] = Digest::MD5.hexdigest(RUBY_VERSION + Bundler.default_lockfile.to_s)
+ end
+ configure_spring_for_multiple_gemfiles
+
  lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
  spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
  if spring
    Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
    gem 'spring', spring.version
    require 'spring/binstub'
  end
end

To check this is working, perform the following steps:

  1. Kill all current spring servers: Running bin/spring stop and pkill -9 spring should do it.
  2. Run a rails console using the default Gemfile: bin/rails c. Check the Rails.version is as expected for Gemfile.
  3. Run another rails console, using a non-default Gemfile: BUNDLE_GEMFILE=Gemfile_next bin/rails c. Check the Rails.version is as expected for Gemfile.
  4. Run bin/spring status. This should show a spring development env running. Note the process ids.
  5. Run BUNDLE_GEMFILE=Gemfile_next bin/spring status. This should show another spring development env running, but the process ids should be different to those noted in the previous step.

To ensure the above config isn't accidentally deleted from bin/spring (say when the binstub is regenerated), in config/spring.rb add the following:

Spring.after_fork do
  require 'digest/md5'
  expected_spring_application_id = Digest::MD5.hexdigest(RUBY_VERSION + Bundler.default_lockfile.to_s)
  if ENV["SPRING_APPLICATION_ID"] != expected_spring_application_id
    raise "Spring is misconfigured! Check bin/spring defines and calls `configure_spring_for_multiple_gemfiles`"
  end
end

eliotsykes avatar Sep 23 '19 10:09 eliotsykes