railties 6.0.3.3 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +234 -370
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.rdoc +1 -1
  5. data/lib/minitest/rails_plugin.rb +16 -1
  6. data/lib/rails.rb +5 -8
  7. data/lib/rails/application.rb +49 -83
  8. data/lib/rails/application/bootstrap.rb +5 -5
  9. data/lib/rails/application/configuration.rb +71 -21
  10. data/lib/rails/application/default_middleware_stack.rb +5 -3
  11. data/lib/rails/application/finisher.rb +15 -2
  12. data/lib/rails/application/routes_reloader.rb +9 -2
  13. data/lib/rails/backtrace_cleaner.rb +12 -7
  14. data/lib/rails/code_statistics.rb +3 -3
  15. data/lib/rails/code_statistics_calculator.rb +6 -6
  16. data/lib/rails/command.rb +7 -1
  17. data/lib/rails/command/base.rb +1 -1
  18. data/lib/rails/command/behavior.rb +1 -1
  19. data/lib/rails/command/environment_argument.rb +1 -1
  20. data/lib/rails/commands/credentials/USAGE +17 -2
  21. data/lib/rails/commands/credentials/credentials_command.rb +28 -4
  22. data/lib/rails/commands/credentials/credentials_command/diffing.rb +41 -0
  23. data/lib/rails/commands/db/system/change/change_command.rb +6 -1
  24. data/lib/rails/commands/dbconsole/dbconsole_command.rb +61 -58
  25. data/lib/rails/commands/encrypted/encrypted_command.rb +4 -4
  26. data/lib/rails/commands/generate/generate_command.rb +1 -1
  27. data/lib/rails/commands/notes/notes_command.rb +1 -11
  28. data/lib/rails/commands/rake/rake_command.rb +9 -8
  29. data/lib/rails/commands/secrets/USAGE +9 -3
  30. data/lib/rails/commands/server/server_command.rb +14 -41
  31. data/lib/rails/commands/test/test_command.rb +2 -2
  32. data/lib/rails/configuration.rb +40 -10
  33. data/lib/rails/engine.rb +35 -32
  34. data/lib/rails/engine/configuration.rb +1 -0
  35. data/lib/rails/engine/updater.rb +1 -1
  36. data/lib/rails/gem_version.rb +3 -3
  37. data/lib/rails/generators.rb +29 -15
  38. data/lib/rails/generators/actions.rb +50 -29
  39. data/lib/rails/generators/actions/create_migration.rb +5 -0
  40. data/lib/rails/generators/app_base.rb +38 -21
  41. data/lib/rails/generators/base.rb +14 -11
  42. data/lib/rails/generators/database.rb +3 -4
  43. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +3 -3
  44. data/lib/rails/generators/generated_attribute.rb +3 -9
  45. data/lib/rails/generators/migration.rb +2 -1
  46. data/lib/rails/generators/model_helpers.rb +26 -2
  47. data/lib/rails/generators/named_base.rb +1 -1
  48. data/lib/rails/generators/rails/app/USAGE +2 -1
  49. data/lib/rails/generators/rails/app/app_generator.rb +89 -15
  50. data/lib/rails/generators/rails/app/templates/Gemfile.tt +11 -11
  51. data/lib/rails/generators/rails/app/templates/Rakefile.tt +1 -1
  52. data/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js +1 -1
  53. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +11 -11
  54. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +2 -1
  55. data/lib/rails/generators/rails/app/templates/bin/rails.tt +5 -2
  56. data/lib/rails/generators/rails/app/templates/bin/rake.tt +5 -2
  57. data/lib/rails/generators/rails/app/templates/bin/setup.tt +4 -4
  58. data/lib/rails/generators/rails/app/templates/bin/spring.tt +13 -0
  59. data/lib/rails/generators/rails/app/templates/bin/yarn.tt +9 -3
  60. data/lib/rails/generators/rails/app/templates/config.ru.tt +2 -1
  61. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +14 -7
  62. data/lib/rails/generators/rails/app/templates/config/boot.rb.tt +2 -2
  63. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +3 -4
  64. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +10 -9
  65. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +11 -10
  66. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +10 -9
  67. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +11 -10
  68. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +11 -10
  69. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +10 -9
  70. data/lib/rails/generators/rails/app/templates/config/environment.rb.tt +1 -1
  71. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +17 -3
  72. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +15 -5
  73. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +12 -1
  74. data/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt +4 -3
  75. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +3 -1
  76. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_1.rb.tt +67 -0
  77. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +11 -0
  78. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +6 -1
  79. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +1 -1
  80. data/lib/rails/generators/rails/app/templates/gitattributes.tt +14 -0
  81. data/lib/rails/generators/rails/app/templates/gitignore.tt +0 -1
  82. data/lib/rails/generators/rails/app/templates/package.json.tt +1 -1
  83. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +5 -5
  84. data/lib/rails/generators/rails/assets/USAGE +2 -3
  85. data/lib/rails/generators/rails/benchmark/USAGE +19 -0
  86. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +29 -0
  87. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +15 -0
  88. data/lib/rails/generators/rails/controller/USAGE +2 -2
  89. data/lib/rails/generators/rails/controller/controller_generator.rb +2 -40
  90. data/lib/rails/generators/rails/credentials/credentials_generator.rb +1 -1
  91. data/lib/rails/generators/rails/generator/USAGE +2 -2
  92. data/lib/rails/generators/rails/generator/templates/USAGE.tt +1 -1
  93. data/lib/rails/generators/rails/helper/USAGE +2 -3
  94. data/lib/rails/generators/rails/integration_test/USAGE +2 -2
  95. data/lib/rails/generators/rails/migration/USAGE +4 -4
  96. data/lib/rails/generators/rails/model/USAGE +15 -16
  97. data/lib/rails/generators/rails/plugin/plugin_generator.rb +25 -23
  98. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +10 -19
  99. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +3 -10
  100. data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +4 -18
  101. data/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt +0 -1
  102. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +3 -3
  103. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +13 -11
  104. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt +1 -0
  105. data/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt +1 -1
  106. data/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt +4 -4
  107. data/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt +1 -1
  108. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +0 -3
  109. data/lib/rails/generators/rails/resource/USAGE +4 -4
  110. data/lib/rails/generators/rails/resource_route/resource_route_generator.rb +2 -27
  111. data/lib/rails/generators/rails/scaffold/USAGE +5 -5
  112. data/lib/rails/generators/rails/scaffold_controller/USAGE +2 -2
  113. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +6 -0
  114. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +1 -1
  115. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +1 -1
  116. data/lib/rails/generators/rails/system_test/USAGE +2 -2
  117. data/lib/rails/generators/rails/task/USAGE +3 -3
  118. data/lib/rails/generators/test_case.rb +1 -1
  119. data/lib/rails/generators/test_unit/controller/controller_generator.rb +2 -0
  120. data/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt +3 -3
  121. data/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt +2 -2
  122. data/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt +1 -1
  123. data/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt +1 -1
  124. data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt +1 -1
  125. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
  126. data/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt +1 -1
  127. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +1 -1
  128. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +2 -2
  129. data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +1 -1
  130. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +1 -1
  131. data/lib/rails/generators/testing/assertions.rb +2 -2
  132. data/lib/rails/generators/testing/behaviour.rb +1 -1
  133. data/lib/rails/info.rb +1 -1
  134. data/lib/rails/info_controller.rb +1 -1
  135. data/lib/rails/mailers_controller.rb +1 -0
  136. data/lib/rails/paths.rb +14 -6
  137. data/lib/rails/rack/logger.rb +1 -1
  138. data/lib/rails/railtie.rb +32 -10
  139. data/lib/rails/railtie/configuration.rb +3 -2
  140. data/lib/rails/source_annotation_extractor.rb +1 -15
  141. data/lib/rails/tasks.rb +0 -4
  142. data/lib/rails/tasks/engine.rake +1 -4
  143. data/lib/rails/tasks/framework.rake +7 -1
  144. data/lib/rails/tasks/misc.rake +1 -1
  145. data/lib/rails/tasks/statistics.rake +1 -1
  146. data/lib/rails/tasks/yarn.rake +14 -2
  147. data/lib/rails/templates/rails/mailers/email.html.erb +1 -0
  148. data/lib/rails/templates/rails/welcome/index.html.erb +1 -1
  149. data/lib/rails/test_unit/reporter.rb +2 -1
  150. data/lib/rails/test_unit/runner.rb +20 -3
  151. data/lib/rails/test_unit/testing.rake +6 -0
  152. metadata +27 -33
  153. data/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt +0 -50
  154. data/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt +0 -86
  155. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt +0 -45
  156. data/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt +0 -23
  157. data/lib/rails/tasks/annotations.rake +0 -22
  158. data/lib/rails/tasks/dev.rake +0 -11
  159. data/lib/rails/tasks/initializers.rake +0 -9
  160. data/lib/rails/tasks/routes.rake +0 -9
