railties 7.0.8.7 → 7.1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +723 -215
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.md +99 -0
  5. data/README.rdoc +4 -4
  6. data/lib/minitest/rails_plugin.rb +63 -0
  7. data/lib/rails/api/task.rb +35 -4
  8. data/lib/rails/app_updater.rb +14 -2
  9. data/lib/rails/application/bootstrap.rb +23 -4
  10. data/lib/rails/application/configuration.rb +190 -69
  11. data/lib/rails/application/default_middleware_stack.rb +8 -2
  12. data/lib/rails/application/dummy_config.rb +19 -0
  13. data/lib/rails/application/finisher.rb +43 -33
  14. data/lib/rails/application.rb +141 -33
  15. data/lib/rails/backtrace_cleaner.rb +5 -3
  16. data/lib/rails/cli.rb +5 -2
  17. data/lib/rails/command/actions.rb +10 -12
  18. data/lib/rails/command/base.rb +55 -53
  19. data/lib/rails/command/environment_argument.rb +32 -16
  20. data/lib/rails/command/helpers/editor.rb +17 -12
  21. data/lib/rails/command.rb +84 -33
  22. data/lib/rails/commands/about/about_command.rb +14 -0
  23. data/lib/rails/commands/application/application_command.rb +2 -0
  24. data/lib/rails/commands/console/console_command.rb +14 -14
  25. data/lib/rails/commands/credentials/USAGE +53 -55
  26. data/lib/rails/commands/credentials/credentials_command/diffing.rb +5 -3
  27. data/lib/rails/commands/credentials/credentials_command.rb +64 -70
  28. data/lib/rails/commands/db/system/change/change_command.rb +2 -1
  29. data/lib/rails/commands/dbconsole/dbconsole_command.rb +25 -115
  30. data/lib/rails/commands/destroy/destroy_command.rb +3 -2
  31. data/lib/rails/commands/dev/dev_command.rb +1 -6
  32. data/lib/rails/commands/encrypted/USAGE +15 -20
  33. data/lib/rails/commands/encrypted/encrypted_command.rb +46 -35
  34. data/lib/rails/commands/gem_help/USAGE +16 -0
  35. data/lib/rails/commands/gem_help/gem_help_command.rb +13 -0
  36. data/lib/rails/commands/generate/generate_command.rb +2 -2
  37. data/lib/rails/commands/help/USAGE +13 -13
  38. data/lib/rails/commands/help/help_command.rb +21 -2
  39. data/lib/rails/commands/initializers/initializers_command.rb +1 -4
  40. data/lib/rails/commands/middleware/middleware_command.rb +17 -0
  41. data/lib/rails/commands/new/new_command.rb +2 -0
  42. data/lib/rails/commands/notes/notes_command.rb +2 -1
  43. data/lib/rails/commands/plugin/plugin_command.rb +2 -0
  44. data/lib/rails/commands/rake/rake_command.rb +25 -22
  45. data/lib/rails/commands/restart/restart_command.rb +14 -0
  46. data/lib/rails/commands/routes/routes_command.rb +13 -1
  47. data/lib/rails/commands/runner/USAGE +14 -12
  48. data/lib/rails/commands/runner/runner_command.rb +32 -20
  49. data/lib/rails/commands/secret/secret_command.rb +13 -0
  50. data/lib/rails/commands/secrets/USAGE +44 -49
  51. data/lib/rails/commands/secrets/secrets_command.rb +20 -38
  52. data/lib/rails/commands/server/server_command.rb +33 -32
  53. data/lib/rails/commands/test/USAGE +14 -0
  54. data/lib/rails/commands/test/test_command.rb +56 -14
  55. data/lib/rails/commands/unused_routes/unused_routes_command.rb +75 -0
  56. data/lib/rails/commands/version/version_command.rb +1 -0
  57. data/lib/rails/configuration.rb +5 -5
  58. data/lib/rails/console/app.rb +1 -4
  59. data/lib/rails/deprecator.rb +7 -0
  60. data/lib/rails/engine/configuration.rb +50 -6
  61. data/lib/rails/engine.rb +49 -21
  62. data/lib/rails/gem_version.rb +4 -4
  63. data/lib/rails/generators/actions.rb +6 -15
  64. data/lib/rails/generators/active_model.rb +28 -14
  65. data/lib/rails/generators/app_base.rb +355 -82
  66. data/lib/rails/generators/app_name.rb +3 -14
  67. data/lib/rails/generators/base.rb +17 -9
  68. data/lib/rails/generators/database.rb +40 -2
  69. data/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt +1 -1
  70. data/lib/rails/generators/generated_attribute.rb +12 -0
  71. data/lib/rails/generators/migration.rb +4 -5
  72. data/lib/rails/generators/model_helpers.rb +2 -1
  73. data/lib/rails/generators/rails/app/USAGE +22 -6
  74. data/lib/rails/generators/rails/app/app_generator.rb +85 -64
  75. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +103 -0
  76. data/lib/rails/generators/rails/app/templates/Gemfile.tt +9 -11
  77. data/lib/rails/generators/rails/app/templates/app/views/layouts/mailer.html.erb.tt +1 -1
  78. data/lib/rails/generators/rails/app/templates/bin/setup.tt +10 -1
  79. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +6 -17
  80. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +4 -4
  81. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +3 -3
  82. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +4 -6
  83. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +3 -3
  84. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +59 -0
  85. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +12 -2
  86. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +32 -28
  87. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +13 -9
  88. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +2 -0
  89. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +2 -2
  90. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +1 -1
  91. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +3 -3
  92. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +280 -0
  93. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -9
  94. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +11 -13
  95. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +21 -20
  96. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +5 -1
  97. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +6 -4
  98. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +10 -0
  99. data/lib/rails/generators/rails/app/templates/dockerignore.tt +43 -0
  100. data/lib/rails/generators/rails/app/templates/gitignore.tt +4 -8
  101. data/lib/rails/generators/rails/app/templates/node-version.tt +1 -0
  102. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +10 -8
  103. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +9 -7
  104. data/lib/rails/generators/rails/application_record/application_record_generator.rb +4 -0
  105. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +2 -1
  106. data/lib/rails/generators/rails/controller/USAGE +12 -4
  107. data/lib/rails/generators/rails/controller/controller_generator.rb +5 -0
  108. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +1 -1
  109. data/lib/rails/generators/rails/credentials/credentials_generator.rb +29 -24
  110. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +8 -0
  111. data/lib/rails/generators/rails/db/system/change/change_generator.rb +30 -0
  112. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +1 -2
  113. data/lib/rails/generators/rails/migration/USAGE +21 -11
  114. data/lib/rails/generators/rails/model/model_generator.rb +4 -0
  115. data/lib/rails/generators/rails/plugin/USAGE +17 -6
  116. data/lib/rails/generators/rails/plugin/plugin_generator.rb +5 -15
  117. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +2 -2
  118. data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +1 -1
  119. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +1 -17
  120. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +0 -2
  121. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +4 -4
  122. data/lib/rails/generators/rails/resource/resource_generator.rb +6 -0
  123. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +2 -1
  124. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +1 -1
  125. data/lib/rails/generators/test_case.rb +2 -2
  126. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +1 -1
  127. data/lib/rails/generators/testing/{behaviour.rb → behavior.rb} +4 -1
  128. data/lib/rails/generators.rb +6 -14
  129. data/lib/rails/health_controller.rb +55 -0
  130. data/lib/rails/info.rb +1 -1
  131. data/lib/rails/info_controller.rb +33 -11
  132. data/lib/rails/mailers_controller.rb +15 -5
  133. data/lib/rails/paths.rb +13 -10
  134. data/lib/rails/rack/logger.rb +15 -12
  135. data/lib/rails/rackup/server.rb +15 -0
  136. data/lib/rails/railtie/configuration.rb +14 -1
  137. data/lib/rails/railtie.rb +31 -31
  138. data/lib/rails/ruby_version_check.rb +2 -0
  139. data/lib/rails/source_annotation_extractor.rb +67 -18
  140. data/lib/rails/tasks/engine.rake +8 -8
  141. data/lib/rails/tasks/framework.rake +4 -10
  142. data/lib/rails/tasks/log.rake +1 -1
  143. data/lib/rails/tasks/misc.rake +3 -14
  144. data/lib/rails/tasks/statistics.rake +5 -4
  145. data/lib/rails/tasks/tmp.rake +5 -5
  146. data/lib/rails/tasks/zeitwerk.rake +15 -35
  147. data/lib/rails/tasks.rb +0 -2
  148. data/lib/rails/templates/rails/mailers/email.html.erb +32 -0
  149. data/lib/rails/templates/rails/mailers/index.html.erb +14 -7
  150. data/lib/rails/templates/rails/mailers/mailer.html.erb +11 -5
  151. data/lib/rails/templates/rails/welcome/index.html.erb +1 -0
  152. data/lib/rails/test_help.rb +9 -14
  153. data/lib/rails/test_unit/line_filtering.rb +1 -1
  154. data/lib/rails/test_unit/reporter.rb +6 -2
  155. data/lib/rails/test_unit/runner.rb +36 -18
  156. data/lib/rails/test_unit/test_parser.rb +88 -0
  157. data/lib/rails/test_unit/testing.rake +13 -33
  158. data/lib/rails/testing/maintain_test_schema.rb +16 -0
  159. data/lib/rails/version.rb +1 -1
  160. data/lib/rails/zeitwerk_checker.rb +15 -0
  161. data/lib/rails.rb +15 -15
  162. metadata +64 -27
  163. data/RDOC_MAIN.rdoc +0 -97
  164. data/lib/rails/application/dummy_erb_compiler.rb +0 -18
  165. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_0.rb.tt +0 -143
  166. data/lib/rails/generators/rails/model/USAGE +0 -113
  167. data/lib/rails/tasks/middleware.rake +0 -9
  168. data/lib/rails/tasks/restart.rake +0 -9
