aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/commands
diff options
context:
space:
mode:
Diffstat (limited to 'railties/lib/rails/commands')
-rw-r--r--railties/lib/rails/commands/application/application_command.rb2
-rw-r--r--railties/lib/rails/commands/console/console_command.rb19
-rw-r--r--railties/lib/rails/commands/credentials/USAGE40
-rw-r--r--railties/lib/rails/commands/credentials/credentials_command.rb79
-rw-r--r--railties/lib/rails/commands/dbconsole/dbconsole_command.rb31
-rw-r--r--railties/lib/rails/commands/destroy/destroy_command.rb15
-rw-r--r--railties/lib/rails/commands/dev/dev_command.rb17
-rw-r--r--railties/lib/rails/commands/encrypted/encrypted_command.rb86
-rw-r--r--railties/lib/rails/commands/generate/generate_command.rb12
-rw-r--r--railties/lib/rails/commands/help/USAGE17
-rw-r--r--railties/lib/rails/commands/help/help_command.rb4
-rw-r--r--railties/lib/rails/commands/initializers/initializers_command.rb16
-rw-r--r--railties/lib/rails/commands/new/new_command.rb12
-rw-r--r--railties/lib/rails/commands/notes/notes_command.rb39
-rw-r--r--railties/lib/rails/commands/plugin/plugin_command.rb4
-rw-r--r--railties/lib/rails/commands/rake/rake_command.rb2
-rw-r--r--railties/lib/rails/commands/routes/routes_command.rb37
-rw-r--r--railties/lib/rails/commands/runner/USAGE3
-rw-r--r--railties/lib/rails/commands/runner/runner_command.rb33
-rw-r--r--railties/lib/rails/commands/secrets/USAGE12
-rw-r--r--railties/lib/rails/commands/secrets/secrets_command.rb53
-rw-r--r--railties/lib/rails/commands/server/server_command.rb182
-rw-r--r--railties/lib/rails/commands/test/test_command.rb31
-rw-r--r--railties/lib/rails/commands/version/version_command.rb2
24 files changed, 643 insertions, 105 deletions
diff --git a/railties/lib/rails/commands/application/application_command.rb b/railties/lib/rails/commands/application/application_command.rb
index 7675d3b3d1..f77553b830 100644
--- a/railties/lib/rails/commands/application/application_command.rb
+++ b/railties/lib/rails/commands/application/application_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails/generators"
require "rails/generators/rails/app/app_generator"
diff --git a/railties/lib/rails/commands/console/console_command.rb b/railties/lib/rails/commands/console/console_command.rb
index 62e3aa19df..e35faa5b01 100644
--- a/railties/lib/rails/commands/console/console_command.rb
+++ b/railties/lib/rails/commands/console/console_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "irb"
require "irb/completion"
@@ -70,8 +72,19 @@ module Rails
class_option :sandbox, aliases: "-s", type: :boolean, default: false,
desc: "Rollback database modifications on exit."
- class_option :environment, aliases: "-e", type: :string,
- desc: "Specifies the environment to run this console under (test/development/production)."
+ def initialize(args = [], local_options = {}, config = {})
+ console_options = []
+
+ # For the same behavior as OptionParser, leave only options after "--" in ARGV.
+ termination = local_options.find_index("--")
+ if termination
+ console_options = local_options[termination + 1..-1]
+ local_options = local_options[0...termination]
+ end
+
+ ARGV.replace(console_options)
+ super(args, local_options, config)
+ end
def perform
extract_environment_option_from_argument
@@ -79,8 +92,6 @@ module Rails
# RAILS_ENV needs to be set before config/application is required.
ENV["RAILS_ENV"] = options[:environment]
- ARGV.clear # Clear ARGV so IRB doesn't freak.
-
require_application_and_environment!
Rails::Console.start(Rails.application, options)
end
diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE
new file mode 100644
index 0000000000..ea429f58d8
--- /dev/null
+++ b/railties/lib/rails/commands/credentials/USAGE
@@ -0,0 +1,40 @@
+=== Storing Encrypted Credentials in Source Control
+
+The Rails `credentials` commands provide access to encrypted credentials,
+so you can safely store access tokens, database passwords, and the like
+safely inside the app without relying on a mess of ENVs.
+
+This also allows for atomic deploys: no need to coordinate key changes
+to get everything working as the keys are shipped with the code.
+
+=== Setup
+
+Applications after Rails 5.2 automatically have a basic credentials file generated
+that just contains the secret_key_base used by MessageVerifiers/MessageEncryptors, like the ones
+signing and encrypting cookies.
+
+For applications created prior to Rails 5.2, we'll automatically generate a new
+credentials file in `config/credentials.yml.enc` the first time you run `rails credentials:edit`.
+If you didn't have a master key saved in `config/master.key`, that'll be created too.
+
+Don't lose this master key! Put it in a password manager your team can access.
+Should you lose it no one, including you, will be able to access any encrypted
+credentials.
+
+Don't commit the key! Add `config/master.key` to your source control's
+ignore file. If you use Git, Rails handles this for you.
+
+Rails also looks for the master key in `ENV["RAILS_MASTER_KEY"]`, if that's easier to manage.
+
+You could prepend that to your server's start command like this:
+
+ RAILS_MASTER_KEY="very-secret-and-secure" server.start
+
+=== Editing Credentials
+
+This will open a temporary file in `$EDITOR` with the decrypted contents to edit
+the encrypted credentials.
+
+When the temporary file is next saved the contents are encrypted and written to
+`config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
+from leaking.
diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb
new file mode 100644
index 0000000000..65c5218fc8
--- /dev/null
+++ b/railties/lib/rails/commands/credentials/credentials_command.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require "active_support"
+require "rails/command/helpers/editor"
+
+module Rails
+ module Command
+ class CredentialsCommand < Rails::Command::Base # :nodoc:
+ include Helpers::Editor
+
+ no_commands do
+ def help
+ say "Usage:\n #{self.class.banner}"
+ say ""
+ say self.class.desc
+ end
+ end
+
+ def edit
+ require_application_and_environment!
+
+ ensure_editor_available(command: "bin/rails credentials:edit") || (return)
+ ensure_master_key_has_been_added if Rails.application.credentials.key.nil?
+ ensure_credentials_have_been_added
+
+ catch_editing_exceptions do
+ change_credentials_in_system_editor
+ end
+
+ say "New credentials encrypted and saved."
+ end
+
+ def show
+ require_application_and_environment!
+
+ say Rails.application.credentials.read.presence || missing_credentials_message
+ end
+
+ private
+ def ensure_master_key_has_been_added
+ master_key_generator.add_master_key_file
+ master_key_generator.ignore_master_key_file
+ end
+
+ def ensure_credentials_have_been_added
+ credentials_generator.add_credentials_file_silently
+ end
+
+ def change_credentials_in_system_editor
+ Rails.application.credentials.change do |tmp_path|
+ system("#{ENV["EDITOR"]} #{tmp_path}")
+ end
+ end
+
+
+ def master_key_generator
+ require "rails/generators"
+ require "rails/generators/rails/master_key/master_key_generator"
+
+ Rails::Generators::MasterKeyGenerator.new
+ end
+
+ def credentials_generator
+ require "rails/generators"
+ require "rails/generators/rails/credentials/credentials_generator"
+
+ Rails::Generators::CredentialsGenerator.new
+ end
+
+ def missing_credentials_message
+ if Rails.application.credentials.key.nil?
+ "Missing master key to decrypt credentials. See `rails credentials:help`"
+ else
+ "No credentials have been added yet. Use `rails credentials:edit` to change that."
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
index 588fb06b15..806b7de6d6 100644
--- a/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
+++ b/railties/lib/rails/commands/dbconsole/dbconsole_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "rails/command/environment_argument"
module Rails
@@ -11,7 +13,7 @@ module Rails
end
def start
- ENV["RAILS_ENV"] = @options[:environment] || environment
+ ENV["RAILS_ENV"] ||= @options[:environment] || environment
case config["adapter"]
when /^(jdbc)?mysql/
@@ -58,7 +60,7 @@ module Rails
logon = ""
if config["username"]
- logon = config["username"]
+ logon = config["username"].dup
logon << "/#{config['password']}" if config["password"] && @options["include_password"]
logon << "@#{config['database']}" if config["database"]
end
@@ -73,7 +75,7 @@ module Rails
args += ["-P", "#{config['password']}"] if config["password"]
if config["host"]
- host_arg = "#{config['host']}"
+ host_arg = "#{config['host']}".dup
host_arg << ":#{config['port']}" if config["port"]
args += ["-S", host_arg]
end
@@ -87,10 +89,15 @@ module Rails
def config
@config ||= begin
- if configurations[environment].blank?
+ # We need to check whether the user passed the connection the
+ # first time around to show a consistent error message to people
+ # relying on 2-level database configuration.
+ if @options["connection"] && configurations[connection].blank?
+ raise ActiveRecord::AdapterNotSpecified, "'#{connection}' connection is not configured. Available configuration: #{configurations.inspect}"
+ elsif configurations[environment].blank? && configurations[connection].blank?
raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
else
- configurations[environment]
+ configurations[connection] || configurations[environment].presence
end
end
end
@@ -99,6 +106,10 @@ module Rails
Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
end
+ def connection
+ @options.fetch(:connection, "primary")
+ end
+
private
def configurations # :doc:
require APP_PATH
@@ -140,14 +151,18 @@ module Rails
class_option :mode, enum: %w( html list line column ), type: :string,
desc: "Automatically put the sqlite3 database in the specified mode (html, list, line, column)."
- class_option :header, type: :string
+ class_option :header, type: :boolean
- class_option :environment, aliases: "-e", type: :string,
- desc: "Specifies the environment to run this console under (test/development/production)."
+ class_option :connection, aliases: "-c", type: :string,
+ desc: "Specifies the connection to use."
def perform
extract_environment_option_from_argument
+ # RAILS_ENV needs to be set before config/application is required.
+ ENV["RAILS_ENV"] = options[:environment]
+
+ require_application_and_environment!
Rails::DBConsole.start(options)
end
end
diff --git a/railties/lib/rails/commands/destroy/destroy_command.rb b/railties/lib/rails/commands/destroy/destroy_command.rb
index 5b552b2070..dd432d28fd 100644
--- a/railties/lib/rails/commands/destroy/destroy_command.rb
+++ b/railties/lib/rails/commands/destroy/destroy_command.rb
@@ -1,10 +1,17 @@
+# frozen_string_literal: true
+
require "rails/generators"
module Rails
module Command
class DestroyCommand < Base # :nodoc:
- def help
- Rails::Generators.help self.class.command_name
+ no_commands do
+ def help
+ require_application_and_environment!
+ load_generators
+
+ Rails::Generators.help self.class.command_name
+ end
end
def perform(*)
@@ -12,9 +19,9 @@ module Rails
return help unless generator
require_application_and_environment!
- Rails.application.load_generators
+ load_generators
- Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails.root
+ Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails::Command.root
end
end
end
diff --git a/railties/lib/rails/commands/dev/dev_command.rb b/railties/lib/rails/commands/dev/dev_command.rb
new file mode 100644
index 0000000000..a3f02f3172
--- /dev/null
+++ b/railties/lib/rails/commands/dev/dev_command.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "rails/dev_caching"
+
+module Rails
+ module Command
+ class DevCommand < Base # :nodoc:
+ def help
+ say "rails dev:cache # Toggle development mode caching on/off."
+ end
+
+ def cache
+ Rails::DevCaching.enable_by_file
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/encrypted/encrypted_command.rb b/railties/lib/rails/commands/encrypted/encrypted_command.rb
new file mode 100644
index 0000000000..8d5947652a
--- /dev/null
+++ b/railties/lib/rails/commands/encrypted/encrypted_command.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require "pathname"
+require "active_support"
+require "rails/command/helpers/editor"
+
+module Rails
+ module Command
+ class EncryptedCommand < Rails::Command::Base # :nodoc:
+ include Helpers::Editor
+
+ class_option :key, aliases: "-k", type: :string,
+ default: "config/master.key", desc: "The Rails.root relative path to the encryption key"
+
+ no_commands do
+ def help
+ say "Usage:\n #{self.class.banner}"
+ say ""
+ end
+ end
+
+ def edit(file_path)
+ require_application_and_environment!
+ encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
+
+ ensure_editor_available(command: "bin/rails encrypted:edit") || (return)
+ ensure_encryption_key_has_been_added(options[:key]) if encrypted.key.nil?
+ ensure_encrypted_file_has_been_added(file_path, options[:key])
+
+ catch_editing_exceptions do
+ change_encrypted_file_in_system_editor(file_path, options[:key])
+ end
+
+ say "File encrypted and saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ say "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?"
+ end
+
+ def show(file_path)
+ require_application_and_environment!
+ encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
+
+ say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: options[:key], file_path: file_path)
+ end
+
+ private
+ def ensure_encryption_key_has_been_added(key_path)
+ encryption_key_file_generator.add_key_file(key_path)
+ encryption_key_file_generator.ignore_key_file(key_path)
+ end
+
+ def ensure_encrypted_file_has_been_added(file_path, key_path)
+ encrypted_file_generator.add_encrypted_file_silently(file_path, key_path)
+ end
+
+ def change_encrypted_file_in_system_editor(file_path, key_path)
+ Rails.application.encrypted(file_path, key_path: key_path).change do |tmp_path|
+ system("#{ENV["EDITOR"]} #{tmp_path}")
+ end
+ end
+
+
+ def encryption_key_file_generator
+ require "rails/generators"
+ require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
+
+ Rails::Generators::EncryptionKeyFileGenerator.new
+ end
+
+ def encrypted_file_generator
+ require "rails/generators"
+ require "rails/generators/rails/encrypted_file/encrypted_file_generator"
+
+ Rails::Generators::EncryptedFileGenerator.new
+ end
+
+ def missing_encrypted_message(key:, key_path:, file_path:)
+ if key.nil?
+ "Missing '#{key_path}' to decrypt data. See `rails encrypted:help`"
+ else
+ "File '#{file_path}' does not exist. Use `rails encrypted:edit #{file_path}` to change that."
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb
index 2718b453a8..93d7a0ce3a 100644
--- a/railties/lib/rails/commands/generate/generate_command.rb
+++ b/railties/lib/rails/commands/generate/generate_command.rb
@@ -1,13 +1,17 @@
+# frozen_string_literal: true
+
require "rails/generators"
module Rails
module Command
class GenerateCommand < Base # :nodoc:
- def help
- require_application_and_environment!
- load_generators
+ no_commands do
+ def help
+ require_application_and_environment!
+ load_generators
- Rails::Generators.help self.class.command_name
+ Rails::Generators.help self.class.command_name
+ end
end
def perform(*)
diff --git a/railties/lib/rails/commands/help/USAGE b/railties/lib/rails/commands/help/USAGE
index c5f8ab72bb..8eb98319d2 100644
--- a/railties/lib/rails/commands/help/USAGE
+++ b/railties/lib/rails/commands/help/USAGE
@@ -1,13 +1,14 @@
The most common rails commands are:
- generate Generate new code (short-cut alias: "g")
- console Start the Rails console (short-cut alias: "c")
- server Start the Rails server (short-cut alias: "s")
- test Run tests (short-cut alias: "t")
- dbconsole Start a console for the database specified in config/database.yml
- (short-cut alias: "db")
+ generate Generate new code (short-cut alias: "g")
+ console Start the Rails console (short-cut alias: "c")
+ server Start the Rails server (short-cut alias: "s")
+ test Run tests except system tests (short-cut alias: "t")
+ test:system Run system tests
+ dbconsole Start a console for the database specified in config/database.yml
+ (short-cut alias: "db")
<% unless engine? %>
- new Create a new Rails application. "rails new my_app" creates a
- new application called MyApp in "./my_app"
+ new Create a new Rails application. "rails new my_app" creates a
+ new application called MyApp in "./my_app"
<% end %>
All commands can be run with -h (or --help) for more information.
diff --git a/railties/lib/rails/commands/help/help_command.rb b/railties/lib/rails/commands/help/help_command.rb
index 90d37217fc..9df34e9b79 100644
--- a/railties/lib/rails/commands/help/help_command.rb
+++ b/railties/lib/rails/commands/help/help_command.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
module Rails
module Command
class HelpCommand < Base # :nodoc:
hide_command!
def help(*)
- puts self.class.desc
+ say self.class.desc
Rails::Command.print_commands
end
diff --git a/railties/lib/rails/commands/initializers/initializers_command.rb b/railties/lib/rails/commands/initializers/initializers_command.rb
new file mode 100644
index 0000000000..33596177af
--- /dev/null
+++ b/railties/lib/rails/commands/initializers/initializers_command.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Rails
+ module Command
+ class InitializersCommand < Base # :nodoc:
+ desc "initializers", "Print out all defined initializers in the order they are invoked by Rails."
+ def perform
+ require_application_and_environment!
+
+ Rails.application.initializers.tsort_each do |initializer|
+ say "#{initializer.context_class}.#{initializer.name}"
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/new/new_command.rb b/railties/lib/rails/commands/new/new_command.rb
index 74d1fa5021..a4f2081510 100644
--- a/railties/lib/rails/commands/new/new_command.rb
+++ b/railties/lib/rails/commands/new/new_command.rb
@@ -1,13 +1,17 @@
+# frozen_string_literal: true
+
module Rails
module Command
class NewCommand < Base # :nodoc:
- def help
- Rails::Command.invoke :application, [ "--help" ]
+ no_commands do
+ def help
+ Rails::Command.invoke :application, [ "--help" ]
+ end
end
def perform(*)
- puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
- puts "Type 'rails' for help."
+ say "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
+ say "Type 'rails' for help."
exit 1
end
end
diff --git a/railties/lib/rails/commands/notes/notes_command.rb b/railties/lib/rails/commands/notes/notes_command.rb
new file mode 100644
index 0000000000..64b339b3cd
--- /dev/null
+++ b/railties/lib/rails/commands/notes/notes_command.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "rails/source_annotation_extractor"
+
+module Rails
+ module Command
+ class NotesCommand < Base # :nodoc:
+ class_option :annotations, aliases: "-a", desc: "Filter by specific annotations, e.g. Foobar TODO", type: :array, default: %w(OPTIMIZE FIXME TODO)
+
+ def perform(*)
+ require_application_and_environment!
+
+ deprecation_warning
+ display_annotations
+ end
+
+ private
+ def display_annotations
+ annotations = options[:annotations]
+ tag = (annotations.length > 1)
+
+ Rails::SourceAnnotationExtractor.enumerate annotations.join("|"), tag: tag, dirs: directories
+ end
+
+ def directories
+ Rails::SourceAnnotationExtractor::Annotation.directories + source_annotation_directories
+ end
+
+ def deprecation_warning
+ return if source_annotation_directories.empty?
+ ActiveSupport::Deprecation.warn("`SOURCE_ANNOTATION_DIRECTORIES` is deprecated and will be removed in Rails 6.1. You can add default directories by using config.annotations.register_directories instead.")
+ end
+
+ def source_annotation_directories
+ ENV["SOURCE_ANNOTATION_DIRECTORIES"].to_s.split(",")
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/plugin/plugin_command.rb b/railties/lib/rails/commands/plugin/plugin_command.rb
index b40ab006af..96187aa952 100644
--- a/railties/lib/rails/commands/plugin/plugin_command.rb
+++ b/railties/lib/rails/commands/plugin/plugin_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class PluginCommand < Base # :nodoc:
@@ -24,7 +26,7 @@ module Rails
if File.exist?(railsrc)
extra_args = File.read(railsrc).split(/\n+/).flat_map(&:split)
- puts "Using #{extra_args.join(" ")} from #{railsrc}"
+ say "Using #{extra_args.join(" ")} from #{railsrc}"
plugin_args.insert(1, *extra_args)
end
end
diff --git a/railties/lib/rails/commands/rake/rake_command.rb b/railties/lib/rails/commands/rake/rake_command.rb
index 075b1fd23d..535df0c430 100644
--- a/railties/lib/rails/commands/rake/rake_command.rb
+++ b/railties/lib/rails/commands/rake/rake_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class RakeCommand < Base # :nodoc:
diff --git a/railties/lib/rails/commands/routes/routes_command.rb b/railties/lib/rails/commands/routes/routes_command.rb
new file mode 100644
index 0000000000..b592a5212f
--- /dev/null
+++ b/railties/lib/rails/commands/routes/routes_command.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require "rails/command"
+
+module Rails
+ module Command
+ class RoutesCommand < Base # :nodoc:
+ class_option :controller, aliases: "-c", desc: "Filter by a specific controller, e.g. PostsController or Admin::PostsController."
+ class_option :grep, aliases: "-g", desc: "Grep routes by a specific pattern."
+ class_option :expanded, type: :boolean, aliases: "-E", desc: "Print routes expanded vertically with parts explained."
+
+ def perform(*)
+ require_application_and_environment!
+ require "action_dispatch/routing/inspector"
+
+ say inspector.format(formatter, routes_filter)
+ end
+
+ private
+ def inspector
+ ActionDispatch::Routing::RoutesInspector.new(Rails.application.routes.routes)
+ end
+
+ def formatter
+ if options.key?("expanded")
+ ActionDispatch::Routing::ConsoleFormatter::Expanded.new
+ else
+ ActionDispatch::Routing::ConsoleFormatter::Sheet.new
+ end
+ end
+
+ def routes_filter
+ options.symbolize_keys.slice(:controller, :grep)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands/runner/USAGE b/railties/lib/rails/commands/runner/USAGE
index b2a6e8493d..24b60037f0 100644
--- a/railties/lib/rails/commands/runner/USAGE
+++ b/railties/lib/rails/commands/runner/USAGE
@@ -8,6 +8,9 @@ Run the Ruby file located at `path/to/filename.rb` after loading the app:
<%= executable %> path/to/filename.rb
+Run the Ruby script read from stdin after loading the app:
+ <%= executable %> -
+
<% unless Gem.win_platform? %>
You can also use the runner command as a shebang line for your executables:
diff --git a/railties/lib/rails/commands/runner/runner_command.rb b/railties/lib/rails/commands/runner/runner_command.rb
index 4989a7837d..cb693bcf34 100644
--- a/railties/lib/rails/commands/runner/runner_command.rb
+++ b/railties/lib/rails/commands/runner/runner_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class RunnerCommand < Base # :nodoc:
@@ -5,16 +7,18 @@ module Rails
default: Rails::Command.environment.dup,
desc: "The environment for the runner to operate under (test/development/production)"
- def help
- super
- puts self.class.desc
+ no_commands do
+ def help
+ super
+ say self.class.desc
+ end
end
def self.banner(*)
- "#{super} [<'Some.ruby(code)'> | <filename.rb>]"
+ "#{super} [<'Some.ruby(code)'> | <filename.rb> | -]"
end
- def perform(code_or_file = nil, *file_argv)
+ def perform(code_or_file = nil, *command_argv)
unless code_or_file
help
exit 1
@@ -25,18 +29,21 @@ module Rails
require_application_and_environment!
Rails.application.load_runner
- if File.exist?(code_or_file)
+ ARGV.replace(command_argv)
+
+ if code_or_file == "-"
+ eval($stdin.read, TOPLEVEL_BINDING, "stdin")
+ elsif File.exist?(code_or_file)
$0 = code_or_file
- ARGV.replace(file_argv)
Kernel.load code_or_file
else
begin
- eval(code_or_file, binding, __FILE__, __LINE__)
- rescue SyntaxError, NameError => error
- $stderr.puts "Please specify a valid ruby command or the path of a script to run."
- $stderr.puts "Run '#{self.class.executable} -h' for help."
- $stderr.puts
- $stderr.puts error
+ eval(code_or_file, TOPLEVEL_BINDING, __FILE__, __LINE__)
+ rescue SyntaxError, NameError => e
+ error "Please specify a valid ruby command or the path of a script to run."
+ error "Run '#{self.class.executable} -h' for help."
+ error ""
+ error e
exit 1
end
end
diff --git a/railties/lib/rails/commands/secrets/USAGE b/railties/lib/rails/commands/secrets/USAGE
index 4b7deb4e2a..e205cdc001 100644
--- a/railties/lib/rails/commands/secrets/USAGE
+++ b/railties/lib/rails/commands/secrets/USAGE
@@ -7,7 +7,7 @@ with the code.
=== Setup
-Run `bin/rails secrets:setup` to opt in and generate the `config/secrets.yml.key`
+Run `rails secrets:setup` to opt in and generate the `config/secrets.yml.key`
and `config/secrets.yml.enc` files.
The latter contains all the keys to be encrypted while the former holds the
@@ -40,9 +40,17 @@ be encrypted.
A `shared:` top level key is also supported such that any keys there is merged
into the other environments.
+Additionally, Rails won't read encrypted secrets out of the box even if you have
+the key. Add this:
+
+ config.read_encrypted_secrets = true
+
+to the environment you'd like to read encrypted secrets. `rails secrets:setup`
+inserts this into the production environment by default.
+
=== Editing Secrets
-After `bin/rails secrets:setup`, run `bin/rails secrets:edit`.
+After `rails secrets:setup`, run `rails secrets:edit`.
That command opens a temporary file in `$EDITOR` with the decrypted contents of
`config/secrets.yml.enc` to edit the encrypted secrets.
diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb
index 3ba8c0c85b..2eebc0f35f 100644
--- a/railties/lib/rails/commands/secrets/secrets_command.rb
+++ b/railties/lib/rails/commands/secrets/secrets_command.rb
@@ -1,36 +1,65 @@
+# frozen_string_literal: true
+
require "active_support"
require "rails/secrets"
module Rails
module Command
class SecretsCommand < Rails::Command::Base # :nodoc:
- def help
- say "Usage:\n #{self.class.banner}"
- say ""
- say self.class.desc
+ no_commands do
+ def help
+ say "Usage:\n #{self.class.banner}"
+ say ""
+ say self.class.desc
+ end
end
def setup
- require "rails/generators"
- require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator"
-
- Rails::Generators::EncryptedSecretsGenerator.start
+ deprecate_in_favor_of_credentials_and_exit
end
def edit
+ if ENV["EDITOR"].to_s.empty?
+ say "No $EDITOR to open decrypted secrets in. Assign one like this:"
+ say ""
+ say %(EDITOR="mate --wait" rails secrets:edit)
+ say ""
+ say "For editors that fork and exit immediately, it's important to pass a wait flag,"
+ say "otherwise the secrets will be saved immediately with no chance to edit."
+
+ return
+ end
+
require_application_and_environment!
Rails::Secrets.read_for_editing do |tmp_path|
- puts "Waiting for secrets file to be saved. Abort with Ctrl-C."
- system("\$EDITOR #{tmp_path}")
+ system("#{ENV["EDITOR"]} #{tmp_path}")
end
- puts "New secrets encrypted and saved."
+ say "New secrets encrypted and saved."
rescue Interrupt
- puts "Aborted changing encrypted secrets: nothing saved."
+ say "Aborted changing encrypted secrets: nothing saved."
rescue Rails::Secrets::MissingKeyError => error
say error.message
+ rescue Errno::ENOENT => error
+ if /secrets\.yml\.enc/.match?(error.message)
+ deprecate_in_favor_of_credentials_and_exit
+ else
+ raise
+ end
+ end
+
+ def show
+ say Rails::Secrets.read
end
+
+ private
+ def deprecate_in_favor_of_credentials_and_exit
+ say "Encrypted secrets is deprecated in favor of credentials. Run:"
+ say "rails credentials:help"
+
+ exit 1
+ end
end
end
end
diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 1fa27a3155..9d517f3239 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
require "fileutils"
-require "optparse"
require "action_dispatch"
require "rails"
+require "active_support/deprecation"
+require "active_support/core_ext/string/filters"
require "rails/dev_caching"
module Rails
@@ -18,10 +21,15 @@ module Rails
set_environment
end
- # TODO: this is no longer required but we keep it for the moment to support older config.ru files.
def app
@app ||= begin
app = super
+ if app.is_a?(Class)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Using `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0.
+ Please change `run #{app}` to `run Rails.application` in config.ru.
+ MSG
+ end
app.respond_to?(:to_app) ? app.to_app : app
end
end
@@ -34,18 +42,22 @@ module Rails
ENV["RAILS_ENV"] ||= options[:environment]
end
- def start
- print_boot_information
+ def start(after_stop_callback = nil)
trap(:INT) { exit }
create_tmp_directories
setup_dev_caching
log_to_stdout if options[:log_stdout]
- super
+ super()
ensure
- # The '-h' option calls exit before @options is set.
- # If we call 'options' with it unset, we get double help banners.
- puts "Exiting" unless @options && options[:daemonize]
+ after_stop_callback.call if after_stop_callback
+ end
+
+ def serveable? # :nodoc:
+ server
+ true
+ rescue LoadError, NameError
+ false
end
def middleware
@@ -56,6 +68,10 @@ module Rails
super.merge(@default_options)
end
+ def served_url
+ "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" unless use_puma?
+ end
+
private
def setup_dev_caching
if options[:environment] == "development"
@@ -63,13 +79,6 @@ module Rails
end
end
- def print_boot_information
- url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"
- puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
- puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
- puts "=> Run `rails server -h` for more startup options"
- end
-
def create_tmp_directories
%w(cache pids sockets).each do |dir_to_make|
FileUtils.mkdir_p(File.join(Rails.root, "tmp", dir_to_make))
@@ -88,17 +97,24 @@ module Rails
end
end
- def restart_command
- "bin/rails server #{ARGV.join(' ')}"
+ def use_puma?
+ server.to_s == "Rack::Handler::Puma"
end
end
module Command
class ServerCommand < Base # :nodoc:
+ # Hard-coding a bunch of handlers here as we don't have a public way of
+ # querying them from the Rack::Handler registry.
+ RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn)
+
+ DEFAULT_PORT = 3000
DEFAULT_PID_PATH = "tmp/pids/server.pid".freeze
+ argument :using, optional: true
+
class_option :port, aliases: "-p", type: :numeric,
- desc: "Runs Rails on the specified port.", banner: :port, default: 3000
+ desc: "Runs Rails on the specified port - defaults to 3000.", banner: :port
class_option :binding, aliases: "-b", type: :string,
desc: "Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments'.",
banner: :IP
@@ -108,26 +124,41 @@ module Rails
desc: "Runs server as a Daemon."
class_option :environment, aliases: "-e", type: :string,
desc: "Specifies the environment to run this server under (development/test/production).", banner: :name
+ class_option :using, aliases: "-u", type: :string,
+ desc: "Specifies the Rack server used to run the application (thin/puma/webrick).", banner: :name
class_option :pid, aliases: "-P", type: :string, default: DEFAULT_PID_PATH,
desc: "Specifies the PID file."
- class_option "dev-caching", aliases: "-C", type: :boolean, default: nil,
+ class_option :dev_caching, aliases: "-C", type: :boolean, default: nil,
desc: "Specifies whether to perform caching in development."
+ class_option :restart, type: :boolean, default: nil, hide: true
+ class_option :early_hints, type: :boolean, default: nil, desc: "Enables HTTP/2 early hints."
+ class_option :log_to_stdout, type: :boolean, default: nil, optional: true,
+ desc: "Whether to log to stdout. Enabled by default in development when not daemonized."
- def initialize(args = [], local_options = {}, config = {})
- @original_options = local_options
+ def initialize(args, local_options, *)
super
- @server = self.args.shift
- @log_stdout = options[:daemon].blank? && (options[:environment] || Rails.env) == "development"
+
+ @original_options = local_options - %w( --restart )
+ deprecate_positional_rack_server_and_rewrite_to_option(@original_options)
end
def perform
set_application_directory!
+ prepare_restart
+
Rails::Server.new(server_options).tap do |server|
# Require application after server sets environment to propagate
# the --environment option.
require APP_PATH
Dir.chdir(Rails.application.root)
- server.start
+
+ if server.serveable?
+ print_boot_information(server.server, server.served_url)
+ after_stop_callback = -> { say "Exiting" unless options[:daemon] }
+ server.start(after_stop_callback)
+ else
+ say rack_server_suggestion(using)
+ end
end
end
@@ -135,8 +166,8 @@ module Rails
def server_options
{
user_supplied_options: user_supplied_options,
- server: @server,
- log_stdout: @log_stdout,
+ server: using,
+ log_stdout: log_to_stdout?,
Port: port,
Host: host,
DoNotReverseLookup: true,
@@ -144,8 +175,9 @@ module Rails
environment: environment,
daemonize: options[:daemon],
pid: pid,
- caching: options["dev-caching"],
- restart_cmd: restart_command
+ caching: options[:dev_caching],
+ restart_cmd: restart_command,
+ early_hints: early_hints
}
end
end
@@ -154,9 +186,16 @@ module Rails
def user_supplied_options
@user_supplied_options ||= begin
# Convert incoming options array to a hash of flags
- # ["-p", "3001", "-c", "foo"] # => {"-p" => true, "-c" => true}
+ # ["-p3001", "-C", "--binding", "127.0.0.1"] # => {"-p"=>true, "-C"=>true, "--binding"=>true}
user_flag = {}
- @original_options.each_with_index { |command, i| user_flag[command] = true if i.even? }
+ @original_options.each do |command|
+ if command.to_s.start_with?("--")
+ option = command.split("=")[0]
+ user_flag[option] = true
+ elsif command =~ /\A(-.)/
+ user_flag[Regexp.last_match[0]] = true
+ end
+ end
# Collect all options that the user has explicitly defined so we can
# differentiate them from defaults
@@ -169,7 +208,7 @@ module Rails
name = :Port
when :binding
name = :Host
- when :"dev-caching"
+ when :dev_caching
name = :caching
when :daemonize
name = :daemon
@@ -177,21 +216,33 @@ module Rails
user_supplied_options << name
end
end
- user_supplied_options << :Host if ENV["Host"]
+ user_supplied_options << :Host if ENV["HOST"] || ENV["BINDING"]
user_supplied_options << :Port if ENV["PORT"]
user_supplied_options.uniq
end
end
def port
- ENV.fetch("PORT", options[:port]).to_i
+ options[:port] || ENV.fetch("PORT", DEFAULT_PORT).to_i
end
def host
- unless (default_host = options[:binding])
+ if options[:binding]
+ options[:binding]
+ else
default_host = environment == "development" ? "localhost" : "0.0.0.0"
+
+ if ENV["HOST"] && !ENV["BINDING"]
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Using the `HOST` environment to specify the IP is deprecated and will be removed in Rails 6.1.
+ Please use `BINDING` environment instead.
+ MSG
+
+ return ENV["HOST"]
+ end
+
+ ENV.fetch("BINDING", default_host)
end
- ENV.fetch("HOST", default_host)
end
def environment
@@ -199,7 +250,17 @@ module Rails
end
def restart_command
- "bin/rails server #{@server} #{@original_options.join(" ")}"
+ "bin/rails server #{@original_options.join(" ")} --restart"
+ end
+
+ def early_hints
+ options[:early_hints]
+ end
+
+ def log_to_stdout?
+ options.fetch(:log_to_stdout) do
+ options[:daemon].blank? && environment == "development"
+ end
end
def pid
@@ -207,7 +268,54 @@ module Rails
end
def self.banner(*)
- "rails server [puma, thin etc] [options]"
+ "rails server [thin/puma/webrick] [options]"
+ end
+
+ def prepare_restart
+ FileUtils.rm_f(options[:pid]) if options[:restart]
+ end
+
+ def deprecate_positional_rack_server_and_rewrite_to_option(original_options)
+ if using
+ ActiveSupport::Deprecation.warn(<<~MSG)
+ Passing the Rack server name as a regular argument is deprecated
+ and will be removed in the next Rails version. Please, use the -u
+ option instead.
+ MSG
+
+ original_options.concat [ "-u", using ]
+ else
+ # Use positional internally to get around Thor's immutable options.
+ # TODO: Replace `using` occurrences with `options[:using]` after deprecation removal.
+ @using = options[:using]
+ end
+ end
+
+ def rack_server_suggestion(server)
+ if server.in?(RACK_SERVERS)
+ <<~MSG
+ Could not load server "#{server}". Maybe you need to the add it to the Gemfile?
+
+ gem "#{server}"
+
+ Run `rails server --help` for more options.
+ MSG
+ else
+ suggestion = Rails::Command::Spellchecker.suggest(server, from: RACK_SERVERS)
+
+ <<~MSG
+ Could not find server "#{server}". Maybe you meant #{suggestion.inspect}?
+ Run `rails server --help` for more options.
+ MSG
+ end
+ end
+
+ def print_boot_information(server, url)
+ say <<~MSG
+ => Booting #{ActiveSupport::Inflector.demodulize(server)}
+ => Rails #{Rails.version} application starting in #{Rails.env} #{url}
+ => Run `rails server --help` for more startup options
+ MSG
end
end
end
diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb
index 629fb5b425..00ea9ac4a6 100644
--- a/railties/lib/rails/commands/test/test_command.rb
+++ b/railties/lib/rails/commands/test/test_command.rb
@@ -1,19 +1,36 @@
+# frozen_string_literal: true
+
require "rails/command"
-require "rails/test_unit/minitest_plugin"
+require "rails/test_unit/runner"
+require "rails/test_unit/reporter"
module Rails
module Command
class TestCommand < Base # :nodoc:
- def help
- perform # Hand over help printing to minitest.
+ no_commands do
+ def help
+ say "Usage: #{Rails::TestUnitReporter.executable} [options] [files or directories]"
+ say ""
+ say "You can run a single test by appending a line number to a filename:"
+ say ""
+ say " #{Rails::TestUnitReporter.executable} test/models/user_test.rb:27"
+ say ""
+ say "You can run multiple files and directories at the same time:"
+ say ""
+ say " #{Rails::TestUnitReporter.executable} test/controllers test/integration/login_test.rb"
+ say ""
+ say "By default test failures and errors are reported inline during a run."
+ say ""
+
+ Minitest.run(%w(--help))
+ end
end
def perform(*)
- $LOAD_PATH << Rails::Command.root.join("test")
-
- Minitest.run_via = :rails
+ $LOAD_PATH << Rails::Command.root.join("test").to_s
- require "active_support/testing/autorun"
+ Rails::TestUnit::Runner.parse_options(ARGV)
+ Rails::TestUnit::Runner.run(ARGV)
end
end
end
diff --git a/railties/lib/rails/commands/version/version_command.rb b/railties/lib/rails/commands/version/version_command.rb
index ac745594ee..3e2112b6d4 100644
--- a/railties/lib/rails/commands/version/version_command.rb
+++ b/railties/lib/rails/commands/version/version_command.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Rails
module Command
class VersionCommand < Base # :nodoc: