railties 6.0.0 → 7.1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +699 -245
  3. data/MIT-LICENSE +1 -1
  4. data/RDOC_MAIN.md +99 -0
  5. data/README.rdoc +7 -8
  6. data/lib/minitest/rails_plugin.rb +80 -2
  7. data/lib/rails/all.rb +0 -1
  8. data/lib/rails/api/task.rb +37 -5
  9. data/lib/rails/app_updater.rb +9 -6
  10. data/lib/rails/application/bootstrap.rb +52 -14
  11. data/lib/rails/application/configuration.rb +327 -86
  12. data/lib/rails/application/default_middleware_stack.rb +33 -7
  13. data/lib/rails/application/dummy_config.rb +19 -0
  14. data/lib/rails/application/finisher.rb +94 -113
  15. data/lib/rails/application/routes_reloader.rb +17 -3
  16. data/lib/rails/application.rb +258 -201
  17. data/lib/rails/application_controller.rb +3 -4
  18. data/lib/rails/autoloaders/inflector.rb +21 -0
  19. data/lib/rails/autoloaders.rb +42 -42
  20. data/lib/rails/backtrace_cleaner.rb +13 -8
  21. data/lib/rails/cli.rb +5 -2
  22. data/lib/rails/code_statistics.rb +6 -4
  23. data/lib/rails/code_statistics_calculator.rb +16 -7
  24. data/lib/rails/command/actions.rb +10 -12
  25. data/lib/rails/command/base.rb +66 -46
  26. data/lib/rails/command/behavior.rb +3 -3
  27. data/lib/rails/command/environment_argument.rb +33 -17
  28. data/lib/rails/command/helpers/editor.rb +17 -12
  29. data/lib/rails/command.rb +90 -32
  30. data/lib/rails/commands/about/about_command.rb +14 -0
  31. data/lib/rails/commands/application/application_command.rb +2 -0
  32. data/lib/rails/commands/console/console_command.rb +17 -13
  33. data/lib/rails/commands/credentials/USAGE +54 -39
  34. data/lib/rails/commands/credentials/credentials_command/diffing.rb +53 -0
  35. data/lib/rails/commands/credentials/credentials_command.rb +83 -60
  36. data/lib/rails/commands/db/system/change/change_command.rb +8 -2
  37. data/lib/rails/commands/dbconsole/dbconsole_command.rb +39 -124
  38. data/lib/rails/commands/destroy/destroy_command.rb +3 -2
  39. data/lib/rails/commands/dev/dev_command.rb +1 -6
  40. data/lib/rails/commands/encrypted/USAGE +15 -20
  41. data/lib/rails/commands/encrypted/encrypted_command.rb +46 -35
  42. data/lib/rails/commands/gem_help/USAGE +16 -0
  43. data/lib/rails/commands/gem_help/gem_help_command.rb +13 -0
  44. data/lib/rails/commands/generate/generate_command.rb +3 -3
  45. data/lib/rails/commands/help/USAGE +15 -14
  46. data/lib/rails/commands/help/help_command.rb +21 -2
  47. data/lib/rails/commands/initializers/initializers_command.rb +1 -4
  48. data/lib/rails/commands/middleware/middleware_command.rb +17 -0
  49. data/lib/rails/commands/new/new_command.rb +2 -0
  50. data/lib/rails/commands/notes/notes_command.rb +5 -14
  51. data/lib/rails/commands/plugin/plugin_command.rb +2 -0
  52. data/lib/rails/commands/rake/rake_command.rb +27 -23
  53. data/lib/rails/commands/restart/restart_command.rb +14 -0
  54. data/lib/rails/commands/routes/routes_command.rb +13 -1
  55. data/lib/rails/commands/runner/USAGE +14 -12
  56. data/lib/rails/commands/runner/runner_command.rb +34 -21
  57. data/lib/rails/commands/secret/secret_command.rb +13 -0
  58. data/lib/rails/commands/secrets/USAGE +44 -43
  59. data/lib/rails/commands/secrets/secrets_command.rb +20 -38
  60. data/lib/rails/commands/server/server_command.rb +40 -64
  61. data/lib/rails/commands/test/USAGE +14 -0
  62. data/lib/rails/commands/test/test_command.rb +58 -16
  63. data/lib/rails/commands/unused_routes/unused_routes_command.rb +75 -0
  64. data/lib/rails/commands/version/version_command.rb +1 -0
  65. data/lib/rails/configuration.rb +53 -23
  66. data/lib/rails/console/app.rb +1 -4
  67. data/lib/rails/console/helpers.rb +2 -2
  68. data/lib/rails/deprecator.rb +7 -0
  69. data/lib/rails/engine/configuration.rb +54 -9
  70. data/lib/rails/engine/updater.rb +1 -1
  71. data/lib/rails/engine.rb +105 -81
  72. data/lib/rails/gem_version.rb +5 -5
  73. data/lib/rails/generators/actions/create_migration.rb +9 -5
  74. data/lib/rails/generators/actions.rb +275 -97
  75. data/lib/rails/generators/active_model.rb +28 -14
  76. data/lib/rails/generators/app_base.rb +487 -165
  77. data/lib/rails/generators/app_name.rb +3 -14
  78. data/lib/rails/generators/base.rb +42 -31
  79. data/lib/rails/generators/database.rb +41 -4
  80. data/lib/rails/generators/erb/mailer/mailer_generator.rb +0 -1
  81. data/lib/rails/generators/erb/mailer/templates/layout.html.erb.tt +1 -1
  82. data/lib/rails/generators/erb/scaffold/scaffold_generator.rb +2 -1
  83. data/lib/rails/generators/erb/scaffold/templates/_form.html.erb.tt +11 -11
  84. data/lib/rails/generators/erb/scaffold/templates/edit.html.erb.tt +8 -4
  85. data/lib/rails/generators/erb/scaffold/templates/index.html.erb.tt +11 -28
  86. data/lib/rails/generators/erb/scaffold/templates/new.html.erb.tt +7 -3
  87. data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +17 -0
  88. data/lib/rails/generators/erb/scaffold/templates/show.html.erb.tt +8 -17
  89. data/lib/rails/generators/erb.rb +1 -2
  90. data/lib/rails/generators/generated_attribute.rb +60 -19
  91. data/lib/rails/generators/migration.rb +4 -8
  92. data/lib/rails/generators/model_helpers.rb +29 -4
  93. data/lib/rails/generators/named_base.rb +15 -15
  94. data/lib/rails/generators/rails/app/USAGE +23 -6
  95. data/lib/rails/generators/rails/app/app_generator.rb +151 -80
  96. data/lib/rails/generators/rails/app/templates/Dockerfile.tt +103 -0
  97. data/lib/rails/generators/rails/app/templates/Gemfile.tt +44 -54
  98. data/lib/rails/generators/rails/app/templates/Rakefile.tt +1 -1
  99. data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css.tt +2 -2
  100. data/lib/rails/generators/rails/app/templates/app/mailers/application_mailer.rb.tt +2 -2
  101. data/lib/rails/generators/rails/app/templates/app/models/application_record.rb.tt +1 -1
  102. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +4 -9
  103. data/lib/rails/generators/rails/app/templates/app/views/layouts/mailer.html.erb.tt +1 -1
  104. data/lib/rails/generators/rails/app/templates/bin/rails.tt +3 -3
  105. data/lib/rails/generators/rails/app/templates/bin/rake.tt +2 -2
  106. data/lib/rails/generators/rails/app/templates/bin/setup.tt +19 -15
  107. data/lib/rails/generators/rails/app/templates/config/application.rb.tt +18 -23
  108. data/lib/rails/generators/rails/app/templates/config/boot.rb.tt +3 -3
  109. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml.tt +5 -6
  110. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml.tt +12 -11
  111. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml.tt +17 -16
  112. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml.tt +5 -5
  113. data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +12 -11
  114. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml.tt +12 -11
  115. data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +17 -18
  116. data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +4 -4
  117. data/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml.tt +14 -13
  118. data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +59 -0
  119. data/lib/rails/generators/rails/app/templates/config/environment.rb.tt +1 -1
  120. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +32 -14
  121. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +42 -55
  122. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +27 -15
  123. data/lib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt +3 -5
  124. data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +21 -28
  125. data/lib/rails/generators/rails/app/templates/config/initializers/cors.rb.tt +3 -3
  126. data/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt +6 -2
  127. data/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb.tt +4 -4
  128. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_7_1.rb.tt +284 -0
  129. data/lib/rails/generators/rails/app/templates/config/initializers/permissions_policy.rb.tt +13 -0
  130. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +13 -15
  131. data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +17 -20
  132. data/lib/rails/generators/rails/app/templates/config/routes.rb.tt +8 -1
  133. data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +5 -5
  134. data/lib/rails/generators/rails/app/templates/config.ru.tt +2 -1
  135. data/lib/rails/generators/rails/app/templates/db/seeds.rb.tt +7 -5
  136. data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +10 -0
  137. data/lib/rails/generators/rails/app/templates/dockerignore.tt +43 -0
  138. data/lib/rails/generators/rails/app/templates/gitattributes.tt +9 -0
  139. data/lib/rails/generators/rails/app/templates/gitignore.tt +12 -9
  140. data/lib/rails/generators/rails/app/templates/node-version.tt +1 -0
  141. data/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +10 -8
  142. data/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt +13 -11
  143. data/lib/rails/generators/rails/application_record/application_record_generator.rb +4 -0
  144. data/lib/rails/generators/rails/benchmark/USAGE +19 -0
  145. data/lib/rails/generators/rails/benchmark/benchmark_generator.rb +30 -0
  146. data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +15 -0
  147. data/lib/rails/generators/rails/controller/USAGE +13 -5
  148. data/lib/rails/generators/rails/controller/controller_generator.rb +7 -42
  149. data/lib/rails/generators/rails/controller/templates/controller.rb.tt +1 -5
  150. data/lib/rails/generators/rails/credentials/credentials_generator.rb +30 -25
  151. data/lib/rails/generators/rails/credentials/templates/credentials.yml.tt +8 -0
  152. data/lib/rails/generators/rails/db/system/change/change_generator.rb +31 -1
  153. data/lib/rails/generators/rails/encrypted_file/encrypted_file_generator.rb +6 -2
  154. data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +1 -2
  155. data/lib/rails/generators/rails/generator/USAGE +2 -2
  156. data/lib/rails/generators/rails/generator/generator_generator.rb +0 -1
  157. data/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt +1 -1
  158. data/lib/rails/generators/rails/generator/templates/USAGE.tt +1 -1
  159. data/lib/rails/generators/rails/helper/USAGE +2 -3
  160. data/lib/rails/generators/rails/integration_test/USAGE +2 -2
  161. data/lib/rails/generators/rails/migration/USAGE +22 -12
  162. data/lib/rails/generators/rails/model/model_generator.rb +4 -0
  163. data/lib/rails/generators/rails/plugin/USAGE +17 -6
  164. data/lib/rails/generators/rails/plugin/plugin_generator.rb +86 -46
  165. data/lib/rails/generators/rails/plugin/templates/%name%.gemspec.tt +15 -20
  166. data/lib/rails/generators/rails/plugin/templates/Gemfile.tt +10 -39
  167. data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +1 -1
  168. data/lib/rails/generators/rails/plugin/templates/README.md.tt +1 -1
  169. data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +4 -18
  170. data/lib/rails/generators/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt +0 -1
  171. data/lib/rails/generators/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt +2 -2
  172. data/lib/rails/generators/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt +0 -3
  173. data/lib/rails/generators/rails/plugin/templates/bin/rails.tt +7 -22
  174. data/lib/rails/generators/rails/plugin/templates/gitignore.tt +10 -14
  175. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt +2 -2
  176. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt +1 -1
  177. data/lib/rails/generators/rails/plugin/templates/lib/%namespaced_name%.rb.tt +1 -0
  178. data/lib/rails/generators/rails/plugin/templates/rails/boot.rb.tt +3 -3
  179. data/lib/rails/generators/rails/plugin/templates/test/%namespaced_name%_test.rb.tt +4 -4
  180. data/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb.tt +1 -1
  181. data/lib/rails/generators/rails/plugin/templates/test/test_helper.rb.tt +6 -14
  182. data/lib/rails/generators/rails/resource/USAGE +4 -4
  183. data/lib/rails/generators/rails/resource/resource_generator.rb +6 -0
  184. data/lib/rails/generators/rails/resource_route/resource_route_generator.rb +2 -27
  185. data/lib/rails/generators/rails/scaffold/USAGE +5 -5
  186. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +2 -20
  187. data/lib/rails/generators/rails/scaffold_controller/USAGE +2 -2
  188. data/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb +7 -2
  189. data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +2 -6
  190. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +7 -11
  191. data/lib/rails/generators/rails/system_test/USAGE +2 -2
  192. data/lib/rails/generators/rails/task/USAGE +3 -3
  193. data/lib/rails/generators/resource_helpers.rb +2 -2
  194. data/lib/rails/generators/test_case.rb +3 -3
  195. data/lib/rails/generators/test_unit/controller/controller_generator.rb +2 -0
  196. data/lib/rails/generators/test_unit/controller/templates/functional_test.rb.tt +3 -3
  197. data/lib/rails/generators/test_unit/generator/generator_generator.rb +0 -1
  198. data/lib/rails/generators/test_unit/generator/templates/generator_test.rb.tt +3 -3
  199. data/lib/rails/generators/test_unit/integration/integration_generator.rb +0 -1
  200. data/lib/rails/generators/test_unit/integration/templates/integration_test.rb.tt +1 -1
  201. data/lib/rails/generators/test_unit/job/templates/unit_test.rb.tt +1 -1
  202. data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb.tt +1 -1
  203. data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +3 -3
  204. data/lib/rails/generators/test_unit/model/templates/unit_test.rb.tt +1 -1
  205. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +1 -1
  206. data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +2 -2
  207. data/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +4 -5
  208. data/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb.tt +6 -6
  209. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb.tt +8 -8
  210. data/lib/rails/generators/test_unit/scaffold/templates/system_test.rb.tt +9 -11
  211. data/lib/rails/generators/testing/assertions.rb +2 -2
  212. data/lib/rails/generators/testing/{behaviour.rb → behavior.rb} +8 -7
  213. data/lib/rails/generators.rb +43 -45
  214. data/lib/rails/health_controller.rb +55 -0
  215. data/lib/rails/info.rb +4 -4
  216. data/lib/rails/info_controller.rb +30 -13
  217. data/lib/rails/initializable.rb +1 -1
  218. data/lib/rails/mailers_controller.rb +18 -9
  219. data/lib/rails/paths.rb +28 -19
  220. data/lib/rails/rack/logger.rb +17 -17
  221. data/lib/rails/rackup/server.rb +15 -0
  222. data/lib/rails/railtie/configurable.rb +0 -1
  223. data/lib/rails/railtie/configuration.rb +15 -3
  224. data/lib/rails/railtie.rb +80 -37
  225. data/lib/rails/ruby_version_check.rb +5 -3
  226. data/lib/rails/secrets.rb +14 -9
  227. data/lib/rails/source_annotation_extractor.rb +69 -34
  228. data/lib/rails/tasks/engine.rake +8 -11
  229. data/lib/rails/tasks/framework.rake +4 -6
  230. data/lib/rails/tasks/log.rake +1 -1
  231. data/lib/rails/tasks/misc.rake +4 -15
  232. data/lib/rails/tasks/statistics.rake +9 -6
  233. data/lib/rails/tasks/tmp.rake +13 -6
  234. data/lib/rails/tasks/yarn.rake +17 -6
  235. data/lib/rails/tasks/zeitwerk.rake +16 -41
  236. data/lib/rails/tasks.rb +0 -6
  237. data/lib/rails/templates/layouts/application.html.erb +15 -0
  238. data/lib/rails/templates/rails/mailers/email.html.erb +46 -11
  239. data/lib/rails/templates/rails/mailers/index.html.erb +14 -7
  240. data/lib/rails/templates/rails/mailers/mailer.html.erb +11 -5
  241. data/lib/rails/templates/rails/welcome/index.html.erb +65 -48
  242. data/lib/rails/test_help.rb +13 -14
  243. data/lib/rails/test_unit/line_filtering.rb +1 -1
  244. data/lib/rails/test_unit/railtie.rb +0 -4
  245. data/lib/rails/test_unit/reporter.rb +8 -3
  246. data/lib/rails/test_unit/runner.rb +61 -19
  247. data/lib/rails/test_unit/test_parser.rb +88 -0
  248. data/lib/rails/test_unit/testing.rake +17 -36
  249. data/lib/rails/testing/maintain_test_schema.rb +16 -0
  250. data/lib/rails/version.rb +1 -1
  251. data/lib/rails/welcome_controller.rb +1 -0
  252. data/lib/rails/zeitwerk_checker.rb +15 -0
  253. data/lib/rails.rb +31 -23
  254. metadata +89 -58
  255. data/RDOC_MAIN.rdoc +0 -98
  256. data/lib/rails/application/dummy_erb_compiler.rb +0 -18
  257. data/lib/rails/command/spellchecker.rb +0 -58
  258. data/lib/rails/generators/css/assets/assets_generator.rb +0 -15
  259. data/lib/rails/generators/css/assets/templates/stylesheet.css +0 -4
  260. data/lib/rails/generators/css/scaffold/scaffold_generator.rb +0 -18
  261. data/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js +0 -6
  262. data/lib/rails/generators/rails/app/templates/app/javascript/channels/index.js +0 -5
  263. data/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +0 -23
  264. data/lib/rails/generators/rails/app/templates/bin/yarn.tt +0 -10
  265. data/lib/rails/generators/rails/app/templates/config/databases/frontbase.yml.tt +0 -50
  266. data/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml.tt +0 -86
  267. data/lib/rails/generators/rails/app/templates/config/initializers/application_controller_renderer.rb.tt +0 -8
  268. data/lib/rails/generators/rails/app/templates/config/initializers/backtrace_silencers.rb.tt +0 -7
  269. data/lib/rails/generators/rails/app/templates/config/initializers/cookies_serializer.rb.tt +0 -5
  270. data/lib/rails/generators/rails/app/templates/config/initializers/mime_types.rb.tt +0 -4
  271. data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_6_0.rb.tt +0 -45
  272. data/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt +0 -16
  273. data/lib/rails/generators/rails/app/templates/config/spring.rb.tt +0 -6
  274. data/lib/rails/generators/rails/app/templates/package.json.tt +0 -11
  275. data/lib/rails/generators/rails/assets/USAGE +0 -17
  276. data/lib/rails/generators/rails/assets/assets_generator.rb +0 -26
  277. data/lib/rails/generators/rails/assets/templates/stylesheet.css +0 -4
  278. data/lib/rails/generators/rails/model/USAGE +0 -114
  279. data/lib/rails/generators/rails/plugin/templates/rails/application.rb.tt +0 -23
  280. data/lib/rails/generators/rails/scaffold/templates/scaffold.css +0 -80
  281. data/lib/rails/tasks/annotations.rake +0 -22
  282. data/lib/rails/tasks/dev.rake +0 -11
  283. data/lib/rails/tasks/initializers.rake +0 -9
  284. data/lib/rails/tasks/middleware.rake +0 -9
  285. data/lib/rails/tasks/restart.rake +0 -9
  286. data/lib/rails/tasks/routes.rake +0 -9
