railties 8.0.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -219
- data/README.rdoc +1 -1
- data/lib/minitest/rails_plugin.rb +48 -12
- data/lib/rails/application/bootstrap.rb +6 -3
- data/lib/rails/application/configuration.rb +26 -0
- data/lib/rails/application/default_middleware_stack.rb +1 -1
- data/lib/rails/application/finisher.rb +2 -1
- data/lib/rails/application/routes_reloader.rb +1 -2
- data/lib/rails/application.rb +6 -4
- data/lib/rails/application_controller.rb +2 -0
- data/lib/rails/command/base.rb +0 -2
- data/lib/rails/command/environment_argument.rb +0 -1
- data/lib/rails/command.rb +1 -1
- data/lib/rails/commands/console/irb_console.rb +6 -5
- data/lib/rails/commands/credentials/credentials_command.rb +25 -5
- data/lib/rails/commands/encrypted/encrypted_command.rb +0 -1
- data/lib/rails/engine/lazy_route_set.rb +8 -11
- data/lib/rails/engine.rb +0 -1
- data/lib/rails/gem_version.rb +3 -3
- data/lib/rails/generators/actions.rb +2 -3
- data/lib/rails/generators/app_base.rb +43 -54
- data/lib/rails/generators/bundle_helper.rb +34 -0
- data/lib/rails/generators/database.rb +1 -1
- data/lib/rails/generators/erb/authentication/authentication_generator.rb +2 -0
- data/lib/rails/generators/erb/scaffold/templates/partial.html.erb.tt +2 -2
- data/lib/rails/generators/generated_attribute.rb +1 -1
- data/lib/rails/generators/migration.rb +0 -1
- data/lib/rails/generators/rails/app/app_generator.rb +14 -4
- data/lib/rails/generators/rails/app/templates/Dockerfile.tt +15 -13
- data/lib/rails/generators/rails/app/templates/Gemfile.tt +3 -0
- data/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt +5 -0
- data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +1 -0
- data/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt +5 -0
- data/lib/rails/generators/rails/app/templates/bin/ci.tt +5 -0
- data/lib/rails/generators/rails/app/templates/bin/rubocop.tt +1 -1
- data/lib/rails/generators/rails/app/templates/bin/setup.tt +1 -0
- data/lib/rails/generators/rails/app/templates/config/bundler-audit.yml.tt +5 -0
- data/lib/rails/generators/rails/app/templates/config/ci.rb.tt +34 -0
- data/lib/rails/generators/rails/app/templates/config/databases/mysql.yml.tt +9 -1
- data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt +10 -2
- data/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt +1 -1
- data/lib/rails/generators/rails/app/templates/config/databases/trilogy.yml.tt +9 -1
- data/lib/rails/generators/rails/app/templates/config/deploy.yml.tt +5 -5
- data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +5 -0
- data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +16 -4
- data/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt +4 -0
- data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt +66 -0
- data/lib/rails/generators/rails/app/templates/config/puma.rb.tt +3 -2
- data/lib/rails/generators/rails/app/templates/config/storage.yml.tt +0 -7
- data/lib/rails/generators/rails/app/templates/docker-entrypoint.tt +0 -6
- data/lib/rails/generators/rails/app/templates/github/ci.yml.tt +101 -19
- data/lib/rails/generators/rails/app/templates/github/dependabot.yml +2 -2
- data/lib/rails/generators/rails/app/templates/kamal-secrets.tt +3 -0
- data/lib/rails/generators/rails/app/templates/public/400.html +1 -1
- data/lib/rails/generators/rails/app/templates/public/404.html +2 -2
- data/lib/rails/generators/rails/app/templates/public/422.html +1 -1
- data/lib/rails/generators/rails/app/templates/public/500.html +2 -2
- data/lib/rails/generators/rails/authentication/authentication_generator.rb +22 -9
- data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt +6 -0
- data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt +2 -2
- data/lib/rails/generators/rails/authentication/templates/test/test_helpers/session_test_helper.rb.tt +15 -0
- data/lib/rails/generators/rails/benchmark/USAGE +1 -1
- data/lib/rails/generators/rails/benchmark/templates/benchmark.rb.tt +0 -2
- data/lib/rails/generators/rails/devcontainer/templates/devcontainer/Dockerfile.tt +4 -0
- data/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.tt +2 -2
- data/lib/rails/generators/rails/encryption_key_file/encryption_key_file_generator.rb +17 -5
- data/lib/rails/generators/rails/master_key/master_key_generator.rb +0 -12
- data/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt +20 -9
- data/lib/rails/generators/rails/plugin/templates/github/dependabot.yml +2 -2
- data/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb.tt +2 -2
- data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb.tt +2 -2
- data/lib/rails/generators/rails/script/USAGE +1 -1
- data/lib/rails/generators/test_unit/authentication/authentication_generator.rb +5 -0
- data/lib/rails/generators/test_unit/authentication/templates/test/controllers/passwords_controller_test.rb.tt +67 -0
- data/lib/rails/generators/test_unit/authentication/templates/test/controllers/sessions_controller_test.rb +33 -0
- data/lib/rails/generators/test_unit/authentication/templates/test/models/user_test.rb.tt +4 -3
- data/lib/rails/generators/test_unit/model/templates/fixtures.yml.tt +1 -1
- data/lib/rails/generators/testing/behavior.rb +0 -3
- data/lib/rails/generators.rb +3 -1
- data/lib/rails/health_controller.rb +10 -2
- data/lib/rails/info.rb +4 -4
- data/lib/rails/info_controller.rb +2 -3
- data/lib/rails/initializable.rb +63 -19
- data/lib/rails/rack/silence_request.rb +5 -2
- data/lib/rails/railtie/configurable.rb +0 -1
- data/lib/rails/railtie.rb +0 -1
- data/lib/rails/templates/rails/info/notes.html.erb +23 -0
- data/lib/rails/templates/rails/mailers/email.html.erb +2 -1
- data/lib/rails/templates/rails/welcome/index.html.erb +17 -1
- data/lib/rails/test_unit/reporter.rb +5 -4
- data/lib/rails/test_unit/runner.rb +8 -5
- data/lib/rails.rb +9 -2
- metadata +35 -16
- data/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_0.rb.tt +0 -30
- data/lib/rails/generators/test_unit/plugin/plugin_generator.rb +0 -15
- data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +0 -7
- data/lib/rails/generators/test_unit/plugin/templates/test_helper.rb +0 -2
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<head>
|
6
6
|
|
7
|
-
<title>We
|
7
|
+
<title>We're sorry, but something went wrong (500 Internal Server Error)</title>
|
8
8
|
|
9
9
|
<meta charset="utf-8">
|
10
10
|
<meta name="viewport" content="initial-scale=1, width=device-width">
|
@@ -105,7 +105,7 @@
|
|
105
105
|
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m101.23 93.8427c-8.1103 0-15.4098 3.7849-19.7354 8.3813h-36.2269v-99.21891h103.8143v37.03791h-68.3984v24.8722c5.1366-2.7035 15.1396-5.9477 24.6014-5.9477 35.146 0 56.233 22.7094 56.233 55.4215 0 34.605-23.791 57.315-60.558 57.315-37.8492 0-61.64-22.169-63.8028-55.963h42.9857c1.0814 10.814 9.1919 19.195 21.6281 19.195 11.355 0 19.465-8.381 19.465-20.547 0-11.625-7.299-20.5463-20.006-20.5463zm138.833 77.8613c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm140.456 133.2831c-40.823 0-64.884-35.146-64.884-85.7015 0-50.5554 24.061-85.700907 64.884-85.700907 40.822 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.062 85.7015-64.884 85.7015zm0-133.2831c-17.573 0-22.71 21.8984-22.71 47.5816 0 25.6835 5.137 47.5815 22.71 47.5815 17.302 0 22.709-21.898 22.709-47.5815 0-25.6832-5.407-47.5816-22.709-47.5816z" fill="#f0eff0"/><path d="m23.1377 68.9967v34.0033h-8.9162v-34.0033zm4.3157 34.0033v-24.921h8.6947v2.1598c1.3845-1.5506 3.8212-2.7136 6.701-2.7136 5.538 0 8.8054 3.5997 8.8054 9.1377v16.3371h-8.6393v-14.2327c0-2.049-1.0522-3.5443-3.2674-3.5443-1.7168 0-3.1567.9969-3.5997 2.7136v15.0634zm29.9913-8.5839v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.5839v6.8671h5.2058v6.7564h-5.2058v8.307c0 1.9383.9415 2.769 2.6583 2.769.9414 0 1.9937-.2216 2.769-.5538v7.3654c-.9969.443-2.8798.775-4.8181.775-5.8703 0-9.1931-2.769-9.1931-9.0819zm32.3666-.1108h8.0301c-.8861 5.7597-5.2057 9.2487-11.6852 9.2487-7.6424 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.3165-13.0143 12.5159-13.0143 7.6424 0 11.9621 5.095 11.9621 12.5159v2.1598h-16.1156c.2769 2.9905 1.8275 4.5965 4.3196 4.5965 1.7722 0 3.1567-.7753 3.6551-2.4921zm-3.8212-10.0237c-2.0491 0-3.4336 1.2737-3.9874 3.5997h7.5317c-.1107-2.0491-1.3845-3.5997-3.5443-3.5997zm31.4299-6.3134v8.3624c-1.052-.5538-2.215-.7753-3.599-.7753-2.382 0-3.988 1.0522-4.431 2.8244v14.6203h-8.694v-24.921h8.694v2.2152c1.219-1.6614 3.157-2.769 5.649-2.769 1.108 0 1.994.2215 2.381.443zm2.949 25.0318v-24.921h8.694v2.1598c1.385-1.5506 3.821-2.7136 6.701-2.7136 5.538 0 8.806 3.5997 8.806 9.1377v16.3371h-8.64v-14.2327c0-2.049-1.052-3.5443-3.267-3.5443-1.717 0-3.157.9969-3.6 2.7136v15.0634zm50.371 0h-8.363v-1.274c-.83.831-3.323 1.717-5.981 1.717-4.929 0-9.082-2.769-9.082-8.0301 0-4.818 4.153-7.9193 9.581-7.9193 2.049 0 4.485.6646 5.482 1.3845v-1.606c0-1.606-.941-2.9905-3.046-2.9905-1.606 0-2.547.7199-2.935 1.8275h-8.196c.72-4.8181 4.984-8.6393 11.408-8.6393 7.089 0 11.132 3.7659 11.132 10.2453zm-8.363-6.9779v-1.4399c-.554-1.0522-2.049-1.7167-3.655-1.7167-1.717 0-3.433.7199-3.433 2.3813 0 1.7168 1.716 2.4367 3.433 2.4367 1.606 0 3.101-.6645 3.655-1.6614zm20.742-29.0191v35.997h-8.694v-35.997zm13.036 25.9178h9.248c.72 2.326 2.714 3.489 5.483 3.489 2.713 0 4.596-1.163 4.596-3.2674 0-1.6061-1.052-2.326-3.212-2.8244l-6.534-1.3845c-4.985-1.1076-8.751-3.7105-8.751-9.47 0-6.6456 5.538-11.0206 13.07-11.0206 8.307 0 13.014 4.5411 13.956 10.4114h-8.695c-.72-1.8829-2.27-3.3228-5.205-3.3228-2.548 0-4.265 1.1076-4.265 2.9905 0 1.4953 1.052 2.326 2.825 2.7137l6.645 1.5506c5.815 1.3845 9.027 4.5412 9.027 9.8023 0 6.9778-5.87 10.9654-13.291 10.9654-8.141 0-13.679-3.9322-14.897-10.6332zm46.509 1.3845h8.031c-.887 5.7597-5.206 9.2487-11.686 9.2487-7.642 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.317-13.0143 12.516-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.319 4.5965 1.773 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm31.431-6.3134v8.3624c-1.053-.5538-2.216-.7753-3.6-.7753-2.381 0-3.988 1.0522-4.431 2.8244v14.6203h-8.694v-24.921h8.694v2.2152c1.219-1.6614 3.157-2.769 5.649-2.769 1.108 0 1.994.2215 2.382.443zm18.288 25.0318h-7.809l-9.47-24.921h8.861l4.763 14.288 4.652-14.288h8.528zm25.614-8.6947h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.642 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.516-13.0143 7.642 0 11.962 5.095 11.962 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.434 1.2737-3.988 3.5997h7.532c-.111-2.0491-1.384-3.5997-3.544-3.5997zm31.43-6.3134v8.3624c-1.052-.5538-2.215-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.157-2.769 5.649-2.769 1.107 0 1.993.2215 2.381.443zm13.703-8.9715h24.312v7.6424h-15.562v5.3165h14.232v7.4763h-14.232v5.8703h15.562v7.6978h-24.312zm44.667 8.9715v8.3624c-1.052-.5538-2.215-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.156-2.769 5.648-2.769 1.108 0 1.994.2215 2.382.443zm19.673 0v8.3624c-1.053-.5538-2.216-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.156-2.769 5.648-2.769 1.108 0 1.994.2215 2.382.443zm26.769 12.5713c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm28.082-12.5713v8.3624c-1.052-.5538-2.215-.7753-3.6-.7753-2.381 0-3.987 1.0522-4.43 2.8244v14.6203h-8.695v-24.921h8.695v2.2152c1.218-1.6614 3.157-2.769 5.649-2.769 1.107 0 1.993.2215 2.381.443z" fill="#d30001"/></svg>
|
106
106
|
</header>
|
107
107
|
<article>
|
108
|
-
<p><strong>We
|
108
|
+
<p><strong>We're sorry, but something went wrong.</strong><br> If you're the application owner check the logs for more information.</p>
|
109
109
|
</article>
|
110
110
|
</main>
|
111
111
|
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "rails/generators/bundle_helper"
|
4
|
+
|
3
5
|
module Rails
|
4
6
|
module Generators
|
5
7
|
class AuthenticationGenerator < Base # :nodoc:
|
8
|
+
include BundleHelper
|
9
|
+
|
6
10
|
class_option :api, type: :boolean,
|
7
11
|
desc: "Generate API-only controllers and models, with no view templates"
|
8
12
|
|
@@ -21,12 +25,16 @@ module Rails
|
|
21
25
|
|
22
26
|
template "app/channels/application_cable/connection.rb" if defined?(ActionCable::Engine)
|
23
27
|
|
24
|
-
|
28
|
+
if defined?(ActionMailer::Railtie)
|
29
|
+
template "app/mailers/passwords_mailer.rb"
|
30
|
+
|
31
|
+
template "app/views/passwords_mailer/reset.html.erb"
|
32
|
+
template "app/views/passwords_mailer/reset.text.erb"
|
25
33
|
|
26
|
-
|
27
|
-
|
34
|
+
template "test/mailers/previews/passwords_mailer_preview.rb"
|
35
|
+
end
|
28
36
|
|
29
|
-
template "test/
|
37
|
+
template "test/test_helpers/session_test_helper.rb"
|
30
38
|
end
|
31
39
|
|
32
40
|
def configure_application_controller
|
@@ -39,17 +47,22 @@ module Rails
|
|
39
47
|
end
|
40
48
|
|
41
49
|
def enable_bcrypt
|
42
|
-
if File.read("Gemfile").include?('gem "bcrypt"')
|
50
|
+
if File.read(File.expand_path("Gemfile", destination_root)).include?('gem "bcrypt"')
|
43
51
|
uncomment_lines "Gemfile", /gem "bcrypt"/
|
44
|
-
|
52
|
+
bundle_command("install --quiet")
|
45
53
|
else
|
46
|
-
|
54
|
+
bundle_command("add bcrypt", {}, quiet: true)
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
50
58
|
def add_migrations
|
51
|
-
generate "migration CreateUsers email_address:string!:uniq password_digest:string! --force"
|
52
|
-
generate "migration CreateSessions user:references ip_address:string user_agent:string --force"
|
59
|
+
generate "migration", "CreateUsers", "email_address:string!:uniq password_digest:string!", "--force"
|
60
|
+
generate "migration", "CreateSessions", "user:references ip_address:string user_agent:string", "--force"
|
61
|
+
end
|
62
|
+
|
63
|
+
def configure_test_helper
|
64
|
+
inject_into_file "test/test_helper.rb", "require_relative \"test_helpers/session_test_helper\"\n", after: "require \"rails/test_help\"\n"
|
65
|
+
inject_into_class "test/test_helper.rb", "TestCase", " include SessionTestHelper\n"
|
53
66
|
end
|
54
67
|
|
55
68
|
hook_for :test_framework
|
data/lib/rails/generators/rails/authentication/templates/app/controllers/passwords_controller.rb.tt
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
class PasswordsController < ApplicationController
|
2
2
|
allow_unauthenticated_access
|
3
3
|
before_action :set_user_by_token, only: %i[ edit update ]
|
4
|
+
<%- if defined?(ActionMailer::Railtie) -%>
|
5
|
+
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." }
|
6
|
+
<%- end -%>
|
4
7
|
|
5
8
|
def new
|
6
9
|
end
|
10
|
+
<%- if defined?(ActionMailer::Railtie) -%>
|
7
11
|
|
8
12
|
def create
|
9
13
|
if user = User.find_by(email_address: params[:email_address])
|
@@ -12,12 +16,14 @@ class PasswordsController < ApplicationController
|
|
12
16
|
|
13
17
|
redirect_to new_session_path, notice: "Password reset instructions sent (if user with that email address exists)."
|
14
18
|
end
|
19
|
+
<%- end -%>
|
15
20
|
|
16
21
|
def edit
|
17
22
|
end
|
18
23
|
|
19
24
|
def update
|
20
25
|
if @user.update(params.permit(:password, :password_confirmation))
|
26
|
+
@user.sessions.destroy_all
|
21
27
|
redirect_to new_session_path, notice: "Password has been reset."
|
22
28
|
else
|
23
29
|
redirect_to edit_password_path(params[:token]), alert: "Passwords did not match."
|
data/lib/rails/generators/rails/authentication/templates/app/controllers/sessions_controller.rb.tt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class SessionsController < ApplicationController
|
2
2
|
allow_unauthenticated_access only: %i[ new create ]
|
3
|
-
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to
|
3
|
+
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." }
|
4
4
|
|
5
5
|
def new
|
6
6
|
end
|
@@ -16,6 +16,6 @@ class SessionsController < ApplicationController
|
|
16
16
|
|
17
17
|
def destroy
|
18
18
|
terminate_session
|
19
|
-
redirect_to new_session_path
|
19
|
+
redirect_to new_session_path, status: :see_other
|
20
20
|
end
|
21
21
|
end
|
data/lib/rails/generators/rails/authentication/templates/test/test_helpers/session_test_helper.rb.tt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module SessionTestHelper
|
2
|
+
def sign_in_as(user)
|
3
|
+
Current.session = user.sessions.create!
|
4
|
+
|
5
|
+
ActionDispatch::TestRequest.create.cookie_jar.tap do |cookie_jar|
|
6
|
+
cookie_jar.signed[:session_id] = Current.session.id
|
7
|
+
cookies[:session_id] = cookie_jar[:session_id]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def sign_out
|
12
|
+
Current.session&.destroy!
|
13
|
+
cookies.delete(:session_id)
|
14
|
+
end
|
15
|
+
end
|
@@ -13,7 +13,7 @@ Example:
|
|
13
13
|
script/benchmarks/opt_compare.rb
|
14
14
|
|
15
15
|
You can run the generated benchmark file using:
|
16
|
-
`
|
16
|
+
`bin/rails runner script/benchmarks/opt_compare.rb`
|
17
17
|
|
18
18
|
You can specify different reports:
|
19
19
|
`bin/rails generate benchmark opt_compare patch1 patch2 patch3`
|
@@ -1,3 +1,7 @@
|
|
1
1
|
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
|
2
2
|
ARG RUBY_VERSION=<%= Gem.ruby_version %>
|
3
3
|
FROM ghcr.io/rails/devcontainer/images/ruby:$RUBY_VERSION
|
4
|
+
|
5
|
+
# Ensure binding is always 0.0.0.0
|
6
|
+
# Binds the server to all IP addresses of the container, so it can be accessed from outside the container.
|
7
|
+
ENV BINDING="0.0.0.0"
|
@@ -7,7 +7,7 @@ services:
|
|
7
7
|
dockerfile: .devcontainer/Dockerfile
|
8
8
|
|
9
9
|
volumes:
|
10
|
-
-
|
10
|
+
- ../../<%= options[:app_name] %>:/workspaces/<%= options[:app_name] %>:cached
|
11
11
|
|
12
12
|
# Overrides default command so things don't shut down after the process ends.
|
13
13
|
command: sleep infinity
|
@@ -32,7 +32,7 @@ services:
|
|
32
32
|
|
33
33
|
<%- if options[:redis] -%>
|
34
34
|
redis:
|
35
|
-
image:
|
35
|
+
image: valkey/valkey:8
|
36
36
|
restart: unless-stopped
|
37
37
|
volumes:
|
38
38
|
- redis-data:/data
|
@@ -21,18 +21,20 @@ module Rails
|
|
21
21
|
|
22
22
|
log ""
|
23
23
|
add_key_file_silently(key_path, key)
|
24
|
+
ensure_key_files_are_ignored(key_path)
|
24
25
|
log ""
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
def add_key_file_silently(key_path, key = nil)
|
29
30
|
create_file key_path, key || ActiveSupport::EncryptedFile.generate_key, perm: 0600
|
31
|
+
ensure_key_files_are_ignored_silently(key_path)
|
30
32
|
end
|
31
33
|
|
32
|
-
def
|
34
|
+
def ensure_key_files_are_ignored(key_path, ignore: key_ignore(key_path))
|
33
35
|
if File.exist?(".gitignore")
|
34
36
|
unless File.read(".gitignore").include?(ignore)
|
35
|
-
log "Ignoring #{
|
37
|
+
log "Ignoring #{ignore} so it won't end up in Git history:"
|
36
38
|
log ""
|
37
39
|
append_to_file ".gitignore", ignore
|
38
40
|
log ""
|
@@ -44,13 +46,23 @@ module Rails
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
def
|
48
|
-
|
49
|
+
def ensure_key_files_are_ignored_silently(key_path, ignore: key_ignore(key_path))
|
50
|
+
if File.exist?(".gitignore")
|
51
|
+
unless File.read(".gitignore").include?(ignore)
|
52
|
+
append_to_file ".gitignore", ignore
|
53
|
+
end
|
54
|
+
end
|
49
55
|
end
|
50
56
|
|
51
57
|
private
|
52
58
|
def key_ignore(key_path)
|
53
|
-
|
59
|
+
key_path = Pathname.new(key_path) unless key_path.is_a?(Pathname)
|
60
|
+
<<~IGNORE
|
61
|
+
|
62
|
+
# Ignore key files for decrypting credentials and more.
|
63
|
+
/#{key_path.dirname.join("*.key")}
|
64
|
+
|
65
|
+
IGNORE
|
54
66
|
end
|
55
67
|
end
|
56
68
|
end
|
@@ -32,22 +32,10 @@ module Rails
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def ignore_master_key_file
|
36
|
-
key_file_generator.ignore_key_file(MASTER_KEY_PATH, ignore: key_ignore)
|
37
|
-
end
|
38
|
-
|
39
|
-
def ignore_master_key_file_silently
|
40
|
-
key_file_generator.ignore_key_file_silently(MASTER_KEY_PATH, ignore: key_ignore)
|
41
|
-
end
|
42
|
-
|
43
35
|
private
|
44
36
|
def key_file_generator
|
45
37
|
EncryptionKeyFileGenerator.new([], options)
|
46
38
|
end
|
47
|
-
|
48
|
-
def key_ignore
|
49
|
-
[ "", "# Ignore master key for decrypting credentials and more.", "/#{MASTER_KEY_PATH}", "" ].join("\n")
|
50
|
-
end
|
51
39
|
end
|
52
40
|
end
|
53
41
|
end
|
@@ -3,22 +3,35 @@ name: CI
|
|
3
3
|
on:
|
4
4
|
pull_request:
|
5
5
|
push:
|
6
|
-
branches: [
|
6
|
+
branches: [ <%= user_default_branch %> ]
|
7
7
|
|
8
8
|
jobs:
|
9
9
|
<%- unless skip_rubocop? -%>
|
10
10
|
lint:
|
11
11
|
runs-on: ubuntu-latest
|
12
|
+
env:
|
13
|
+
RUBY_VERSION: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
|
14
|
+
RUBOCOP_CACHE_ROOT: tmp/rubocop
|
12
15
|
steps:
|
13
16
|
- name: Checkout code
|
14
|
-
uses: actions/checkout@
|
17
|
+
uses: actions/checkout@v5
|
15
18
|
|
16
19
|
- name: Set up Ruby
|
17
20
|
uses: ruby/setup-ruby@v1
|
18
21
|
with:
|
19
|
-
ruby-version:
|
22
|
+
ruby-version: ${{ env.RUBY_VERSION }}
|
20
23
|
bundler-cache: true
|
21
24
|
|
25
|
+
- name: Prepare RuboCop cache
|
26
|
+
uses: actions/cache@v4
|
27
|
+
env:
|
28
|
+
DEPENDENCIES_HASH: ${{ hashFiles('**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }}
|
29
|
+
with:
|
30
|
+
path: ${{ env.RUBOCOP_CACHE_ROOT }}
|
31
|
+
key: rubocop-${{ runner.os }}-${{ env.RUBY_VERSION }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
|
32
|
+
restore-keys: |
|
33
|
+
rubocop-${{ runner.os }}-${{ env.RUBY_VERSION }}-${{ env.DEPENDENCIES_HASH }}-
|
34
|
+
|
22
35
|
- name: Lint code for consistent style
|
23
36
|
run: bin/rubocop -f github
|
24
37
|
|
@@ -30,7 +43,7 @@ jobs:
|
|
30
43
|
<%- if options[:database] == "sqlite3" -%>
|
31
44
|
# services:
|
32
45
|
# redis:
|
33
|
-
# image:
|
46
|
+
# image: valkey/valkey:8
|
34
47
|
# ports:
|
35
48
|
# - 6379:6379
|
36
49
|
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
@@ -56,18 +69,15 @@ jobs:
|
|
56
69
|
<%- end -%>
|
57
70
|
|
58
71
|
# redis:
|
59
|
-
# image:
|
72
|
+
# image: valkey/valkey:8
|
60
73
|
# ports:
|
61
74
|
# - 6379:6379
|
62
75
|
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
63
76
|
|
64
77
|
<%- end -%>
|
65
78
|
steps:
|
66
|
-
- name: Install packages
|
67
|
-
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y <%= ci_packages.join(" ") %>
|
68
|
-
|
69
79
|
- name: Checkout code
|
70
|
-
uses: actions/checkout@
|
80
|
+
uses: actions/checkout@v5
|
71
81
|
|
72
82
|
- name: Set up Ruby
|
73
83
|
uses: ruby/setup-ruby@v1
|
@@ -91,6 +101,7 @@ jobs:
|
|
91
101
|
<%- elsif options[:database] == "postgresql" -%>
|
92
102
|
DATABASE_URL: postgres://postgres:postgres@localhost:5432
|
93
103
|
<%- end -%>
|
104
|
+
# RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
|
94
105
|
# REDIS_URL: redis://localhost:6379/0
|
95
106
|
run: <%= test_command %>
|
96
107
|
|
@@ -3,10 +3,10 @@ updates:
|
|
3
3
|
- package-ecosystem: bundler
|
4
4
|
directory: "/"
|
5
5
|
schedule:
|
6
|
-
interval:
|
6
|
+
interval: weekly
|
7
7
|
open-pull-requests-limit: 10
|
8
8
|
- package-ecosystem: github-actions
|
9
9
|
directory: "/"
|
10
10
|
schedule:
|
11
|
-
interval:
|
11
|
+
interval: weekly
|
12
12
|
open-pull-requests-limit: 10
|
@@ -21,7 +21,7 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|
21
21
|
if @<%= orm_instance.save %>
|
22
22
|
render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
|
23
23
|
else
|
24
|
-
render json: <%= "@#{orm_instance.errors}" %>, status:
|
24
|
+
render json: <%= "@#{orm_instance.errors}" %>, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -30,7 +30,7 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|
30
30
|
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
31
31
|
render json: <%= "@#{singular_table_name}" %>
|
32
32
|
else
|
33
|
-
render json: <%= "@#{orm_instance.errors}" %>, status:
|
33
|
+
render json: <%= "@#{orm_instance.errors}" %>, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -27,7 +27,7 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|
27
27
|
if @<%= orm_instance.save %>
|
28
28
|
redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %>
|
29
29
|
else
|
30
|
-
render :new, status:
|
30
|
+
render :new, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,7 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|
36
36
|
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
37
37
|
redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully updated.") %>, status: :see_other
|
38
38
|
else
|
39
|
-
render :edit, status:
|
39
|
+
render :edit, status: <%= ActionDispatch::Constants::UNPROCESSABLE_CONTENT.inspect %>
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -9,6 +9,11 @@ module TestUnit # :nodoc:
|
|
9
9
|
template "test/fixtures/users.yml"
|
10
10
|
template "test/models/user_test.rb"
|
11
11
|
end
|
12
|
+
|
13
|
+
def create_controller_test_files
|
14
|
+
template "test/controllers/sessions_controller_test.rb"
|
15
|
+
template "test/controllers/passwords_controller_test.rb"
|
16
|
+
end
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PasswordsControllerTest < ActionDispatch::IntegrationTest
|
4
|
+
setup { @user = User.take }
|
5
|
+
|
6
|
+
test "new" do
|
7
|
+
get new_password_path
|
8
|
+
assert_response :success
|
9
|
+
end
|
10
|
+
|
11
|
+
test "create" do
|
12
|
+
post passwords_path, params: { email_address: @user.email_address }
|
13
|
+
assert_enqueued_email_with PasswordsMailer, :reset, args: [ @user ]
|
14
|
+
assert_redirected_to new_session_path
|
15
|
+
|
16
|
+
follow_redirect!
|
17
|
+
assert_notice "reset instructions sent"
|
18
|
+
end
|
19
|
+
|
20
|
+
test "create for an unknown user redirects but sends no mail" do
|
21
|
+
post passwords_path, params: { email_address: "missing-user@example.com" }
|
22
|
+
assert_enqueued_emails 0
|
23
|
+
assert_redirected_to new_session_path
|
24
|
+
|
25
|
+
follow_redirect!
|
26
|
+
assert_notice "reset instructions sent"
|
27
|
+
end
|
28
|
+
|
29
|
+
test "edit" do
|
30
|
+
get edit_password_path(@user.password_reset_token)
|
31
|
+
assert_response :success
|
32
|
+
end
|
33
|
+
|
34
|
+
test "edit with invalid password reset token" do
|
35
|
+
get edit_password_path("invalid token")
|
36
|
+
assert_redirected_to new_password_path
|
37
|
+
|
38
|
+
follow_redirect!
|
39
|
+
assert_notice "reset link is invalid"
|
40
|
+
end
|
41
|
+
|
42
|
+
test "update" do
|
43
|
+
assert_changes -> { @user.reload.password_digest } do
|
44
|
+
put password_path(@user.password_reset_token), params: { password: "new", password_confirmation: "new" }
|
45
|
+
assert_redirected_to new_session_path
|
46
|
+
end
|
47
|
+
|
48
|
+
follow_redirect!
|
49
|
+
assert_notice "Password has been reset"
|
50
|
+
end
|
51
|
+
|
52
|
+
test "update with non matching passwords" do
|
53
|
+
token = @user.password_reset_token
|
54
|
+
assert_no_changes -> { @user.reload.password_digest } do
|
55
|
+
put password_path(token), params: { password: "no", password_confirmation: "match" }
|
56
|
+
assert_redirected_to edit_password_path(token)
|
57
|
+
end
|
58
|
+
|
59
|
+
follow_redirect!
|
60
|
+
assert_notice "Passwords did not match"
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def assert_notice(text)
|
65
|
+
assert_select "div", /#{text}/
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class SessionsControllerTest < ActionDispatch::IntegrationTest
|
4
|
+
setup { @user = User.take }
|
5
|
+
|
6
|
+
test "new" do
|
7
|
+
get new_session_path
|
8
|
+
assert_response :success
|
9
|
+
end
|
10
|
+
|
11
|
+
test "create with valid credentials" do
|
12
|
+
post session_path, params: { email_address: @user.email_address, password: "password" }
|
13
|
+
|
14
|
+
assert_redirected_to root_path
|
15
|
+
assert cookies[:session_id]
|
16
|
+
end
|
17
|
+
|
18
|
+
test "create with invalid credentials" do
|
19
|
+
post session_path, params: { email_address: @user.email_address, password: "wrong" }
|
20
|
+
|
21
|
+
assert_redirected_to new_session_path
|
22
|
+
assert_nil cookies[:session_id]
|
23
|
+
end
|
24
|
+
|
25
|
+
test "destroy" do
|
26
|
+
sign_in_as(User.take)
|
27
|
+
|
28
|
+
delete session_path
|
29
|
+
|
30
|
+
assert_redirected_to new_session_path
|
31
|
+
assert_empty cookies[:session_id]
|
32
|
+
end
|
33
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
3
|
class UserTest < ActiveSupport::TestCase
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
test "downcases and strips email_address" do
|
5
|
+
user = User.new(email_address: " DOWNCASED@EXAMPLE.COM ")
|
6
|
+
assert_equal("downcased@example.com", user.email_address)
|
7
|
+
end
|
7
8
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<%= name %>:
|
5
5
|
<% attributes.each do |attribute| -%>
|
6
6
|
<%- if attribute.password_digest? -%>
|
7
|
-
password_digest:
|
7
|
+
password_digest: <%= BCrypt::Password.create("secret") %> # Generated with BCrypt::Password.create("secret")
|
8
8
|
<%- elsif attribute.reference? -%>
|
9
9
|
<%= yaml_key_value(attribute.column_name.delete_suffix("_id"), attribute.default || name) %>
|
10
10
|
<%- elsif !attribute.virtual? -%>
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/class/attribute"
|
4
|
-
require "active_support/core_ext/module/delegation"
|
5
3
|
require "active_support/core_ext/hash/reverse_merge"
|
6
4
|
require "active_support/core_ext/kernel/reporting"
|
7
5
|
require "active_support/testing/stream"
|
8
|
-
require "active_support/concern"
|
9
6
|
require "rails/generators"
|
10
7
|
|
11
8
|
module Rails
|
data/lib/rails/generators.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "action_controller"
|
4
|
+
|
3
5
|
module Rails
|
4
6
|
# Built-in Health Check Endpoint
|
5
7
|
#
|
@@ -41,11 +43,17 @@ module Rails
|
|
41
43
|
|
42
44
|
private
|
43
45
|
def render_up
|
44
|
-
|
46
|
+
respond_to do |format|
|
47
|
+
format.html { render html: html_status(color: "green") }
|
48
|
+
format.json { render json: { status: "up", timestamp: Time.current.iso8601 } }
|
49
|
+
end
|
45
50
|
end
|
46
51
|
|
47
52
|
def render_down
|
48
|
-
|
53
|
+
respond_to do |format|
|
54
|
+
format.html { render html: html_status(color: "red"), status: 500 }
|
55
|
+
format.json { render json: { status: "down", timestamp: Time.current.iso8601 }, status: 500 }
|
56
|
+
end
|
49
57
|
end
|
50
58
|
|
51
59
|
def html_status(color:)
|
data/lib/rails/info.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "active_support/core_ext/erb/util"
|
4
4
|
|
5
5
|
module Rails
|
6
6
|
# This module helps build the runtime properties that are displayed in
|
@@ -43,11 +43,11 @@ module Rails
|
|
43
43
|
def to_html
|
44
44
|
(+"<table>").tap do |table|
|
45
45
|
properties.each do |(name, value)|
|
46
|
-
table << %(<tr><td class="name">#{
|
46
|
+
table << %(<tr><td class="name">#{ERB::Util.html_escape(name.to_s)}</td>)
|
47
47
|
formatted_value = if value.kind_of?(Array)
|
48
|
-
"<ul>" + value.map { |v| "<li>#{
|
48
|
+
"<ul>" + value.map { |v| "<li>#{ERB::Util.html_escape(v.to_s)}</li>" }.join + "</ul>"
|
49
49
|
else
|
50
|
-
|
50
|
+
ERB::Util.html_escape(value.to_s)
|
51
51
|
end
|
52
52
|
table << %(<td class="value">#{formatted_value}</td></tr>)
|
53
53
|
end
|
@@ -33,9 +33,8 @@ class Rails::InfoController < Rails::ApplicationController # :nodoc:
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def notes
|
36
|
-
|
37
|
-
|
38
|
-
).find(
|
36
|
+
tags = params[:tag].presence || Rails::SourceAnnotationExtractor::Annotation.tags.join("|")
|
37
|
+
@annotations = Rails::SourceAnnotationExtractor.new(tags).find(
|
39
38
|
Rails::SourceAnnotationExtractor::Annotation.directories
|
40
39
|
)
|
41
40
|
end
|