railties 8.0.3 → 8.1.0.rc1

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +103 -205
  3. data/lib/minitest/rails_plugin.rb +48 -12
  4. data/lib/rails/application/bootstrap.rb +5 -0
  5. data/lib/rails/application/configuration.rb +31 -9
  6. data/lib/rails/application/default_middleware_stack.rb +1 -1
  7. data/lib/rails/application/finisher.rb +2 -1
  8. data/lib/rails/application/routes_reloader.rb +0 -1
  9. data/lib/rails/application.rb +1 -3
  10. data/lib/rails/code_statistics.rb +4 -1
  11. data/lib/rails/command/base.rb +0 -2
  12. data/lib/rails/command/environment_argument.rb +0 -1
  13. data/lib/rails/command.rb +1 -1
  14. data/lib/rails/commands/app/update_command.rb +1 -0
  15. data/lib/rails/commands/console/irb_console.rb +4 -4
  16. data/lib/rails/commands/credentials/credentials_command.rb +25 -5
  17. data/lib/rails/commands/encrypted/encrypted_command.rb +0 -1
  18. data/lib/rails/engine.rb +0 -1
  19. data/lib/rails/gem_version.rb +3 -3
  20. data/lib/rails/generators/actions.rb +2 -3
  21. data/lib/rails/generators/app_base.rb +49 -28
  22. data/lib/rails/generators/database.rb +1 -1
  23. data/lib/rails/generators/erb/authentication/authentication_generator.rb +2 -0
  24. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +2 -2
  25. data/lib/rails/generators/generated_attribute.rb +1 -1
  26. data/lib/rails/generators/migration.rb +0 -1
  27. data/lib/rails/generators/rails/app/app_generator.rb +16 -5
  28. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +19 -15
  29. data/lib/rails/generators/rails/app/templates/Gemfile.tt +6 -1
  30. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +5 -0
  31. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +1 -0
  32. data/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt +5 -0
  33. data/lib/rails/generators/rails/app/templates/bin/ci.tt +5 -0
  34. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +1 -1
  35. data/lib/rails/generators/rails/app/templates/bin/setup.tt +1 -0
  36. data/lib/rails/generators/rails/app/templates/config/bundler-audit.yml.tt +5 -0
  37. data/lib/rails/generators/rails/app/templates/config/ci.rb.tt +38 -0
  38. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +9 -1
  39. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +10 -2
  40. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
  41. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +9 -1
  42. data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +8 -5
  43. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +8 -0
  44. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +2 -2
  45. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  46. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt +93 -0
  47. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  48. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +0 -7
  49. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +0 -6
  50. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +107 -21
  51. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +2 -2
  52. data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +3 -0
  53. data/lib/rails/generators/rails/app/templates/public/400.html +1 -1
  54. data/lib/rails/generators/rails/app/templates/public/404.html +2 -2
  55. data/lib/rails/generators/rails/app/templates/public/422.html +1 -1
  56. data/lib/rails/generators/rails/app/templates/public/500.html +2 -2
  57. data/lib/rails/generators/rails/authentication/authentication_generator.rb +8 -6
  58. data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +6 -0
  59. data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +2 -2
  60. data/lib/rails/generators/rails/authentication/templates/app/views/passwords_mailer/reset.html.erb.tt +3 -1
  61. data/lib/rails/generators/rails/authentication/templates/app/views/passwords_mailer/reset.text.erb.tt +3 -1
  62. data/lib/rails/generators/rails/benchmark/USAGE +1 -1
  63. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +0 -2
  64. data/lib/rails/generators/rails/devcontainer/devcontainer_generator.rb +1 -1
  65. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +4 -0
  66. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +2 -2
  67. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/devcontainer.json.tt +1 -1
  68. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +17 -5
  69. data/lib/rails/generators/rails/master_key/master_key_generator.rb +0 -12
  70. data/lib/rails/generators/rails/plugin/plugin_generator.rb +1 -0
  71. data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +0 -4
  72. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +20 -9
  73. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +2 -2
  74. data/lib/rails/generators/rails/script/USAGE +1 -1
  75. data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +13 -0
  76. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/passwords_controller_test.rb.tt +67 -0
  77. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/sessions_controller_test.rb +33 -0
  78. data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +4 -3
  79. data/lib/rails/generators/test_unit/authentication/templates/test/test_helpers/session_test_helper.rb.tt +19 -0
  80. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
  81. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +4 -2
  82. data/lib/rails/generators/testing/behavior.rb +0 -3
  83. data/lib/rails/generators.rb +3 -1
  84. data/lib/rails/health_controller.rb +8 -2
  85. data/lib/rails/info.rb +4 -5
  86. data/lib/rails/info_controller.rb +4 -5
  87. data/lib/rails/initializable.rb +63 -19
  88. data/lib/rails/rack/silence_request.rb +5 -2
  89. data/lib/rails/railtie/configurable.rb +0 -1
  90. data/lib/rails/railtie.rb +0 -1
  91. data/lib/rails/tasks/statistics.rake +3 -21
  92. data/lib/rails/tasks.rb +1 -3
  93. data/lib/rails/templates/rails/info/notes.html.erb +23 -0
  94. data/lib/rails/templates/rails/mailers/email.html.erb +2 -1
  95. data/lib/rails/templates/rails/welcome/index.html.erb +19 -3
  96. data/lib/rails/test_unit/reporter.rb +5 -4
  97. data/lib/rails/test_unit/runner.rb +8 -5
  98. data/lib/rails.rb +9 -2
  99. metadata +18 -15
  100. data/lib/rails/console/methods.rb +0 -7
  101. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +0 -30
  102. data/lib/rails/generators/test_unit/plugin/plugin_generator.rb +0 -15
  103. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +0 -7
  104. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +0 -2
@@ -3,8 +3,6 @@
3
3
  require "thor"
4
4
  require "erb"
5
5
 
6
- require "active_support/core_ext/class/attribute"
7
- require "active_support/core_ext/module/delegation"
8
6
  require "active_support/core_ext/string/inflections"
9
7
 
10
8
  require "rails/command/actions"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/core_ext/class/attribute"
5
4
 
6
5
  module Rails
7
6
  module Command
data/lib/rails/command.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
+ require "active_support/rails"
4
5
  require "active_support/core_ext/enumerable"
5
- require "active_support/core_ext/object/blank"
6
6
  require "rails/deprecator"
7
7
 
8
8
  require "thor"
@@ -73,6 +73,7 @@ module Rails
73
73
  skip_action_text: !defined?(ActionText::Engine),
74
74
  skip_action_cable: !defined?(ActionCable::Engine),
75
75
  skip_brakeman: skip_gem?("brakeman"),
76
+ skip_bundler_audit: skip_gem?("bundler-audit"),
76
77
  skip_rubocop: skip_gem?("rubocop"),
77
78
  skip_thruster: skip_gem?("thruster"),
78
79
  skip_test: !defined?(Rails::TestUnitRailtie),
@@ -91,9 +91,9 @@ module Rails
91
91
  IRB.conf[:IRB_NAME] = @app.name if IRB.conf[:IRB_NAME] == "irb"
92
92
 
93
93
  IRB.conf[:PROMPT][:RAILS_PROMPT] = {
94
- PROMPT_I: "#{prompt_prefix}> ",
95
- PROMPT_S: "#{prompt_prefix}%l ",
96
- PROMPT_C: "#{prompt_prefix}* ",
94
+ PROMPT_I: "#{prompt_prefix}:%03n> ",
95
+ PROMPT_S: "#{prompt_prefix}:%03n%l ",
96
+ PROMPT_C: "#{prompt_prefix}:%03n* ",
97
97
  RETURN: "=> %s\n"
98
98
  }
99
99
 
@@ -122,7 +122,7 @@ module Rails
122
122
  when "production"
123
123
  IRB::Color.colorize("prod", [:RED])
