Skip to content

Commit

Permalink
Make Suspenders fail if running with an unsupported Rails version
Browse files Browse the repository at this point in the history
This work is part of the Rescue project:

https://github.com/thoughtbot/suspenders/projects/1

** Problem

In a scenario where the following Rails versions are installed:

- 6.0.4.7
- 6.1.4.1

The `bin/suspenders` command will pick up the most recent version,
6.1.4.1. Now that Rails has surpassed the 6.0 series, it is very
common to have more than one version installed alongside the version
installed by Suspenders.

A failure on the Suspenders command due to a mismatching Rails version
is confusing and violates the principle of least surprise, and may
also drive users away.

The only way to overcome this bug with the current `bin/suspenders`
command is to be explicit about the Rails version:

```sh
suspenders _6.0.4.1_ my-app
```

Unfortunately, this is an obscure and undocumented feature, and we
can't expect users to know about it.

Note that Suspenders currently works with Rails `~> 6.0.0` and is not
compatible with Rails 6.1.

** Solution

The solution is to read the Rails version from Bundler and then
activate its gem bin path. We can't `Bunder.require(:default)` because
a polluted bundler / gem environment may interfere with the
functionality of generators in unexpected ways, given that
subprocesses inherit the environment of parents -- therefore, the
cleaner our environment the better.

As part of the "fail early" mindset, we're also failing if an explicit
Rails version that can't be found is specified.

** Steps to reproduce (using asdf)

```sh
asdf install ruby 2.7.4
git clone [email protected]:thoughtbot/suspenders
cd suspenders
bundle install
gem install rails -v 6.1.4.1
bin/suspenders my-app
```

Last error line:

```
1: from /home/user/suspenders/lib/suspenders/app_builder.rb:101
:in `setup_asset_host'
/home/user/suspenders/lib/suspenders/actions.rb:7:in
`replace_in_file': "# config.action_controller.asset_host =
'http://assets.example.com'" not found in
config/environments/production.rb (RuntimeError)
```

We are getting this error because in Rails 6.1 the commented line that
`replace_in_file` is looking for is now:

```ruby
\# config.asset_host = 'http://assets.example.com'
```
  • Loading branch information
thiagoa committed May 9, 2022
1 parent 232a2cc commit f3ce221
Showing 1 changed file with 20 additions and 6 deletions.
26 changes: 20 additions & 6 deletions bin/suspenders
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ require 'pathname'
source_path = (Pathname.new(__FILE__).dirname + '../lib').expand_path
$LOAD_PATH << source_path

activate_rails_version = ->(rails_version) do
rails_bin_path = Gem.activate_bin_path("railties", "rails", rails_version)
rails_path = File.expand_path("../..", rails_bin_path)
$LOAD_PATH.unshift(rails_path)
end

if str = ARGV.first
str = str.b[/\A_(.*)_\z/, 1]

Expand All @@ -12,23 +18,31 @@ if str = ARGV.first
ARGV.shift

begin
rails_bin_path = Gem.activate_bin_path("railties", "rails", rails_version)
rails_path = File.expand_path("../..", rails_bin_path)
$LOAD_PATH.unshift(rails_path)
activate_rails_version.call(rails_version)
rescue Gem::GemNotFoundException
$stderr.puts "#{rails_version} requested but no Rails is installed yet. Hoping for the best with the default."
abort "Suspenders error: Unable to find Rails version #{rails_version}"
end
else
require "bundler"

spec = Bundler.definition.specs.find { |s| s.name == "rails" }

if spec.nil?
abort "Suspenders error: Unable to find Rails in Bundle"
end

activate_rails_version.call(spec.version.to_s)
end
end

require 'suspenders'
require "suspenders"

if ARGV.empty?
puts "Please provide a path for the new application"
puts
puts "See --help for more info"
exit 0
elsif ['-v', '--version'].include? ARGV[0]
elsif ["-v", "--version"].include? ARGV[0]
puts Suspenders::VERSION
exit 0
end
Expand Down

0 comments on commit f3ce221

Please sign in to comment.