data/lib/rails/command.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "active_support"
4
4
  require "active_support/core_ext/enumerable"
5
5
  require "active_support/core_ext/object/blank"
6
+ require "rails/deprecator"
6
7
 
7
8
  require "thor"
8
9
 
@@ -13,9 +14,40 @@ module Rails
13
14
  autoload :Behavior
14
15
  autoload :Base
15
16
 
17
+ class CorrectableNameError < StandardError # :nodoc:
18
+ attr_reader :name
19
+
20
+ def initialize(message, name, alternatives)
21
+ @name = name
22
+ @alternatives = alternatives
23
+ super(message)
24
+ end
25
+
26
+ if !Exception.method_defined?(:detailed_message)
27
+ def detailed_message(...)
28
+ message
29
+ end
30
+ end
31
+
32
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
33
+ include DidYouMean::Correctable
34
+
35
+ def corrections
36
+ @corrections ||= DidYouMean::SpellChecker.new(dictionary: @alternatives).correct(name)
37
+ end
38
+ end
39
+ end
40
+
41
+ class UnrecognizedCommandError < CorrectableNameError # :nodoc:
42
+ def initialize(name)
43
+ super("Unrecognized command #{name.inspect}", name, Command.printing_commands.map(&:first))
44
+ end
45
+ end
46
+
16
47
  include Behavior
