railties 8.0.3 → 8.1.0.beta1

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -232
  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 +26 -0
  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/command/base.rb +0 -2
  11. data/lib/rails/command/environment_argument.rb +0 -1
  12. data/lib/rails/command.rb +1 -1
  13. data/lib/rails/commands/console/irb_console.rb +4 -4
  14. data/lib/rails/commands/credentials/credentials_command.rb +25 -5
  15. data/lib/rails/commands/encrypted/encrypted_command.rb +0 -1
  16. data/lib/rails/engine.rb +0 -1
  17. data/lib/rails/gem_version.rb +3 -3
  18. data/lib/rails/generators/actions.rb +2 -3
  19. data/lib/rails/generators/app_base.rb +37 -28
  20. data/lib/rails/generators/database.rb +1 -1
  21. data/lib/rails/generators/erb/authentication/authentication_generator.rb +2 -0
  22. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +2 -2
  23. data/lib/rails/generators/generated_attribute.rb +1 -1
  24. data/lib/rails/generators/migration.rb +0 -1
  25. data/lib/rails/generators/rails/app/app_generator.rb +12 -2
  26. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +14 -12
  27. data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -0
  28. data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +5 -0
  29. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +1 -0
  30. data/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt +5 -0
  31. data/lib/rails/generators/rails/app/templates/bin/ci.tt +5 -0
  32. data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +1 -1
  33. data/lib/rails/generators/rails/app/templates/bin/setup.tt +1 -0
  34. data/lib/rails/generators/rails/app/templates/config/bundler-audit.yml.tt +5 -0
  35. data/lib/rails/generators/rails/app/templates/config/ci.rb.tt +34 -0
  36. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +9 -1
  37. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +10 -2
  38. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
  39. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +9 -1
  40. data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +5 -5
  41. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +5 -0
  42. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +16 -4
  43. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
  44. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt +66 -0
  45. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
  46. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +0 -7
  47. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +0 -6
  48. data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +101 -19
  49. data/lib/rails/generators/rails/app/templates/github/dependabot.yml +2 -2
  50. data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +3 -0
  51. data/lib/rails/generators/rails/app/templates/public/400.html +1 -1
  52. data/lib/rails/generators/rails/app/templates/public/404.html +2 -2
  53. data/lib/rails/generators/rails/app/templates/public/422.html +1 -1
  54. data/lib/rails/generators/rails/app/templates/public/500.html +2 -2
  55. data/lib/rails/generators/rails/authentication/authentication_generator.rb +17 -6
  56. data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +6 -0
  57. data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +2 -2
  58. data/lib/rails/generators/rails/authentication/templates/test/test_helpers/session_test_helper.rb.tt +15 -0
  59. data/lib/rails/generators/rails/benchmark/USAGE +1 -1
  60. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +0 -2
  61. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +4 -0
  62. data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +2 -2
  63. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +17 -5
  64. data/lib/rails/generators/rails/master_key/master_key_generator.rb +0 -12
  65. data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +20 -9
  66. data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +2 -2
  67. data/lib/rails/generators/rails/script/USAGE +1 -1
  68. data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +3 -2
  69. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/passwords_controller_test.rb.tt +67 -0
  70. data/lib/rails/generators/test_unit/authentication/templates/test/controllers/sessions_controller_test.rb +33 -0
  71. data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +4 -3
  72. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
  73. data/lib/rails/generators/testing/behavior.rb +0 -3
  74. data/lib/rails/generators.rb +3 -1
  75. data/lib/rails/health_controller.rb +8 -2
  76. data/lib/rails/info.rb +4 -5
  77. data/lib/rails/info_controller.rb +2 -3
  78. data/lib/rails/initializable.rb +63 -19
  79. data/lib/rails/rack/silence_request.rb +5 -2
  80. data/lib/rails/railtie/configurable.rb +0 -1
  81. data/lib/rails/railtie.rb +0 -1
  82. data/lib/rails/templates/rails/info/notes.html.erb +23 -0
  83. data/lib/rails/templates/rails/mailers/email.html.erb +3 -2
  84. data/lib/rails/templates/rails/welcome/index.html.erb +17 -1
  85. data/lib/rails/test_unit/reporter.rb +5 -4
  86. data/lib/rails/test_unit/runner.rb +8 -5
  87. data/lib/rails.rb +9 -2
  88. metadata +19 -15
  89. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +0 -30
  90. data/lib/rails/generators/test_unit/plugin/plugin_generator.rb +0 -15
  91. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +0 -7
  92. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +0 -2
  93. /data/lib/rails/generators/{test_unit → rails}/authentication/templates/test/mailers/previews/passwords_mailer_preview.rb.tt +0 -0