@@ -4,6 +4,7 @@ require "fileutils"
4
4
  require "digest/md5"
5
5
  require "rails/version" unless defined?(Rails::VERSION)
6
6
  require "open-uri"
7
+ require "tsort"
7
8
  require "uri"
8
9
  require "rails/generators"
9
10
  require "active_support/core_ext/array/extract_options"
@@ -14,6 +15,9 @@ module Rails
14
15
  include Database
15
16
  include AppName
16
17
 
18
+ NODE_LTS_VERSION = "18.15.0"
19
+ BUN_VERSION = "1.0.1"
20
+
17
21
  attr_accessor :rails_template
18
22
  add_shebang_option!
19
23
 
@@ -24,124 +28,114 @@ module Rails
24
28
  end
25
29
 
26
30
  def self.add_shared_options_for(name)
31
+ class_option :name, type: :string, aliases: "-n",
32
+ desc: "Name of the app"
33
+
27
34
  class_option :template, type: :string, aliases: "-m",
28
35
  desc: "Path to some #{name} template (can be a filesystem path or URL)"
29
36
 
30
37
  class_option :database, type: :string, aliases: "-d", default: "sqlite3",
31
38
  desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
32
39
 
33
- class_option :skip_gemfile, type: :boolean, default: false,
34
- desc: "Don't create a Gemfile"
40
+ class_option :skip_git, type: :boolean, aliases: "-G", default: nil,
41
+ desc: "Skip git init, .gitignore and .gitattributes"
35
42
 
36
- class_option :skip_git, type: :boolean, aliases: "-G", default: false,
37
- desc: "Skip .gitignore file"
43
+ class_option :skip_docker, type: :boolean, default: nil,
44
+ desc: "Skip Dockerfile, .dockerignore and bin/docker-entrypoint"
38
45
 
39
- class_option :skip_keeps, type: :boolean, default: false,
46
+ class_option :skip_keeps, type: :boolean, default: nil,
40
47
  desc: "Skip source control .keep files"
41
48
 
42
49
  class_option :skip_action_mailer, type: :boolean, aliases: "-M",
43
- default: false,
50
+ default: nil,
44
51
  desc: "Skip Action Mailer files"
45
52
 
46
- class_option :skip_action_mailbox, type: :boolean, default: false,
53
+ class_option :skip_action_mailbox, type: :boolean, default: nil,
47
54
  desc: "Skip Action Mailbox gem"
48
55
 
49
- class_option :skip_action_text, type: :boolean, default: false,
56
+ class_option :skip_action_text, type: :boolean, default: nil,
50
57
  desc: "Skip Action Text gem"
51
58
 
52
- class_option :skip_active_record, type: :boolean, aliases: "-O", default: false,
59
+ class_option :skip_active_record, type: :boolean, aliases: "-O", default: nil,
53
60
  desc: "Skip Active Record files"