17
48
 
18
- HELP_MAPPINGS = %w(-h -? --help)
49
+ HELP_MAPPINGS = %w(-h -? --help).to_set
50
+ VERSION_MAPPINGS = %w(-v --version).to_set
19
51
 
20
52
  class << self
21
53
  def hidden_commands # :nodoc:
@@ -28,30 +60,26 @@ module Rails
28
60
 
29
61
  # Receives a namespace, arguments, and the behavior to invoke the command.
30
62
  def invoke(full_namespace, args = [], **config)
31
- namespace = full_namespace = full_namespace.to_s
32
-
33
- if char = namespace =~ /:(\w+)$/
34
- command_name, namespace = $1, namespace.slice(0, char)
35
- else
36
- command_name = namespace
37
- end
38
-
39
- command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
40
- command_name, namespace, args = "application", "application", ["--help"] if rails_new_with_no_path?(args)
41
- command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)
42
-
43
- original_argv = ARGV.dup
44
- ARGV.replace(args)
63
+ args = ["--help"] if rails_new_with_no_path?(args)
45
64
 
65
+ full_namespace = full_namespace.to_s
66
+ namespace, command_name = split_namespace(full_namespace)
46
67
  command = find_by_namespace(namespace, command_name)
47
- if command && command.all_commands[command_name]
48
- command.perform(command_name, args, config)
68
+
69
+ with_argv(args) do
70
+ if command && command.all_commands[command_name]
71
+ command.perform(command_name, args, config)
72
+ else
73
+ invoke_rake(full_namespace, args, config)
74
+ end
75
+ end
76
+ rescue UnrecognizedCommandError => error
77
+ if error.name == full_namespace && command && command_name == full_namespace
78
+ command.perform("help", [], config)
49
79
  else
50
- args = ["--describe", full_namespace] if HELP_MAPPINGS.include?(args[0])
51
- find_by_namespace("rake").perform(full_namespace, args, config)
80
+ puts error.detailed_message
52
81
  end
53
- ensure
54
- ARGV.replace(original_argv)
82
+ exit(1)
55
83
  end
56
84
 
57
85
  # Rails finds namespaces similar to Thor, it only adds one rule:
@@ -76,33 +104,56 @@ module Rails
76
104
  namespaces[(lookups & namespaces.keys).first]
77
105
  end
78
106
 
79
- # Returns the root of the Rails engine or app running the command.
107
+ # Returns the root of the \Rails engine or app running the command.
80
108
  def root
81
109
  if defined?(ENGINE_ROOT)
82
110
  Pathname.new(ENGINE_ROOT)
83
- elsif defined?(APP_PATH)
84
- Pathname.new(File.expand_path("../..", APP_PATH))
111
+ else
112
+ application_root
85
113
  end
86
114
  end
87
115
 
88
- def print_commands # :nodoc:
89
- commands.each { |command| puts(" #{command}") }
116
+ def application_root # :nodoc:
117
+ Pathname.new(File.expand_path("../..", APP_PATH)) if defined?(APP_PATH)
90
118
  end
91
119
 
92
- private
93
- COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
94
- private_constant :COMMANDS_IN_USAGE
120
+ def printing_commands # :nodoc:
121
+ lookup!
122
+
123
+ (subclasses - hidden_commands).flat_map(&:printing_commands)
124
+ end
95
125
 
126
+ private
96
127
  def rails_new_with_no_path?(args)
97
128
  args == ["new"]
98
129
  end
99
130
 
100
- def commands
101
- lookup!
131
+ def split_namespace(namespace)
132
+ case namespace
133
+ when /^(.+):(\w+)$/
134
+ [$1, $2]
135
+ when ""
136
+ ["help", "help"]
137
+ when HELP_MAPPINGS, "help"
138
+ ["help", "help_extended"]
139
+ when VERSION_MAPPINGS
140
+ ["version", "version"]
141
+ else
142
+ [namespace, namespace]
143
+ end
144
+ end
102
145
 
103
- visible_commands = (subclasses - hidden_commands).flat_map(&:printing_commands)
146
+ def with_argv(argv)
147
+ original_argv = ARGV.dup
148
+ ARGV.replace(argv)
149
+ yield
150
+ ensure
151
+ ARGV.replace(original_argv)
152
+ end
104
153
 
105
- (visible_commands - COMMANDS_IN_USAGE).sort
154
+ def invoke_rake(task, args, config)
155
+ args = ["--describe", task] if HELP_MAPPINGS.include?(args[0])
156
+ find_by_namespace("rake").perform(task, args, config)
106
157
  end
107
158
 
108
159
  def command_type # :doc:
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Command
5
+ class AboutCommand < Base # :nodoc:
6
+ desc "about", "List versions of all Rails frameworks and the environment"
7
+ def perform
8
+ boot_application!
9
+
10
+ say Rails::Info
11
+ end
12
+ end
13
+ end
14
+ end
@@ -18,6 +18,8 @@ module Rails
18
18
  class ApplicationCommand < Base # :nodoc:
19
19
  hide_command!
20
20
 
21
+ self.bin = "rails"
22
+
21
23
  def help
22
24
  perform # Punt help output to the generator.
23
25
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "irb"
4
- require "irb/completion"
5
-
6
3
  require "rails/command/environment_argument"
7
4
 
8
5
  module Rails
@@ -34,19 +31,26 @@ module Rails
34
31
 
35
32
  app.load_console
36
33
 
37
- @console = app.config.console || IRB
34
+ @console = app.config.console || begin
35
+ require "irb"
36
+ require "irb/completion"
38
37
 
39
- if @console == IRB
40
38
  IRB::WorkSpace.prepend(BacktraceCleaner)
41
39
 