@@ -13,10 +13,11 @@ module Rails
13
13
 
14
14
  def build_stack
15
15
  ActionDispatch::MiddlewareStack.new do |middleware|
16
- middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app
16
+ middleware.use ::ActionDispatch::HostAuthorization, config.hosts, config.action_dispatch.hosts_response_app, **config.host_authorization
17
17
 
18
18
  if config.force_ssl
19
- middleware.use ::ActionDispatch::SSL, **config.ssl_options
19
+ middleware.use ::ActionDispatch::SSL, **config.ssl_options,
20
+ ssl_default_redirect_status: config.action_dispatch.ssl_default_redirect_status
20
21
  end
21
22
 
22
23
  middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
@@ -43,7 +44,7 @@ module Rails
43
44
 
44
45
  middleware.use ::Rack::Runtime
45
46
  middleware.use ::Rack::MethodOverride unless config.api_only
46
- middleware.use ::ActionDispatch::RequestId
47
+ middleware.use ::ActionDispatch::RequestId, header: config.action_dispatch.request_id_header
47
48
  middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
48
49
 
49
50
  middleware.use ::Rails::Rack::Logger, config.log_tags
@@ -68,6 +69,7 @@ module Rails
68
69
 
69
70
  unless config.api_only
70
71
  middleware.use ::ActionDispatch::ContentSecurityPolicy::Middleware
72
+ middleware.use ::ActionDispatch::PermissionsPolicy::Middleware
71
73
  end
72
74
 
73
75
  middleware.use ::Rack::Head
@@ -60,7 +60,18 @@ module Rails
60
60
 
61
61
  #{unload_message}
62
62
 
63
- Please, check the "Autoloading and Reloading Constants" guide for solutions.
63
+ In order to autoload safely at boot time, please wrap your code in a reloader
64
+ callback this way:
65
+
66
+ Rails.application.reloader.to_prepare do
67
+ # Autoload classes and modules needed at boot time here.
68
+ end
69
+
70
+ That block runs when the application boots, and every time there is a reload.
71
+ For historical reasons, it may run twice, so it has to be idempotent.
72
+
73
+ Check the "Autoloading and Reloading Constants" guide to learn more about how
74
+ Rails autoloads and reloads.
64
75
  WARNING
65
76
  end
66
77
 
@@ -216,7 +227,9 @@ module Rails
216
227
  app.reloader.check = lambda { true }
217
228
  end
218
229
 
219
- if config.reload_classes_only_on_change
230
+ if config.cache_classes
231
+ # No reloader
232
+ elsif config.reload_classes_only_on_change
220
233
  reloader = config.file_watcher.new(*watchable_args, &callback)
221
234
  reloaders << reloader
222
235
 
@@ -5,13 +5,14 @@ require "active_support/core_ext/module/delegation"
5
5
  module Rails
6
6
  class Application
7
7
  class RoutesReloader
8
- attr_reader :route_sets, :paths
8
+ attr_reader :route_sets, :paths, :external_routes
9
9
  attr_accessor :eager_load