@@ -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 = "beta1"
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"
@@ -493,7 +494,7 @@ module Rails
493
494
  def javascript_gemfile_entry
494
495
  return if options[:skip_javascript]
495
496
 
496
- if options[:javascript] == "importmap"
497
+ if using_importmap?
497
498
  GemfileEntry.floats "importmap-rails", "Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]"
498
499
  else
499
500
  GemfileEntry.floats "jsbundling-rails", "Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]"
@@ -512,9 +513,12 @@ module Rails
512
513
  [ turbo_rails_entry, stimulus_rails_entry ]
513
514
  end
514
515
 
516
+ def using_importmap?
517
+ !options.skip_javascript? && options[:javascript] == "importmap"
518
+ end
519
+
515
520
  def using_js_runtime?
516
- (options[:javascript] && !%w[importmap].include?(options[:javascript])) ||
517
- (options[:css] && !%w[tailwind sass].include?(options[:css]))
521
+ !options.skip_javascript? && (!using_importmap? || (options[:css] && !%w[tailwind sass].include?(options[:css])))
518
522
  end
519
523
 
520
524
  def using_node?
@@ -525,31 +529,35 @@ module Rails
525
529
  using_js_runtime? && %w[bun].include?(options[:javascript])
526
530
  end
527
531
 
532
+ def capture_command(command, pattern = nil)
533
+ output = `#{command}`
534
+ if pattern
535
+ output[pattern]
536
+ else
537
+ output
538
+ end
539
+ rescue SystemCallError
540
+ nil
541
+ end
542
+
528
543
  def node_version
529
544
  if using_node?
530
545
  ENV.fetch("NODE_VERSION") do
531
- `node --version`[/\d+\.\d+\.\d+/]
532
- rescue
533
- NODE_LTS_VERSION
546
+ capture_command("node --version", /\d+\.\d+\.\d+/) || NODE_LTS_VERSION
534
547
  end
535
548
  end
536
549
  end
537
550
 
538
551
  def dockerfile_yarn_version
539
- using_node? and `yarn --version`[/\d+\.\d+\.\d+/]
540
- rescue
541
- "latest"
552
+ capture_command("yarn --version", /\d+\.\d+\.\d+/) || "latest"
542
553
  end
543
554
 
544
555
  def yarn_through_corepack?
545
- true if dockerfile_yarn_version == "latest"
546
- dockerfile_yarn_version >= "2"
556
+ using_node? and "#{dockerfile_yarn_version}" >= "2"
547
557
  end
548
558
 
549
559
  def dockerfile_bun_version
550
- using_bun? and `bun --version`[/\d+\.\d+\.\d+/]
551
- rescue
552
- BUN_VERSION
560
+ capture_command("bun --version", /\d+\.\d+\.\d+/) || BUN_VERSION
553
561
  end
554
562
 
555
563
  def dockerfile_binfile_fixups
@@ -617,11 +625,16 @@ module Rails
617
625
  end
618
626
 
619
627
  def ci_packages
620
- if depends_on_system_test?
621
- dockerfile_build_packages << "google-chrome-stable"
622
- else
623
- dockerfile_build_packages
624
- end
628
+ dockerfile_build_packages - [
629
+ # GitHub Actions doesn't have build-essential,
630
+ # but it's a meta-packages and all its dependencies are already installed.
631
+ "build-essential",
632
+ "git",
633
+ "pkg-config",
634
+ "libyaml-dev",
635
+ "unzip",
636
+ "python-is-python3",
637
+ ]
625
638
  end
626
639
 
627
640
  def css_gemfile_entry
@@ -738,12 +751,6 @@ module Rails
738
751
  end
739
752
  end
740
753
 
741
- def generate_bundler_binstub
742
- if bundle_install?
743
- bundle_command("binstubs bundler")
744
- end
745
- end
746
-
747
754
  def jruby?
748
755
  defined?(JRUBY_VERSION)
749
756
  end
@@ -758,11 +765,13 @@ module Rails
758
765
  end
759
766
 
760
767
  def user_default_branch
761
- @user_default_branch ||= `git config init.defaultbranch`
768
+ @user_default_branch ||= capture_command("git config init.defaultbranch").strip.presence || "main"
762
769
  end
763
770
 
764
771
  def git_init_command
765
- return "git init" if user_default_branch.strip.present?
772
+ if capture_command("git config init.defaultbranch").present?
773
+ return "git init"
774
+ end
766
775
 