42
- if Rails.env.production?
40
+ if !Rails.env.local?
43
41
  ENV["IRB_USE_AUTOCOMPLETE"] ||= "false"
44
42
  end
43
+
44
+ IRB
45
45
  end
46
46
  end
47
47
 
48
48
  def sandbox?
49
- options[:sandbox]
49
+ return options[:sandbox] if !options[:sandbox].nil?
50
+
51
+ return false if Rails.env.local?
52
+
53
+ app.config.sandbox_by_default
50
54
  end
51
55
 
52
56
  def environment
@@ -79,7 +83,7 @@ module Rails
79
83
  class ConsoleCommand < Base # :nodoc:
80
84
  include EnvironmentArgument
81
85
 
82
- class_option :sandbox, aliases: "-s", type: :boolean, default: false,
86
+ class_option :sandbox, aliases: "-s", type: :boolean, default: nil,
83
87
  desc: "Rollback database modifications on exit."
84
88
 
85
89
  def initialize(args = [], local_options = {}, config = {})
@@ -96,13 +100,9 @@ module Rails
96
100
  super(args, local_options, config)
97
101
  end
98
102
 
103
+ desc "console", "Start the Rails console"
99
104
  def perform
100
- extract_environment_option_from_argument
101
-
102
- # RAILS_ENV needs to be set before config/application is required.
103
- ENV["RAILS_ENV"] = options[:environment]
104
-
105
- require_application_and_environment!
105
+ boot_application!
106
106
  Rails::Console.start(Rails.application, options)
107
107
  end
108
108
  end
@@ -1,75 +1,73 @@
1
- === Storing Encrypted Credentials in Source Control
1
+ Description:
2
+ The Rails `credentials` commands provide access to encrypted credentials,
3
+ so you can safely store access tokens, database passwords, and the like
4
+ safely inside the app without relying on a mess of ENVs.
2
5
 
3
- The Rails `credentials` commands provide access to encrypted credentials,
4
- so you can safely store access tokens, database passwords, and the like
5
- safely inside the app without relying on a mess of ENVs.
6
+ This also allows for atomic deploys: no need to coordinate key changes
7
+ to get everything working as the keys are shipped with the code.
6
8
 
7
- This also allows for atomic deploys: no need to coordinate key changes
8
- to get everything working as the keys are shipped with the code.
9
+ Setup:
10
+ Applications after Rails 5.2 automatically have a basic credentials file generated
11
+ that just contains the secret_key_base used by MessageVerifiers/MessageEncryptors, like the ones
12
+ signing and encrypting cookies.
9
13
 
10
- === Setup
14
+ For applications created prior to Rails 5.2, we'll automatically generate a new
15
+ credentials file in `config/credentials.yml.enc` the first time you run `<%= executable(:edit) %>`.
16
+ If you didn't have a master key saved in `config/master.key`, that'll be created too.
11
17
 
12
- Applications after Rails 5.2 automatically have a basic credentials file generated
13
- that just contains the secret_key_base used by MessageVerifiers/MessageEncryptors, like the ones
14
- signing and encrypting cookies.
18
+ Don't lose this master key! Put it in a password manager your team can access.
19
+ Should you lose it no one, including you, will be able to access any encrypted
20
+ credentials.
15
21
 
16
- For applications created prior to Rails 5.2, we'll automatically generate a new
17
- credentials file in `config/credentials.yml.enc` the first time you run `bin/rails credentials:edit`.
18
- If you didn't have a master key saved in `config/master.key`, that'll be created too.
22
+ Don't commit the key! Add `config/master.key` to your source control's
23
+ ignore file. If you use Git, Rails handles this for you.
19
24
 
20
- Don't lose this master key! Put it in a password manager your team can access.
21
- Should you lose it no one, including you, will be able to access any encrypted
22
- credentials.
25
+ Rails also looks for the master key in `ENV["RAILS_MASTER_KEY"]`, in case that
26
+ is easier to manage. You could set `RAILS_MASTER_KEY` in a deployment
27
+ configuration, or you could prepend it to your server's start command like so:
23
28
 
24
- Don't commit the key! Add `config/master.key` to your source control's
25
- ignore file. If you use Git, Rails handles this for you.
29
+ RAILS_MASTER_KEY="very-secret-and-secure" bin/rails server
26
30
 