124
124
  else
125
- Rails.env
125
+ IRB::Color.colorize(Rails.env, [:MAGENTA])
126
126
  end
127
127
  end
128
128
  end
@@ -35,7 +35,7 @@ module Rails
35
35
  def show
36
36
  load_environment_config!
37
37
 
38
- say credentials.read.presence || missing_credentials_message
38
+ say credentials.read.presence || missing_credentials!
39
39
  end
40
40
 
41
41
  desc "diff", "Enroll/disenroll in decrypted diffs of credentials using git"
@@ -57,6 +57,26 @@ module Rails
57
57
  say credentials.content_path.read
58
58
  end
59
59
 
60
+ desc "fetch PATH", "Fetch a value in the decrypted credentials"
61
+ def fetch(path)
62
+ load_environment_config!
63
+
64
+ if (yaml = credentials.read)
65
+ begin
66
+ value = YAML.load(yaml)
67
+ value = path.split(".").inject(value) do |doc, key|
68
+ doc.fetch(key)
69
+ end
70
+ say value.to_s
71
+ rescue KeyError, NoMethodError
72
+ say_error "Invalid or missing credential path: #{path}"
73
+ exit 1
74
+ end
75
+ else
76
+ missing_credentials!
77
+ end
78
+ end
79
+
60
80
  private
61
81
  def config
62
82
  Rails.application.config.credentials
@@ -81,7 +101,6 @@ module Rails
81
101
 
82
102
  encryption_key_file_generator = Rails::Generators::EncryptionKeyFileGenerator.new
83
103
  encryption_key_file_generator.add_key_file(key_path)
84
- encryption_key_file_generator.ignore_key_file(key_path)
85
104
  end
86
105
 
87
106
  def ensure_credentials_have_been_added
@@ -115,12 +134,13 @@ module Rails
115
134
  say "Your application will not be able to load '#{content_path}' until the error has been fixed.", :red
116
135
  end
117
136
 
118
- def missing_credentials_message
137
+ def missing_credentials!
119
138
  if !credentials.key?
120
- "Missing '#{key_path}' to decrypt credentials. See `#{executable(:help)}`."
139
+ say_error "Missing '#{key_path}' to decrypt credentials. See `#{executable(:help)}`."
121
140
  else
122
- "File '#{content_path}' does not exist. Use `#{executable(:edit)}` to change that."
141
+ say_error "File '#{content_path}' does not exist. Use `#{executable(:edit)}` to change that."
123
142
  end
143
+ exit 1
124
144
  end
125
145
 
126
146
  def relative_path(path)
@@ -45,7 +45,6 @@ module Rails
45
45
  def ensure_encryption_key_has_been_added
46
46
  return if encrypted_configuration.key?
47
47
  encryption_key_file_generator.add_key_file(key_path)
48
- encryption_key_file_generator.ignore_key_file(key_path)
49
48
  end
50
49
 
51
50
  def ensure_encrypted_configuration_has_been_added
data/lib/rails/engine.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require "rails/railtie"
4
4
  require "rails/engine/railties"
5
5
  require "active_support/callbacks"
6
- require "active_support/core_ext/module/delegation"
7
6
  require "active_support/core_ext/object/try"
8
7
  require "pathname"
9
8
 
@@ -8,9 +8,9 @@ module Rails
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 8
11
- MINOR = 0
12
- TINY = 3
13
- PRE = nil
11
+ MINOR = 1
12
+ TINY = 0
13
+ PRE = "rc1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -445,11 +445,10 @@ module Rails
445
445
 
446
446
  private
447
447
  # Define log for backwards compatibility. If just one argument is sent,
448
- # invoke +say+, otherwise invoke +say_status+. Differently from +say+ and
449
- # similarly to +say_status+, this method respects the +quiet?+ option given.
448
+ # invoke +say+, otherwise invoke +say_status+.
450
449
  def log(*args) # :doc:
451
450
  if args.size == 1