54
61
 
55
- class_option :skip_active_storage, type: :boolean, default: false,
56
- desc: "Skip Active Storage files"
62
+ class_option :skip_active_job, type: :boolean, default: nil,
63
+ desc: "Skip Active Job"
57
64
 
58
- class_option :skip_puma, type: :boolean, aliases: "-P", default: false,
59
- desc: "Skip Puma related files"
65
+ class_option :skip_active_storage, type: :boolean, default: nil,
66
+ desc: "Skip Active Storage files"
60
67
 
61
- class_option :skip_action_cable, type: :boolean, aliases: "-C", default: false,
68
+ class_option :skip_action_cable, type: :boolean, aliases: "-C", default: nil,
62
69
  desc: "Skip Action Cable files"
63
70
 
64
- class_option :skip_sprockets, type: :boolean, aliases: "-S", default: false,
65
- desc: "Skip Sprockets files"
66
-
67
- class_option :skip_spring, type: :boolean, default: false,
68
- desc: "Don't install Spring application preloader"
71
+ class_option :skip_asset_pipeline, type: :boolean, aliases: "-A", default: nil
69
72
 
70
- class_option :skip_listen, type: :boolean, default: false,
71
- desc: "Don't generate configuration that depends on the listen gem"
73
+ class_option :asset_pipeline, type: :string, aliases: "-a", default: "sprockets",
74
+ desc: "Choose your asset pipeline [options: sprockets (default), propshaft]"
72
75
 
73
- class_option :skip_javascript, type: :boolean, aliases: "-J", default: name == "plugin",
76
+ class_option :skip_javascript, type: :boolean, aliases: ["-J", "--skip-js"], default: (true if name == "plugin"),
74
77
  desc: "Skip JavaScript files"
75
78
 
76
- class_option :skip_turbolinks, type: :boolean, default: false,
77
- desc: "Skip turbolinks gem"
79
+ class_option :skip_hotwire, type: :boolean, default: nil,
80
+ desc: "Skip Hotwire integration"
81
+
82
+ class_option :skip_jbuilder, type: :boolean, default: nil,
83
+ desc: "Skip jbuilder gem"
78
84
 
79
- class_option :skip_test, type: :boolean, aliases: "-T", default: false,
85
+ class_option :skip_test, type: :boolean, aliases: "-T", default: nil,
80
86
  desc: "Skip test files"
81
87
 
82
- class_option :skip_system_test, type: :boolean, default: false,
88
+ class_option :skip_system_test, type: :boolean, default: nil,
83
89
  desc: "Skip system test files"
84
90
 
85
- class_option :skip_bootsnap, type: :boolean, default: false,
91
+ class_option :skip_bootsnap, type: :boolean, default: nil,
86
92
  desc: "Skip bootsnap gem"
87
93
 
88
- class_option :dev, type: :boolean, default: false,
89
- desc: "Setup the #{name} with Gemfile pointing to your Rails checkout"
94
+ class_option :skip_dev_gems, type: :boolean, default: nil,
95
+ desc: "Skip development gems (e.g., web-console)"
90
96
 
91
- class_option :edge, type: :boolean, default: false,
92
- desc: "Setup the #{name} with Gemfile pointing to Rails repository"
97
+ class_option :dev, type: :boolean, default: nil,
98
+ desc: "Set up the #{name} with Gemfile pointing to your Rails checkout"
99
+
100
+ class_option :edge, type: :boolean, default: nil,
101
+ desc: "Set up the #{name} with a Gemfile pointing to the #{edge_branch} branch on the Rails repository"
102
+
103
+ class_option :main, type: :boolean, default: nil, aliases: "--master",
104
+ desc: "Set up the #{name} with Gemfile pointing to Rails repository main branch"
93
105
 
94
106
  class_option :rc, type: :string, default: nil,
95
107
  desc: "Path to file containing extra configuration options for rails command"
96
108
 
97
- class_option :no_rc, type: :boolean, default: false,
109
+ class_option :no_rc, type: :boolean, default: nil,
98
110
  desc: "Skip loading of extra configuration options from .railsrc file"
99
111
 
100
112
  class_option :help, type: :boolean, aliases: "-h", group: :rails,
101
113
  desc: "Show this help message and quit"
102
114
  end
103
115
 
104
- def initialize(*args)
105
- @gem_filter = lambda { |gem| true }
106
- @extra_entries = []
107
- super
116
+ def self.edge_branch # :nodoc:
117
+ Rails.gem_version.prerelease? ? "main" : [*Rails.gem_version.segments.first(2), "stable"].join("-")
108
118
  end
109
119
 