27
- Rails also looks for the master key in `ENV["RAILS_MASTER_KEY"]`, if that's easier to manage.
31
+ If `ENV["RAILS_MASTER_KEY"]` is present, it takes precedence over
32
+ `config/master.key`.
28
33
 
29
- You could prepend that to your server's start command like this:
34
+ Set up Git to Diff Credentials:
35
+ Rails provides `<%= executable(:diff) %> --enroll` to instruct Git to call
36
+ `<%= executable(:diff) %>` when `git diff` is run on a credentials file.
30
37
 
31
- RAILS_MASTER_KEY="very-secret-and-secure" server.start
38
+ Running the command enrolls the project such that all credentials files use the
39
+ "rails_credentials" diff driver in .gitattributes.
32
40
 
33
- === Set up Git to Diff Credentials
41
+ Additionally since Git requires the driver itself to be set up in a config file
42
+ that isn't tracked Rails automatically ensures it's configured when running
43
+ `<%= executable(:edit) %>`.
34
44
 
35
- Rails provides `bin/rails credentials:diff --enroll` to instruct Git to call
36
- `bin/rails credentials:diff` when `git diff` is run on a credentials file.
45
+ Otherwise each co-worker would have to run enable manually, including on each new
46
+ repo clone.
37
47
 
38
- Running the command enrolls the project such that all credentials files use the
39
- "rails_credentials" diff driver in .gitattributes.
48
+ To disenroll from this feature, run `<%= executable(:diff) %> --disenroll`.
40
49
 
41
- Additionally since Git requires the driver itself to be set up in a config file
42
- that isn't tracked Rails automatically ensures it's configured when running
43
- `credentials:edit`.
50
+ Editing Credentials:
51
+ This will open a temporary file in `$VISUAL` or `$EDITOR` with the decrypted
52
+ contents to edit the encrypted credentials.
44
53
 
45
- Otherwise each co-worker would have to run enable manually, including on each new
46
- repo clone.
54
+ When the temporary file is next saved the contents are encrypted and written to
55
+ `config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
56
+ from leaking.
47
57
 
48
- To disenroll from this feature, run `bin/rails credentials:diff --disenroll`.
58
+ Environment Specific Credentials:
59
+ The `credentials` command supports passing an `--environment` option to create an
60
+ environment specific override. That override will take precedence over the
61
+ global `config/credentials.yml.enc` file when running in that environment. So:
49
62
 
50
- === Editing Credentials
63
+ <%= executable(:edit) %> --environment development
51
64
 
52
- This will open a temporary file in `$EDITOR` with the decrypted contents to edit
53
- the encrypted credentials.
65
+ will create `config/credentials/development.yml.enc` with the corresponding
66
+ encryption key in `config/credentials/development.key` if the credentials file
67
+ doesn't exist.
54
68
 
55
- When the temporary file is next saved the contents are encrypted and written to
56
- `config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
57
- from leaking.
69
+ In addition to that, the default credentials lookup paths can be overridden through
70
+ `config.credentials.content_path` and `config.credentials.key_path`.
58
71
 
59
- === Environment Specific Credentials
60
-
61
- The `credentials` command supports passing an `--environment` option to create an
62
- environment specific override. That override will take precedence over the
63
- global `config/credentials.yml.enc` file when running in that environment. So:
64
-
65
- bin/rails credentials:edit --environment development
66
-
67
- will create `config/credentials/development.yml.enc` with the corresponding
68
- encryption key in `config/credentials/development.key` if the credentials file
69
- doesn't exist.
70
-
71
- The encryption key can also be put in `ENV["RAILS_MASTER_KEY"]`, which takes
72
- precedence over the file encryption key.
73
-
74
- In addition to that, the default credentials lookup paths can be overridden through
75
- `config.credentials.content_path` and `config.credentials.key_path`.
72
+ Just as with `config/master.key`, `ENV["RAILS_MASTER_KEY"]` takes precedence
73
+ over any environment specific or specially configured key files.
@@ -13,7 +13,8 @@ module Rails::Command::CredentialsCommand::Diffing # :nodoc:
13
13
  gitattributes.write(GITATTRIBUTES_ENTRY, mode: "a")
14
14
 
15
15
  say "Enrolled project in credentials file diffing!"
