diff --git a/Gemfile b/Gemfile
index b0670a7..8bc754c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,7 +5,7 @@ ruby file: '.ruby-version'
gem 'nkf'
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
-gem "rails", "~> 8.0.0"
+gem "rails", "~> 8.1.0"
# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem "sprockets-rails"
diff --git a/Gemfile.lock b/Gemfile.lock
index 8dd6c74..e681515 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2,29 +2,31 @@ GEM
remote: https://rubygems.org/
specs:
action_args (2.7.3)
- actioncable (8.0.3)
- actionpack (= 8.0.3)
- activesupport (= 8.0.3)
+ action_text-trix (2.1.15)
+ railties
+ actioncable (8.1.1)
+ actionpack (= 8.1.1)
+ activesupport (= 8.1.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
- actionmailbox (8.0.3)
- actionpack (= 8.0.3)
- activejob (= 8.0.3)
- activerecord (= 8.0.3)
- activestorage (= 8.0.3)
- activesupport (= 8.0.3)
+ actionmailbox (8.1.1)
+ actionpack (= 8.1.1)
+ activejob (= 8.1.1)
+ activerecord (= 8.1.1)
+ activestorage (= 8.1.1)
+ activesupport (= 8.1.1)
mail (>= 2.8.0)
- actionmailer (8.0.3)
- actionpack (= 8.0.3)
- actionview (= 8.0.3)
- activejob (= 8.0.3)
- activesupport (= 8.0.3)
+ actionmailer (8.1.1)
+ actionpack (= 8.1.1)
+ actionview (= 8.1.1)
+ activejob (= 8.1.1)
+ activesupport (= 8.1.1)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
- actionpack (8.0.3)
- actionview (= 8.0.3)
- activesupport (= 8.0.3)
+ actionpack (8.1.1)
+ actionview (= 8.1.1)
+ activesupport (= 8.1.1)
nokogiri (>= 1.8.5)
rack (>= 2.2.4)
rack-session (>= 1.0.1)
@@ -32,44 +34,45 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
- actiontext (8.0.3)
- actionpack (= 8.0.3)
- activerecord (= 8.0.3)
- activestorage (= 8.0.3)
- activesupport (= 8.0.3)
+ actiontext (8.1.1)
+ action_text-trix (~> 2.1.15)
+ actionpack (= 8.1.1)
+ activerecord (= 8.1.1)
+ activestorage (= 8.1.1)
+ activesupport (= 8.1.1)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (8.0.3)
- activesupport (= 8.0.3)
+ actionview (8.1.1)
+ activesupport (= 8.1.1)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
active_decorator (1.5.1)
activesupport
- activejob (8.0.3)
- activesupport (= 8.0.3)
+ activejob (8.1.1)
+ activesupport (= 8.1.1)
globalid (>= 0.3.6)
- activemodel (8.0.3)
- activesupport (= 8.0.3)
- activerecord (8.0.3)
- activemodel (= 8.0.3)
- activesupport (= 8.0.3)
+ activemodel (8.1.1)
+ activesupport (= 8.1.1)
+ activerecord (8.1.1)
+ activemodel (= 8.1.1)
+ activesupport (= 8.1.1)
timeout (>= 0.4.0)
- activestorage (8.0.3)
- actionpack (= 8.0.3)
- activejob (= 8.0.3)
- activerecord (= 8.0.3)
- activesupport (= 8.0.3)
+ activestorage (8.1.1)
+ actionpack (= 8.1.1)
+ activejob (= 8.1.1)
+ activerecord (= 8.1.1)
+ activesupport (= 8.1.1)
marcel (~> 1.0)
- activesupport (8.0.3)
+ activesupport (8.1.1)
base64
- benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
+ json
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
@@ -98,7 +101,6 @@ GEM
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.3.0)
- benchmark (0.4.1)
bigdecimal (3.3.1)
bindex (0.8.1)
bootsnap (1.18.6)
@@ -217,20 +219,20 @@ GEM
rack (>= 1.3)
rackup (2.2.1)
rack (>= 3)
- rails (8.0.3)
- actioncable (= 8.0.3)
- actionmailbox (= 8.0.3)
- actionmailer (= 8.0.3)
- actionpack (= 8.0.3)
- actiontext (= 8.0.3)
- actionview (= 8.0.3)
- activejob (= 8.0.3)
- activemodel (= 8.0.3)
- activerecord (= 8.0.3)
- activestorage (= 8.0.3)
- activesupport (= 8.0.3)
+ rails (8.1.1)
+ actioncable (= 8.1.1)
+ actionmailbox (= 8.1.1)
+ actionmailer (= 8.1.1)
+ actionpack (= 8.1.1)
+ actiontext (= 8.1.1)
+ actionview (= 8.1.1)
+ activejob (= 8.1.1)
+ activemodel (= 8.1.1)
+ activerecord (= 8.1.1)
+ activestorage (= 8.1.1)
+ activesupport (= 8.1.1)
bundler (>= 1.15.0)
- railties (= 8.0.3)
+ railties (= 8.1.1)
rails-dom-testing (2.3.0)
activesupport (>= 5.0.0)
minitest
@@ -238,9 +240,9 @@ GEM
rails-html-sanitizer (1.6.2)
loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
- railties (8.0.3)
- actionpack (= 8.0.3)
- activesupport (= 8.0.3)
+ railties (8.1.1)
+ actionpack (= 8.1.1)
+ activesupport (= 8.1.1)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
@@ -362,7 +364,7 @@ DEPENDENCIES
nkf
pg
puma (>= 5.0)
- rails (~> 8.0.0)
+ rails (~> 8.1.0)
rubocop
selenium-webdriver
simplecov
diff --git a/app/models/message.rb b/app/models/message.rb
index 259d638..a2eca62 100644
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -77,9 +77,9 @@ def from_mail(mail, list, list_seq)
file = StringIO.new(part.decoded)
attachments.attach(io: file, filename: part.filename || 'noname', content_type: part.content_type)
when /^text\/plain/, /text\/enriched;/, 'message/rfc822', nil
- (self.body ||= '') << Kconv.toutf8(part.body.raw_source)
+ (self.body ||= ''.dup) << Kconv.toutf8(part.body.raw_source)
when /^text\/html/
- (self.html_body ||= '') << Kconv.toutf8(part.body.raw_source)
+ (self.html_body ||= ''.dup) << Kconv.toutf8(part.body.raw_source)
else
puts "Unknown content_type: #{part.content_type}"
end
diff --git a/bin/bundler-audit b/bin/bundler-audit
new file mode 100755
index 0000000..e2ef226
--- /dev/null
+++ b/bin/bundler-audit
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+require_relative "../config/boot"
+require "bundler/audit/cli"
+
+ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check")
+Bundler::Audit::CLI.start
diff --git a/bin/ci b/bin/ci
new file mode 100755
index 0000000..4137ad5
--- /dev/null
+++ b/bin/ci
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+require_relative "../config/boot"
+require "active_support/continuous_integration"
+
+CI = ActiveSupport::ContinuousIntegration
+require_relative "../config/ci.rb"
diff --git a/bin/dev b/bin/dev
index ad72c7d..5f91c20 100755
--- a/bin/dev
+++ b/bin/dev
@@ -1,16 +1,2 @@
-#!/usr/bin/env sh
-
-if ! gem list foreman -i --silent; then
- echo "Installing foreman..."
- gem install foreman
-fi
-
-# Default to port 3000 if not specified
-export PORT="${PORT:-3000}"
-
-# Let the debug gem allow remote connections,
-# but avoid loading until `debugger` is called
-export RUBY_DEBUG_OPEN="true"
-export RUBY_DEBUG_LAZY="true"
-
-exec foreman start -f Procfile.dev "$@"
+#!/usr/bin/env ruby
+exec "./bin/rails", "server", *ARGV
diff --git a/bin/rubocop b/bin/rubocop
index 40330c0..5a20504 100755
--- a/bin/rubocop
+++ b/bin/rubocop
@@ -2,7 +2,7 @@
require "rubygems"
require "bundler/setup"
-# explicit rubocop config increases performance slightly while avoiding config confusion.
+# Explicit RuboCop config increases performance slightly while avoiding config confusion.
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
load Gem.bin_path("rubocop", "rubocop")
diff --git a/bin/setup b/bin/setup
index be3db3c..81be011 100755
--- a/bin/setup
+++ b/bin/setup
@@ -22,6 +22,7 @@ FileUtils.chdir APP_ROOT do
puts "\n== Preparing database =="
system! "bin/rails db:prepare"
+ system! "bin/rails db:reset" if ARGV.include?("--reset")
puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"
diff --git a/config/application.rb b/config/application.rb
index 41a71e3..7ef56cb 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -9,7 +9,7 @@
module BladeRubyLangOrg
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
- config.load_defaults 8.0
+ config.load_defaults 8.1
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
diff --git a/config/bundler-audit.yml b/config/bundler-audit.yml
new file mode 100644
index 0000000..e74b3af
--- /dev/null
+++ b/config/bundler-audit.yml
@@ -0,0 +1,5 @@
+# Audit all gems listed in the Gemfile for known security problems by running bin/bundler-audit.
+# CVEs that are not relevant to the application can be enumerated on the ignore list below.
+
+ignore:
+ - CVE-THAT-DOES-NOT-APPLY
diff --git a/config/ci.rb b/config/ci.rb
new file mode 100644
index 0000000..e56a92e
--- /dev/null
+++ b/config/ci.rb
@@ -0,0 +1,23 @@
+# Run using bin/ci
+
+CI.run do
+ step "Setup", "bin/setup --skip-server"
+
+ step "Style: Ruby", "bin/rubocop"
+
+ step "Security: Gem audit", "bin/bundler-audit"
+ step "Security: Importmap vulnerability audit", "bin/importmap audit"
+ step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
+
+ step "Tests: Rails", "bin/rails test"
+ step "Tests: System", "bin/rails test:system"
+ step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
+
+ # Optional: set a green GitHub commit status to unblock PR merge.
+ # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
+ # if success?
+ # step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
+ # else
+ # failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
+ # end
+end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 4cc21c4..885961f 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -55,6 +55,9 @@
# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true
+ # Highlight code that triggered redirect in logs.
+ config.action_dispatch.verbose_redirect_logs = true
+
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 1749607..90824bc 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -37,7 +37,7 @@
config.log_tags = [ :request_id ]
config.logger = ActiveSupport::TaggedLogging.logger(STDOUT)
- # Change to "debug" to log everything (including potentially personally-identifiable information!)
+ # Change to "debug" to log everything (including potentially personally-identifiable information!).
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
# Prevent health checks from clogging up the logs.
@@ -59,7 +59,7 @@
# Set host to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: "example.com" }
- # Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit.
+ # Specify outgoing SMTP server. Remember to add smtp/* credentials via bin/rails credentials:edit.
# config.action_mailer.smtp_settings = {
# user_name: Rails.application.credentials.dig(:smtp, :user_name),
# password: Rails.application.credentials.dig(:smtp, :password),
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index b3076b3..d51d713 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -20,6 +20,10 @@
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
+# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag`
+# # if the corresponding directives are specified in `content_security_policy_nonce_directives`.
+# # config.content_security_policy_nonce_auto = true
+#
# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end
diff --git a/config/puma.rb b/config/puma.rb
index a248513..38c4b86 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -7,7 +7,8 @@
#
# You can control the number of workers using ENV["WEB_CONCURRENCY"]. You
# should only set this value when you want to run 2 or more workers. The
-# default is already 1.
+# default is already 1. You can set it to `auto` to automatically start a worker
+# for each available processor.
#
# The ideal number of threads per worker depends both on how much time the
# application spends waiting for IO operations and on how much you wish to
@@ -33,7 +34,7 @@
# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart
-# Run the Solid Queue supervisor inside of Puma for single-server deployments
+# Run the Solid Queue supervisor inside of Puma for single-server deployments.
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
diff --git a/public/400.html b/public/400.html
index 282dbc8..640de03 100644
--- a/public/400.html
+++ b/public/400.html
@@ -35,12 +35,35 @@
font-weight: 400;
letter-spacing: -0.0025em;
line-height: 1.4;
- min-height: 100vh;
+ min-height: 100dvh;
place-items: center;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
+ #error-description {
+ fill: #d30001;
+ }
+
+ #error-id {
+ fill: #f0eff0;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ body {
+ background: #101010;
+ color: #e0e0e0;
+ }
+
+ #error-description {
+ fill: #FF6161;
+ }
+
+ #error-id {
+ fill: #2c2c2c;
+ }
+ }
+
a {
color: inherit;
font-weight: 700;
@@ -83,13 +106,11 @@
}
main article br {
-
display: none;
@media(min-width: 48em) {
display: inline;
}
-
}
@@ -102,10 +123,10 @@
- The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.
+ The server cannot process the request due to a client error. Please check the request and try again. If you're the application owner check the logs for more information.
diff --git a/public/404.html b/public/404.html
index c0670bc..d7f0f14 100644
--- a/public/404.html
+++ b/public/404.html
@@ -4,7 +4,7 @@
- The page you were looking for doesn’t exist (404 Not found)
+ The page you were looking for doesn't exist (404 Not found)
@@ -35,12 +35,35 @@
font-weight: 400;
letter-spacing: -0.0025em;
line-height: 1.4;
- min-height: 100vh;
+ min-height: 100dvh;
place-items: center;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
+ #error-description {
+ fill: #d30001;
+ }
+
+ #error-id {
+ fill: #f0eff0;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ body {
+ background: #101010;
+ color: #e0e0e0;
+ }
+
+ #error-description {
+ fill: #FF6161;
+ }
+
+ #error-id {
+ fill: #2c2c2c;
+ }
+ }
+
a {
color: inherit;
font-weight: 700;
@@ -83,13 +106,11 @@
}
main article br {
-
display: none;
@media(min-width: 48em) {
display: inline;
}
-
}
@@ -102,10 +123,10 @@
- The page you were looking for doesn’t exist. You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.
+ The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved. If you're the application owner check the logs for more information.
diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html
index 9532a9c..43d2811 100644
--- a/public/406-unsupported-browser.html
+++ b/public/406-unsupported-browser.html
@@ -35,12 +35,35 @@
font-weight: 400;
letter-spacing: -0.0025em;
line-height: 1.4;
- min-height: 100vh;
+ min-height: 100dvh;
place-items: center;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
+ #error-description {
+ fill: #d30001;
+ }
+
+ #error-id {
+ fill: #f0eff0;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ body {
+ background: #101010;
+ color: #e0e0e0;
+ }
+
+ #error-description {
+ fill: #FF6161;
+ }
+
+ #error-id {
+ fill: #2c2c2c;
+ }
+ }
+
a {
color: inherit;
font-weight: 700;
@@ -83,13 +106,11 @@
}
main article br {
-
display: none;
@media(min-width: 48em) {
display: inline;
}
-
}
@@ -102,7 +123,7 @@
Your browser is not supported.
Please upgrade your browser to continue.
diff --git a/public/422.html b/public/422.html
index 8bcf060..f12fb4a 100644
--- a/public/422.html
+++ b/public/422.html
@@ -35,12 +35,35 @@
font-weight: 400;
letter-spacing: -0.0025em;
line-height: 1.4;
- min-height: 100vh;
+ min-height: 100dvh;
place-items: center;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
+ #error-description {
+ fill: #d30001;
+ }
+
+ #error-id {
+ fill: #f0eff0;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ body {
+ background: #101010;
+ color: #e0e0e0;
+ }
+
+ #error-description {
+ fill: #FF6161;
+ }
+
+ #error-id {
+ fill: #2c2c2c;
+ }
+ }
+
a {
color: inherit;
font-weight: 700;
@@ -83,13 +106,11 @@
}
main article br {
-
display: none;
@media(min-width: 48em) {
display: inline;
}
-
}
@@ -102,10 +123,10 @@
- The change you wanted was rejected. Maybe you tried to change something you didn’t have access to. If you’re the application owner check the logs for more information.
+ The change you wanted was rejected. Maybe you tried to change something you didn't have access to. If you're the application owner check the logs for more information.
diff --git a/public/500.html b/public/500.html
index d77718c..e4eb18a 100644
--- a/public/500.html
+++ b/public/500.html
@@ -4,7 +4,7 @@
- We’re sorry, but something went wrong (500 Internal Server Error)
+ We're sorry, but something went wrong (500 Internal Server Error)
@@ -35,12 +35,35 @@
font-weight: 400;
letter-spacing: -0.0025em;
line-height: 1.4;
- min-height: 100vh;
+ min-height: 100dvh;
place-items: center;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
+ #error-description {
+ fill: #d30001;
+ }
+
+ #error-id {
+ fill: #f0eff0;
+ }
+
+ @media (prefers-color-scheme: dark) {
+ body {
+ background: #101010;
+ color: #e0e0e0;
+ }
+
+ #error-description {
+ fill: #FF6161;
+ }
+
+ #error-id {
+ fill: #2c2c2c;
+ }
+ }
+
a {
color: inherit;
font-weight: 700;
@@ -83,13 +106,11 @@
}
main article br {
-
display: none;
@media(min-width: 48em) {
display: inline;
}
-
}
@@ -102,10 +123,10 @@
- We’re sorry, but something went wrong.
If you’re the application owner check the logs for more information.
+ We're sorry, but something went wrong.
If you're the application owner check the logs for more information.