110
- private
111
-
112
- def gemfile_entry(name, *args) # :doc:
113
- options = args.extract_options!
114
- version = args.first
115
- github = options[:github]
116
- path = options[:path]
117
-
118
- if github
119
- @extra_entries << GemfileEntry.github(name, github)
120
- elsif path
121
- @extra_entries << GemfileEntry.path(name, path)
122
- else
123
- @extra_entries << GemfileEntry.version(name, version)
124
- end
125
- self
120
+ def initialize(positional_argv, option_argv, *)
121
+ @argv = [*positional_argv, *option_argv]
122
+ @gem_filter = lambda { |gem| true }
123
+ super
126
124
  end
127
125
 
126
+ private
128
127
  def gemfile_entries # :doc:
129
- [rails_gemfile_entry,
130
- database_gemfile_entry,
131
- web_server_gemfile_entry,
132
- assets_gemfile_entry,
133
- webpacker_gemfile_entry,
134
- javascript_gemfile_entry,
135
- jbuilder_gemfile_entry,
136
- psych_gemfile_entry,
137
- cable_gemfile_entry,
138
- @extra_entries].flatten.find_all(&@gem_filter)
139
- end
140
-
141
- def add_gem_entry_filter # :doc:
142
- @gem_filter = lambda { |next_filter, entry|
143
- yield(entry) && next_filter.call(entry)
144
- }.curry[@gem_filter]
128
+ [
129
+ rails_gemfile_entry,
130
+ asset_pipeline_gemfile_entry,
131
+ database_gemfile_entry,
132
+ web_server_gemfile_entry,
133
+ javascript_gemfile_entry,
134
+ hotwire_gemfile_entry,
135
+ css_gemfile_entry,
136
+ jbuilder_gemfile_entry,
137
+ cable_gemfile_entry,
138
+ ].flatten.compact.select(&@gem_filter)
145
139
  end
146
140
 
147
141
  def builder # :doc:
@@ -153,7 +147,90 @@ module Rails
153
147
  end
154
148
 
155
149
  def build(meth, *args) # :doc:
156
- builder.send(meth, *args) if builder.respond_to?(meth)
150
+ builder.public_send(meth, *args) if builder.respond_to?(meth)
151
+ end
152
+
153
+ def deduce_implied_options(options, option_reasons, meta_options)
154
+ active = options.transform_values { |value| [] if value }.compact
155
+ irrevocable = (active.keys - meta_options).to_set
156
+
157
+ deduction_order = TSort.tsort(
158
+ ->(&block) { option_reasons.each_key(&block) },
159
+ ->(key, &block) { option_reasons[key]&.each(&block) }
160
+ )
161
+
162
+ deduction_order.each do |name|
163
+ active_reasons = option_reasons[name].to_a.select(&active)
164
+ active[name] ||= active_reasons if active_reasons.any?
165
+ irrevocable << name if active_reasons.any?(irrevocable)
166
+ end
167
+
168
+ revoked = options.select { |name, value| value == false }.keys.to_set - irrevocable
169
+ deduction_order.reverse_each do |name|
170
+ revoked += option_reasons[name].to_a if revoked.include?(name)
171
+ end
172
+ revoked -= meta_options
173
+
174
+ active.filter_map do |name, reasons|
175
+ unless revoked.include?(name) || reasons.all?(revoked)
176
+ [name, reasons - revoked.to_a]
177
+ end
178
+ end.to_h
179
+ end
180
+
181
+ OPTION_IMPLICATIONS = { # :nodoc:
182
+ skip_active_job: [:skip_action_mailer, :skip_active_storage],
183
+ skip_active_record: [:skip_active_storage],
184
+ skip_active_storage: [:skip_action_mailbox, :skip_action_text],
185
+ skip_javascript: [:skip_hotwire],
186
+ }
187
+
188
+ # ==== Options
189
+ #
190
+ # [+:meta_options+]
191
+ # A list of generator options which only serve to trigger other options.
192
+ # These options should have no other effects, and will be treated
193
+ # transparently when revoking other options.
194
+ #
195
+ # For example: --minimal implies both --skip-active-job and
196
+ # --skip-active-storage. Also, --skip-active-job by itself implies
197
+ # --skip-active-storage. If --skip-active-job is explicitly
198
+ # specified, --no-skip-active-storage should raise an error. But, if
199
+ # only --minimal is specified, --no-skip-active-storage should "undo"
200
+ # the implied --skip-active-job. This can be accomplished by passing
201
+ # <tt>meta_options: [:minimal]</tt>.
202
+ #
203
+ # In contrast, --api is not a meta option because it does other things
204
+ # besides implying options such as --skip-asset-pipeline. (And so --api
205
+ # with --no-skip-asset-pipeline should raise an error.)
206
+ def imply_options(option_implications = OPTION_IMPLICATIONS, meta_options: [])
207
+ option_reasons = {}
208
+ option_implications.each do |reason, implications|
209
+ implications.each do |implication|
210
+ (option_reasons[implication.to_s] ||= []) << reason.to_s
211
+ end
212
+ end
213
+
214
+ @implied_options = deduce_implied_options(options, option_reasons, meta_options.map(&:to_s))
215
+ @implied_options_conflicts = @implied_options.keys.select { |name| options[name] == false }
216
+ self.options = options.merge(@implied_options.transform_values { true }).freeze
217
+ end
218
+
219
+ def report_implied_options
220
+ return if @implied_options.blank?
221
+
222
+ say "Based on the specified options, the following options will also be activated:"
223
+ say ""
224
+ @implied_options.each do |name, reasons|
225
+ due_to = reasons.map { |reason| "--#{reason.dasherize}" }.join(", ")
226
+ say " --#{name.dasherize} [due to #{due_to}]"
227
+ if @implied_options_conflicts.include?(name)
228
+ say " ERROR: Conflicts with --no-#{name.dasherize}", :red
229
+ end
230
+ end
231
+ say ""
232
+
233
+ raise "Cannot proceed due to conflicting options" if @implied_options_conflicts.any?
157
234
  end
158
235
 
159
236
  def create_root # :doc:
@@ -171,43 +248,72 @@ module Rails
171
248
 
172
249
  def set_default_accessors! # :doc:
173
250
  self.destination_root = File.expand_path(app_path, destination_root)