16
- say "Rails ensures the rails_credentials diff driver is set when running `credentials:edit`. See `credentials:help` for more."
16
+ say ""
17
+ say "Rails will configure the Git diff driver for credentials when running `#{executable(:edit)}`. See `#{executable(:help)}` for more information."
17
18
  end
18
19
  end
19
20
 
@@ -42,10 +43,11 @@ module Rails::Command::CredentialsCommand::Diffing # :nodoc:
42
43
  end
43
44
 
44
45
  def configure_diffing_driver
45
- system "git config diff.rails_credentials.textconv 'bin/rails credentials:diff'"
46
+ system "git config diff.rails_credentials.textconv '#{executable(:diff)}'"
47
+ say "Configured Git diff driver for credentials."
46
48
  end
47
49
 
48
50
  def gitattributes
49
- Rails.root.join(".gitattributes")
51
+ @gitattributes ||= (Rails::Command.root || Pathname.pwd).join(".gitattributes")
50
52
  end
51
53
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pathname"
4
- require "shellwords"
5
4
  require "active_support"
6
5
  require "rails/command/helpers/editor"
7
6
  require "rails/command/environment_argument"
@@ -15,56 +14,42 @@ module Rails
15
14
  require_relative "credentials_command/diffing"
16
15
  include Diffing
17
16
 
18
- self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
19
-
20
- no_commands do
21
- def help
22
- say "Usage:\n #{self.class.banner}"
23
- say ""
24
- say self.class.desc
25
- end
26
- end
27
-
17
+ desc "edit", "Open the decrypted credentials in `$VISUAL` or `$EDITOR` for editing"
28
18
  def edit
29
- extract_environment_option_from_argument(default_environment: nil)
30
- require_application!
19
+ load_environment_config!
20
+ load_generators
31
21
 
32
- ensure_editor_available(command: "bin/rails credentials:edit") || (return)
22
+ if environment_specified?
23
+ @content_path = "config/credentials/#{environment}.yml.enc" unless config.key?(:content_path)
24
+ @key_path = "config/credentials/#{environment}.key" unless config.key?(:key_path)
25
+ end
33
26
 
34
- ensure_encryption_key_has_been_added if credentials.key.nil?
27
+ ensure_encryption_key_has_been_added
35
28
  ensure_credentials_have_been_added
36
29
  ensure_diffing_driver_is_configured
37
30
 
38
- catch_editing_exceptions do
39
- change_credentials_in_system_editor
40
- end
41
-
42
- say "File encrypted and saved."
43
- rescue ActiveSupport::MessageEncryptor::InvalidMessage
44
- say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
31
+ change_credentials_in_system_editor
45
32
  end
46
33
 
34
+ desc "show", "Show the decrypted credentials"
47
35
  def show
48
- extract_environment_option_from_argument(default_environment: nil)
49
- require_application!
36
+ load_environment_config!
50
37
 
51
38
  say credentials.read.presence || missing_credentials_message
52
39
  end
53
40
 
41
+ desc "diff", "Enroll/disenroll in decrypted diffs of credentials using git"
54
42
  option :enroll, type: :boolean, default: false,
55
- desc: "Enrolls project in credentials file diffing with `git diff`"
56
-
43
+ desc: "Enroll project in credentials file diffing with `git diff`"
57
44
  option :disenroll, type: :boolean, default: false,
58
- desc: "Disenrolls project from credentials file diffing"
59
-
45
+ desc: "Disenroll project from credentials file diffing"
60
46
  def diff(content_path = nil)
61
47
  if @content_path = content_path
62
- extract_environment_option_from_argument(default_environment: extract_environment_from_path(content_path))
63
- require_application!
48
+ self.environment = extract_environment_from_path(content_path)
49
+ load_environment_config!
64
50
 
65
51
  say credentials.read.presence || credentials.content_path.read
66
52
  else
67
- require_application!
68
53
  disenroll_project_from_credentials_diffing if options[:disenroll]
69
54
  enroll_project_in_credentials_diffing if options[:enroll]
70
55
  end
@@ -73,68 +58,77 @@ module Rails
73
58
  end
74
59
 
75
60
  private
61
+ def config
62
+ Rails.application.config.credentials
63
+ end
64
+
65
+ def content_path
66
+ @content_path ||= relative_path(config.content_path)
67
+ end
68
+
69
+ def key_path
70
+ @key_path ||= relative_path(config.key_path)
71
+ end
72
+
76
73
  def credentials
77
- Rails.application.encrypted(content_path, key_path: key_path)
74
+ @credentials ||= Rails.application.encrypted(content_path, key_path: key_path)
78
75
  end
79
76
 
80
77
  def ensure_encryption_key_has_been_added
78
+ return if credentials.key?
79
+
80
+ require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
81
+
82
+ encryption_key_file_generator = Rails::Generators::EncryptionKeyFileGenerator.new
81
83
  encryption_key_file_generator.add_key_file(key_path)
82
84
  encryption_key_file_generator.ignore_key_file(key_path)
83
85
  end
84
86
 
85
87
  def ensure_credentials_have_been_added
86
- if options[:environment]
87
- encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
88
- else
89
- credentials_generator.add_credentials_file_silently
90
- end
88
+ require "rails/generators/rails/credentials/credentials_generator"
89
+
90
+ Rails::Generators::CredentialsGenerator.new(
91
+ [content_path, key_path],
92
+ skip_secret_key_base: environment_specified? && %w[development test].include?(environment),
93
+ quiet: true
94
+ ).invoke_all
91
95
  end
92
96
 
93
97
  def change_credentials_in_system_editor
94
- credentials.change do |tmp_path|
95
- system(*Shellwords.split(ENV["EDITOR"]), tmp_path.to_s)
98
+ using_system_editor do
99
+ say "Editing #{content_path}..."
100
+ credentials.change { |tmp_path| system_editor(tmp_path) }
101
+ say "File encrypted and saved."
102
+ warn_if_credentials_are_invalid
96
103
  end
104
+ rescue ActiveSupport::EncryptedFile::MissingKeyError => error
105
+ say error.message
106
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
107
+ say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
108
+ end
109
+
110
+ def warn_if_credentials_are_invalid
111
+ credentials.validate!
112
+ rescue ActiveSupport::EncryptedConfiguration::InvalidContentError => error
113
+ say "WARNING: #{error.message}", :red
114
+ say ""
115
+ say "Your application will not be able to load '#{content_path}' until the error has been fixed.", :red
97
116
  end
98
117
 
99
118
  def missing_credentials_message
100
- if credentials.key.nil?
101
- "Missing '#{key_path}' to decrypt credentials. See `bin/rails credentials:help`"
119
+ if !credentials.key?
120
+ "Missing '#{key_path}' to decrypt credentials. See `#{executable(:help)}`."
102
121
  else
103
- "File '#{content_path}' does not exist. Use `bin/rails credentials:edit` to change that."
122
+ "File '#{content_path}' does not exist. Use `#{executable(:edit)}` to change that."
104
123
  end
105
124
  end
106
125
 
107
- def content_path
108
- @content_path ||= options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
109
- end
110
-
111
- def key_path
112
- options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
126
+ def relative_path(path)
127
+ Rails.root.join(path).relative_path_from(Rails.root).to_s
113
128
  end
114
129
 
115
130
  def extract_environment_from_path(path)
116
- available_environments.find { |env| path.include? env } if path.end_with?(".yml.enc")
117
- end
118
-
119
- def encryption_key_file_generator
120
- require "rails/generators"
121
- require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
122
-
123
- Rails::Generators::EncryptionKeyFileGenerator.new
124
- end
125
-
126
- def encrypted_file_generator
127
- require "rails/generators"
128
- require "rails/generators/rails/encrypted_file/encrypted_file_generator"
129
-
130
- Rails::Generators::EncryptedFileGenerator.new
131
- end
132
-
133
- def credentials_generator
134
- require "rails/generators"
135
- require "rails/generators/rails/credentials/credentials_generator"
136
-
137
- Rails::Generators::CredentialsGenerator.new
131
+ available_environments.find { |env| path.end_with?("#{env}.yml.enc") }
138
132
  end
139
133
  end
140
134
  end
@@ -15,7 +15,8 @@ module Rails
15
15
  super
16
16
  end
17
17
 
18
- def perform
18
+ desc "change", "Change `config/database.yml` and your database gem to the target database"
19
+ def perform(*)
19
20
  Rails::Generators::Db::System::ChangeGenerator.start(@argv)
20
21
  end
21
22
  end