767
776
  git_version = `git --version`[/\d+\.\d+\.\d+/]
768
777
 
@@ -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
@@ -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 "bundler-audit.yml"
130
131
  template "cable.yml" unless options[:update] || options[: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")
@@ -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 !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
@@ -570,7 +581,6 @@ module Rails
570
581
  public_task :apply_rails_template
571
582
  public_task :run_bundle
572
583
  public_task :add_bundler_platforms
573
- public_task :generate_bundler_binstub
574
584
  public_task :run_javascript
575
585
  public_task :run_hotwire
576
586
  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,7 +61,8 @@ 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
68
  bundle exec bootsnap precompile --gemfile<% end %>
@@ -95,23 +98,22 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
95
98
 
96
99
  <% end -%>
97
100
 
98
- <% if using_node? -%>
101
+ <% if using_node? || using_bun? -%>
99
102
  RUN rm -rf node_modules
100
103
  <% end %>
101
104
 
102
105
  # Final stage for app image
103
106
  FROM base
104
107
 
105
- # Copy built artifacts: gems, application
106
- COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
107
- COPY --from=build /rails /rails
108
-
109
108
  # Run and own only the runtime files as a non-root user for security
110
109
  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(" ") %>
110
+ useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash
113
111
  USER 1000:1000
114
112
 
113
+ # Copy built artifacts: gems, application
114
+ COPY --chown=rails:rails --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
115
+ COPY --chown=rails:rails --from=build /rails /rails
116
+
115
117
  # Entrypoint prepares the database.
116
118
  ENTRYPOINT ["/rails/bin/docker-entrypoint"]
117
119
 
@@ -123,4 +125,4 @@ CMD ["./bin/rails", "server"]
123
125
  # Start server via Thruster by default, this can be overwritten at runtime
124
126
  EXPOSE 80
125
127
  CMD ["./bin/thrust", "./bin/rails", "server"]
126
- <% end -%>
128
+ <% end -%>
@@ -54,6 +54,9 @@ 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
+
58
+ # Audits gems for known security defects (use config/bundler-audit.yml to ignore issues)
59
+ gem "bundler-audit", require: false
57
60
  <%- unless options.skip_brakeman? -%>
58
61
 
59
62
  # 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
@@ -0,0 +1,34 @@
1
+ # Run using bin/ci
2
+
3
+ CI.run do
4
+ step "Setup", "bin/setup --skip-server"
5
+ <% unless options.skip_rubocop? %>
6
+ step "Style: Ruby", "bin/rubocop"
7
+ <% end -%>
8
+
9
+ step "Security: Gem audit", "bin/bundler-audit"
10
+ <% if using_node? -%>
11
+ step "Security: Yarn vulnerability audit", "yarn audit"
12
+ <% end -%>
13
+ <% if using_importmap? -%>
14
+ step "Security: Importmap vulnerability audit", "bin/importmap audit"
15
+ <% end -%>
16
+ <% unless options.skip_brakeman? -%>
17
+ step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
18
+ <% end -%>
19
+ <% if options[:api] || options[:skip_system_test] -%>
20
+ step "Tests: Rails", "bin/rails test"
21
+ <% else %>
22
+ step "Tests: Rails", "bin/rails test"
23
+ step "Tests: System", "bin/rails test:system"
24
+ <% end -%>
25
+ step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
26
+
27
+ # Optional: set a green GitHub commit status to unblock PR merge.
28
+ # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
29
+ # if success?
30
+ # step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
31
+ # else
32
+ # failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
33
+ # end
34
+ end
@@ -12,7 +12,7 @@
12
12
  default: &default
13
13
  adapter: mysql2
14
14
  encoding: utf8mb4
15
- pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
15
+ max_connections: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
16
16
  username: root
17
17
  password:
18
18
  <% if database.socket -%>
@@ -49,6 +49,14 @@ test:
49
49
  # production:
50
50
  # url: <%%= ENV["MY_APP_DATABASE_URL"] %>
51
51
  #
52
+ <%- unless options.skip_solid? -%>
53
+ # Connection URLs for non-primary databases can also be configured using
54
+ # environment variables. The variable name is formed by concatenating the
55
+ # connection name with `_DATABASE_URL`. For example:
56
+ #
57
+ # CACHE_DATABASE_URL="mysql2://cacheuser:cachepass@localhost/cachedatabase"
58
+ #
59
+ <%- end -%>
52
60
  # Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
53
61
  # for a full overview on how database connection configuration can be specified.
54
62
  #
@@ -3,7 +3,7 @@
3
3
  # Install the pg driver:
4
4
  # gem install pg
5
5
  # On macOS with Homebrew:
6
- # gem install pg -- --with-pg-config=/usr/local/bin/pg_config
6
+ # gem install pg -- --with-pg-config=/opt/homebrew/bin/pg_config
7
7
  # On Windows:
8
8
  # gem install pg
9
9
  # Choose the win32 build.
@@ -17,7 +17,7 @@ default: &default
17
17
  encoding: unicode
18
18
  # For details on connection pooling, see Rails configuration guide
19
19
  # https://guides.rubyonrails.org/configuring.html#database-pooling
20
- pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
20
+ max_connections: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
21
21
  <% if devcontainer? -%>
22
22
  <%% if ENV["DB_HOST"] %>
23
23
  host: <%%= ENV["DB_HOST"] %>
@@ -81,6 +81,14 @@ test:
81
81
  # production:
82
82
  # url: <%%= ENV["MY_APP_DATABASE_URL"] %>
83
83
  #
84
+ <%- unless options.skip_solid? -%>
85
+ # Connection URLs for non-primary databases can also be configured using
86
+ # environment variables. The variable name is formed by concatenating the
87
+ # connection name with `_DATABASE_URL`. For example:
88
+ #
89
+ # CACHE_DATABASE_URL="postgres://cacheuser:cachepass@localhost/cachedatabase"
90
+ #
91
+ <%- end -%>
84
92
  # Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
85
93
  # for a full overview on how database connection configuration can be specified.
86
94
  #
@@ -6,7 +6,7 @@
6
6
  #
7
7
  default: &default
8
8
  adapter: sqlite3
9
- pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
9
+ max_connections: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10
10
  timeout: 5000
11
11
 
12
12
  development:
@@ -12,7 +12,7 @@
12
12
  default: &default
13
13
  adapter: trilogy
14
14
  encoding: utf8mb4
15
- pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
15
+ max_connections: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
16
16
  username: root
17
17
  password:
18
18
  host: <%%= ENV.fetch("DB_HOST") { "<%= database.host %>" } %>
@@ -51,6 +51,14 @@ test:
51
51
  # production:
52
52
  # url: <%%= ENV["MY_APP_DATABASE_URL"] %>
53
53
  #
54
+ <%- unless options.skip_solid? -%>
55
+ # Connection URLs for non-primary databases can also be configured using
56
+ # environment variables. The variable name is formed by concatenating the
57
+ # connection name with `_DATABASE_URL`. For example:
58
+ #
59
+ # CACHE_DATABASE_URL="trilogy://cacheuser:cachepass@localhost/cachedatabase"
60
+ #
61
+ <%- end -%>
54
62
  # Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
55
63
  # for a full overview on how database connection configuration can be specified.
56
64
  #
@@ -38,7 +38,7 @@ env:
38
38
  <% if skip_solid? -%>
39
39
  # clear:
40
40
  # # Set number of cores available to the application on each server (default: 1).
41
- # WEB_CONCURRENCY: 2
41
+ # WEB_CONCURRENCY: auto
42
42
 
43
43
  # # Match this to any external database server to configure Active Record correctly
44
44
  # DB_HOST: 192.168.0.2
@@ -71,15 +71,15 @@ aliases:
71
71
  console: app exec --interactive --reuse "bin/rails console"
72
72
  shell: app exec --interactive --reuse "bash"
73
73
  logs: app logs -f
74
- dbc: app exec --interactive --reuse "bin/rails dbconsole"
74
+ dbc: app exec --interactive --reuse "bin/rails dbconsole --include-password"
75
75
 
76
- <% unless skip_storage? %>
76
+ <% unless skip_storage? -%>
77
77
  # Use a persistent storage volume for sqlite database files and local Active Storage files.
78
78
  # Recommended to change this to a mounted volume path that is backed up off server.
79
79
  volumes:
80
80
  - "<%= app_name %>_storage:/rails/storage"
81
81
 
82
- <% end %>
82
+ <% end -%>
83
83
  # Bridge fingerprinted assets, like JS and CSS, between versions to avoid
84
84
  # hitting 404 on in-flight requests. Combines all files from new and old
85
85
  # version inside the asset_path.
@@ -121,7 +121,7 @@ builder:
121
121
  # directories:
122
122
  # - data:/var/lib/mysql
123
123
  # redis:
124
- # image: redis:7.0
124
+ # image: valkey/valkey:8
125
125
  # host: 192.168.0.2
126
126
  # port: 6379
127
127
  # directories: