railties 5.2.7.1 → 6.1.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +276 -158
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.rdoc +39 -33
  5. data/README.rdoc +3 -3
  6. data/lib/minitest/rails_plugin.rb +23 -12
  7. data/lib/rails/all.rb +4 -0
  8. data/lib/rails/api/generator.rb +2 -1
  9. data/lib/rails/api/task.rb +18 -1
  10. data/lib/rails/app_loader.rb +2 -2
  11. data/lib/rails/app_updater.rb +3 -1
  12. data/lib/rails/application/bootstrap.rb +9 -17
  13. data/lib/rails/application/configuration.rb +184 -26
  14. data/lib/rails/application/default_middleware_stack.rb +7 -3
  15. data/lib/rails/application/dummy_erb_compiler.rb +18 -0
  16. data/lib/rails/application/finisher.rb +68 -1
  17. data/lib/rails/application/routes_reloader.rb +9 -3
  18. data/lib/rails/application.rb +86 -65
  19. data/lib/rails/application_controller.rb +0 -1
  20. data/lib/rails/autoloaders.rb +48 -0
  21. data/lib/rails/backtrace_cleaner.rb +14 -21
  22. data/lib/rails/code_statistics.rb +8 -6
  23. data/lib/rails/code_statistics_calculator.rb +6 -6
  24. data/lib/rails/command/base.rb +13 -5
  25. data/lib/rails/command/behavior.rb +8 -49
  26. data/lib/rails/command/environment_argument.rb +9 -16
  27. data/lib/rails/command/spellchecker.rb +57 -0
  28. data/lib/rails/command.rb +16 -11
  29. data/lib/rails/commands/console/console_command.rb +6 -0
  30. data/lib/rails/commands/credentials/USAGE +33 -0
  31. data/lib/rails/commands/credentials/credentials_command/diffing.rb +41 -0
  32. data/lib/rails/commands/credentials/credentials_command.rb +76 -19
  33. data/lib/rails/commands/db/system/change/change_command.rb +25 -0
  34. data/lib/rails/commands/dbconsole/dbconsole_command.rb +66 -51
  35. data/lib/rails/commands/dev/dev_command.rb +19 -0
  36. data/lib/rails/commands/encrypted/USAGE +1 -1
  37. data/lib/rails/commands/encrypted/encrypted_command.rb +4 -4
  38. data/lib/rails/commands/generate/generate_command.rb +1 -1
  39. data/lib/rails/commands/help/help_command.rb +1 -1
  40. data/lib/rails/commands/initializers/initializers_command.rb +23 -0
  41. data/lib/rails/commands/new/new_command.rb +2 -2
  42. data/lib/rails/commands/notes/notes_command.rb +29 -0
  43. data/lib/rails/commands/plugin/plugin_command.rb +1 -1
  44. data/lib/rails/commands/rake/rake_command.rb +9 -8
  45. data/lib/rails/commands/routes/routes_command.rb +37 -0
  46. data/lib/rails/commands/runner/runner_command.rb +13 -9
  47. data/lib/rails/commands/secrets/USAGE +6 -0
  48. data/lib/rails/commands/secrets/secrets_command.rb +3 -3
  49. data/lib/rails/commands/server/server_command.rb +92 -56
  50. data/lib/rails/commands/test/test_command.rb +2 -2
  51. data/lib/rails/configuration.rb +48 -19
  52. data/lib/rails/engine/configuration.rb +6 -2
  53. data/lib/rails/engine/updater.rb +1 -1
  54. data/lib/rails/engine.rb +59 -49
  55. data/lib/rails/gem_version.rb +4 -4
  56. data/lib/rails/generators/actions/create_migration.rb +7 -1
  57. data/lib/rails/generators/actions.rb +89 -56
  58. data/lib/rails/generators/app_base.rb +90 -106
  59. data/lib/rails/generators/app_name.rb +50 -0
  60. data/lib/rails/generators/base.rb +15 -12
  61. data/lib/rails/generators/database.rb +57 -0
  62. data/lib/rails/generators/erb/mailer/mailer_generator.rb +1 -2
  63. data/lib/rails/generators/erb/scaffold/scaffold_generator.rb +0 -1
  64. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +7 -4
  65. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +1 -1
  66. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +9 -1
  67. data/lib/rails/generators/erb.rb +0 -1
  68. data/lib/rails/generators/generated_attribute.rb +50 -31
  69. data/lib/rails/generators/migration.rb +3 -3
  70. data/lib/rails/generators/model_helpers.rb +33 -2
  71. data/lib/rails/generators/named_base.rb +3 -7
  72. data/lib/rails/generators/rails/app/USAGE +2 -1
  73. data/lib/rails/generators/rails/app/app_generator.rb +120 -84
  74. data/lib/rails/generators/rails/app/templates/Gemfile.tt +17 -20
  75. data/lib/rails/generators/rails/app/templates/Rakefile.tt +1 -1
  76. data/lib/rails/generators/rails/app/templates/app/assets/config/manifest.js.tt +0 -3
  77. data/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js +6 -0
  78. data/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js +5 -0
  79. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +23 -0
  80. data/lib/rails/generators/rails/app/templates/app/jobs/application_job.rb.tt +5 -0
  81. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +6 -5
  82. data/lib/rails/generators/rails/app/templates/bin/rails.tt +5 -2
  83. data/lib/rails/generators/rails/app/templates/bin/rake.tt +5 -2
  84. data/lib/rails/generators/rails/app/templates/bin/setup.tt +9 -9
  85. data/lib/rails/generators/rails/app/templates/bin/spring.tt +13 -0
  86. data/lib/rails/generators/rails/app/templates/bin/yarn.tt +9 -3
  87. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +16 -7
  88. data/lib/rails/generators/rails/app/templates/config/boot.rb.tt +2 -2
  89. data/lib/rails/generators/rails/app/templates/config/cable.yml.tt +1 -1
  90. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +4 -5
  91. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +12 -11
  92. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +13 -12
  93. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +1 -1
  94. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +13 -12
  95. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +12 -11
  96. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +16 -15
  97. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
  98. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +11 -10
  99. data/lib/rails/generators/rails/app/templates/config/environment.rb.tt +1 -1
  100. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +22 -5
  101. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +43 -17
  102. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +26 -7
  103. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +1 -1
  104. data/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt +4 -3
  105. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +7 -0
  106. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +3 -1
  107. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_1.rb.tt +67 -0
  108. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -0
  109. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +1 -1
  110. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +10 -4
  111. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +1 -1
  112. data/lib/rails/generators/rails/app/templates/config/spring.rb.tt +6 -6
  113. data/lib/rails/generators/rails/app/templates/config.ru.tt +2 -1
  114. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +1 -1
  115. data/lib/rails/generators/rails/app/templates/gitattributes.tt +14 -0
  116. data/lib/rails/generators/rails/app/templates/gitignore.tt +8 -8
  117. data/lib/rails/generators/rails/app/templates/package.json.tt +8 -2
  118. data/lib/rails/generators/rails/app/templates/public/robots.txt +1 -1
  119. data/lib/rails/generators/rails/app/templates/ruby-version.tt +1 -1
  120. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +11 -0
  121. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +9 -2
  122. data/lib/rails/generators/rails/assets/USAGE +3 -7
  123. data/lib/rails/generators/rails/assets/assets_generator.rb +0 -1
  124. data/lib/rails/generators/rails/benchmark/USAGE +19 -0
  125. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +29 -0
  126. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +15 -0
  127. data/lib/rails/generators/rails/controller/USAGE +2 -2
  128. data/lib/rails/generators/rails/controller/controller_generator.rb +10 -39
  129. data/lib/rails/generators/rails/credentials/credentials_generator.rb +6 -7
  130. data/lib/rails/generators/rails/db/system/change/change_generator.rb +65 -0
  131. data/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb +10 -7
  132. data/lib/rails/generators/rails/generator/USAGE +2 -2
  133. data/lib/rails/generators/rails/generator/generator_generator.rb +0 -1
  134. data/lib/rails/generators/rails/generator/templates/USAGE.tt +1 -1
  135. data/lib/rails/generators/rails/helper/USAGE +2 -3
  136. data/lib/rails/generators/rails/helper/helper_generator.rb +5 -0
  137. data/lib/rails/generators/rails/integration_test/USAGE +2 -2
  138. data/lib/rails/generators/rails/migration/USAGE +4 -4
  139. data/lib/rails/generators/rails/model/USAGE +15 -16
  140. data/lib/rails/generators/rails/plugin/plugin_generator.rb +33 -56
  141. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +10 -19
  142. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +3 -10
  143. data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +4 -18
  144. data/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt +1 -2
  145. data/lib/rails/generators/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt +1 -1
  146. data/lib/rails/generators/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt +1 -1
  147. data/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +1 -1
  148. data/lib/rails/generators/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt +1 -1
  149. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +3 -3
  150. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +14 -11
  151. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt +1 -1
  152. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt +1 -1
  153. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt +1 -0
  154. data/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt +1 -1
  155. data/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt +4 -4
  156. data/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt +1 -1
  157. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +0 -4
  158. data/lib/rails/generators/rails/resource/USAGE +4 -4
  159. data/lib/rails/generators/rails/resource_route/resource_route_generator.rb +2 -27
  160. data/lib/rails/generators/rails/scaffold/USAGE +5 -5
  161. data/lib/rails/generators/rails/scaffold_controller/USAGE +2 -2
  162. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +19 -0
  163. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +2 -2
  164. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +2 -2
  165. data/lib/rails/generators/rails/system_test/USAGE +2 -2
  166. data/lib/rails/generators/rails/task/USAGE +3 -3
  167. data/lib/rails/generators/resource_helpers.rb +1 -6
  168. data/lib/rails/generators/test_case.rb +1 -1
  169. data/lib/rails/generators/test_unit/controller/controller_generator.rb +2 -0
  170. data/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt +3 -3
  171. data/lib/rails/generators/test_unit/generator/generator_generator.rb +0 -1
  172. data/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt +2 -2
  173. data/lib/rails/generators/test_unit/integration/integration_generator.rb +5 -0
  174. data/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt +1 -1
  175. data/lib/rails/generators/test_unit/job/job_generator.rb +5 -0
  176. data/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt +1 -1
  177. data/lib/rails/generators/test_unit/mailer/mailer_generator.rb +1 -1
  178. data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt +1 -1
  179. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +3 -3
  180. data/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt +1 -1
  181. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +1 -1
  182. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +2 -2
  183. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +8 -4
  184. data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +1 -1
  185. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +1 -1
  186. data/lib/rails/generators/test_unit/system/system_generator.rb +5 -0
  187. data/lib/rails/generators/testing/assertions.rb +2 -2
  188. data/lib/rails/generators/testing/behaviour.rb +4 -2
  189. data/lib/rails/generators.rb +38 -24
  190. data/lib/rails/info.rb +4 -4
  191. data/lib/rails/info_controller.rb +2 -3
  192. data/lib/rails/mailers_controller.rb +8 -4
  193. data/lib/rails/paths.rb +26 -10
  194. data/lib/rails/rack/logger.rb +1 -2
  195. data/lib/rails/railtie/configurable.rb +0 -1
  196. data/lib/rails/railtie/configuration.rb +3 -3
  197. data/lib/rails/railtie.rb +33 -13
  198. data/lib/rails/ruby_version_check.rb +3 -3
  199. data/lib/rails/secrets.rb +0 -1
  200. data/lib/rails/source_annotation_extractor.rb +124 -117
  201. data/lib/rails/tasks/engine.rake +1 -4
  202. data/lib/rails/tasks/framework.rake +12 -2
  203. data/lib/rails/tasks/log.rake +0 -1
  204. data/lib/rails/tasks/misc.rake +1 -1
  205. data/lib/rails/tasks/statistics.rake +5 -1
  206. data/lib/rails/tasks/yarn.rake +14 -2
  207. data/lib/rails/tasks/zeitwerk.rake +69 -0
  208. data/lib/rails/tasks.rb +1 -4
  209. data/lib/rails/templates/rails/mailers/email.html.erb +1 -0
  210. data/lib/rails/templates/rails/welcome/index.html.erb +3 -3
  211. data/lib/rails/test_help.rb +11 -9
  212. data/lib/rails/test_unit/reporter.rb +3 -2
  213. data/lib/rails/test_unit/runner.rb +25 -8
  214. data/lib/rails/test_unit/testing.rake +7 -1
  215. data/lib/rails.rb +10 -8
  216. metadata +46 -40
  217. data/lib/rails/generators/js/assets/assets_generator.rb +0 -15
  218. data/lib/rails/generators/js/assets/templates/javascript.js +0 -2
  219. data/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +0 -22
  220. data/lib/rails/generators/rails/app/templates/app/assets/javascripts/cable.js.tt +0 -13
  221. data/lib/rails/generators/rails/app/templates/bin/bundle.tt +0 -2
  222. data/lib/rails/generators/rails/app/templates/bin/update.tt +0 -34
  223. data/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt +0 -50
  224. data/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt +0 -86
  225. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt +0 -38
  226. data/lib/rails/generators/rails/assets/templates/javascript.js +0 -2
  227. data/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt +0 -23
  228. data/lib/rails/tasks/annotations.rake +0 -22
  229. data/lib/rails/tasks/dev.rake +0 -10
  230. data/lib/rails/tasks/initializers.rake +0 -8
  231. data/lib/rails/tasks/routes.rake +0 -31
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Command
5
+ module Spellchecker # :nodoc:
6
+ class << self
7
+ def suggest(word, from:)
8
+ if defined?(DidYouMean::SpellChecker)
9
+ DidYouMean::SpellChecker.new(dictionary: from.map(&:to_s)).correct(word).first
10
+ else
11
+ from.sort_by { |w| levenshtein_distance(word, w) }.first
12
+ end
13
+ end
14
+
15
+ private
16
+ # This code is based directly on the Text gem implementation.
17
+ # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
18
+ #
19
+ # Returns a value representing the "cost" of transforming str1 into str2.
20
+ def levenshtein_distance(str1, str2) # :doc:
21
+ s = str1
22
+ t = str2
23
+ n = s.length
24
+ m = t.length
25
+
26
+ return m if 0 == n
27
+ return n if 0 == m
28
+
29
+ d = (0..m).to_a
30
+ x = nil
31
+
32
+ # avoid duplicating an enumerable object in the loop
33
+ str2_codepoint_enumerable = str2.each_codepoint
34
+
35
+ str1.each_codepoint.with_index do |char1, i|
36
+ e = i + 1
37
+
38
+ str2_codepoint_enumerable.with_index do |char2, j|
39
+ cost = (char1 == char2) ? 0 : 1
40
+ x = [
41
+ d[j + 1] + 1, # insertion
42
+ e + 1, # deletion
43
+ d[j] + cost # substitution
44
+ ].min
45
+ d[j] = e
46
+ e = x
47
+ end
48
+
49
+ d[m] = x
50
+ end
51
+
52
+ x
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
data/lib/rails/command.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/dependencies/autoload"
5
4
  require "active_support/core_ext/enumerable"
6
5
  require "active_support/core_ext/object/blank"
7
- require "active_support/core_ext/hash/transform_values"
8
6
 
9
7
  require "thor"
10
8
 
@@ -12,6 +10,7 @@ module Rails
12
10
  module Command
13
11
  extend ActiveSupport::Autoload
14
12
 
13
+ autoload :Spellchecker
15
14
  autoload :Behavior
16
15
  autoload :Base
17
16
 
@@ -41,12 +40,17 @@ module Rails
41
40
  command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
42
41
  command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)
43
42
 
43
+ original_argv = ARGV.dup
44
+ ARGV.replace(args)
45
+
44
46
  command = find_by_namespace(namespace, command_name)
45
47
  if command && command.all_commands[command_name]
46
48
  command.perform(command_name, args, config)
47
49
  else
48
50
  find_by_namespace("rake").perform(full_namespace, args, config)
49
51
  end
52
+ ensure
53
+ ARGV.replace(original_argv)
50
54
  end
51
55
 
52
56
  # Rails finds namespaces similar to Thor, it only adds one rule:
@@ -83,20 +87,21 @@ module Rails
83
87
  end
84
88
 
85
89
  def print_commands # :nodoc:
86
- sorted_groups.each { |b, n| print_list(b, n) }
90
+ commands.each { |command| puts(" #{command}") }
87
91
  end
88
92
 
89
- def sorted_groups # :nodoc:
90
- lookup!
93
+ private
94
+ COMMANDS_IN_USAGE = %w(generate console server test test:system dbconsole new)
95
+ private_constant :COMMANDS_IN_USAGE
91
96
 
92
- groups = (subclasses - hidden_commands).group_by { |c| c.namespace.split(":").first }
93
- groups.transform_values! { |commands| commands.flat_map(&:printing_commands).sort }
97
+ def commands
98
+ lookup!
94
99
 
95
- rails = groups.delete("rails")
96
- [[ "rails", rails ]] + groups.sort.to_a
97
- end
100
+ visible_commands = (subclasses - hidden_commands).flat_map(&:printing_commands)
101
+
102
+ (visible_commands - COMMANDS_IN_USAGE).sort
103
+ end
98
104
 
99
- private
100
105
  def command_type # :doc:
101
106
  @command_type ||= "command"
102
107
  end
@@ -26,6 +26,12 @@ module Rails
26
26
  @options = options
27
27
 
28
28
  app.sandbox = sandbox?
29
+
30
+ if sandbox? && app.config.disable_sandbox
31
+ puts "Error: Unable to start console in sandbox mode as sandbox mode is disabled (config.disable_sandbox is true)."
32
+ exit 1
33
+ end
34
+
29
35
  app.load_console
