From a836f35eb4f8f318ae8d0b1d4e82bc23985806e5 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Tue, 17 Mar 2026 18:07:50 -1000 Subject: [PATCH 1/7] Upgrade Rails to 8.1.2 and modernize defaults --- Gemfile | 3 +- Gemfile.lock | 165 +++++++++--------- bin/ci | 6 + bin/rubocop | 2 +- config/application.rb | 7 +- config/boot.rb | 3 +- config/ci.rb | 22 +++ config/environment.rb | 2 - config/environments/development.rb | 23 +-- config/environments/production.rb | 54 +----- config/environments/test.rb | 4 +- .../application_controller_renderer.rb | 10 -- config/initializers/assets.rb | 2 - .../initializers/content_security_policy.rb | 6 +- config/initializers/cookies_serializer.rb | 7 - config/initializers/cors.rb | 2 - .../initializers/filter_parameter_logging.rb | 6 +- config/initializers/inflections.rb | 2 - config/initializers/per_form_csrf_tokens.rb | 6 - .../request_forgery_protection.rb | 6 - config/puma.rb | 21 +-- 21 files changed, 151 insertions(+), 208 deletions(-) create mode 100755 bin/ci create mode 100644 config/ci.rb delete mode 100644 config/initializers/application_controller_renderer.rb delete mode 100644 config/initializers/cookies_serializer.rb delete mode 100644 config/initializers/per_form_csrf_tokens.rb delete mode 100644 config/initializers/request_forgery_protection.rb diff --git a/Gemfile b/Gemfile index 391565109..49d76fc5d 100644 --- a/Gemfile +++ b/Gemfile @@ -10,12 +10,13 @@ gem "shakapacker", "9.6.1" # Bundle edge Rails instead: gem "rails", github: "rails/rails" gem "listen" -gem "rails", "~> 8.0" +gem "rails", "~> 8.1.2" gem "pg" gem "puma" gem "thruster", "~> 0.1" +gem "bootsnap", require: false # Use SCSS for stylesheets gem "sass-rails" diff --git a/Gemfile.lock b/Gemfile.lock index cc9947747..9c9a86a6c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,31 @@ GEM remote: https://rubygems.org/ specs: - action_text-trix (2.1.15) + action_text-trix (2.1.17) railties - actioncable (8.1.1) - actionpack (= 8.1.1) - activesupport (= 8.1.1) + actioncable (8.1.2) + actionpack (= 8.1.2) + activesupport (= 8.1.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.1.1) - actionpack (= 8.1.1) - activejob (= 8.1.1) - activerecord (= 8.1.1) - activestorage (= 8.1.1) - activesupport (= 8.1.1) + actionmailbox (8.1.2) + actionpack (= 8.1.2) + activejob (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) mail (>= 2.8.0) - actionmailer (8.1.1) - actionpack (= 8.1.1) - actionview (= 8.1.1) - activejob (= 8.1.1) - activesupport (= 8.1.1) + actionmailer (8.1.2) + actionpack (= 8.1.2) + actionview (= 8.1.2) + activejob (= 8.1.2) + activesupport (= 8.1.2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.1.1) - actionview (= 8.1.1) - activesupport (= 8.1.1) + actionpack (8.1.2) + actionview (= 8.1.2) + activesupport (= 8.1.2) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -33,36 +33,36 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.1.1) + actiontext (8.1.2) action_text-trix (~> 2.1.15) - actionpack (= 8.1.1) - activerecord (= 8.1.1) - activestorage (= 8.1.1) - activesupport (= 8.1.1) + actionpack (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.1.1) - activesupport (= 8.1.1) + actionview (8.1.2) + activesupport (= 8.1.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.1.1) - activesupport (= 8.1.1) + activejob (8.1.2) + activesupport (= 8.1.2) globalid (>= 0.3.6) - activemodel (8.1.1) - activesupport (= 8.1.1) - activerecord (8.1.1) - activemodel (= 8.1.1) - activesupport (= 8.1.1) + activemodel (8.1.2) + activesupport (= 8.1.2) + activerecord (8.1.2) + activemodel (= 8.1.2) + activesupport (= 8.1.2) timeout (>= 0.4.0) - activestorage (8.1.1) - actionpack (= 8.1.1) - activejob (= 8.1.1) - activerecord (= 8.1.1) - activesupport (= 8.1.1) + activestorage (8.1.2) + actionpack (= 8.1.2) + activejob (= 8.1.2) + activerecord (= 8.1.2) + activesupport (= 8.1.2) marcel (~> 1.0) - activesupport (8.1.1) + activesupport (8.1.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -82,10 +82,12 @@ GEM execjs (~> 2) awesome_print (1.9.2) base64 (0.3.0) - bigdecimal (3.3.1) + bigdecimal (4.0.1) bindex (0.8.1) binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) + bootsnap (1.23.0) + msgpack (~> 1.2) builder (3.3.0) byebug (11.1.3) capybara (3.40.0) @@ -111,8 +113,8 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.3.5) - connection_pool (2.5.5) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) coveralls_reborn (0.25.0) simplecov (>= 0.18.1, < 0.22.0) term-ansicolor (~> 1.6) @@ -125,7 +127,7 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.5.0) + date (3.5.1) debug (1.9.2) irb (~> 1.10) reline (>= 0.3.8) @@ -133,7 +135,7 @@ GEM diff-lcs (1.5.1) docile (1.4.0) drb (2.2.3) - erb (6.0.0) + erb (6.0.2) erubi (1.13.1) erubis (2.7.0) execjs (2.10.0) @@ -150,18 +152,19 @@ GEM railties (>= 3.0.0) globalid (1.3.0) activesupport (>= 6.1) - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) interception (0.5) - io-console (0.8.1) - irb (1.15.3) + io-console (0.8.2) + irb (1.17.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jbuilder (2.12.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - json (2.16.0) + json (2.19.1) language_server-protocol (3.17.0.5) launchy (3.0.1) addressable (~> 2.8) @@ -170,7 +173,7 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) - loofah (2.24.1) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.9.0) @@ -183,10 +186,13 @@ GEM matrix (0.4.2) method_source (1.1.0) mini_mime (1.1.5) - minitest (5.26.2) + minitest (6.0.2) + drb (~> 2.0) + prism (~> 1.5) mize (0.4.1) protocol (~> 2.0) - net-imap (0.5.12) + msgpack (1.8.0) + net-imap (0.6.3) date net-protocol net-pop (0.1.2) @@ -196,9 +202,9 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.18.10-arm64-darwin) + nokogiri (1.19.1-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) + nokogiri (1.19.1-x86_64-linux-gnu) racc (~> 1.4) package_json (0.2.0) parallel (1.27.0) @@ -229,14 +235,14 @@ GEM pry-stack_explorer (0.6.1) binding_of_caller (~> 1.0) pry (~> 0.13) - psych (5.2.6) + psych (5.3.1) date stringio public_suffix (6.0.2) puma (6.4.2) nio4r (~> 2.0) racc (1.8.1) - rack (3.2.4) + rack (3.2.5) rack-proxy (0.7.7) rack rack-session (2.1.1) @@ -244,28 +250,28 @@ GEM rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 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) + rails (8.1.2) + actioncable (= 8.1.2) + actionmailbox (= 8.1.2) + actionmailer (= 8.1.2) + actionpack (= 8.1.2) + actiontext (= 8.1.2) + actionview (= 8.1.2) + activejob (= 8.1.2) + activemodel (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) bundler (>= 1.15.0) - railties (= 8.1.1) + railties (= 8.1.2) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) 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) rails_best_practices (1.23.2) activesupport @@ -275,9 +281,9 @@ GEM json require_all (~> 3.0) ruby-progressbar - railties (8.1.1) - actionpack (= 8.1.1) - activesupport (= 8.1.1) + railties (8.1.2) + actionpack (= 8.1.2) + activesupport (= 8.1.2) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -289,7 +295,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rdoc (6.15.1) + rdoc (7.2.0) erb psych (>= 4.0.0) tsort @@ -408,17 +414,17 @@ GEM sprockets (>= 3.0.0) stimulus-rails (1.3.3) railties (>= 6.0.0) - stringio (3.1.8) + stringio (3.2.0) strscan (3.1.0) sync (0.5.0) term-ansicolor (1.10.2) mize tins (~> 1.0) - thor (1.4.0) + thor (1.5.0) thruster (0.1.16-arm64-darwin) thruster (0.1.16-x86_64-linux) tilt (2.4.0) - timeout (0.4.4) + timeout (0.6.1) tins (1.33.0) bigdecimal sync @@ -448,7 +454,7 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.36) - zeitwerk (2.7.3) + zeitwerk (2.7.5) PLATFORMS arm64-darwin @@ -459,6 +465,7 @@ PLATFORMS DEPENDENCIES autoprefixer-rails awesome_print + bootsnap capybara capybara-screenshot coffee-rails @@ -479,7 +486,7 @@ DEPENDENCIES pry-rescue pry-stack_explorer puma - rails (~> 8.0) + rails (~> 8.1.2) rails-html-sanitizer rails_best_practices rainbow diff --git a/bin/ci b/bin/ci new file mode 100755 index 000000000..4137ad5bb --- /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/rubocop b/bin/rubocop index 40330c0ff..5a2050471 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/config/application.rb b/config/application.rb index e7f750090..507c68654 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require_relative "boot" require "rails/all" @@ -11,7 +9,7 @@ module RailsReactTutorial 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. @@ -22,8 +20,7 @@ class Application < Rails::Application # # These settings can be overridden in specific environments using the files # in config/environments, which are processed later. - - config.active_support.to_time_preserves_timezone = false + # # config.time_zone = "Central Time (US & Canada)" # config.eager_load_paths << Rails.root.join("extras") end diff --git a/config/boot.rb b/config/boot.rb index c2241d707..988a5ddc4 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,5 +1,4 @@ -# frozen_string_literal: true - ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/ci.rb b/config/ci.rb new file mode 100644 index 000000000..b0fadef76 --- /dev/null +++ b/config/ci.rb @@ -0,0 +1,22 @@ +# Run using bin/ci + +CI.run do + step "Setup", "bin/setup --skip-server" + + step "Style: Ruby", "bin/rubocop" + step "Style: JavaScript", "yarn lint:eslint" + + step "Tests: RSpec", "bin/rspec" + step "Tests: Jest", "yarn test:client" + + # Optional: Run system tests + # step "Tests: System", "bin/rails test:system" + + # 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/environment.rb b/config/environment.rb index 7df99e89c..cac531577 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Load the Rails application. require_relative "application" diff --git a/config/environments/development.rb b/config/environments/development.rb index d7291fd83..885961f81 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "active_support/core_ext/integer/time" Rails.application.configure do @@ -22,13 +20,14 @@ if Rails.root.join("tmp/caching-dev.txt").exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true - config.cache_store = :memory_store config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } else - config.cache_store = :null_store config.action_controller.perform_caching = false end + # Change to :null_store to avoid any caching. + config.cache_store = :memory_store + # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local @@ -38,15 +37,12 @@ # Make template changes take effect immediately. config.action_mailer.perform_caching = false + # Set localhost to be used by links generated in mailer templates. + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Raise exceptions for disallowed deprecations. - config.active_support.disallowed_deprecation = :raise - - # Tell Active Support which deprecation messages to disallow. - config.active_support.disallowed_deprecation_warnings = [] - # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load @@ -59,16 +55,15 @@ # 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 # Annotate rendered view with file names. config.action_view.annotate_rendered_view_with_filenames = true - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker - # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true diff --git a/config/environments/production.rb b/config/environments/production.rb index d4c6934a0..e4659e2e9 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "active_support/core_ext/integer/time" Rails.application.configure do @@ -17,12 +15,7 @@ # Turn on fragment caching in view templates. config.action_controller.perform_caching = true - # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] - # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). - # config.require_master_key = true - - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. + # Serve static files if enabled by the environment. config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? # Cache assets for far-future expiry since they are all digest stamped. @@ -44,15 +37,18 @@ # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } # Log to STDOUT with the current request id as a default log tag. - config.log_tags = [:request_id] - config.logger = ActiveSupport::TaggedLogging.logger($stdout) + 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. config.silence_healthcheck_path = "/up" + # Don't log any deprecations. + config.active_support.report_deprecations = false + # Replace the default in-process memory cache store with a durable alternative. # config.cache_store = :mem_cache_store @@ -66,7 +62,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), @@ -79,36 +75,11 @@ # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - # Don't log any deprecations. - # config.active_support.report_deprecations = false - - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - # Log disallowed deprecations. - config.active_support.disallowed_deprecation = :log - - # Tell Active Support which deprecation messages to disallow. - config.active_support.disallowed_deprecation_warnings = [] - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = Logger::Formatter.new - - # Use a different logger for distributed setups. - # require "syslog/logger" - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new($stdout) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) - end - # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false # Only use :id for inspections in production. - # config.active_record.attributes_for_inspect = [:id] + config.active_record.attributes_for_inspect = [ :id ] # Enable DNS rebinding protection and other `Host` header attacks. # config.hosts = [ @@ -119,14 +90,7 @@ # Skip DNS rebinding protection for the default health check endpoint. # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } - # strategy for connection switching and pass that into the middleware through - # these configuration options. - # config.active_record.database_selector = { delay: 2.seconds } - # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver - # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session - # Action Cable endpoint configuration - config.action_cable.url = "wss://#{ENV.fetch('PRODUCTION_HOST', nil)}/cable" config.action_cable.allowed_request_origins = ["https://#{ENV.fetch('PRODUCTION_HOST', nil)}"] end diff --git a/config/environments/test.rb b/config/environments/test.rb index 012cc3611..c2095b117 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -39,7 +37,7 @@ config.action_mailer.delivery_method = :test # Set host to be used by links generated in mailer templates. - # config.action_mailer.default_url_options = { host: "example.com" } + config.action_mailer.default_url_options = { host: "example.com" } # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb deleted file mode 100644 index 6d56e4390..000000000 --- a/config/initializers/application_controller_renderer.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -# ActiveSupport::Reloader.to_prepare do -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) -# end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 8544c07c5..487324424 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 35ab3fd6a..d51d71397 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Be sure to restart your server when you modify this file. # Define an application-wide content security policy. @@ -22,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/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb deleted file mode 100644 index ee8dff9c9..000000000 --- a/config/initializers/cookies_serializer.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -# Specify a serializer for the signed and encrypted cookie jars. -# Valid options are :json, :marshal, and :hybrid. -Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 7af62e593..0c5dd99ac 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Be sure to restart your server when you modify this file. # Avoid CORS issues when API is called from the frontend app. diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index d81a902e6..c0b717f7e 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,10 +1,8 @@ -# frozen_string_literal: true - # Be sure to restart your server when you modify this file. # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. -Rails.application.config.filter_parameters += %i[ - passw email secret token _key crypt salt certificate otp ssn cvv cvc +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc ] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 9e049dcc9..3860f659e 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/initializers/per_form_csrf_tokens.rb b/config/initializers/per_form_csrf_tokens.rb deleted file mode 100644 index fe50520d0..000000000 --- a/config/initializers/per_form_csrf_tokens.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -# Enable per-form CSRF tokens. -Rails.application.config.action_controller.per_form_csrf_tokens = true diff --git a/config/initializers/request_forgery_protection.rb b/config/initializers/request_forgery_protection.rb deleted file mode 100644 index 0e1994ad2..000000000 --- a/config/initializers/request_forgery_protection.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -# Enable origin-checking CSRF mitigation. -Rails.application.config.action_controller.forgery_protection_origin_check = true diff --git a/config/puma.rb b/config/puma.rb index 80098b36f..38c4b8659 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # This configuration file will be evaluated by Puma. The top-level methods that # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. @@ -9,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 @@ -26,13 +25,8 @@ # Any libraries that use a connection pool or another resource pool should # be configured to provide at least as many connections as the number of # threads. This includes Active Record's `pool` parameter in `database.yml`. -max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } -threads min_threads_count, max_threads_count - -# Specifies the `worker_timeout` threshold that Puma will use to wait before -# terminating a worker in development environments. -worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. port ENV.fetch("PORT", 3000) @@ -40,11 +34,8 @@ # 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 -# plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] - -# Specifies the `environment` that Puma will run in. -environment ENV.fetch("RAILS_ENV", "development") +# 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. # In other environments, only set the PID file if requested. From 6d69ae7d4cf623f4e9b141a3356ce90cea70f5d2 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 22 Mar 2026 20:37:42 -1000 Subject: [PATCH 2/7] Fix RuboCop violations and PRODUCTION_HOST nil safety from PR review - Add frozen_string_literal comments to boot.rb, ci.rb, filter_parameter_logging.rb, and production.rb - Use %i[] notation for symbol array in filter_parameter_logging.rb - Fix Gemfile gem ordering (bootsnap before puma before thruster) - Fix array bracket spacing in production.rb - Use $stdout instead of STDOUT per Style/GlobalStdStream - Remove nil fallback from ENV.fetch('PRODUCTION_HOST') so missing config fails at boot instead of producing broken Action Cable URLs Co-Authored-By: Claude Opus 4.6 (1M context) --- Gemfile | 2 +- config/boot.rb | 2 ++ config/ci.rb | 2 ++ config/environments/production.rb | 12 +++++++----- config/initializers/filter_parameter_logging.rb | 6 ++++-- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 49d76fc5d..18401cbb8 100644 --- a/Gemfile +++ b/Gemfile @@ -14,9 +14,9 @@ gem "rails", "~> 8.1.2" gem "pg" +gem "bootsnap", require: false gem "puma" gem "thruster", "~> 0.1" -gem "bootsnap", require: false # Use SCSS for stylesheets gem "sass-rails" diff --git a/config/boot.rb b/config/boot.rb index 988a5ddc4..aef6d031e 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) require "bundler/setup" # Set up gems listed in the Gemfile. diff --git a/config/ci.rb b/config/ci.rb index b0fadef76..37e6211da 100644 --- a/config/ci.rb +++ b/config/ci.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Run using bin/ci CI.run do diff --git a/config/environments/production.rb b/config/environments/production.rb index e4659e2e9..c96783f1c 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/integer/time" Rails.application.configure do @@ -37,8 +39,8 @@ # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } # Log to STDOUT with the current request id as a default log tag. - config.log_tags = [ :request_id ] - config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) + config.log_tags = [:request_id] + config.logger = ActiveSupport::TaggedLogging.logger($stdout) # Change to "debug" to log everything (including potentially personally-identifiable information!). config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") @@ -79,7 +81,7 @@ config.active_record.dump_schema_after_migration = false # Only use :id for inspections in production. - config.active_record.attributes_for_inspect = [ :id ] + config.active_record.attributes_for_inspect = [:id] # Enable DNS rebinding protection and other `Host` header attacks. # config.hosts = [ @@ -91,6 +93,6 @@ # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } # Action Cable endpoint configuration - config.action_cable.url = "wss://#{ENV.fetch('PRODUCTION_HOST', nil)}/cable" - config.action_cable.allowed_request_origins = ["https://#{ENV.fetch('PRODUCTION_HOST', nil)}"] + config.action_cable.url = "wss://#{ENV.fetch('PRODUCTION_HOST')}/cable" + config.action_cable.allowed_request_origins = ["https://#{ENV.fetch('PRODUCTION_HOST')}"] end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index c0b717f7e..d81a902e6 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. -Rails.application.config.filter_parameters += [ - :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc +Rails.application.config.filter_parameters += %i[ + passw email secret token _key crypt salt certificate otp ssn cvv cvc ] From b3dfe8121483ff66844b4ea6390716d2684b6b07 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Mon, 23 Mar 2026 14:16:23 -1000 Subject: [PATCH 3/7] Add frozen_string_literal comments to fix RuboCop lint CI Co-Authored-By: Claude Opus 4.6 (1M context) --- config/application.rb | 2 ++ config/environment.rb | 2 ++ config/environments/development.rb | 2 ++ config/environments/test.rb | 2 ++ config/initializers/assets.rb | 2 ++ config/initializers/content_security_policy.rb | 2 ++ config/initializers/cors.rb | 2 ++ config/initializers/inflections.rb | 2 ++ config/puma.rb | 2 ++ 9 files changed, 18 insertions(+) diff --git a/config/application.rb b/config/application.rb index 507c68654..2e2d4a56b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "boot" require "rails/all" diff --git a/config/environment.rb b/config/environment.rb index cac531577..7df99e89c 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Load the Rails application. require_relative "application" diff --git a/config/environments/development.rb b/config/environments/development.rb index 885961f81..fa0afc94a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/core_ext/integer/time" Rails.application.configure do diff --git a/config/environments/test.rb b/config/environments/test.rb index c2095b117..3b04bf521 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 487324424..8544c07c5 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index d51d71397..74b18c5a1 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Define an application-wide content security policy. diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 0c5dd99ac..7af62e593 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Avoid CORS issues when API is called from the frontend app. diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 3860f659e..9e049dcc9 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections diff --git a/config/puma.rb b/config/puma.rb index 38c4b8659..284ca2f53 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This configuration file will be evaluated by Puma. The top-level methods that # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. From 182bb69c56b7c13d9cf71e35a388d60ec7e62cf1 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Tue, 24 Mar 2026 21:00:32 -1000 Subject: [PATCH 4/7] Address PR review feedback: fix bin/ci, puma, ActionCable, and config defaults - Rewrite bin/ci to use plain Ruby (TutorialCI module) instead of non-existent ActiveSupport::ContinuousIntegration DSL - Add test asset build step to CI pipeline - Use bin/conductor-exec wrapper for all CI commands - Restore worker_timeout 3600 for development in puma.rb - Restore default thread count to 5 in puma.rb - Comment out solid_queue plugin (gem not in Gemfile) - Make PRODUCTION_HOST optional with conditional guard - Make deprecation reporting configurable via RAILS_REPORT_DEPRECATIONS env var - Restore conditional cache store toggle in development.rb - Preserve to_time_preserves_timezone = false in application.rb - Use action_cable_meta_tag for client-side WebSocket URL construction - Add actionCableUrl.js helper to read cable URL from meta tag Co-Authored-By: Claude Opus 4.6 (1M context) --- app/views/layouts/application.html.erb | 1 + bin/ci | 23 +++++++++--- .../components/CommentBox/CommentBox.jsx | 8 ++--- client/app/controllers/comments_controller.js | 6 ++-- client/app/libs/actionCableUrl.js | 4 +++ config/application.rb | 3 ++ config/ci.rb | 35 ++++++++----------- config/environments/development.rb | 5 ++- config/environments/production.rb | 11 +++--- config/puma.rb | 10 +++--- 10 files changed, 61 insertions(+), 45 deletions(-) create mode 100644 client/app/libs/actionCableUrl.js diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c07a80a76..35c9fbfa9 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -25,6 +25,7 @@ <%= javascript_pack_tag('data-turbolinks-track': true, defer: true) %> <%= csrf_meta_tags %> + <%= action_cable_meta_tag %> <%= yield :body_content %> diff --git a/bin/ci b/bin/ci index 4137ad5bb..164263a82 100755 --- a/bin/ci +++ b/bin/ci @@ -1,6 +1,21 @@ #!/usr/bin/env ruby -require_relative "../config/boot" -require "active_support/continuous_integration" +# frozen_string_literal: true -CI = ActiveSupport::ContinuousIntegration -require_relative "../config/ci.rb" +require "English" +require_relative "../config/ci" + +def run_step(name, command) + puts "\n== #{name} ==" + puts "$ #{command}" + system(command) + return if $CHILD_STATUS.success? + + warn "\nStep failed: #{name}" + exit($CHILD_STATUS.exitstatus || 1) +end + +TutorialCI::STEPS.each do |name, command| + run_step(name, command) +end + +puts "\nAll CI steps passed." diff --git a/client/app/bundles/comments/components/CommentBox/CommentBox.jsx b/client/app/bundles/comments/components/CommentBox/CommentBox.jsx index 3376f74a5..8f00ca349 100644 --- a/client/app/bundles/comments/components/CommentBox/CommentBox.jsx +++ b/client/app/bundles/comments/components/CommentBox/CommentBox.jsx @@ -2,8 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import Immutable from 'immutable'; import _ from 'lodash'; +import * as ActionCable from '@rails/actioncable'; import { injectIntl } from 'react-intl'; import BaseComponent from 'libs/components/BaseComponent'; +import actionCableUrl from 'libs/actionCableUrl'; import SelectLanguage from 'libs/i18n/selectLanguage'; import { defaultMessages, defaultLocale } from 'libs/i18n/default'; import CommentForm from './CommentForm/CommentForm'; @@ -34,11 +36,7 @@ class CommentBox extends BaseComponent { subscribeChannel() { const { messageReceived } = this.props.actions; - const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; - const cableUrl = `${protocol}${window.location.hostname}:${window.location.port}/cable`; - // ActionCable is a global added through webpack.providePlugin - // eslint-disable-next-line no-undef - this.cable = ActionCable.createConsumer(cableUrl); + this.cable = ActionCable.createConsumer(actionCableUrl()); /* eslint no-console: ["error", { allow: ["log"] }] */ this.cable.subscriptions.create( diff --git a/client/app/controllers/comments_controller.js b/client/app/controllers/comments_controller.js index 594d43fbc..222562e8f 100644 --- a/client/app/controllers/comments_controller.js +++ b/client/app/controllers/comments_controller.js @@ -3,6 +3,7 @@ import * as ActionCable from '@rails/actioncable'; import * as marked from 'marked'; import { gfmHeadingId } from 'marked-gfm-heading-id'; import { mangle } from 'marked-mangle'; +import actionCableUrl from 'libs/actionCableUrl'; marked.use(gfmHeadingId()); marked.use(mangle()); @@ -47,10 +48,7 @@ export default class CommentsController extends Controller { connect() { console.log('connected to Stimulus comments_controller'); - const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; - const cableUrl = `${protocol}${window.location.hostname}:${window.location.port}/cable`; - - this.cable = ActionCable.createConsumer(cableUrl); + this.cable = ActionCable.createConsumer(actionCableUrl()); this.cable.subscriptions.create('CommentsChannel', { connected: () => { diff --git a/client/app/libs/actionCableUrl.js b/client/app/libs/actionCableUrl.js new file mode 100644 index 000000000..fabde35ed --- /dev/null +++ b/client/app/libs/actionCableUrl.js @@ -0,0 +1,4 @@ +export default function actionCableUrl() { + const cableMetaTag = document.querySelector("meta[name='action-cable-url']"); + return cableMetaTag ? cableMetaTag.getAttribute('content') : undefined; +} diff --git a/config/application.rb b/config/application.rb index 2e2d4a56b..942a1c508 100644 --- a/config/application.rb +++ b/config/application.rb @@ -13,6 +13,9 @@ class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 8.1 + # Preserve historical app behavior for Time#to_time conversions. + config.active_support.to_time_preserves_timezone = false + # 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. # Common ones are `templates`, `generators`, or `middleware`, for example. diff --git a/config/ci.rb b/config/ci.rb index 37e6211da..eee7b05c9 100644 --- a/config/ci.rb +++ b/config/ci.rb @@ -1,24 +1,17 @@ # frozen_string_literal: true -# Run using bin/ci - -CI.run do - step "Setup", "bin/setup --skip-server" - - step "Style: Ruby", "bin/rubocop" - step "Style: JavaScript", "yarn lint:eslint" - - step "Tests: RSpec", "bin/rspec" - step "Tests: Jest", "yarn test:client" - - # Optional: Run system tests - # step "Tests: System", "bin/rails test:system" - - # 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 +module TutorialCI + STEPS = [ + ["Setup", "bin/conductor-exec bin/setup --skip-server"], + ["Style: Ruby", "bin/conductor-exec bin/rubocop"], + ["Style: JavaScript", "bin/conductor-exec yarn lint:eslint"], + [ + "Build: Test assets", + "RAILS_ENV=test bin/conductor-exec bin/rails react_on_rails:generate_packs && " \ + "bin/conductor-exec yarn res:build && " \ + "bin/conductor-exec yarn build:test", + ], + ["Tests: RSpec", "bin/conductor-exec bin/rspec"], + ["Tests: Jest", "bin/conductor-exec yarn test:client"], + ].freeze end diff --git a/config/environments/development.rb b/config/environments/development.rb index fa0afc94a..6a6e0b625 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -23,13 +23,12 @@ config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } + config.cache_store = :memory_store else config.action_controller.perform_caching = false + config.cache_store = :null_store end - # Change to :null_store to avoid any caching. - config.cache_store = :memory_store - # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local diff --git a/config/environments/production.rb b/config/environments/production.rb index c96783f1c..b2a6ead6f 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -48,8 +48,8 @@ # Prevent health checks from clogging up the logs. config.silence_healthcheck_path = "/up" - # Don't log any deprecations. - config.active_support.report_deprecations = false + # Keep deprecations visible by default so upgrades don't hide breaking changes. + config.active_support.report_deprecations = ENV["RAILS_REPORT_DEPRECATIONS"] != "false" # Replace the default in-process memory cache store with a durable alternative. # config.cache_store = :mem_cache_store @@ -93,6 +93,9 @@ # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } # Action Cable endpoint configuration - config.action_cable.url = "wss://#{ENV.fetch('PRODUCTION_HOST')}/cable" - config.action_cable.allowed_request_origins = ["https://#{ENV.fetch('PRODUCTION_HOST')}"] + production_host = ENV["PRODUCTION_HOST"] + if production_host.present? + config.action_cable.url = "wss://#{production_host}/cable" + config.action_cable.allowed_request_origins = ["https://#{production_host}"] + end end diff --git a/config/puma.rb b/config/puma.rb index 284ca2f53..97208e2f7 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -21,15 +21,17 @@ # Global VM Lock (GVL) it has diminishing returns and will degrade the # response time (latency) of the application. # -# The default is set to 3 threads as it's deemed a decent compromise between -# throughput and latency for the average Rails application. +# The default is 5 threads in the Rails template. # # Any libraries that use a connection pool or another resource pool should # be configured to provide at least as many connections as the number of # threads. This includes Active Record's `pool` parameter in `database.yml`. -threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) threads threads_count, threads_count +# Allow long-running requests in development (e.g. debugger breakpoints). +worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" + # Specifies the `port` that Puma will listen on to receive requests; default is 3000. port ENV.fetch("PORT", 3000) @@ -37,7 +39,7 @@ plugin :tmp_restart # Run the Solid Queue supervisor inside of Puma for single-server deployments. -plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] +# plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] # Specify the PID file. Defaults to tmp/pids/server.pid in development. # In other environments, only set the PID file if requested. From 871e4c0ab7f6cb722b8c47de5d8bd02e9870cefe Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 28 Mar 2026 12:45:41 -1000 Subject: [PATCH 5/7] Fix CI lint offenses and SSR ActionCable loading --- .../bundles/comments/components/CommentBox/CommentBox.jsx | 8 ++++++-- config/ci.rb | 4 ++-- config/environments/production.rb | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/client/app/bundles/comments/components/CommentBox/CommentBox.jsx b/client/app/bundles/comments/components/CommentBox/CommentBox.jsx index 8f00ca349..52c2097fd 100644 --- a/client/app/bundles/comments/components/CommentBox/CommentBox.jsx +++ b/client/app/bundles/comments/components/CommentBox/CommentBox.jsx @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import Immutable from 'immutable'; import _ from 'lodash'; -import * as ActionCable from '@rails/actioncable'; import { injectIntl } from 'react-intl'; import BaseComponent from 'libs/components/BaseComponent'; import actionCableUrl from 'libs/actionCableUrl'; @@ -36,6 +35,11 @@ class CommentBox extends BaseComponent { subscribeChannel() { const { messageReceived } = this.props.actions; + + // Avoid loading ActionCable during server-side rendering. + // eslint-disable-next-line global-require + const actionCableModule = require('@rails/actioncable'); + const ActionCable = actionCableModule.default || actionCableModule; this.cable = ActionCable.createConsumer(actionCableUrl()); /* eslint no-console: ["error", { allow: ["log"] }] */ @@ -62,7 +66,7 @@ class CommentBox extends BaseComponent { } componentWillUnmount() { - this.cable.subscriptions.remove({ channel: 'CommentsChannel' }); + this.cable?.subscriptions.remove({ channel: 'CommentsChannel' }); } refreshComments() { diff --git a/config/ci.rb b/config/ci.rb index eee7b05c9..6a4517cf4 100644 --- a/config/ci.rb +++ b/config/ci.rb @@ -9,9 +9,9 @@ module TutorialCI "Build: Test assets", "RAILS_ENV=test bin/conductor-exec bin/rails react_on_rails:generate_packs && " \ "bin/conductor-exec yarn res:build && " \ - "bin/conductor-exec yarn build:test", + "bin/conductor-exec yarn build:test" ], ["Tests: RSpec", "bin/conductor-exec bin/rspec"], - ["Tests: Jest", "bin/conductor-exec yarn test:client"], + ["Tests: Jest", "bin/conductor-exec yarn test:client"] ].freeze end diff --git a/config/environments/production.rb b/config/environments/production.rb index b2a6ead6f..7ea9db85d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -93,7 +93,7 @@ # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } # Action Cable endpoint configuration - production_host = ENV["PRODUCTION_HOST"] + production_host = ENV.fetch("PRODUCTION_HOST", nil) if production_host.present? config.action_cable.url = "wss://#{production_host}/cable" config.action_cable.allowed_request_origins = ["https://#{production_host}"] From 346a1905cb158ea84cb2be410ca272ee005fd30f Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 28 Mar 2026 17:35:24 -1000 Subject: [PATCH 6/7] Fix ActionCable subscription leaks and remove deprecated config - Store subscription ref from subscriptions.create() in CommentBox and Stimulus comments_controller so cleanup actually works on unmount - Add disconnect() to Stimulus controller to close WebSocket on teardown - Remove deprecated to_time_preserves_timezone setting (no-op in Rails 8.1) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../bundles/comments/components/CommentBox/CommentBox.jsx | 5 +++-- client/app/controllers/comments_controller.js | 7 ++++++- config/application.rb | 3 --- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/app/bundles/comments/components/CommentBox/CommentBox.jsx b/client/app/bundles/comments/components/CommentBox/CommentBox.jsx index 52c2097fd..7f954ce1b 100644 --- a/client/app/bundles/comments/components/CommentBox/CommentBox.jsx +++ b/client/app/bundles/comments/components/CommentBox/CommentBox.jsx @@ -43,7 +43,7 @@ class CommentBox extends BaseComponent { this.cable = ActionCable.createConsumer(actionCableUrl()); /* eslint no-console: ["error", { allow: ["log"] }] */ - this.cable.subscriptions.create( + this.subscription = this.cable.subscriptions.create( { channel: 'CommentsChannel' }, { connected: () => { @@ -66,7 +66,8 @@ class CommentBox extends BaseComponent { } componentWillUnmount() { - this.cable?.subscriptions.remove({ channel: 'CommentsChannel' }); + this.subscription?.unsubscribe(); + this.cable?.disconnect(); } refreshComments() { diff --git a/client/app/controllers/comments_controller.js b/client/app/controllers/comments_controller.js index 222562e8f..82a76fda1 100644 --- a/client/app/controllers/comments_controller.js +++ b/client/app/controllers/comments_controller.js @@ -50,7 +50,7 @@ export default class CommentsController extends Controller { this.cable = ActionCable.createConsumer(actionCableUrl()); - this.cable.subscriptions.create('CommentsChannel', { + this.subscription = this.cable.subscriptions.create('CommentsChannel', { connected: () => { console.log('connected to comments channel using Stimulus controller'); }, @@ -64,4 +64,9 @@ export default class CommentsController extends Controller { }, }); } + + disconnect() { + this.subscription?.unsubscribe(); + this.cable?.disconnect(); + } } diff --git a/config/application.rb b/config/application.rb index 942a1c508..2e2d4a56b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -13,9 +13,6 @@ class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 8.1 - # Preserve historical app behavior for Time#to_time conversions. - config.active_support.to_time_preserves_timezone = false - # 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. # Common ones are `templates`, `generators`, or `middleware`, for example. From 207cbb1292075ec618dbea2ceac58b41e2058aaa Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sat, 28 Mar 2026 21:11:21 -1000 Subject: [PATCH 7/7] Remove unused listen gem The listen gem was only needed for ActiveSupport::EventedFileUpdateChecker in development.rb, which was removed during the Rails 8.1 config modernization. Rails 8.1 defaults to polling without it. Co-Authored-By: Claude Opus 4.6 (1M context) --- Gemfile | 1 - Gemfile.lock | 4 ---- 2 files changed, 5 deletions(-) diff --git a/Gemfile b/Gemfile index 18401cbb8..369613d3a 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,6 @@ gem "react_on_rails", "16.4.0.rc.9" gem "shakapacker", "9.6.1" # Bundle edge Rails instead: gem "rails", github: "rails/rails" -gem "listen" gem "rails", "~> 8.1.2" gem "pg" diff --git a/Gemfile.lock b/Gemfile.lock index 9c9a86a6c..4ceaaad9f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -169,9 +169,6 @@ GEM launchy (3.0.1) addressable (~> 2.8) childprocess (~> 5.0) - listen (3.9.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) loofah (2.25.1) crass (~> 1.0.2) @@ -477,7 +474,6 @@ DEPENDENCIES generator_spec jbuilder launchy - listen pg pry pry-byebug