452
- say args.first.to_s unless options.quiet?
451
+ say args.first.to_s
453
452
  else
454
453
  args << (behavior == :invoke ? :green : :red)
455
454
  say_status(*args)
@@ -73,7 +73,8 @@ module Rails
73
73
  class_option :skip_action_cable, type: :boolean, aliases: "-C", default: nil,
74
74
  desc: "Skip Action Cable files"
75
75
 
76
- class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: nil
76
+ class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: nil,
77
+ desc: "Skip the asset pipeline setup"
77
78
 
78
79
  class_option :skip_javascript, type: :boolean, aliases: ["-J", "--skip-js"], default: (true if name == "plugin"),
79
80
  desc: "Skip JavaScript files"
@@ -105,6 +106,9 @@ module Rails
105
106
  class_option :skip_brakeman, type: :boolean, default: nil,
106
107
  desc: "Skip brakeman setup"
107
108
 
109
+ class_option :skip_bundler_audit, type: :boolean, default: nil,
110
+ desc: "Skip bundler-audit setup"
111
+
108
112
  class_option :skip_ci, type: :boolean, default: nil,
109
113
  desc: "Skip GitHub CI files"
110
114
 
@@ -399,6 +403,10 @@ module Rails
399
403
  options[:skip_brakeman]
400
404
  end
401
405
 
406
+ def skip_bundler_audit?
407
+ options[:skip_bundler_audit]
408
+ end
409
+
402
410
  def skip_ci?
403
411
  options[:skip_ci]
404
412
  end
@@ -493,7 +501,7 @@ module Rails
493
501
  def javascript_gemfile_entry
494
502
  return if options[:skip_javascript]
495
503
 
496
- if options[:javascript] == "importmap"
504
+ if using_importmap?
497
505
  GemfileEntry.floats "importmap-rails", "Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]"
498
506
  else
499
507
  GemfileEntry.floats "jsbundling-rails", "Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]"
@@ -512,9 +520,12 @@ module Rails
512
520
  [ turbo_rails_entry, stimulus_rails_entry ]
513
521
  end
514
522
 
523
+ def using_importmap?
524
+ !options.skip_javascript? && options[:javascript] == "importmap"
525
+ end
526
+
515
527
  def using_js_runtime?
516
- (options[:javascript] && !%w[importmap].include?(options[:javascript])) ||
517
- (options[:css] && !%w[tailwind sass].include?(options[:css]))
528
+ !options.skip_javascript? && (!using_importmap? || (options[:css] && !%w[tailwind sass].include?(options[:css])))
518
529
  end
519
530
 
520
531
  def using_node?
@@ -525,31 +536,35 @@ module Rails
525
536
  using_js_runtime? && %w[bun].include?(options[:javascript])
526
537
  end
527
538
 
539
+ def capture_command(command, pattern = nil)
540
+ output = `#{command}`
541
+ if pattern
542
+ output[pattern]
543
+ else
544
+ output
545
+ end
546
+ rescue SystemCallError
547
+ nil
548
+ end
549
+
528
550
  def node_version
529
551
  if using_node?
530
552
  ENV.fetch("NODE_VERSION") do
531
- `node --version`[/\d+\.\d+\.\d+/]
532
- rescue
533
- NODE_LTS_VERSION
553
+ capture_command("node --version", /\d+\.\d+\.\d+/) || NODE_LTS_VERSION
534
554
  end
535
555
  end
536
556
  end
537
557
 
538
558
  def dockerfile_yarn_version
539
- using_node? and `yarn --version`[/\d+\.\d+\.\d+/]
540
- rescue
541
- "latest"
559
+ capture_command("yarn --version", /\d+\.\d+\.\d+/) || "latest"
542
560
  end
543
561
 
544
562
  def yarn_through_corepack?
545
- true if dockerfile_yarn_version == "latest"
546
- dockerfile_yarn_version >= "2"
563
+ using_node? and "#{dockerfile_yarn_version}" >= "2"
547
564
  end
548
565
 
549
566
  def dockerfile_bun_version
550
- using_bun? and `bun --version`[/\d+\.\d+\.\d+/]
551
- rescue
552
- BUN_VERSION
567
+ capture_command("bun --version", /\d+\.\d+\.\d+/) || BUN_VERSION
553
568
  end
554
569
 
555
570
  def dockerfile_binfile_fixups
@@ -617,11 +632,16 @@ module Rails
617
632
  end
618
633
 
619
634
  def ci_packages
620
- if depends_on_system_test?
621
- dockerfile_build_packages << "google-chrome-stable"
622
- else
623
- dockerfile_build_packages
624
- end
635
+ dockerfile_build_packages - [
636
+ # GitHub Actions doesn't have build-essential,
637
+ # but it's a meta-packages and all its dependencies are already installed.
638
+ "build-essential",
639
+ "git",
640
+ "pkg-config",
641
+ "libyaml-dev",
642
+ "unzip",
643
+ "python-is-python3",
644
+ ]
625
645
  end
626
646
 
627
647
  def css_gemfile_entry
@@ -644,6 +664,11 @@ module Rails
644
664
  end
645
665
  end
646
666
 
667
+ def rails_command(command, command_options = {})
668
+ command_options[:capture] = true if options[:quiet]
669
+ super
670
+ end
671
+
647
672
  def bundle_install?
648
673
  !(options[:skip_bundle] || options[:pretend])
649
674
  end
@@ -738,12 +763,6 @@ module Rails
738
763
  end
739
764
  end
740
765
 
741
- def generate_bundler_binstub
742
- if bundle_install?
743
- bundle_command("binstubs bundler")
744
- end
745
- end
746
-
747
766
  def jruby?
748
767
  defined?(JRUBY_VERSION)
749
768
  end
@@ -758,11 +777,13 @@ module Rails
758
777
  end
759
778
 
760
779
  def user_default_branch
761
- @user_default_branch ||= `git config init.defaultbranch`
780
+ @user_default_branch ||= capture_command("git config init.defaultbranch").strip.presence || "main"
762
781
  end
763
782
 
764
783
  def git_init_command
765
- return "git init" if user_default_branch.strip.present?
784
+ if capture_command("git config init.defaultbranch").present?
785
+ return "git init"
786
+ end
766
787
 
767
788
  git_version = `git --version`[/\d+\.\d+\.\d+/]
768
789
 
@@ -218,7 +218,7 @@ module Rails
218
218
  end
219
219
 
220
220
  def base_package
221
- nil
221
+ "default-mysql-client"
222
222
  end
223
223
 
224
224
  def build_package
@@ -5,6 +5,8 @@ require "rails/generators/erb"
5
5
  module Erb # :nodoc:
6
6
  module Generators # :nodoc:
7
7
  class AuthenticationGenerator < Rails::Generators::Base # :nodoc:
8
+ hide!
9
+
8
10
  def create_files
9
11
  template "app/views/passwords/new.html.erb"
10
12
  template "app/views/passwords/edit.html.erb"
@@ -1,6 +1,6 @@
1
1
  <div id="<%%= dom_id <%= singular_name %> %>">
2
2
  <% attributes.reject(&:password_digest?).each do |attribute| -%>
3
- <p>
3
+ <div>
4
4
  <strong><%= attribute.human_name %>:</strong>
5
5
  <% if attribute.attachment? -%>
6
6
  <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
@@ -11,7 +11,7 @@
11
11
  <% else -%>
12
12
  <%%= <%= singular_name %>.<%= attribute.column_name %> %>
13
13
  <% end -%>
14
- </p>
14
+ </div>
15
15
 
16
16
  <% end -%>
17
17
  </div>
@@ -73,7 +73,7 @@ module Rails
73
73
  def valid_type?(type)
74
74
  DEFAULT_TYPES.include?(type.to_s) ||
75
75
  !defined?(ActiveRecord::Base) ||