10
10
  delegate :execute_if_updated, :execute, :updated?, to: :updater
11
11
 
12
12
  def initialize
13
13
  @paths = []
14
14
  @route_sets = []
15
+ @external_routes = []
15
16
  @eager_load = false
16
17
  end
17
18
 
@@ -26,7 +27,13 @@ module Rails
26
27
 
27
28
  private
28
29
  def updater
29
- @updater ||= ActiveSupport::FileUpdateChecker.new(paths) { reload! }
30
+ @updater ||= begin
31
+ dirs = @external_routes.each_with_object({}) do |dir, hash|
32
+ hash[dir.to_s] = %w(rb)
33
+ end
34
+
35
+ ActiveSupport::FileUpdateChecker.new(paths, dirs) { reload! }
36
+ end
30
37
  end
31
38
 
32
39
  def clear!
@@ -1,21 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/backtrace_cleaner"
4
+ require "active_support/core_ext/string/access"
4
5
 
5
6
  module Rails
6
7
  class BacktraceCleaner < ActiveSupport::BacktraceCleaner
7
- APP_DIRS_PATTERN = /^\/?(app|config|lib|test|\(\w*\))/
8
+ APP_DIRS_PATTERN = /\A(?:\.\/)?(?:app|config|lib|test|\(\w*\))/
8
9
  RENDER_TEMPLATE_PATTERN = /:in `.*_\w+_{2,3}\d+_\d+'/
9
- EMPTY_STRING = ""
10
- SLASH = "/"
11
- DOT_SLASH = "./"
12
10
 
13
11
  def initialize
14
12
  super
15
13
  @root = "#{Rails.root}/"
16
- add_filter { |line| line.sub(@root, EMPTY_STRING) }
17
- add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, EMPTY_STRING) }
18
- add_filter { |line| line.sub(DOT_SLASH, SLASH) } # for tests
14
+ add_filter do |line|
15
+ line.start_with?(@root) ? line.from(@root.size) : line
16
+ end
17
+ add_filter do |line|
18
+ if RENDER_TEMPLATE_PATTERN.match?(line)
19
+ line.sub(RENDER_TEMPLATE_PATTERN, "")
20
+ else
21
+ line
22
+ end
23
+ end
19
24
  add_silencer { |line| !APP_DIRS_PATTERN.match?(line) }
20
25
  end
21
26
  end
@@ -40,13 +40,13 @@ class CodeStatistics #:nodoc:
40
40
  Hash[@pairs.map { |pair| [pair.first, calculate_directory_statistics(pair.last)] }]
41
41
  end
42
42
 
43
- def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|coffee|rake)$/)
43
+ def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|coffee|rake)$/)
44
44
  stats = CodeStatisticsCalculator.new
45
45
 
46
46
  Dir.foreach(directory) do |file_name|
47
47
  path = "#{directory}/#{file_name}"
48
48
 
49
- if File.directory?(path) && (/^\./ !~ file_name)
49
+ if File.directory?(path) && !file_name.start_with?(".")
50
50
  stats.add(calculate_directory_statistics(path, pattern))
51
51
  elsif file_name&.match?(pattern)
52
52
  stats.add_by_file_path(path)
@@ -75,7 +75,7 @@ class CodeStatistics #:nodoc:
75
75
  end
76
76
 
77
77
  def width_for(label)
78
- [@statistics.values.sum { |s| s.send(label) }.to_s.size, HEADERS[label].length].max
78
+ [@statistics.values.sum { |s| s.public_send(label) }.to_s.size, HEADERS[label].length].max
79
79
  end
80
80
 
81
81
  def print_header
@@ -58,20 +58,20 @@ class CodeStatisticsCalculator #:nodoc:
58
58
  @lines += 1
59
59
 
60
60
  if comment_started
61
- if patterns[:end_block_comment] && line =~ patterns[:end_block_comment]
61
+ if patterns[:end_block_comment] && patterns[:end_block_comment].match?(line)
62
62
  comment_started = false
63
63
  end
64
64
  next
65
65
  else
66
- if patterns[:begin_block_comment] && line =~ patterns[:begin_block_comment]
66
+ if patterns[:begin_block_comment] && patterns[:begin_block_comment].match?(line)
67
67
  comment_started = true
68
68
  next
69
69
  end
70
70
  end
71
71
 
72
- @classes += 1 if patterns[:class] && line =~ patterns[:class]
73
- @methods += 1 if patterns[:method] && line =~ patterns[:method]
74
- if line !~ /^\s*$/ && (patterns[:line_comment].nil? || line !~ patterns[:line_comment])
72
+ @classes += 1 if patterns[:class] && patterns[:class].match?(line)
73
+ @methods += 1 if patterns[:method] && patterns[:method].match?(line)
74
+ if !line.match?(/^\s*$/) && (patterns[:line_comment].nil? || !line.match?(patterns[:line_comment]))
75
75
  @code_lines += 1
76
76
  end
77
77
  end
@@ -82,7 +82,7 @@ class CodeStatisticsCalculator #:nodoc:
82
82
  if file_path.end_with? "_test.rb"
83
83
  :minitest
84
84
  else
85
- File.extname(file_path).sub(/\A\./, "").downcase.to_sym
85
+ File.extname(file_path).delete_prefix(".").downcase.to_sym
86
86
  end
87
87
  end
88
88
  end
@@ -1,7 +1,6 @@
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
6
 
@@ -41,12 +40,19 @@ 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
+ # isolate ARGV to ensure that commands depend only on the args they are given
44
+ args = args.dup # args might *be* ARGV so dup before clearing
45
+ old_argv = ARGV.dup
46
+ ARGV.clear
47
+
44
48
  command = find_by_namespace(namespace, command_name)
45
49
  if command && command.all_commands[command_name]
46
50
  command.perform(command_name, args, config)
47
51
  else
48
52
  find_by_namespace("rake").perform(full_namespace, args, config)
49
53
  end
54
+ ensure
55
+ ARGV.replace(old_argv)
50
56
  end
51
57
 
52
58
  # Rails finds namespaces similar to Thor, it only adds one rule:
@@ -56,7 +56,7 @@ module Rails
56
56
  def inherited(base) #:nodoc:
57
57
  super
58
58
 
59
- if base.name && base.name !~ /Base$/
59
+ if base.name && !base.name.end_with?("Base")
60
60
  Rails::Command.subclasses << base
61
61
  end
62
62
  end
@@ -56,7 +56,7 @@ module Rails
56
56
  def lookup!
57
57
  $LOAD_PATH.each do |base|
58
58
  Dir[File.join(base, *file_lookup_paths)].each do |path|
59
- path = path.sub("#{base}/", "")
59
+ path = path.delete_prefix("#{base}/")
60
60
  require path
61
61
  rescue Exception
62
62
  # No problem
@@ -28,7 +28,7 @@ module Rails
28
28
  if available_environments.include? env
29
29
  env
30
30
  else
31
- %w( production development test ).detect { |e| e =~ /^#{env}/ } || env
31
+ %w( production development test ).detect { |e| /^#{env}/.match?(e) } || env
32
32
  end
33
33
  end
34
34
 
@@ -14,7 +14,7 @@ that just contains the secret_key_base used by MessageVerifiers/MessageEncryptor
14
14
  signing and encrypting cookies.
15
15
 
16
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 `rails credentials:edit`.
17
+ credentials file in `config/credentials.yml.enc` the first time you run `bin/rails credentials:edit`.
18
18
  If you didn't have a master key saved in `config/master.key`, that'll be created too.
19
19
 
20
20
  Don't lose this master key! Put it in a password manager your team can access.
@@ -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
@@ -45,7 +60,7 @@ The `credentials` command supports passing an `--environment` option to create a
45
60
  environment specific override. That override will take precedence over the
46
61
  global `config/credentials.yml.enc` file when running in that environment. So:
47
62
 
48
- rails credentials:edit --environment development
63
+ bin/rails credentials:edit --environment development
49
64
 
50
65
  will create `config/credentials/development.yml.enc` with the corresponding
51
66
  encryption key in `config/credentials/development.key` if the credentials file
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pathname"
3
4
  require "active_support"
4
5
  require "rails/command/helpers/editor"
5
6
  require "rails/command/environment_argument"
@@ -10,6 +11,9 @@ module Rails
10
11
  include Helpers::Editor
11
12
  include EnvironmentArgument
12
13
 
14
+ require_relative "credentials_command/diffing"
15
+ include Diffing
16
+
13
17
  self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
14
18
 
15
19
  no_commands do
@@ -28,6 +32,7 @@ module Rails
28
32
 
29
33
  ensure_encryption_key_has_been_added if credentials.key.nil?
30
34
  ensure_credentials_have_been_added
35
+ ensure_rails_credentials_driver_is_set
31
36
 
32
37
  catch_editing_exceptions do
33
38
  change_credentials_in_system_editor
@@ -45,6 +50,23 @@ module Rails
45
50
  say credentials.read.presence || missing_credentials_message
46
51
  end
47
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
68
+ end
69
+
48
70
  private
49
71
  def credentials
50
72
  Rails.application.encrypted(content_path, key_path: key_path)
@@ -71,21 +93,23 @@ module Rails
71
93
 
72
94
  def missing_credentials_message
73
95
  if credentials.key.nil?
74
- "Missing '#{key_path}' to decrypt credentials. See `rails credentials:help`"
96
+ "Missing '#{key_path}' to decrypt credentials. See `bin/rails credentials:help`"
75
97
  else
76
- "File '#{content_path}' does not exist. Use `rails credentials:edit` to change that."
98
+ "File '#{content_path}' does not exist. Use `bin/rails credentials:edit` to change that."
77
99
  end
78
100
  end
79
101
 
80
-
81
102
  def content_path
82
- options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
103
+ @content_path ||= options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
83
104
  end
84
105
 
85
106
  def key_path
86
107
  options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
87
108
  end
88
109
 
110
+ def extract_environment_from_path(path)
111
+ available_environments.find { |env| path.include? env } if path.end_with?(".yml.enc")
112
+ end
89
113
 
90
114
  def encryption_key_file_generator
91
115
  require "rails/generators"
@@ -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
@@ -10,8 +10,13 @@ module Rails
10
10
  class ChangeCommand < Base # :nodoc:
11
11
  class_option :to, desc: "The database system to switch to."
12
12
 
13
+ def initialize(positional_args, option_args, *)
14
+ @argv = positional_args + option_args
15
+ super
16
+ end
17
+
13
18
  def perform
14
- Rails::Generators::Db::System::ChangeGenerator.start
19
+ Rails::Generators::Db::System::ChangeGenerator.start(@argv)
15
20
  end
16
21
  end
17
22
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/deprecation"
4
3
  require "active_support/core_ext/string/filters"
4
+ require "active_support/deprecation"
5
5
  require "rails/command/environment_argument"
6
6
 
7
7
  module Rails
@@ -16,55 +16,56 @@ module Rails
16
16
 
17
17
  def start
18
18
  ENV["RAILS_ENV"] ||= @options[:environment] || environment
19
+ config = db_config.configuration_hash
19
20
 
20
- case config["adapter"]
21
+ case db_config.adapter
21
22
  when /^(jdbc)?mysql/
22
23
  args = {
23
- "host" => "--host",
24
- "port" => "--port",
25
- "socket" => "--socket",
26
- "username" => "--user",
27
- "encoding" => "--default-character-set",
28
- "sslca" => "--ssl-ca",
29
- "sslcert" => "--ssl-cert",
30
- "sslcapath" => "--ssl-capath",
31
- "sslcipher" => "--ssl-cipher",
32
- "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"
33
34
  }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
34
35
 
35
- if config["password"] && @options["include_password"]
36
- args << "--password=#{config['password']}"
37
- 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?
38
39
  args << "-p"
39
40
  end
40
41
 
41
- args << config["database"]
42
+ args << db_config.database
42
43
 
43
44
  find_cmd_and_exec(["mysql", "mysql5"], *args)
44
45
 
45
46
  when /^postgres|^postgis/
46
- ENV["PGUSER"] = config["username"] if config["username"]
47
- ENV["PGHOST"] = config["host"] if config["host"]
48
- ENV["PGPORT"] = config["port"].to_s if config["port"]
49
- ENV["PGPASSWORD"] = config["password"].to_s if config["password"] && @options["include_password"]
50
- 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)
51
52
 
52
53
  when "sqlite3"
53
54
  args = []
54
55
 
55
- args << "-#{@options['mode']}" if @options["mode"]
56
- args << "-header" if @options["header"]
57
- 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)
58
59
 
59
60
  find_cmd_and_exec("sqlite3", *args)
60
61
 
61
62
  when "oracle", "oracle_enhanced"
62
63
  logon = ""
63
64
 
64
- if config["username"]
65
- logon = config["username"].dup
66
- logon << "/#{config['password']}" if config["password"] && @options["include_password"]
67
- 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
68
69
  end
69
70
 
70
71
  find_cmd_and_exec("sqlplus", logon)
@@ -72,36 +73,43 @@ module Rails
72
73
  when "sqlserver"
73
74
  args = []
74
75
 
75
- args += ["-D", "#{config['database']}"] if config["database"]
76
- args += ["-U", "#{config['username']}"] if config["username"]
77
- 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]
78
79
 
79
- if config["host"]
80
- host_arg = +"#{config['host']}"
81
- 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]
82
83
  args += ["-S", host_arg]
83
84
  end
84
85
 
85
- find_cmd_and_exec("sqsh", *args)
86
+ find_cmd_and_exec("sqlcmd", *args)
86
87
 
87
88
  else
88
- abort "Unknown command-line client for #{config['database']}."
89
+ abort "Unknown command-line client for #{db_config.database}."
89
90
  end
90
91
  end
91
92
 
92
93
  def config
93
- @config ||= begin
94
- # We need to check whether the user passed the database the
95
- # first time around to show a consistent error message to people
96
- # relying on 2-level database configuration.
97
- if @options["database"] && configurations[database].blank?
98
- raise ActiveRecord::AdapterNotSpecified, "'#{database}' database is not configured. Available configuration: #{configurations.inspect}"
99
- elsif configurations[environment].blank? && configurations[database].blank?
100
- raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
101
- else
102
- configurations[database] || configurations[environment].presence
103
- 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)
106
+
107
+ unless @db_config
108
+ raise ActiveRecord::AdapterNotSpecified,
109
+ "'#{database}' database is not configured for '#{environment}'. Available configuration: #{configurations.inspect}"
104
110
  end
111
+
112
+ @db_config
105
113
  end
106
114
 
107
115
  def environment
@@ -131,7 +139,12 @@ module Rails
131
139
  found = commands.detect do |cmd|
132
140
  dirs_on_path.detect do |path|
133
141
  full_path_command = File.join(path, cmd)
134
- 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
135
148
  end
136
149
  end
137
150
 
@@ -155,9 +168,6 @@ module Rails
155
168
 
156
169
  class_option :header, type: :boolean
157
170
 
158
- class_option :connection, aliases: "-c", type: :string,
159
- desc: "Specifies the connection to use."
160
-
161
171
  class_option :database, aliases: "--db", type: :string,
162
172
  desc: "Specifies the database to use."
163
173
 
@@ -167,13 +177,6 @@ module Rails
167
177
  # RAILS_ENV needs to be set before config/application is required.
168
178
  ENV["RAILS_ENV"] = options[:environment]
169
179
 
170
- if options["connection"]
171
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
172
- `connection` option is deprecated and will be removed in Rails 6.1. Please use `database` option instead.
173
- MSG
174
- options["database"] = options["connection"]
175
- end
176
-
177
180
  require_application_and_environment!
178
181
  Rails::DBConsole.start(options)
179
182
  end