Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exit with error when rails is called with invalid arguments from wrong directory #42608

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions railties/CHANGELOG.md
@@ -1,3 +1,7 @@
* Exit with non-zero status when `rails` is called with invalid parameters outside of project directory.

*endenis*

* `package.json` now uses a strict version constraint for Rails JavaScript packages on new Rails apps.

*Zachary Scott*, *Alex Ghiculescu*
Expand Down
7 changes: 1 addition & 6 deletions railties/lib/rails/cli.rb
Expand Up @@ -11,9 +11,4 @@

require "rails/command"

if ARGV.first == "plugin"
ARGV.shift
Rails::Command.invoke :plugin, ARGV
else
Rails::Command.invoke :application, ARGV
end
Rails::Command.exec_outside_of_project_directory(ARGV)
18 changes: 17 additions & 1 deletion railties/lib/rails/command.rb
Expand Up @@ -16,6 +16,7 @@ module Command
include Behavior

HELP_MAPPINGS = %w(-h -? --help)
VERSION_MAPPINGS = %w(-v --version)

class << self
def hidden_commands # :nodoc:
Expand All @@ -38,7 +39,7 @@ def invoke(full_namespace, args = [], **config)

command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
command_name, namespace, args = "application", "application", ["--help"] if rails_new_with_no_path?(args)
command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)
command_name, namespace = "version", "version" if VERSION_MAPPINGS.include?(command_name)

original_argv = ARGV.dup
ARGV.replace(args)
Expand Down Expand Up @@ -88,6 +89,21 @@ def print_commands # :nodoc:
commands.each { |command| puts(" #{command}") }
end

def exec_outside_of_project_directory(argv)
case argv.first
when "plugin"
argv.shift
invoke :plugin, argv
when *VERSION_MAPPINGS
invoke :version
when nil, "new", *HELP_MAPPINGS
invoke :application, argv
else
invoke :application, ["--help"]
exit(1)
end
end

private
COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
private_constant :COMMANDS_IN_USAGE
Expand Down
3 changes: 2 additions & 1 deletion railties/lib/rails/commands/version/version_command.rb
Expand Up @@ -4,7 +4,8 @@ module Rails
module Command
class VersionCommand < Base # :nodoc:
def perform
Rails::Command.invoke :application, [ "--version" ]
require "rails/version"
puts "Rails #{Rails::VERSION::STRING}"
end
end
end
Expand Down
13 changes: 2 additions & 11 deletions railties/lib/rails/generators/rails/app/app_generator.rb
Expand Up @@ -600,8 +600,8 @@ def get_builder_class
end

# This class handles preparation of the arguments before the AppGenerator is
# called. The class provides version or help information if they were
# requested, and also constructs the railsrc file (used for extra configuration
# called. The class provides help information if it was requested,
# and also constructs the railsrc file (used for extra configuration
# options).
#
# This class should be called before the AppGenerator is required and started
Expand All @@ -612,7 +612,6 @@ def initialize(argv = ARGV)
end

def prepare!
handle_version_request!(@argv.first)
handle_invalid_command!(@argv.first, @argv) do
handle_rails_rc!(@argv.drop(1))
end
Expand All @@ -629,14 +628,6 @@ def self.default_rc_file
end

private
def handle_version_request!(argument)
if ["--version", "-v"].include?(argument)
require "rails/version"
puts "Rails #{Rails::VERSION::STRING}"
exit(0)
end
end

def handle_invalid_command!(argument, argv)
if argument == "new"
yield
Expand Down
47 changes: 47 additions & 0 deletions railties/test/application/outside_cli_test.rb
@@ -0,0 +1,47 @@
# frozen_string_literal: true

RAILS_ISOLATED_ENGINE = true
require "isolation/abstract_unit"

require "rails/gem_version"
require "open3"

# These tests check rails CLI launched outside of the project directory
class OutsideCliTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation

test "help command works" do
output = rails_cli_output("--help")
assert_match "The 'rails new' command creates a new Rails application", output
end

test "help short-cut alias works" do
output = rails_cli_output("-h")
assert_match "The 'rails new' command creates a new Rails application", output
end

test "invalid command displays help and exits with non-zero status" do
output, exit_status = rails_cli("some-invalid-command")
assert_not_equal 0, exit_status
assert_match "The 'rails new' command creates a new Rails application", output
end

test "version command works" do
output = rails_cli_output("--version")
assert_equal "Rails #{Rails.gem_version}\n", output
end

test "version short-cut alias works" do
output = rails_cli_output("-v")
assert_equal "Rails #{Rails.gem_version}\n", output
end

def rails_cli(cmd)
output, status = Open3.capture2("#{rails_executable} #{cmd}")
[output, status.exitstatus]
end

def rails_cli_output(cmd)
rails_cli(cmd).first
end
end
15 changes: 0 additions & 15 deletions railties/test/generators/argv_scrubber_test.rb
Expand Up @@ -16,21 +16,6 @@ class ARGVScrubberTest < ActiveSupport::TestCase # :nodoc:

include EnvHelpers

def test_version
["-v", "--version"].each do |str|
scrubber = ARGVScrubber.new [str]
output = nil
exit_code = nil
scrubber.extend(Module.new {
define_method(:puts) { |string| output = string }
define_method(:exit) { |code| exit_code = code }
})
scrubber.prepare!
assert_equal "Rails #{Rails::VERSION::STRING}", output
assert_equal 0, exit_code
end
end

def test_default_help
argv = ["zomg", "how", "are", "you"]
scrubber = ARGVScrubber.new argv
Expand Down
10 changes: 9 additions & 1 deletion railties/test/isolation/abstract_unit.rb
Expand Up @@ -95,6 +95,12 @@ def assert_welcome(resp)
end
end

module Cli
def rails_executable
"#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails"
end
end

module Generation
# Build an application by invoking the generator and going through the whole stack.
def build_app(options = {})
Expand Down Expand Up @@ -490,6 +496,7 @@ def reload

class ActiveSupport::TestCase
include TestHelpers::Paths
include TestHelpers::Cli
include TestHelpers::Rack
include TestHelpers::Generation
include TestHelpers::Reload
Expand All @@ -500,6 +507,7 @@ class ActiveSupport::TestCase
# Create a scope and build a fixture rails app
Module.new do
extend TestHelpers::Paths
extend TestHelpers::Cli

def self.sh(cmd)
output = `#{cmd}`
Expand All @@ -510,7 +518,7 @@ def self.sh(cmd)
FileUtils.rm_rf(app_template_path)
FileUtils.mkdir_p(app_template_path)

sh "#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-bundle --skip-spring --skip-listen --no-rc --skip-webpack-install --quiet"
sh "#{rails_executable} new #{app_template_path} --skip-bundle --skip-spring --skip-listen --no-rc --skip-webpack-install --quiet"
File.open("#{app_template_path}/config/boot.rb", "w") do |f|
f.puts 'require "rails/all"'
end
Expand Down
2 changes: 1 addition & 1 deletion railties/test/railties/generators_test.rb
Expand Up @@ -28,7 +28,7 @@ def bundled_rails(cmd)
end

def rails(cmd)
`#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails #{cmd}`
`#{rails_executable} #{cmd}`
end

def build_engine(is_mountable = false)
Expand Down