Skip to content

Commit be7f922

Browse files
zzakbyroot
authored andcommitted
Ensure yarn and bun version fallback
If we allow the `version` methods to return nil, it's possible to end up in a situation where Dockerfile contains something like ``` ARG BUN_VERSION= ``` This can occur when bun or yarn are not installed locally on the current version of node, and using a version manager like `nodenv`. In this case, `rails new` would previously raise an error: ``` nodenv: yarn: command not found The `yarn' command exists in these Node versions: 20.15.0 E Error: AppGeneratorTest#test_css_option_with_cssbundling_gem_does_not_force_jsbundling_gem: NoMethodError: undefined method '>=' for nil lib/rails/generators/app_base.rb:550:in 'Rails::Generators::AppBase#yarn_through_corepack?' lib/rails/generators/rails/app/templates/Dockerfile.tt:41:in 'Thor::Actions#template' ``` By ensuring we fallback to a non-nil value we can ensure this case doesn't occur. <details> <summary>Full example:</summary> ``` $ bundle exec railties/exe/rails new ~/code/apps/TestApp_EsBuild_Tailwind_Pg -j=esbuild -c=tailwind -d=postgresql --dev nodenv: yarn: command not found The `yarn' command exists in these Node versions: 20.15.0 nodenv: yarn: command not found The `yarn' command exists in these Node versions: 20.15.0 nodenv: yarn: command not found The `yarn' command exists in these Node versions: 20.15.0 bundler: failed to load command: railties/exe/rails (railties/exe/rails) /home/zzak/code/rails/railties/lib/rails/generators/app_base.rb:550:in 'Rails::Generators::AppBase#yarn_through_corepack?': undefined method '>=' for nil (NoMethodError) dockerfile_yarn_version >= "2" ^^ from /home/zzak/code/rails/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt:41:in 'Thor::Actions#template' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/3.4.0/erb.rb:429:in 'Kernel#eval' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/3.4.0/erb.rb:429:in 'ERB#result' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/file_manipulation.rb:128:in 'block in Thor::Actions#template' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/create_file.rb:54:in 'Thor::Actions::CreateFile#render' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/create_file.rb:64:in 'block (2 levels) in Thor::Actions::CreateFile#invoke!' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/create_file.rb:64:in 'IO.open' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/create_file.rb:64:in 'block in Thor::Actions::CreateFile#invoke!' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/empty_directory.rb:117:in 'Thor::Actions::EmptyDirectory#invoke_with_conflict_check' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/create_file.rb:61:in 'Thor::Actions::CreateFile#invoke!' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions.rb:93:in 'Thor::Actions#action' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/create_file.rb:25:in 'Thor::Actions#create_file' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/actions/file_manipulation.rb:124:in 'Thor::Actions#template' from /home/zzak/code/rails/railties/lib/rails/generators/rails/app/app_generator.rb:20:in 'Rails::ActionMethods#template' from /home/zzak/code/rails/railties/lib/rails/generators/rails/app/app_generator.rb:79:in 'Rails::AppBuilder#dockerfiles' from /home/zzak/code/rails/railties/lib/rails/generators/app_base.rb:174:in 'Kernel#public_send' from /home/zzak/code/rails/railties/lib/rails/generators/app_base.rb:174:in 'Rails::Generators::AppBase#build' from /home/zzak/code/rails/railties/lib/rails/generators/rails/app/app_generator.rb:402:in 'Rails::Generators::AppGenerator#create_dockerfiles' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/command.rb:28:in 'Thor::Command#run' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/invocation.rb:127:in 'Thor::Invocation#invoke_command' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/invocation.rb:134:in 'block in Thor::Invocation#invoke_all' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/invocation.rb:134:in 'Hash#each' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/invocation.rb:134:in 'Enumerable#map' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/invocation.rb:134:in 'Thor::Invocation#invoke_all' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/group.rb:243:in 'Thor::Group.dispatch' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/base.rb:584:in 'Thor::Base::ClassMethods#start' from /home/zzak/code/rails/railties/lib/rails/commands/application/application_command.rb:28:in 'Rails::Command::ApplicationCommand#perform' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/command.rb:28:in 'Thor::Command#run' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor/invocation.rb:127:in 'Thor::Invocation#invoke_command' from /home/zzak/code/rails/railties/lib/rails/command/base.rb:176:in 'Rails::Command::Base#invoke_command' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/thor-1.3.2/lib/thor.rb:538:in 'Thor.dispatch' from /home/zzak/code/rails/railties/lib/rails/command/base.rb:71:in 'Rails::Command::Base.perform' from /home/zzak/code/rails/railties/lib/rails/command.rb:65:in 'block in Rails::Command.invoke' from /home/zzak/code/rails/railties/lib/rails/command.rb:143:in 'Rails::Command.with_argv' from /home/zzak/code/rails/railties/lib/rails/command.rb:63:in 'Rails::Command.invoke' from /home/zzak/code/rails/railties/lib/rails/cli.rb:20:in '<top (required)>' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/3.4.0/bundled_gems.rb:82:in 'Kernel.require' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/3.4.0/bundled_gems.rb:82:in 'block (2 levels) in Kernel#replace_require' from railties/exe/rails:10:in '<top (required)>' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/cli/exec.rb:59:in 'Kernel.load' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/cli/exec.rb:59:in 'Bundler::CLI::Exec#kernel_load' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/cli/exec.rb:23:in 'Bundler::CLI::Exec#run' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/cli.rb:452:in 'Bundler::CLI#exec' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/vendor/thor/lib/thor.rb:538:in 'Bundler::Thor.dispatch' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/cli.rb:29:in 'Bundler::CLI.start' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/exe/bundle:28:in 'block in <top (required)>' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/lib/bundler/friendly_errors.rb:117:in 'Bundler.with_friendly_errors' from /home/zzak/.rbenv/versions/3.4.4/lib/ruby/gems/3.4.0/gems/bundler-2.6.2/exe/bundle:20:in '<top (required)>' from /home/zzak/.rbenv/versions/3.4.4/bin/bundle:25:in 'Kernel#load' from /home/zzak/.rbenv/versions/3.4.4/bin/bundle:25:in '<main>' [ble: exit 1] ``` </details>
1 parent 3fd19bd commit be7f922

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

railties/lib/rails/generators/app_base.rb

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -540,20 +540,27 @@ def node_version
540540
end
541541

542542
def dockerfile_yarn_version
543-
using_node? and `yarn --version`[/\d+\.\d+\.\d+/]
544-
rescue
545-
"latest"
543+
version = begin
544+
`yarn --version`[/\d+\.\d+\.\d+/]
545+
rescue
546+
nil
547+
end
548+
549+
version || "latest"
546550
end
547551

548552
def yarn_through_corepack?
549-
true if dockerfile_yarn_version == "latest"
550-
dockerfile_yarn_version >= "2"
553+
using_node? and "#{dockerfile_yarn_version}" >= "2"
551554
end
552555

553556
def dockerfile_bun_version
554-
using_bun? and `bun --version`[/\d+\.\d+\.\d+/]
555-
rescue
556-
BUN_VERSION
557+
version = begin
558+
`bun --version`[/\d+\.\d+\.\d+/]
559+
rescue
560+
nil
561+
end
562+
563+
version || BUN_VERSION
557564
end
558565

559566
def dockerfile_binfile_fixups

railties/test/generators/app_generator_test.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,22 @@ def test_esbuild_option_with_js_argument
925925
assert_gem "jsbundling-rails"
926926
end
927927

928+
def test_esbuild_without_yarn_installed
929+
generator([destination_root], javascript: "esbuild")
930+
931+
# fallback to latest when yarn is not installed
932+
generator.stub :dockerfile_yarn_version, "latest" do
933+
quietly { generator.invoke_all }
934+
end
935+
936+
assert_gem "jsbundling-rails"
937+
assert_file "Dockerfile" do |content|
938+
assert_match(/ARG YARN_VERSION=latest/, content)
939+
940+
assert_match("RUN corepack enable && yarn set version $YARN_VERSION", content)
941+
end
942+
end
943+
928944
def test_bun_option
929945
generator([destination_root], javascript: "bun")
930946

@@ -949,6 +965,23 @@ def test_bun_option_with_js_argument
949965
assert_gem "jsbundling-rails"
950966
end
951967

968+
def test_bun_without_bun_installed
969+
generator([destination_root], javascript: "bun")
970+
bun_version = generator.class.const_get(:BUN_VERSION)
971+
972+
# fallback to constant when bun is not installed
973+
generator.stub :dockerfile_bun_version, bun_version do
974+
quietly { generator.invoke_all }
975+
end
976+
977+
assert_gem "jsbundling-rails"
978+
assert_file "Dockerfile" do |content|
979+
assert_match(/ARG BUN_VERSION=#{bun_version}/, content)
980+
981+
assert_match("RUN bun install --frozen-lockfile", content)
982+
end
983+
end
984+
952985
def test_skip_javascript_option_with_skip_javascript_argument
953986
run_generator [destination_root, "--skip-javascript"]
954987
assert_no_gem "stimulus-rails"

0 commit comments

Comments
 (0)