76
- ActiveRecord::Base.lease_connection.valid_type?(type)
76
+ ActiveRecord::Base.connection_db_config.adapter_class.valid_type?(type)
77
77
  end
78
78
 
79
79
  def valid_index_type?(index_type)
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/concern"
4
3
  require "rails/generators/actions/create_migration"
5
4
 
6
5
  module Rails
@@ -109,7 +109,7 @@ module Rails
109
109
  end
110
110
 
111
111
  def bin
112
- exclude_pattern = Regexp.union([(/thrust/ if skip_thruster?), (/rubocop/ if skip_rubocop?), (/brakeman/ if skip_brakeman?)].compact)
112
+ exclude_pattern = Regexp.union([(/thrust/ if skip_thruster?), (/rubocop/ if skip_rubocop?), (/brakeman/ if skip_brakeman?), (/bundler-audit/ if skip_bundler_audit?)].compact)
113
113
  directory "bin", { exclude_pattern: exclude_pattern } do |content|
114
114
  "#{shebang}\n" + content
115
115
  end
@@ -127,7 +127,9 @@ module Rails
127
127
  template "routes.rb" unless options[:update]
128
128
  template "application.rb"
129
129
  template "environment.rb"
130
- template "cable.yml" unless options[:update] || options[:skip_action_cable]
130
+ template "bundler-audit.yml" unless skip_bundler_audit?
131
+ template "cable.yml" unless options[:update] || skip_action_cable?
132
+ template "ci.rb"
131
133
  template "puma.rb"
132
134
  template "storage.yml" unless options[:update] || skip_active_storage?
133
135
 
@@ -140,6 +142,8 @@ module Rails
140
142
  def config_when_updating
141
143
  action_cable_config_exist = File.exist?("config/cable.yml")
142
144
  active_storage_config_exist = File.exist?("config/storage.yml")
145
+ ci_config_exist = File.exist?("config/ci.rb")
146
+ bundle_audit_config_exist = File.exist?("config/bundler-audit.yml")
143
147
  rack_cors_config_exist = File.exist?("config/initializers/cors.rb")
144
148
  assets_config_exist = File.exist?("config/initializers/assets.rb")
145
149
  asset_app_stylesheet_exist = File.exist?("app/assets/stylesheets/application.css")
@@ -149,7 +153,7 @@ module Rails
149
153
 
150
154
  config
151
155
 
152
- if !options[:skip_action_cable] && !action_cable_config_exist
156
+ if !skip_action_cable? && !action_cable_config_exist
153
157
  template "config/cable.yml"
154
158
  end
155
159
 
@@ -157,6 +161,10 @@ module Rails
157
161
  template "config/storage.yml"
158
162
  end
159
163
 
164
+ if !ci_config_exist
165
+ template "config/ci.rb"
166
+ end
167
+
160
168
  if skip_asset_pipeline? && !assets_config_exist
161
169
  remove_file "config/initializers/assets.rb"
162
170
  end
@@ -169,6 +177,10 @@ module Rails
169
177
  remove_file "config/initializers/cors.rb"
170
178
  end
171
179
 
180
+ if !skip_bundler_audit? && !bundle_audit_config_exist
181
+ template "config/bundler-audit.yml"
182
+ end
183
+
172
184
  if options[:api]
173
185
  unless csp_config_exist
174
186
  remove_file "config/initializers/content_security_policy.rb"
@@ -182,7 +194,6 @@ module Rails
182
194
  require "rails/generators/rails/master_key/master_key_generator"
183
195
  master_key_generator = Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet], force: options[:force])
184
196
  master_key_generator.add_master_key_file_silently
185
- master_key_generator.ignore_master_key_file_silently
186
197
  end
187
198
 
188
199
  def credentials
@@ -306,6 +317,7 @@ module Rails
306
317
  :skip_active_storage,
307
318
  :skip_bootsnap,
308
319
  :skip_brakeman,
320
+ :skip_bundler_audit,
309
321
  :skip_ci,