30
36
 
31
37
  @console = app.config.console || IRB
@@ -30,6 +30,21 @@ You could prepend that to your server's start command like this:
30
30
 
31
31
  RAILS_MASTER_KEY="very-secret-and-secure" server.start
32
32
 
33
+ === Set up Git to Diff Credentials
34
+
35
+ Rails provides `rails credentials:diff --enroll` to instruct Git to call `rails credentials:diff`
36
+ when `git diff` is run on a credentials file.
37
+
38
+ Running the command enrolls the project such that all credentials files use the
39
+ "rails_credentials" diff driver in .gitattributes.
40
+
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`.
44
+
45
+ Otherwise each co-worker would have to run enable manually, including on each new
46
+ repo clone.
47
+
33
48
  === Editing Credentials
34
49
 
35
50
  This will open a temporary file in `$EDITOR` with the decrypted contents to edit
@@ -38,3 +53,21 @@ the encrypted credentials.
38
53
  When the temporary file is next saved the contents are encrypted and written to
39
54
  `config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
40
55
  from leaking.
56
+
57
+ === Environment Specific Credentials
58
+
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:
62
+
63
+ bin/rails credentials:edit --environment development
64
+
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.
68
+
69
+ The encryption key can also be put in `ENV["RAILS_MASTER_KEY"]`, which takes
70
+ precedence over the file encryption key.
71
+
72
+ In addition to that, the default credentials lookup paths can be overridden through
73
+ `config.credentials.content_path` and `config.credentials.key_path`.
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails::Command::CredentialsCommand::Diffing # :nodoc:
4
+ def enroll_project_in_credentials_diffing
5
+ if enrolled?
6
+ true
7
+ else
8
+ gitattributes.write(<<~end_of_template, mode: "a")
9
+ config/credentials/*.yml.enc diff=rails_credentials
10
+ config/credentials.yml.enc diff=rails_credentials
11
+ end_of_template
12
+
13
+ say "Project successfully enrolled!"
14
+ say "Rails ensures the rails_credentials diff driver is set when running `credentials:edit`. See `credentials:help` for more."
15
+ end
16
+ end
17
+
18
+ def ensure_rails_credentials_driver_is_set
19
+ set_driver if enrolled? && !driver_configured?
20
+ end
21
+
22
+ private
23
+ def enrolled?
24
+ gitattributes.read.match?(/config\/credentials(\/\*)?\.yml\.enc diff=rails_credentials/)
25
+ rescue Errno::ENOENT
26
+ false
27
+ end
28
+
29
+ def driver_configured?
30
+ system "git config --get diff.rails_credentials.textconv", out: File::NULL
31
+ end
32
+
33
+ def set_driver
34
+ puts "running"
35
+ system "git config diff.rails_credentials.textconv 'bin/rails credentials:diff'"
36
+ end
37
+
38
+ def gitattributes
39
+ Rails.root.join(".gitattributes")
40
+ end
41
+ end
@@ -1,12 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pathname"
3
4
  require "active_support"
4
5
  require "rails/command/helpers/editor"
6
+ require "rails/command/environment_argument"
5
7
 
6
8
  module Rails
7
9
  module Command
8
10
  class CredentialsCommand < Rails::Command::Base # :nodoc:
9
11
  include Helpers::Editor
12
+ include EnvironmentArgument
13
+
14
+ require_relative "credentials_command/diffing"
15
+ include Diffing
16
+
17
+ self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
10
18
 
11
19
  no_commands do
12
20
  def help
@@ -17,47 +25,104 @@ module Rails
17
25
  end
18
26
 
19
27
  def edit
28
+ extract_environment_option_from_argument(default_environment: nil)
20
29
  require_application!
21
30
 
22
31
  ensure_editor_available(command: "bin/rails credentials:edit") || (return)
23
- ensure_master_key_has_been_added if Rails.application.credentials.key.nil?
32
+
33
+ ensure_encryption_key_has_been_added if credentials.key.nil?
24
34
  ensure_credentials_have_been_added
35
+ ensure_rails_credentials_driver_is_set
25
36
 
26
37
  catch_editing_exceptions do
27
38
  change_credentials_in_system_editor
28
39
  end
29
40
 
30
- say "New credentials encrypted and saved."
41
+ say "File encrypted and saved."
42
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
43
+ say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
31
44
  end
32
45
 
33
46
  def show
47
+ extract_environment_option_from_argument(default_environment: nil)
34
48
  require_application!
35
49
 
36
- say Rails.application.credentials.read.presence || missing_credentials_message
50
+ say credentials.read.presence || missing_credentials_message
51
+ end
52
+
53
+ option :enroll, type: :boolean, default: false,
54
+ desc: "Enrolls project in credential file diffing with `git diff`"
55
+
56
+ def diff(content_path = nil)
57
+ if @content_path = content_path
58
+ extract_environment_option_from_argument(default_environment: extract_environment_from_path(content_path))
59
+ require_application!
60
+
61
+ say credentials.read.presence || credentials.content_path.read
62
+ else
63
+ require_application!
64
+ enroll_project_in_credentials_diffing if options[:enroll]
65
+ end
66
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
67
+ say credentials.content_path.read
37
68
  end
38
69
 
39
70
  private
40
- def ensure_master_key_has_been_added
41
- master_key_generator.add_master_key_file
42
- master_key_generator.ignore_master_key_file
71
+ def credentials
72
+ Rails.application.encrypted(content_path, key_path: key_path)
73
+ end
74
+
75
+ def ensure_encryption_key_has_been_added
76
+ encryption_key_file_generator.add_key_file(key_path)
77
+ encryption_key_file_generator.ignore_key_file(key_path)
43
78
  end
44
79
 
45
80
  def ensure_credentials_have_been_added
46
- credentials_generator.add_credentials_file_silently
81
+ if options[:environment]
82
+ encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
83
+ else
84
+ credentials_generator.add_credentials_file_silently
85
+ end
47
86
  end
48
87
 
49
88
  def change_credentials_in_system_editor
50
- Rails.application.credentials.change do |tmp_path|
89
+ credentials.change do |tmp_path|
51
90
  system("#{ENV["EDITOR"]} #{tmp_path}")
52
91
  end
53
92
  end
54
93
 
94
+ def missing_credentials_message
95
+ if credentials.key.nil?
96
+ "Missing '#{key_path}' to decrypt credentials. See `bin/rails credentials:help`"
97
+ else
98
+ "File '#{content_path}' does not exist. Use `bin/rails credentials:edit` to change that."
99
+ end
100
+ end
55
101
 
56
- def master_key_generator
102
+ def content_path
103
+ @content_path ||= options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
104
+ end
105
+
106
+ def key_path
107
+ options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
108
+ end
109
+
110
+ def extract_environment_from_path(path)
111
+ available_environments.find { |env| path.include? env } if path.end_with?(".yml.enc")
112
+ end
113
+
114
+ def encryption_key_file_generator
57
115
  require "rails/generators"
58
- require "rails/generators/rails/master_key/master_key_generator"
116
+ require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
59
117
 
60
- Rails::Generators::MasterKeyGenerator.new
118
+ Rails::Generators::EncryptionKeyFileGenerator.new
119
+ end
120
+
121
+ def encrypted_file_generator
122
+ require "rails/generators"
123
+ require "rails/generators/rails/encrypted_file/encrypted_file_generator"
124
+
125
+ Rails::Generators::EncryptedFileGenerator.new
61
126
  end
62
127
 
63
128
  def credentials_generator
@@ -66,14 +131,6 @@ module Rails
66
131
 
67
132
  Rails::Generators::CredentialsGenerator.new
68
133
  end
69
-
70
- def missing_credentials_message
71
- if Rails.application.credentials.key.nil?
72
- "Missing master key to decrypt credentials. See bin/rails credentials:help"
73
- else
74
- "No credentials have been added yet. Use bin/rails credentials:edit to change that."
75
- end
76
- end
77
134
  end
78
135
  end
79
136
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/rails/db/system/change/change_generator"
5
+
6
+ module Rails
7
+ module Command
8
+ module Db
9
+ module System
10
+ class ChangeCommand < Base # :nodoc:
11
+ class_option :to, desc: "The database system to switch to."
12
+
13
+ def initialize(positional_args, option_args, *)
14
+ @argv = positional_args + option_args
15
+ super
16
+ end
17
+
18
+ def perform
19
+ Rails::Generators::Db::System::ChangeGenerator.start(@argv)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/filters"
4
+ require "active_support/deprecation"
3
5
  require "rails/command/environment_argument"
4
6
 
5
7
  module Rails
@@ -14,55 +16,56 @@ module Rails
14
16
 
15
17
  def start
16
18
  ENV["RAILS_ENV"] ||= @options[:environment] || environment
19
+ config = db_config.configuration_hash
17
20
 
18
- case config["adapter"]
21
+ case db_config.adapter
19
22
  when /^(jdbc)?mysql/
20
23
  args = {
21
- "host" => "--host",
22
- "port" => "--port",
23
- "socket" => "--socket",
24
- "username" => "--user",
25
- "encoding" => "--default-character-set",
26
- "sslca" => "--ssl-ca",
27
- "sslcert" => "--ssl-cert",
28
- "sslcapath" => "--ssl-capath",
29
- "sslcipher" => "--ssl-cipher",
30
- "sslkey" => "--ssl-key"
24
+ host: "--host",
25
+ port: "--port",
26
+ socket: "--socket",
27
+ username: "--user",
28
+ encoding: "--default-character-set",
29
+ sslca: "--ssl-ca",
30
+ sslcert: "--ssl-cert",
31
+ sslcapath: "--ssl-capath",
32
+ sslcipher: "--ssl-cipher",
33
+ sslkey: "--ssl-key"
31
34
  }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
32
35
 
33
- if config["password"] && @options["include_password"]
34
- args << "--password=#{config['password']}"
35
- elsif config["password"] && !config["password"].to_s.empty?
36
+ if config[:password] && @options[:include_password]
37
+ args << "--password=#{config[:password]}"
38
+ elsif config[:password] && !config[:password].to_s.empty?
36
39
  args << "-p"
37
40
  end
38
41
 
39
- args << config["database"]
42
+ args << db_config.database
40
43
 
41
44
  find_cmd_and_exec(["mysql", "mysql5"], *args)
42
45
 
43
46
  when /^postgres|^postgis/
44
- ENV["PGUSER"] = config["username"] if config["username"]
45
- ENV["PGHOST"] = config["host"] if config["host"]
46
- ENV["PGPORT"] = config["port"].to_s if config["port"]
47
- ENV["PGPASSWORD"] = config["password"].to_s if config["password"] && @options["include_password"]
48
- find_cmd_and_exec("psql", config["database"])
47
+ ENV["PGUSER"] = config[:username] if config[:username]
48
+ ENV["PGHOST"] = config[:host] if config[:host]
49
+ ENV["PGPORT"] = config[:port].to_s if config[:port]
50
+ ENV["PGPASSWORD"] = config[:password].to_s if config[:password] && @options[:include_password]
51
+ find_cmd_and_exec("psql", db_config.database)
49
52
 
50
53
  when "sqlite3"
51
54
  args = []
52
55
 
53
- args << "-#{@options['mode']}" if @options["mode"]
54
- args << "-header" if @options["header"]
55
- args << File.expand_path(config["database"], Rails.respond_to?(:root) ? Rails.root : nil)
56
+ args << "-#{@options[:mode]}" if @options[:mode]
57
+ args << "-header" if @options[:header]
58
+ args << File.expand_path(db_config.database, Rails.respond_to?(:root) ? Rails.root : nil)
56
59
 
57
60
  find_cmd_and_exec("sqlite3", *args)
58
61
 
59
62
  when "oracle", "oracle_enhanced"
60
63
  logon = ""
61
64
 
62
- if config["username"]
63
- logon = config["username"].dup
64
- logon << "/#{config['password']}" if config["password"] && @options["include_password"]
65
- logon << "@#{config['database']}" if config["database"]
65
+ if config[:username]
66
+ logon = config[:username].dup
67
+ logon << "/#{config[:password]}" if config[:password] && @options[:include_password]
68
+ logon << "@#{db_config.database}" if db_config.database
66
69
  end
67
70
 
68
71
  find_cmd_and_exec("sqlplus", logon)
@@ -70,44 +73,51 @@ module Rails
70
73
  when "sqlserver"
71
74
  args = []
72
75
 
73
- args += ["-D", "#{config['database']}"] if config["database"]
74
- args += ["-U", "#{config['username']}"] if config["username"]
75
- args += ["-P", "#{config['password']}"] if config["password"]
76
+ args += ["-d", "#{db_config.database}"] if db_config.database
77
+ args += ["-U", "#{config[:username]}"] if config[:username]
78
+ args += ["-P", "#{config[:password]}"] if config[:password]
76
79
 
77
- if config["host"]
78
- host_arg = "#{config['host']}".dup
79
- host_arg << ":#{config['port']}" if config["port"]
80
+ if config[:host]
81
+ host_arg = +"tcp:#{config[:host]}"
82
+ host_arg << ",#{config[:port]}" if config[:port]
80
83
  args += ["-S", host_arg]
81
84
  end
82
85
 
83
- find_cmd_and_exec("sqsh", *args)
86
+ find_cmd_and_exec("sqlcmd", *args)
84
87
 
85
88
  else
86
- abort "Unknown command-line client for #{config['database']}."
89
+ abort "Unknown command-line client for #{db_config.database}."
87
90
  end
88
91
  end
89
92
 
90
93
  def config
91
- @config ||= begin
92
- # We need to check whether the user passed the connection the
93
- # first time around to show a consistent error message to people
94
- # relying on 2-level database configuration.
95
- if @options["connection"] && configurations[connection].blank?
96
- raise ActiveRecord::AdapterNotSpecified, "'#{connection}' connection is not configured. Available configuration: #{configurations.inspect}"
97
- elsif configurations[environment].blank? && configurations[connection].blank?
98
- raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
99
- else
100
- configurations[environment].presence || configurations[connection]
101
- end
94
+ db_config.configuration_hash
95
+ end
96
+ deprecate config: "please use db_config.configuration_hash"
97
+
98
+ def db_config
99
+ return @db_config if defined?(@db_config)
100
+
101
+ # We need to check whether the user passed the database the
102
+ # first time around to show a consistent error message to people
103
+ # relying on 2-level database configuration.
104
+
105
+ @db_config = configurations.configs_for(env_name: environment, name: database, include_replicas: true)
106
+
107
+ unless @db_config
108
+ raise ActiveRecord::AdapterNotSpecified,
109
+ "'#{database}' database is not configured for '#{environment}'. Available configuration: #{configurations.inspect}"
102
110
  end
111
+
112
+ @db_config
103
113
  end
104
114
 
105
115
  def environment
106
116
  Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
107
117
  end
108
118
 
109
- def connection
110
- @options.fetch(:connection, "primary")
119
+ def database
120
+ @options.fetch(:database, "primary")
111
121
  end
112
122
 
113
123
  private
@@ -129,7 +139,12 @@ module Rails
129
139
  found = commands.detect do |cmd|
130
140
  dirs_on_path.detect do |path|
131
141
  full_path_command = File.join(path, cmd)
132
- File.file?(full_path_command) && File.executable?(full_path_command)
142
+ begin
143
+ stat = File.stat(full_path_command)
144
+ rescue SystemCallError
145
+ else
146
+ stat.file? && stat.executable?
147
+ end
133
148
  end
134
149
  end
135
150
 
@@ -153,8 +168,8 @@ module Rails
153
168
 
154
169
  class_option :header, type: :boolean
155
170
 
156
- class_option :connection, aliases: "-c", type: :string,
157
- desc: "Specifies the connection to use."
171
+ class_option :database, aliases: "--db", type: :string,
172
+ desc: "Specifies the database to use."
158
173
 
159
174
  def perform
160
175
  extract_environment_option_from_argument
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/dev_caching"
4
+
5
+ module Rails
6
+ module Command
7
+ class DevCommand < Base # :nodoc:
8
+ no_commands do
9
+ def help
10
+ say "rails dev:cache # Toggle development mode caching on/off."
11
+ end
12
+ end
13
+
14
+ def cache
15
+ Rails::DevCaching.enable_by_file
16
+ end
17
+ end
18
+ end
19
+ end
@@ -6,7 +6,7 @@ See the `Rails.application.encrypted` documentation for using them in your app.
6
6
  === Encryption Keys
7
7
 
8
8
  By default, Rails looks for the encryption key in `config/master.key` or
9
- `ENV["RAILS_MASTER_KEY"]`, but that lookup can be overriden with `--key`:
9
+ `ENV["RAILS_MASTER_KEY"]`, but that lookup can be overridden with `--key`:
10
10
 
11
11
  rails encrypted:edit config/encrypted_file.yml.enc --key config/encrypted_file.key
12
12
 
@@ -21,7 +21,7 @@ module Rails
21
21
  end
22
22
 
23
23
  def edit(file_path)
24
- require_application_and_environment!
24
+ require_application!
25
25
  encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
26
26
 
27
27
  ensure_editor_available(command: "bin/rails encrypted:edit") || (return)
@@ -38,7 +38,7 @@ module Rails
38
38
  end
39
39
 
40
40
  def show(file_path)
41
- require_application_and_environment!
41
+ require_application!
42
42
  encrypted = Rails.application.encrypted(file_path, key_path: options[:key])
43
43
 
44
44
  say encrypted.read.presence || missing_encrypted_message(key: encrypted.key, key_path: options[:key], file_path: file_path)
@@ -77,9 +77,9 @@ module Rails
77
77
 
78
78
  def missing_encrypted_message(key:, key_path:, file_path:)
79
79
  if key.nil?
80
- "Missing '#{key_path}' to decrypt data. See bin/rails encrypted:help"
80
+ "Missing '#{key_path}' to decrypt data. See `bin/rails encrypted:help`"
81
81
  else
82
- "File '#{file_path}' does not exist. Use bin/rails encrypted:edit #{file_path} to change that."
82
+ "File '#{file_path}' does not exist. Use `bin/rails encrypted:edit #{file_path}` to change that."
83
83
  end
84
84
  end
85
85
  end
@@ -21,7 +21,7 @@ module Rails
21
21
  require_application_and_environment!
22
22
  load_generators
23
23
 
24
- ARGV.shift
24
+ ARGV.replace(args) # set up ARGV for third-party libraries
25
25
 
26
26
  Rails::Generators.invoke generator, args, behavior: :invoke, destination_root: Rails::Command.root
27
27
  end
@@ -6,7 +6,7 @@ module Rails
6
6
  hide_command!
7
7
 
8
8
  def help(*)
9
- puts self.class.desc
9
+ say self.class.desc
10
10
 
11
11
  Rails::Command.print_commands
12
12
  end