174
- self.rails_template = \
175
- case options[:template]
176
- when /^https?:\/\//
177
- options[:template]
178
- when String
179
- File.expand_path(options[:template], Dir.pwd)
180
- else
181
- options[:template]
182
- end
251
+
252
+ if options[:template].is_a?(String) && !options[:template].match?(/^https?:\/\//)
253
+ interpolated = options[:template].gsub(/\$(\w+)|\$\{\g<1>\}|%\g<1>%/) { |m| ENV[$1] || m }
254
+ self.rails_template = File.expand_path(interpolated)
255
+ else
256
+ self.rails_template = options[:template]
257
+ end
183
258
  end
184
259
 
185
260
  def database_gemfile_entry # :doc:
186
- return [] if options[:skip_active_record]
261
+ return if options[:skip_active_record]
262
+
187
263
  gem_name, gem_version = gem_for_database
188
264
  GemfileEntry.version gem_name, gem_version,
189
- "Use #{options[:database]} as the database for Active Record"
265
+ "Use #{options[:database]} as the database for Active Record"
190
266
  end
191
267
 
192
268
  def web_server_gemfile_entry # :doc:
193
- return [] if options[:skip_puma]
194
- comment = "Use Puma as the app server"
195
- GemfileEntry.new("puma", "~> 3.11", comment)
269
+ GemfileEntry.new "puma", ">= 5.0", "Use the Puma web server [https://github.com/puma/puma]"
270
+ end
271
+
272
+ def asset_pipeline_gemfile_entry
273
+ return if skip_asset_pipeline?
274
+
275
+ if options[:asset_pipeline] == "sprockets"
276
+ GemfileEntry.floats "sprockets-rails",
277
+ "The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]"
278
+ elsif options[:asset_pipeline] == "propshaft"
279
+ GemfileEntry.floats "propshaft", "The modern asset pipeline for Rails [https://github.com/rails/propshaft]"
280
+ end
281
+ end
282
+
283
+ def required_railties
284
+ @required_railties ||= {
285
+ "active_model/railtie" => true,
286
+ "active_job/railtie" => !options[:skip_active_job],
287
+ "active_record/railtie" => !options[:skip_active_record],
288
+ "active_storage/engine" => !options[:skip_active_storage],
289
+ "action_controller/railtie" => true,
290
+ "action_mailer/railtie" => !options[:skip_action_mailer],
291
+ "action_mailbox/engine" => !options[:skip_action_mailbox],
292
+ "action_text/engine" => !options[:skip_action_text],
293
+ "action_view/railtie" => true,
294
+ "action_cable/engine" => !options[:skip_action_cable],
295
+ "rails/test_unit/railtie" => !options[:skip_test],
296
+ }
196
297
  end
197
298
 
198
299
  def include_all_railties? # :doc:
199
- [
200
- options.values_at(
201
- :skip_active_record,
202
- :skip_action_mailer,
203
- :skip_test,
204
- :skip_sprockets,
205
- :skip_action_cable
206
- ),
207
- skip_active_storage?,
208
- skip_action_mailbox?,
209
- skip_action_text?
210
- ].flatten.none?
300
+ required_railties.values.all?
301
+ end
302
+
303
+ def rails_require_statement
304
+ if include_all_railties?
305
+ %(require "rails/all")
306
+ else
307
+ require_statements = required_railties.map do |railtie, required|
308
+ %(#{"# " if !required}require "#{railtie}")
309
+ end
310
+
311
+ <<~RUBY.strip
312
+ require "rails"
313
+ # Pick the frameworks you want:
314
+ #{require_statements.join("\n")}
315
+ RUBY
316
+ end
211
317
  end
212
318
 
213
319
  def comment_if(value) # :doc:
@@ -228,21 +334,46 @@ module Rails
228
334
  end
229
335
 
230
336
  def sqlite3? # :doc:
231
- !options[:skip_active_record] && options[:database] == "sqlite3"
337
+ !skip_active_record? && options[:database] == "sqlite3"
338
+ end
339
+
340
+ def skip_active_record? # :doc:
341
+ options[:skip_active_record]
232
342
  end
233
343
 
234
344
  def skip_active_storage? # :doc:
235
- options[:skip_active_storage] || options[:skip_active_record]
345
+ options[:skip_active_storage]
346
+ end
347
+
348
+ def skip_action_cable? # :doc:
349
+ options[:skip_action_cable]
350
+ end
351
+
352
+ def skip_action_mailer? # :doc:
353
+ options[:skip_action_mailer]
236
354
  end
237
355
 
238
356
  def skip_action_mailbox? # :doc:
239
- options[:skip_action_mailbox] || skip_active_storage?
357
+ options[:skip_action_mailbox]
240
358
  end
241
359
 
242
360
  def skip_action_text? # :doc:
243
- options[:skip_action_text] || skip_active_storage?
361
+ options[:skip_action_text]
244
362
  end
245
363
 
364
+ def skip_asset_pipeline? # :doc:
365
+ options[:skip_asset_pipeline]
366
+ end
367
+
368
+ def skip_sprockets?
369
+ skip_asset_pipeline? || options[:asset_pipeline] != "sprockets"
370
+ end
371
+
372
+ def skip_propshaft?
373
+ skip_asset_pipeline? || options[:asset_pipeline] != "propshaft"
374
+ end
375
+
376
+
246
377
  class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
247
378
  def initialize(name, version, comment, options = {}, commented_out = false)
248
379
  super
@@ -260,34 +391,43 @@ module Rails
260
391
  new(name, version, comment)
261
392
  end
262
393
 
394
+ def self.floats(name, comment = nil)
395
+ new(name, nil, comment)
396
+ end
397
+
263
398
  def self.path(name, path, comment = nil)
264
399
  new(name, nil, comment, path: path)
265
400
  end
266
401
 
267
- def version
268
- version = super
269
-
270
- if version.is_a?(Array)
271
- version.join("', '")
272
- else
273
- version
274
- end
402
+ def to_s
403
+ [
404
+ (comment.gsub(/^/, "# ").chomp + "\n" if comment),
405
+ ("# " if commented_out),
406
+ "gem \"#{name}\"",
407
+ *Array(version).map { |constraint| ", \"#{constraint}\"" },
408
+ *options.map { |key, value| ", #{key}: #{value.inspect}" },
409
+ ].compact.join
275
410
  end
276
411
  end
277
412
 
413
+ def gem_ruby_version
414
+ Gem::Version.new(Gem::VERSION) >= Gem::Version.new("3.3.13") ? Gem.ruby_version : RUBY_VERSION
415
+ end
416
+
417
+ def rails_prerelease?
418
+ options.dev? || options.edge? || options.main?
419
+ end
420
+
278
421
  def rails_gemfile_entry
279
422
  if options.dev?
280
- [
281
- GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH)
282
- ]
423
+ GemfileEntry.path("rails", Rails::Generators::RAILS_DEV_PATH, "Use local checkout of Rails")
283
424
  elsif options.edge?
284
- [
285
- GemfileEntry.github("rails", "rails/rails", "6-0-stable")
286
- ]
425
+ GemfileEntry.github("rails", "rails/rails", edge_branch, "Use specific branch of Rails")
426
+ elsif options.main?
427
+ GemfileEntry.github("rails", "rails/rails", "main", "Use main development branch of Rails")
287
428
  else
288
- [GemfileEntry.version("rails",
289
- rails_version_specifier,
290
- "Bundle edge Rails instead: gem 'rails', github: 'rails/rails'")]
429
+ GemfileEntry.version("rails", rails_version_specifier,
430
+ %(Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"))
291
431
  end
292
432
  end
293
433
 
@@ -304,50 +444,168 @@ module Rails
304
444
  end
305
445
  end
306
446
 
307
- def assets_gemfile_entry
308
- return [] if options[:skip_sprockets]
309
-
310
- GemfileEntry.version("sass-rails", "~> 5", "Use SCSS for stylesheets")
447
+ def jbuilder_gemfile_entry
448
+ return if options[:skip_jbuilder]
449
+ GemfileEntry.new "jbuilder", nil, "Build JSON APIs with ease [https://github.com/rails/jbuilder]", {}, options[:api]
311
450
  end
312
451
 
313
- def webpacker_gemfile_entry
314
- return [] if options[:skip_javascript]
452
+ def javascript_gemfile_entry
453
+ return if options[:skip_javascript]
315
454
 
316
- if options.dev? || options.edge?
317
- GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker"
455
+ if options[:javascript] == "importmap"
456
+ GemfileEntry.floats "importmap-rails", "Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]"
318
457
  else
319
- GemfileEntry.version "webpacker", "~> 4.0", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker"
458
+ GemfileEntry.floats "jsbundling-rails", "Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]"
320
459
  end
321
460
  end
322
461
 
323
- def jbuilder_gemfile_entry
324
- comment = "Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder"
325
- GemfileEntry.new "jbuilder", "~> 2.7", comment, {}, options[:api]
462
+ def hotwire_gemfile_entry
463
+ return if options[:skip_hotwire]
464
+
465
+ turbo_rails_entry =
466
+ GemfileEntry.floats "turbo-rails", "Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]"
467
+
468
+ stimulus_rails_entry =
469
+ GemfileEntry.floats "stimulus-rails", "Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]"
470
+
471
+ [ turbo_rails_entry, stimulus_rails_entry ]
326
472
  end
327
473
 
328
- def javascript_gemfile_entry
329
- if options[:skip_javascript] || options[:skip_turbolinks]
330
- []
331
- else
332
- [ GemfileEntry.version("turbolinks", "~> 5",
333
- "Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks") ]
474
+ def using_js_runtime?
475
+ (options[:javascript] && !%w[importmap].include?(options[:javascript])) ||
476
+ (options[:css] && !%w[tailwind sass].include?(options[:css]))
477
+ end
478
+
479
+ def using_node?
480
+ using_js_runtime? && !%w[bun].include?(options[:javascript])
481
+ end
482
+
483
+ def using_bun?
484
+ using_js_runtime? && %w[bun].include?(options[:javascript])
485
+ end
486
+
487
+ def node_version
488
+ if using_node?
489
+ ENV.fetch("NODE_VERSION") do
490
+ `node --version`[/\d+\.\d+\.\d+/]
491
+ rescue
492
+ NODE_LTS_VERSION
493
+ end
334
494
  end
335
495
  end
336
496
 
337
- def psych_gemfile_entry
338
- return [] unless defined?(Rubinius)
497
+ def dockerfile_yarn_version
498
+ using_node? and `yarn --version`[/\d+\.\d+\.\d+/]
499
+ rescue
500
+ "latest"
501
+ end
339
502
 
340
- comment = "Use Psych as the YAML engine, instead of Syck, so serialized " \
341
- "data can be read safely from different rubies (see http://git.io/uuLVag)"
342
- GemfileEntry.new("psych", "~> 2.0", comment, platforms: :rbx)
503
+ def dockerfile_bun_version
504
+ using_bun? and `bun --version`[/\d+\.\d+\.\d+/]
505
+ rescue
506
+ BUN_VERSION
507
+ end
508
+
509
+ def dockerfile_binfile_fixups
510
+ # binfiles may have OS specific paths to ruby. Normalize them.
511
+ shebangs = Dir["bin/*"].map { |file| IO.read(file).lines.first }.join
512
+ rubies = shebangs.scan(%r{#!/usr/bin/env (ruby.*)}).flatten.uniq
513
+
514
+ binfixups = (rubies - %w(ruby)).map do |ruby|
515
+ "sed -i 's/#{Regexp.quote(ruby)}$/ruby/' bin/*"
516
+ end
517
+
518
+ # Windows line endings will cause scripts to fail. If any
519
+ # or found OR this generation is run on a windows platform
520
+ # and there are other binfixups required, then convert
521
+ # line endings. This avoids adding unnecessary fixups if
522
+ # none are required, but prepares for the need to do the
523
+ # fix line endings if other fixups are required.
524
+ has_cr = Dir["bin/*"].any? { |file| IO.read(file).include? "\r" }
525
+ if has_cr || (Gem.win_platform? && !binfixups.empty?)
526
+ binfixups.unshift 'sed -i "s/\r$//g" bin/*'
527
+ end
528
+
529
+ # Windows file systems may not have the concept of executable.
530
+ # In such cases, fix up during the build.
531
+ unless Dir["bin/*"].all? { |file| File.executable? file }
532
+ binfixups.unshift "chmod +x bin/*"
533
+ end
534
+
535
+ binfixups
536
+ end
537
+
538
+ def dockerfile_build_packages
539
+ # start with the essentials
540
+ packages = %w(build-essential git pkg-config)
541
+
542
+ # add database support
543
+ packages << build_package_for_database unless skip_active_record?
544
+
545
+ # ActiveStorage preview support
546
+ packages << "libvips" unless skip_active_storage?
547
+
548
+ packages << "curl" if using_js_runtime?
549
+
550
+ packages << "unzip" if using_bun?
551
+
552
+ # node support, including support for building native modules
553
+ if using_node?
554
+ packages << "node-gyp" # pkg-config already listed above
555
+
556
+ # module build process depends on Python, and debian changed
557
+ # how python is installed with the bullseye release. Below
558
+ # is based on debian release included with the Ruby images on
559
+ # Dockerhub.
560
+ case Gem.ruby_version.to_s
561
+ when /^2\.7/
562
+ bullseye = Gem.ruby_version >= Gem::Version.new("2.7.4")
563
+ when /^3\.0/
564
+ bullseye = Gem.ruby_version >= Gem::Version.new("3.0.2")
565
+ else
566
+ bullseye = true
567
+ end
568
+
569
+ if bullseye
570
+ packages << "python-is-python3"
571
+ else
572
+ packages << "python"
573
+ end
574
+ end
575
+
576
+ packages.compact.sort
577
+ end
578
+
579
+ def dockerfile_deploy_packages
580
+ # Add curl to work with the default healthcheck strategy in Kamal
581
+ packages = ["curl"]
582
+
583
+ # ActiveRecord databases
584
+ packages << deploy_package_for_database unless skip_active_record?
585
+
586
+ # ActiveStorage preview support
587
+ packages << "libvips" unless skip_active_storage?
588
+
589
+ packages.compact.sort
590
+ end
591
+
592
+ def css_gemfile_entry
593
+ return unless options[:css]
594
+
595
+ if !using_js_runtime? && options[:css] == "tailwind"
596
+ GemfileEntry.floats "tailwindcss-rails", "Use Tailwind CSS [https://github.com/rails/tailwindcss-rails]"
597
+ elsif !using_js_runtime? && options[:css] == "sass"
598
+ GemfileEntry.floats "dartsass-rails", "Use Dart SASS [https://github.com/rails/dartsass-rails]"
599
+ else
600
+ GemfileEntry.floats "cssbundling-rails", "Bundle and process CSS [https://github.com/rails/cssbundling-rails]"
601
+ end
343
602
  end
344
603
 
345
604
  def cable_gemfile_entry
346
- return [] if options[:skip_action_cable]
605
+ return if options[:skip_action_cable]
606
+
347
607
  comment = "Use Redis adapter to run Action Cable in production"
348
- gems = []
349
- gems << GemfileEntry.new("redis", "~> 4.0", comment, {}, true)
350
- gems
608
+ GemfileEntry.new("redis", ">= 4.0.1", comment, {}, true)
351
609
  end
352
610
 
353
611
  def bundle_command(command, env = {})
@@ -377,53 +635,88 @@ module Rails
377
635
  end
378
636
 
379
637
  def bundle_install?
380
- !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
381
- end
382
-
383
- def spring_install?
384
- !options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
638
+ !(options[:skip_bundle] || options[:pretend])
385
639
  end
386
640
 
387
- def webpack_install?
388
- !(options[:skip_javascript] || options[:skip_webpack_install])
641
+ def bundler_windows_platforms
642
+ Gem.rubygems_version >= Gem::Version.new("3.3.22") ? "windows" : "mswin mswin64 mingw x64_mingw"
389
643
  end
390
644
 
391
645
  def depends_on_system_test?
392
646
  !(options[:skip_system_test] || options[:skip_test] || options[:api])
393
647
  end
394
648
 
395
- def depend_on_listen?
396
- !options[:skip_listen] && os_supports_listen_out_of_the_box?
397
- end
398
-
399
649
  def depend_on_bootsnap?
400
650
  !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
401
651
  end
402
652
 
403
- def os_supports_listen_out_of_the_box?
404
- RbConfig::CONFIG["host_os"] =~ /darwin|linux/
653
+ def target_rails_prerelease(self_command = "new")
654
+ return unless rails_prerelease? && bundle_install?
655
+
656
+ if !File.exist?(File.expand_path("Gemfile", destination_root))
657
+ create_file("Gemfile", <<~GEMFILE)
658
+ source "https://rubygems.org"
659
+ git_source(:github) { |repo| "https://github.com/\#{repo}.git" }
660
+ #{rails_gemfile_entry}
661
+ GEMFILE
662
+
663
+ run_bundle
664
+
665
+ @argv.delete_at(@argv.index(app_path))
666
+ @argv.unshift(destination_root)
667
+ require "shellwords"
668
+ bundle_command("exec rails #{self_command} #{Shellwords.join(@argv)}")
669
+ exit
670
+ else
671
+ remove_file("Gemfile")
672
+ remove_file("Gemfile.lock")
673
+ end
405
674
  end
406
675
 
407
676
  def run_bundle
408
677
  bundle_command("install", "BUNDLE_IGNORE_MESSAGES" => "1") if bundle_install?
409
678
  end
410
679
 
411
- def run_webpack
412
- if webpack_install?
413
- rails_command "webpacker:install"
414
- rails_command "webpacker:install:#{options[:webpack]}" if options[:webpack] && options[:webpack] != "webpack"
680
+ def run_javascript
681
+ return if options[:skip_javascript] || !bundle_install?
682
+
683
+ case options[:javascript]
684
+ when "importmap" then rails_command "importmap:install"
685
+ when "webpack", "bun", "esbuild", "rollup" then rails_command "javascript:install:#{options[:javascript]}"
415
686
  end
416
687
  end
417
688
 
418
- def generate_bundler_binstub
689
+ def run_hotwire
690
+ return if options[:skip_hotwire] || !bundle_install?
691
+
692
+ rails_command "turbo:install stimulus:install"
693
+ end
694
+
695
+ def run_css
696
+ return if !options[:css] || !bundle_install?
697
+
698
+ if !using_js_runtime? && options[:css] == "tailwind"
699
+ rails_command "tailwindcss:install"
700
+ elsif !using_js_runtime? && options[:css] == "sass"
701
+ rails_command "dartsass:install"
702
+ else
703
+ rails_command "css:install:#{options[:css]}"
704
+ end
705
+ end
706
+
707
+ def add_bundler_platforms
419
708
  if bundle_install?
420
- bundle_command("binstubs bundler")
709
+ # The vast majority of Rails apps will be deployed on `x86_64-linux`.
710
+ bundle_command("lock --add-platform=x86_64-linux")
711
+
712
+ # Users that develop on M1 mac may use docker and would need `aarch64-linux` as well.
713
+ bundle_command("lock --add-platform=aarch64-linux") if RUBY_PLATFORM.start_with?("arm64")
421
714
  end
422
715
  end
423
716
 
424
- def generate_spring_binstubs
425
- if bundle_install? && spring_install?
426
- bundle_command("exec spring binstub --all")
717
+ def generate_bundler_binstub
718
+ if bundle_install?
719
+ bundle_command("binstubs bundler")
427
720
  end
428
721
  end
429
722
 
@@ -435,6 +728,35 @@ module Rails
435
728
  def keep_file(destination)
436
729
  create_file("#{destination}/.keep") if keeps?
437
730
  end
731
+
732
+ def user_default_branch
733
+ @user_default_branch ||= `git config init.defaultbranch`
734
+ end
735
+
736
+ def git_init_command
737
+ return "git init" if user_default_branch.strip.present?
738
+
739
+ git_version = `git --version`[/\d+\.\d+\.\d+/]
740
+
741
+ if Gem::Version.new(git_version) >= Gem::Version.new("2.28.0")
742
+ "git init -b main"
743
+ else
744
+ "git init && git symbolic-ref HEAD refs/heads/main"
745
+ end
746
+ end
747
+
748
+ def edge_branch
749
+ self.class.edge_branch
750
+ end
751
+
752
+ def dockerfile_chown_directories
753
+ directories = %w(log tmp)
754
+
755
+ directories << "storage" unless skip_active_storage? && !sqlite3?
756
+ directories << "db" unless skip_active_record?
757
+
758
+ directories.sort
759
+ end
438
760
  end
439
761
  end
440
762
  end