310
322
  :skip_dev_gems,
311
323
  :skip_docker,
@@ -570,7 +582,6 @@ module Rails
570
582
  public_task :apply_rails_template
571
583
  public_task :run_bundle
572
584
  public_task :add_bundler_platforms
573
- public_task :generate_bundler_binstub
574
585
  public_task :run_javascript
575
586
  public_task :run_hotwire
576
587
  public_task :run_css
@@ -17,18 +17,20 @@ WORKDIR /rails
17
17
  # Install base packages
18
18
  RUN apt-get update -qq && \
19
19
  apt-get install --no-install-recommends -y <%= dockerfile_base_packages.join(" ") %> && \
20
+ ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \
20
21
  rm -rf /var/lib/apt/lists /var/cache/apt/archives
21
22
 
22
- # Set production environment
23
+ # Set production environment variables and enable jemalloc for reduced memory usage and latency.
23
24
  ENV RAILS_ENV="production" \
24
25
  BUNDLE_DEPLOYMENT="1" \
25
26
  BUNDLE_PATH="/usr/local/bundle" \
26
- BUNDLE_WITHOUT="development"
27
+ BUNDLE_WITHOUT="development" \
28
+ LD_PRELOAD="/usr/local/lib/libjemalloc.so"
27
29
 
28
30
  # Throw-away build stage to reduce size of final image
29
31
  FROM base AS build
30
32
 
31
- # Install packages needed to build gems<%= using_node? ? " and node modules" : "" %>
33
+ # Install packages needed to build gems<%= (using_node? || using_bun?) ? " and node modules" : "" %>
32
34
  RUN apt-get update -qq && \
33
35
  apt-get install --no-install-recommends -y <%= dockerfile_build_packages.join(" ") %> && \
34
36
  rm -rf /var/lib/apt/lists /var/cache/apt/archives
@@ -59,10 +61,12 @@ RUN curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}"
59
61
 
60
62
  <% end -%>
61
63
  # Install application gems
62
- COPY Gemfile Gemfile.lock ./
64
+ COPY Gemfile Gemfile.lock vendor ./
65
+
63
66
  RUN bundle install && \
64
67
  rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git<% if depend_on_bootsnap? -%> && \
65
- bundle exec bootsnap precompile --gemfile<% end %>
68
+ # -j 1 disable parallel compilation to avoid a QEMU bug: https://github.com/rails/bootsnap/issues/495
69
+ bundle exec bootsnap precompile -j 1 --gemfile<% end %>
66
70
 
67
71
  <% if using_node? -%>
68
72
  # Install node modules
@@ -80,8 +84,9 @@ RUN bun install --frozen-lockfile
80
84
  COPY . .
81
85
 
82
86
  <% if depend_on_bootsnap? -%>
83
- # Precompile bootsnap code for faster boot times
84
- RUN bundle exec bootsnap precompile app/ lib/
87
+ # Precompile bootsnap code for faster boot times.
88
+ # -j 1 disable parallel compilation to avoid a QEMU bug: https://github.com/rails/bootsnap/issues/495
89
+ RUN bundle exec bootsnap precompile -j 1 app/ lib/
85
90
 
86
91
  <% end -%>
87
92
  <% unless dockerfile_binfile_fixups.empty? -%>
@@ -95,23 +100,22 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
95
100
 
96
101
  <% end -%>
97
102
 
98
- <% if using_node? -%>
103
+ <% if using_node? || using_bun? -%>
99
104
  RUN rm -rf node_modules
100
105
  <% end %>
101
106
 
102
107
  # Final stage for app image
103
108
  FROM base
104
109
 
105
- # Copy built artifacts: gems, application
106
- COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
107
- COPY --from=build /rails /rails
108
-
109
110
  # Run and own only the runtime files as a non-root user for security
110
111
  RUN groupadd --system --gid 1000 rails && \
111
- useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
112
- chown -R rails:rails <%= dockerfile_chown_directories.join(" ") %>
112
+ useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash
113
113
  USER 1000:1000
114
114
 
115
+ # Copy built artifacts: gems, application
116
+ COPY --chown=rails:rails --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
117
+ COPY --chown=rails:rails --from=build /rails /rails
118
+
115
119
  # Entrypoint prepares the database.
116
120
  ENTRYPOINT ["/rails/bin/docker-entrypoint"]
117
121
 
@@ -123,4 +127,4 @@ CMD ["./bin/rails", "server"]
123
127
  # Start server via Thruster by default, this can be overwritten at runtime
124
128
  EXPOSE 80
125
129
  CMD ["./bin/thrust", "./bin/rails", "server"]
126
- <% end -%>
130
+ <% end -%>
@@ -42,7 +42,7 @@ gem "thruster", require: false
42
42
  <% unless skip_active_storage? -%>
43
43
 
44
44
  # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
45
- # gem "image_processing", "~> 1.2"
45
+ gem "image_processing", "~> 1.2"
46
46
  <% end -%>
47
47
  <%- if options.api? -%>
48
48
 
@@ -54,6 +54,11 @@ gem "thruster", require: false
54
54
  group :development, :test do
55
55
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
56
56
  gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
57
+ <%- unless options.skip_bundler_audit? -%>
58
+
59
+ # Audits gems for known security defects (use config/bundler-audit.yml to ignore issues)
60
+ gem "bundler-audit", require: false
61
+ <%- end -%>
57
62
  <%- unless options.skip_brakeman? -%>
58
63
 
59
64
  # Static analysis for security vulnerabilities [https://brakemanscanner.org/]
@@ -2,5 +2,10 @@ class ApplicationController < ActionController::<%= options.api? ? "API" : "Base
2
2
  <%- unless options.api? -%>
3
3
  # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
4
4
  allow_browser versions: :modern
5
+ <%- if using_importmap? -%>
6
+
7
+ # Changes to the importmap will invalidate the etag for HTML responses
8
+ stale_when_importmap_changes
9
+ <% end -%>
5
10
  <% end -%>
6
11
  end
@@ -4,6 +4,7 @@
4
4
  <title><%%= content_for(:title) || "<%= app_name.titleize %>" %></title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <meta name="application-name" content="<%= app_name.titleize %>">
7
8
  <meta name="mobile-web-app-capable" content="yes">
8
9
  <%%= csrf_meta_tags %>
9
10
  <%%= csp_meta_tag %>
@@ -0,0 +1,5 @@
1
+ require_relative "../config/boot"
2
+ require "bundler/audit/cli"
3
+
4
+ ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check")
5
+ Bundler::Audit::CLI.start
@@ -0,0 +1,5 @@
1
+ require_relative "../config/boot"
2
+ require "active_support/continuous_integration"
3
+
4
+ CI = ActiveSupport::ContinuousIntegration
5
+ require_relative "../config/ci.rb"
@@ -1,7 +1,7 @@
1
1
  require "rubygems"
2
2
  require "bundler/setup"
3
3
 
4
- # explicit rubocop config increases performance slightly while avoiding config confusion.
4
+ # Explicit RuboCop config increases performance slightly while avoiding config confusion.
5
5
  ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
6
6
 
7
7
  load Gem.bin_path("rubocop", "rubocop")
@@ -31,6 +31,7 @@ FileUtils.chdir APP_ROOT do
31
31
 
32
32
  puts "\n== Preparing database =="
33
33
  system! "bin/rails db:prepare"
34
+ system! "bin/rails db:reset" if ARGV.include?("--reset")
34
35
  <% end -%>
35
36
 
36
37
  puts "\n== Removing old logs and tempfiles =="
@@ -0,0 +1,5 @@
1
+ # Audit all gems listed in the Gemfile for known security problems by running bin/bundler-audit.
2
+ # CVEs that are not relevant to the application can be enumerated on the ignore list below.
3
+
4
+ ignore:
5
+ - CVE-THAT-DOES-NOT-APPLY