From 5c1e44655f3fa1722aee24308a5f0ce75615a4f6 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:24:22 +0300 Subject: [PATCH 01/14] Switch to the zeitwerk autoloader Preparation for Rails 7+: config.autoloader = :classic has no effect there, the option and the classic autoloader itself were removed in 7.0. Doing the switch here, still on 6.1, keeps autoloading failures apart from framework upgrade failures. The only file that was not zeitwerk compatible is the bounce concern. app/models/concerns is an autoload root of its own, so a file sitting at concerns/contact_request/bounceable.rb had to define ContactRequest::Bounceable, while it actually defined Concerns::ContactRequest::Bounceable - an extra namespace level that only the classic autoloader tolerated. Moved to concerns/bounceable.rb as a plain top level Bounceable, which is where a single concern belongs anyway; reopening the ContactRequest class from inside a second autoload root would have worked too, but it buys a namespace we have no other use for at the price of an explicit-namespace load order. rails zeitwerk:check runs in CI from now on, so the next mismatch is a failed build instead of a failed deploy. --- .github/workflows/ruby.yml | 4 ++ app/models/concerns/bounceable.rb | 37 +++++++++++++++++ .../concerns/contact_request/bounceable.rb | 41 ------------------- app/models/contact_request.rb | 2 +- config/application.rb | 1 - 5 files changed, 42 insertions(+), 43 deletions(-) create mode 100644 app/models/concerns/bounceable.rb delete mode 100644 app/models/concerns/contact_request/bounceable.rb diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 4fd2d00c..648ea4b7 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -53,6 +53,10 @@ jobs: cp config/application-example.yml config/application.yml cp config/database-travis.yml config/database.yml bundle exec rake db:setup + # Guards the autoloader: classic was removed in Rails 7.0, so a file whose + # constant does not match its path stops being a style question and becomes a + # boot failure. Cheaper to catch here than on deploy. + bundle exec rails zeitwerk:check bundle exec rails test test/* # ------------------------------ diff --git a/app/models/concerns/bounceable.rb b/app/models/concerns/bounceable.rb new file mode 100644 index 00000000..9bd65bd3 --- /dev/null +++ b/app/models/concerns/bounceable.rb @@ -0,0 +1,37 @@ +module Bounceable + extend ActiveSupport::Concern + + def registrant_bounced?(bounced_recipients) + registrant_email = whois_record.json['email'] + bounced_recipients.as_json.find { |r| break true if r['emailAddress'] == registrant_email } + end + + class_methods do + def send_bounce_alert(json) + contact_request = find_by(message_id: json['mail']['messageId']) + log_to_registry(json) + return unless contact_request&.registrant_bounced?(json['bounce']['bouncedRecipients']) + + BounceBackMailer.bounce_alert( + contact_request.email, contact_request.whois_record['name'], contact_request.name + ).deliver_now + end + + def log_to_registry(json) + return unless ENV['bounces_api_url'] + + uri = URI.parse(ENV['bounces_api_url']) + secret = ENV['bounces_api_shared_key'] + + header = { 'Content-Type': 'application/json', 'Authorization': "Basic #{secret}" } + body = { data: json } + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true if uri.instance_of?(URI::HTTPS) + request = Net::HTTP::Post.new(uri.request_uri, header) + request.body = body.to_json + + http.request(request) + end + end +end diff --git a/app/models/concerns/contact_request/bounceable.rb b/app/models/concerns/contact_request/bounceable.rb deleted file mode 100644 index 6ac968a2..00000000 --- a/app/models/concerns/contact_request/bounceable.rb +++ /dev/null @@ -1,41 +0,0 @@ -module Concerns - module ContactRequest - module Bounceable - extend ActiveSupport::Concern - - def registrant_bounced?(bounced_recipients) - registrant_email = whois_record.json['email'] - bounced_recipients.as_json.find { |r| break true if r['emailAddress'] == registrant_email } - end - - class_methods do - def send_bounce_alert(json) - contact_request = find_by(message_id: json['mail']['messageId']) - log_to_registry(json) - return unless contact_request&.registrant_bounced?(json['bounce']['bouncedRecipients']) - - BounceBackMailer.bounce_alert( - contact_request.email, contact_request.whois_record['name'], contact_request.name - ).deliver_now - end - - def log_to_registry(json) - return unless ENV['bounces_api_url'] - - uri = URI.parse(ENV['bounces_api_url']) - secret = ENV['bounces_api_shared_key'] - - header = { 'Content-Type': 'application/json', 'Authorization': "Basic #{secret}" } - body = { data: json } - - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true if uri.instance_of?(URI::HTTPS) - request = Net::HTTP::Post.new(uri.request_uri, header) - request.body = body.to_json - - http.request(request) - end - end - end - end -end diff --git a/app/models/contact_request.rb b/app/models/contact_request.rb index 9f830ffe..0cd58d38 100644 --- a/app/models/contact_request.rb +++ b/app/models/contact_request.rb @@ -1,5 +1,5 @@ class ContactRequest < ApplicationRecord - include Concerns::ContactRequest::Bounceable + include Bounceable def self.connect_to_write_database_if_defined return unless Rails.configuration.database_configuration["write_#{Rails.env}"] diff --git a/config/application.rb b/config/application.rb index 1c13b0a0..a238058d 100644 --- a/config/application.rb +++ b/config/application.rb @@ -10,7 +10,6 @@ module RestWhois class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 6.0 - config.autoloader = :classic # Authorize all hosts config.hosts.clear From 79e39861692543785253a020fc86b16a348981a7 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:31:55 +0300 Subject: [PATCH 02/14] Upgrade to Rails 8.1 on Ruby 3.4 Rails 6.1 stopped receiving security fixes on 1 October 2024. The obvious target, 7.2, is not a target any more either: that series ended its security-only window on 9 August 2026, twelve days ago, which is what the open Snyk pull request would have upgraded us to. 8.1 is the only series with a future - bug fixes until 10 October 2026, security fixes until 10 October 2027 - so this goes straight there. Ruby needs no change: .ruby-version already says 3.4.9, which is both above the 3.2 that Rails 8.1 requires and the version the guides recommend for it. CI, however, was still pinned to 3.0.3 in the matrix, which is how a Rails version the app cannot even boot on could have passed. It now reads .ruby-version like the servers do. Along the way: * lib/tasks/db.rake called ActiveRecord::Base.clear_all_connections!, which was a delegation to the connection handler that 6.1 still had and 7.1 removed. Nothing covers that task in tests - it migrates both production databases - so it would have failed silently until a deploy ran it. * mimemagic is gone. Active Storage depended on it up to Rails 5.2 and has used marcel since 6.0; nothing in this application references it, and nothing in the lockfile depends on it either. * simplecov is unpinned. The 0.17.1 pin carried the comment "CC last supported v0.17", but coverage has been going to qlty for a while now. Ruby 3.4 also brings bundler 2.6 to the servers, which finally makes force_ruby_platform understood there, so pg can stay on 1.6.x and be compiled from source instead of being held back to 1.5.x. --- .github/workflows/ruby.yml | 3 - .repowise/sessions/sessions.db | Bin 0 -> 20480 bytes Gemfile | 14 +- Gemfile.lock | 401 +++++++++++++++++++-------------- lib/tasks/db.rake | 4 +- 5 files changed, 242 insertions(+), 180 deletions(-) create mode 100644 .repowise/sessions/sessions.db diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 648ea4b7..fff289ea 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -20,9 +20,6 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04] - # Read from .ruby-version instead of a second copy of the number here. CI used to be - # pinned to 3.0.3 while .ruby-version had been bumped to 3.4.9, so the suite stayed - # green on a Ruby the servers no longer ran and the breakage surfaced only on deploy. ruby: ['.ruby-version'] runs-on: ${{ matrix.os }} continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} diff --git a/.repowise/sessions/sessions.db b/.repowise/sessions/sessions.db new file mode 100644 index 0000000000000000000000000000000000000000..1ee8ddabeaa4c1a038fe11d4c095696ee75da2a6 GIT binary patch literal 20480 zcmeI(O>fgM7zc1CYu5oQ-33+RQddZfN-b(>soD#ogD_2mF-j`YQ{*Nd4Wx;PU0yC6 zn1sZ)LfV1Puus5O;D}SP^aZt*cABQY6{n9a%d&rua&V%)er0-+)8jB2F-hxWhv+(a zOerBmS2eBbd5PPp^M?A>X5wX`PVRs0XLEl@YWs}byq!DUKFj`IHW>X7fB*y_009U< z00Izz00b^1@a@J%CSNFMCtR|D&xc|7-j2qBNP^UpuFE&?37{XFZMai_XRyTw2XbDn@m8T8qYdC)1p;1)yR{QyDP`Y zP%_^ht)A$pd+?&u?bGK*e|G8r7F(wA@=B#zYBO&XG~xv=|13WE>da@eu?#0|JK4Nl zOTzTYT`iL@7PasB37{9e{9I+@+fIB#fdB*`009U<00Izz00bZa0SG|gstR0> zH-_{7RZTDU1px>^00Izz00bZa0SG_<0uY!8#NYq5+)tt|6bL{70uX=z1Rwwb2tWV= z5P$##E~&sHEz`SmOW)Gld@vX^s}C#9R^6#I+Ji==-E6lj4_eg*YgKE!QEeWDLR#wo zAU(nNm>6n5YNzq{e=YZms0#%G5P$##AOHafKmY;|fB*y_0D;Rb&`PVf0M{ 1.40' gem 'bootsnap', '~> 1.18.0', require: false gem 'figaro', '~> 1.3.0' gem 'jbuilder' -gem 'mimemagic', '~> 0.4.3' gem 'passenger', '>= 5.3.2', require: 'phusion_passenger/rack_handler' -# Held on 1.5.x deliberately. 1.6.0 is the first release that publishes an x86_64-linux binary, -# and it is linked against glibc 2.29, which is newer than the deploy servers: the binary loads -# with "libm.so.6: version `GLIBC_2.29' not found". 1.5.x has no Linux binary at all, so it is -# always compiled from source there. The clean way back to 1.6.x is a newer distribution on the -# servers - or, as a stopgap, Bundler >= 2.3.18 on them plus force_ruby_platform on this line, -# which the Bundler currently installed there is too old to understand. -gem 'pg', '~> 1.5.9' -gem 'rails', '>= 6.0.3.1' + +gem 'pg', '~> 1.6.3', force_ruby_platform: true +gem 'rails', '~> 8.1.3' gem 'recaptcha', '~> 5.21', require: 'recaptcha/rails' gem 'sassc', '~> 2.4' gem 'sassc-rails' @@ -36,5 +30,5 @@ end group :test do gem 'selenium-webdriver' - gem 'simplecov', '0.17.1', require: false # CC last supported v0.17 + gem 'simplecov', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 1089865b..0e136e52 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,194 +10,259 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) + action_text-trix (2.1.19) + railties + actioncable (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) - mail (>= 2.7.1) - actionmailer (6.1.4.1) - actionpack (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activesupport (= 6.1.4.1) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.1.4.1) - actionview (= 6.1.4.1) - activesupport (= 6.1.4.1) - rack (~> 2.0, >= 2.0.9) + zeitwerk (~> 2.6) + actionmailbox (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) + mail (>= 2.8.0) + actionmailer (8.1.3.1) + actionpack (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activesupport (= 8.1.3.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (8.1.3.1) + actionview (= 8.1.3.1) + activesupport (= 8.1.3.1) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.4.1) - actionpack (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (8.1.3.1) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) + globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (6.1.4.1) - activesupport (= 6.1.4.1) + actionview (8.1.3.1) + activesupport (= 8.1.3.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.1.4.1) - activesupport (= 6.1.4.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.3.6) - activemodel (6.1.4.1) - activesupport (= 6.1.4.1) - activerecord (6.1.4.1) - activemodel (= 6.1.4.1) - activesupport (= 6.1.4.1) - activestorage (6.1.4.1) - actionpack (= 6.1.4.1) - activejob (= 6.1.4.1) - activerecord (= 6.1.4.1) - activesupport (= 6.1.4.1) - marcel (~> 1.0.0) - mini_mime (>= 1.1.0) - activesupport (6.1.4.1) - concurrent-ruby (~> 1.0, >= 1.0.2) + activemodel (8.1.3.1) + activesupport (= 8.1.3.1) + activerecord (8.1.3.1) + activemodel (= 8.1.3.1) + activesupport (= 8.1.3.1) + timeout (>= 0.4.0) + activestorage (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activesupport (= 8.1.3.1) + marcel (~> 1.0) + activesupport (8.1.3.1) + base64 + 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) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - aws-eventstream (1.1.1) - aws-partitions (1.482.0) - aws-sdk-core (3.119.0) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-ses (1.40.0) - aws-sdk-core (~> 3, >= 3.119.0) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.4) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + aws-eventstream (1.4.0) + aws-partitions (1.1281.0) + aws-sdk-core (3.254.1) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-ses (1.102.0) + aws-sdk-core (~> 3, >= 3.254.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) base64 (0.3.0) - bootsnap (1.18.3) + bigdecimal (4.1.2) + bootsnap (1.18.6) msgpack (~> 1.2) - builder (3.2.4) - capybara (3.35.3) + builder (3.3.0) + capybara (3.40.0) addressable + matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (3.0.0) coderay (1.1.3) - concurrent-ruby (1.1.9) - crack (0.4.5) + concurrent-ruby (1.3.8) + connection_pool (3.0.2) + crack (1.0.1) + bigdecimal rexml - crass (1.0.6) - docile (1.3.2) - erubi (1.10.0) - execjs (2.10.1) + crass (1.0.7) + date (3.5.1) + drb (2.2.3) + erb (6.0.7) + erubi (1.13.1) + execjs (2.10.2) + json (>= 2) ffi (1.17.4) figaro (1.3.0) thor (>= 0.14.0, < 2) - globalid (0.5.2) - activesupport (>= 5.0) - hashdiff (1.0.1) - i18n (1.8.10) + globalid (1.4.0) + activesupport (>= 6.1) + hashdiff (1.2.1) + i18n (1.15.2) concurrent-ruby (~> 1.0) - jbuilder (2.11.2) - activesupport (>= 5.0.0) - jmespath (1.6.1) - json (2.19.9) + io-console (0.9.2) + irb (1.18.0) + pp (>= 0.6.0) + prism (>= 1.3.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.15.1) + actionview (>= 7.0.0) + activesupport (>= 7.0.0) + jmespath (1.6.2) + json (2.21.2) kgio (2.11.4) listen (3.10.0) logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) - loofah (2.19.1) + loofah (2.25.2) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.9.1) + logger mini_mime (>= 0.1.1) - marcel (1.0.1) - method_source (1.0.0) - mimemagic (0.4.3) - nokogiri (~> 1) - rake - mina (1.2.4) - open4 (~> 1.3.4) + net-imap + net-pop + net-smtp + marcel (1.2.1) + matrix (0.4.3) + method_source (1.1.0) + mina (1.2.5) rake - mini_mime (1.1.1) - mini_portile2 (2.8.0) - minitest (5.14.4) - msgpack (1.8.2) - nio4r (2.5.9) - nokogiri (1.13.10) - mini_portile2 (~> 2.8.0) + mini_mime (1.1.5) + mini_portile2 (2.8.9) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) + msgpack (1.8.4) + net-imap (0.6.6) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.5) + nokogiri (1.19.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - open4 (1.3.4) - passenger (6.1.2) + passenger (6.2.0) logger (>= 1.7.0) rack (>= 1.6.13) rackup (>= 1.0.1) rake (>= 12.3.3) - pg (1.5.9) - pry (0.14.1) + + pg (1.6.3) + pp (0.6.4) + prettyprint + prettyprint (0.2.0) + prism (1.9.0) + pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (4.0.6) - puma (5.6.8) + reline (>= 0.6.0) + public_suffix (7.0.5) + puma (8.0.2) nio4r (~> 2.0) - racc (1.6.1) - rack (2.2.23) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rackup (1.0.1) - rack (< 3) - webrick - rails (6.1.4.1) - actioncable (= 6.1.4.1) - actionmailbox (= 6.1.4.1) - actionmailer (= 6.1.4.1) - actionpack (= 6.1.4.1) - actiontext (= 6.1.4.1) - actionview (= 6.1.4.1) - activejob (= 6.1.4.1) - activemodel (= 6.1.4.1) - activerecord (= 6.1.4.1) - activestorage (= 6.1.4.1) - activesupport (= 6.1.4.1) + racc (1.8.1) + rack (3.2.7) + rack-session (2.1.2) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.3.1) + rack (>= 3) + rails (8.1.3.1) + actioncable (= 8.1.3.1) + actionmailbox (= 8.1.3.1) + actionmailer (= 8.1.3.1) + actionpack (= 8.1.3.1) + actiontext (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activemodel (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) bundler (>= 1.15.0) - railties (= 6.1.4.1) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 8.1.3.1) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.4) - loofah (~> 2.19, >= 2.19.1) - railties (6.1.4.1) - actionpack (= 6.1.4.1) - activesupport (= 6.1.4.1) - method_source - rake (>= 0.13) - thor (~> 1.0) - raindrops (0.19.2) + rails-html-sanitizer (1.7.1) + loofah (~> 2.25, >= 2.25.2) + 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.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) + zeitwerk (~> 2.6) + raindrops (0.20.1) rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) + rbs (4.1.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) + erb + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort recaptcha (5.21.2) - regexp_parser (2.1.1) - rexml (3.4.2) - rubyzip (2.3.0) + regexp_parser (2.12.0) + reline (0.7.0) + io-console (~> 0.5) + rexml (3.4.4) + rubyzip (3.5.0) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -206,47 +271,52 @@ GEM sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) - simplecov (0.17.1) - docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) + securerandom (0.4.1) + selenium-webdriver (4.47.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 4.0) + websocket (~> 1.0) + simplecov (1.1.1) simpleidn (0.2.1) unf (~> 0.1.4) - sprockets (4.0.2) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + sprockets (4.4.0) + concurrent-ruby (~> 1.1) + logger + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) thor (1.5.0) - tilt (2.0.10) - tzinfo (2.0.4) + tilt (2.9.0) + timeout (0.6.1) + tsort (0.2.0) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) uglifier (4.2.1) execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicorn (6.0.0) + unf_ext (0.0.9.1) + unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) - webmock (3.14.0) + uri (1.1.1) + useragent (0.16.11) + webmock (3.26.2) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.9.2) + websocket (1.2.11) websocket-driver (0.8.2) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.8.3) PLATFORMS ruby @@ -259,22 +329,21 @@ DEPENDENCIES figaro (~> 1.3.0) jbuilder listen (>= 3.0.5, < 3.10.1) - mimemagic (~> 0.4.3) mina (~> 1.2.4) passenger (>= 5.3.2) pg (~> 1.5.9) pry puma - rails (>= 6.0.3.1) + rails (~> 8.1.3) recaptcha (~> 5.21) sassc (~> 2.4) sassc-rails selenium-webdriver - simplecov (= 0.17.1) + simplecov simpleidn (= 0.2.1) uglifier unicorn webmock BUNDLED WITH - 2.5.7 + 2.6.9 diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 7dd84102..fc100c9e 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -5,7 +5,9 @@ namespace :db do if Rails.env.production? %i[write_production production].each do |connection| - ActiveRecord::Base.clear_all_connections! + # ActiveRecord::Base delegated this to the connection handler until 6.1; the + # delegation is gone since 7.1, the handler method itself is not. + ActiveRecord::Base.connection_handler.clear_all_connections! ActiveRecord::Base.establish_connection(connection) Rake::Task['db:migrate'].invoke end From f8eabc184f2b73767f431a506538a8c54bda7a48 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:35:43 +0300 Subject: [PATCH 03/14] Add minitest-mock minitest 6 extracted mocks and stubs into a gem of their own, and both test_helper and the mailer test use them. --- Gemfile | 3 +++ Gemfile.lock | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 298e711d..61d5e081 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,9 @@ group :development, :test do end group :test do + # minitest 6 moved mocks and stubs out of the gem itself; test_helper requires + # minitest/mock and the mailer test stubs a method with it. + gem 'minitest-mock' gem 'selenium-webdriver' gem 'simplecov', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 0e136e52..a5eda76e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -129,6 +129,7 @@ GEM execjs (2.10.2) json (>= 2) ffi (1.17.4) + ffi (1.17.4-aarch64-linux-gnu) figaro (1.3.0) thor (>= 0.14.0, < 2) globalid (1.4.0) @@ -168,10 +169,10 @@ GEM mina (1.2.5) rake mini_mime (1.1.5) - mini_portile2 (2.8.9) minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) + minitest-mock (5.27.0) msgpack (1.8.4) net-imap (0.6.6) date @@ -183,8 +184,7 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.19.4) - mini_portile2 (~> 2.8.2) + nokogiri (1.19.4-aarch64-linux-gnu) racc (~> 1.4) passenger (6.2.0) logger (>= 1.7.0) @@ -319,7 +319,7 @@ GEM zeitwerk (2.8.3) PLATFORMS - ruby + aarch64-linux DEPENDENCIES apparition! @@ -330,6 +330,7 @@ DEPENDENCIES jbuilder listen (>= 3.0.5, < 3.10.1) mina (~> 1.2.4) + minitest-mock passenger (>= 5.3.2) pg (~> 1.5.9) pry From 0ddb97bdb5b43cbe6e36ace08b15c1c30b873df0 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:38:17 +0300 Subject: [PATCH 04/14] Lock the bundle to the ruby platform The lockfile has always listed "ruby" as its only platform, which is what makes native extensions be compiled from source - the servers cannot use the published Linux binaries of pg anyway. Regenerating it inside a container had replaced that with the container's own platform. --- .gitignore | 3 +++ Gemfile.lock | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 38904a12..49efcfc6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ .rubocop.yml .DS_Store Dockerfile.dev.v2 + +# repowise local index +.repowise/ diff --git a/Gemfile.lock b/Gemfile.lock index a5eda76e..29968fcf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -129,7 +129,6 @@ GEM execjs (2.10.2) json (>= 2) ffi (1.17.4) - ffi (1.17.4-aarch64-linux-gnu) figaro (1.3.0) thor (>= 0.14.0, < 2) globalid (1.4.0) @@ -169,6 +168,7 @@ GEM mina (1.2.5) rake mini_mime (1.1.5) + mini_portile2 (2.8.9) minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) @@ -184,7 +184,8 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.19.4-aarch64-linux-gnu) + nokogiri (1.19.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) passenger (6.2.0) logger (>= 1.7.0) @@ -319,7 +320,7 @@ GEM zeitwerk (2.8.3) PLATFORMS - aarch64-linux + ruby DEPENDENCIES apparition! From ae0f48afac89eaca9c468c554c64c743679d0f7d Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:44:42 +0300 Subject: [PATCH 05/14] Fix three APIs that Rails 7 and net-smtp changed * Date#to_s(:iso8601) - the formatting to_s was removed in Rails 7.0 in favour of to_fs. * config.action_dispatch.show_exceptions takes a symbol since 7.1; the boolean it used to hold stopped meaning anything, so integration tests got rendered error pages instead of raised exceptions. * Net::SMTPFatalError now takes a mandatory argument, so raising the bare class fails with ArgumentError before the code under test is reached. --- app/mailers/contact_request_mailer.rb | 2 +- app/models/whois_record.rb | 2 +- config/environments/test.rb | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/mailers/contact_request_mailer.rb b/app/mailers/contact_request_mailer.rb index f5ec0949..46eb2ab4 100644 --- a/app/mailers/contact_request_mailer.rb +++ b/app/mailers/contact_request_mailer.rb @@ -14,7 +14,7 @@ def confirmation_email(contact_request) end def contact_email(contact_request:, recipients:, mail_body:, raise_error: false) - raise ::Net::SMTPFatalError if Rails.env.test? && raise_error + raise ::Net::SMTPFatalError, 'simulated delivery failure' if Rails.env.test? && raise_error if ApplicationMailer.ses_configured? ses_contact_email( diff --git a/app/models/whois_record.rb b/app/models/whois_record.rb index 047aee84..cef7b099 100644 --- a/app/models/whois_record.rb +++ b/app/models/whois_record.rb @@ -50,7 +50,7 @@ def deserialize_domain delete: json['delete'], registration_deadline: json['registration_deadline'] .try(:to_datetime) - .try(:to_s, :iso8601)) + .try(:to_fs, :iso8601)) end # rubocop:enable Metrics/AbcSize diff --git a/config/environments/test.rb b/config/environments/test.rb index 40f08b5b..f0617676 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -22,8 +22,9 @@ config.consider_all_requests_local = true config.action_controller.perform_caching = false - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false + # Raise exceptions instead of rendering exception templates. Used to be false; since + # Rails 7.1 this setting takes a symbol, and a boolean no longer means anything here. + config.action_dispatch.show_exceptions = :none # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false From d0179a3bd00696b18dbd9ef283d2feec47a8d59c Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:47:38 +0300 Subject: [PATCH 06/14] Keep dates in the whois json column as strings config.active_support.parse_json_times said true and did nothing: on 6.1 the railtie only applies settings ActiveSupport already responds to, and it did not respond to this one that early, which is why a suite full of assertions on ISO strings has been green for years and why the views call .to_datetime on values that would already be times otherwise. Rails 8 does apply the setting, which turns every date in the whois payload into a TimeWithZone and changes how it is rendered on the public pages and in the json API. Written out as false so the upgrade keeps the output it had, with the trap spelled out for whoever reads the history and decides the true was lost by accident. Test merges are stringified for the same reason: symbol keys merged onto the string-keyed json hash produced literally duplicate keys in the encoded document, which json 3.0 will refuse outright. --- config/application.rb | 8 ++++- .../private_person_registrant_json_test.rb | 28 ++++++++--------- test/integration/whois_records/json_test.rb | 4 +-- .../system/whois_records/details/base_test.rb | 14 ++++----- .../details/legal_person_registrant_test.rb | 18 +++++------ .../details/private_person_registrant_test.rb | 30 +++++++++---------- 6 files changed, 54 insertions(+), 48 deletions(-) diff --git a/config/application.rb b/config/application.rb index a238058d..697b64ea 100644 --- a/config/application.rb +++ b/config/application.rb @@ -53,6 +53,12 @@ class Application < Rails::Application 'Referrer-Policy' => 'strict-origin-when-cross-origin' } - config.active_support.parse_json_times = true + # Deliberately off. The line used to say true, but it never took effect: on Rails 6.1 the + # active_support railtie skipped the setting because ActiveSupport did not respond to it + # yet at initializer time, so dates inside the whois json column have always been handed + # to the views and to the json API as plain strings. Rails 8 does apply it, and turning it + # on would silently change the date format of the public whois output - a product + # decision, not something an upgrade gets to do on the side. + config.active_support.parse_json_times = false end end diff --git a/test/integration/whois_records/details/private_person_registrant_json_test.rb b/test/integration/whois_records/details/private_person_registrant_json_test.rb index 4a1abd67..d099873b 100644 --- a/test/integration/whois_records/details/private_person_registrant_json_test.rb +++ b/test/integration/whois_records/details/private_person_registrant_json_test.rb @@ -5,7 +5,7 @@ class WhoisRecordDetailsPrivatePersonRegistrantJSONTest < ActionDispatch::Integr setup do @whois_record = whois_records(:privately_owned) - @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' }).deep_stringify_keys) @original_whitelist_ip = ENV['whitelist_ip'] ENV['whitelist_ip'] = '' @@ -21,7 +21,7 @@ def test_registrant_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ registrant: 'John', registrant_disclosed_attributes: %w[name], - registrant_publishable: 'true'})) + registrant_publishable: 'true'}).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -30,7 +30,7 @@ def test_registrant_name_is_unmasked_when_disclosed def test_registrant_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[email] })) + .merge({ registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -41,7 +41,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ email: 'john@inbox.test', - registrant_disclosed_attributes: %w[email] })) + registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -50,7 +50,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_phone_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -61,7 +61,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ phone: '1234', - registrant_disclosed_attributes: %w[phone] })) + registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -70,7 +70,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_sensitive_data_is_masked_when_not_publishable @whois_record.update!(json: @whois_record.json - .merge({ registrant_publishable: false, registrant_disclosed_attributes: %w[name] })) + .merge({ registrant_publishable: false, registrant_disclosed_attributes: %w[name] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -82,7 +82,7 @@ def test_registrant_sensitive_data_is_masked_when_not_publishable def test_registrant_sensitive_data_is_unmasked_when_publishable @whois_record.update!(json: @whois_record.json .merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -95,7 +95,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true' }] })) + contact_publishable: 'true' }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -104,7 +104,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed def test_admin_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -115,7 +115,7 @@ def test_admin_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -126,7 +126,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true'}] })) + contact_publishable: 'true'}] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -135,7 +135,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed def test_tech_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json @@ -146,7 +146,7 @@ def test_tech_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) get whois_record_path(name: @whois_record.name), as: :json diff --git a/test/integration/whois_records/json_test.rb b/test/integration/whois_records/json_test.rb index 4b5fd314..c2291e60 100644 --- a/test/integration/whois_records/json_test.rb +++ b/test/integration/whois_records/json_test.rb @@ -257,7 +257,7 @@ def test_hide_sensitive_data_of_legal_entity_when_ip_is_not_in_whitelist def test_show_sensitive_data_when_registrant_is_publishable whois_record = whois_records(:legally_owned) whois_record.update!(json: whois_record.json.merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) get '/v1/company-domain.test', params: { format: :json } response_json = JSON.parse(response.body, symbolize_names: true) @@ -269,7 +269,7 @@ def test_show_sensitive_data_when_registrant_is_publishable def test_hide_sensitive_data_when_registrant_is_not_publishable whois_record = whois_records(:privately_owned) - whois_record.update!(json: whois_record.json.merge({ registrant_publishable: false })) + whois_record.update!(json: whois_record.json.merge({ registrant_publishable: false }).deep_stringify_keys) whois_record.reload get '/v1/privatedomain.test', params: { format: :json } diff --git a/test/system/whois_records/details/base_test.rb b/test/system/whois_records/details/base_test.rb index 6c8f4072..14ec2de7 100644 --- a/test/system/whois_records/details/base_test.rb +++ b/test/system/whois_records/details/base_test.rb @@ -25,7 +25,7 @@ def test_domain_details changed: '2010-07-06T00:00:00+00:00', expire: '2010-07-07T00:00:00+00:00', outzone: '2010-07-08T00:00:00+00:00', - delete: '2010-07-09T00:00:00+00:00' })) + delete: '2010-07-09T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -46,7 +46,7 @@ def test_registrant_details .merge({ registrant: 'John', registrant_kind: 'priv', email: 'john@inbox.test', - registrant_changed: '2010-07-05T00:00:00+00:00' })) + registrant_changed: '2010-07-05T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -64,7 +64,7 @@ def test_admin_contacts_details .merge({ admin_contacts: [{ name: 'John', email: 'john@inbox.test', changed: '2010-07-05T00:00:00+00:00', - }] })) + }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -81,7 +81,7 @@ def test_tech_contacts_details .merge({ tech_contacts: [{ name: 'John', email: 'john@inbox.test', changed: '2010-07-05T00:00:00+00:00', - }] })) + }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -97,7 +97,7 @@ def test_registrar_details .merge({ registrar: 'Bestnames', registrar_website: 'http://bestnames.test', registrar_phone: '1234', - registrar_changed: '2010-07-05T00:00:00+00:00' })) + registrar_changed: '2010-07-05T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -114,7 +114,7 @@ def test_nameservers .merge({ nameservers: %w[ns1.bestnames.test ns2.bestnames.test], nameservers_changed: '2010-07-05T00:00:00+00:00', - })) + }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -129,7 +129,7 @@ def test_dnssec_keys @whois_record.update!(json: @whois_record.json .merge({ dnssec_keys: %w[one two], dnssec_changed: '2010-07-05T00:00:00+00:00', - })) + }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) diff --git a/test/system/whois_records/details/legal_person_registrant_test.rb b/test/system/whois_records/details/legal_person_registrant_test.rb index f6676bcc..de4758ac 100644 --- a/test/system/whois_records/details/legal_person_registrant_test.rb +++ b/test/system/whois_records/details/legal_person_registrant_test.rb @@ -3,7 +3,7 @@ class WhoisRecordDetailsLegalPersonRegistrantTest < ApplicationSystemTestCase setup do @whois_record = whois_records(:privately_owned) - @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'org' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'org' }).deep_stringify_keys) @original_whitelist_ip = ENV['whitelist_ip'] ENV['whitelist_ip'] = '' @@ -16,7 +16,7 @@ class WhoisRecordDetailsLegalPersonRegistrantTest < ApplicationSystemTestCase def test_legal_person_specific_registrant_details_are_visible @whois_record.update!(json: @whois_record.json .merge({ registrant_reg_no: '1234', - registrant_ident_country_code: 'US' })) + registrant_ident_country_code: 'US' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -27,7 +27,7 @@ def test_legal_person_specific_registrant_details_are_visible end def test_registrant_name_is_unmasked - @whois_record.update!(json: @whois_record.json.merge({ registrant: 'Acme', registrant_publishable: 'true' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant: 'Acme', registrant_publishable: 'true' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -55,7 +55,7 @@ def test_registrant_phone_is_masked_when_not_disclosed def test_registrant_phone_is_masked_when_disclosed_and_unsolved_captcha @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -67,7 +67,7 @@ def test_registrant_email_and_last_update_are_unmasked_when_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ email: 'john@inbox.test', - registrant_changed: '2010-07-05T00:00:00+00:00' })) + registrant_changed: '2010-07-05T00:00:00+00:00' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -80,7 +80,7 @@ def test_registrant_email_and_last_update_are_unmasked_when_captcha_is_solved def test_registrant_phone_is_unmasked_when_disclosed_and_solved_captcha solve_captcha @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -122,7 +122,7 @@ def test_admin_and_tech_contact_data_is_unmasked_when_captcha_is_solved changed: '2010-07-07T00:00:00+00:00', contact_publishable: true, disclosed_attributes: %w[name email phone changed] - }] })) + }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -142,7 +142,7 @@ def test_admin_and_tech_contact_data_is_unmasked_when_captcha_is_solved end def test_registrant_sensitive_data_is_masked_when_registrant_is_not_publishable - @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -155,7 +155,7 @@ def test_registrant_sensitive_data_is_masked_when_registrant_is_not_publishable def test_registrant_sensitive_data_is_unmasked_when_registrant_is_publishable @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do diff --git a/test/system/whois_records/details/private_person_registrant_test.rb b/test/system/whois_records/details/private_person_registrant_test.rb index d08c553a..409ddde5 100644 --- a/test/system/whois_records/details/private_person_registrant_test.rb +++ b/test/system/whois_records/details/private_person_registrant_test.rb @@ -3,7 +3,7 @@ class WhoisRecordDetailsPrivatePersonRegistrantTest < ApplicationSystemTestCase setup do @whois_record = whois_records(:privately_owned) - @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_kind: 'priv' }).deep_stringify_keys) @original_whitelist_ip = ENV['whitelist_ip'] ENV['whitelist_ip'] = '' @@ -16,7 +16,7 @@ class WhoisRecordDetailsPrivatePersonRegistrantTest < ApplicationSystemTestCase def test_legal_person_specific_registrant_details_are_hidden @whois_record.update!(json: @whois_record.json .merge({ registrant_reg_no: 'legal-person-reg-number', - registrant_ident_country_code: 'legal-person-country' })) + registrant_ident_country_code: 'legal-person-country' }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -36,7 +36,7 @@ def test_sensitive_data_is_masked_when_captcha_is_unsolved end def test_sensitive_data_is_masked_when_registrant_is_not_publishable - @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false })) + @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: false }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -62,7 +62,7 @@ def test_registrant_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ registrant: 'John', registrant_disclosed_attributes: %w[name], - registrant_publishable: true})) + registrant_publishable: true}).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -73,7 +73,7 @@ def test_registrant_name_is_unmasked_when_disclosed def test_registrant_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[email] })) + .merge({ registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -86,7 +86,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ email: 'john@inbox.test', - registrant_disclosed_attributes: %w[email] })) + registrant_disclosed_attributes: %w[email] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -97,7 +97,7 @@ def test_registrant_email_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_phone_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ registrant_disclosed_attributes: %w[phone] })) + .merge({ registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -110,7 +110,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ phone: '1234', - registrant_disclosed_attributes: %w[phone] })) + registrant_disclosed_attributes: %w[phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -121,7 +121,7 @@ def test_registrant_phone_is_unmasked_when_disclosed_and_captcha_is_solved def test_registrant_sensitive_data_is_unmasked_when_registrant_is_publishable @whois_record.update!(json: @whois_record.json.merge({ registrant_publishable: true, - registrant_disclosed_attributes: %w[name email phone] })) + registrant_disclosed_attributes: %w[name email phone] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) within '.registrant' do @@ -135,7 +135,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true'}] })) + contact_publishable: 'true'}] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -146,7 +146,7 @@ def test_admin_contact_name_is_unmasked_when_disclosed def test_admin_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ admin_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -159,7 +159,7 @@ def test_admin_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ admin_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -172,7 +172,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ name: 'John', disclosed_attributes: %w[name], - contact_publishable: 'true' }] })) + contact_publishable: 'true' }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -183,7 +183,7 @@ def test_tech_contact_name_is_unmasked_when_disclosed def test_tech_contact_email_is_masked_when_disclosed_and_captcha_is_unsolved @whois_record.update!(json: @whois_record.json - .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] })) + .merge({ tech_contacts: [{ disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) @@ -196,7 +196,7 @@ def test_tech_contact_email_is_unmasked_when_disclosed_and_captcha_is_solved solve_captcha @whois_record.update!(json: @whois_record.json .merge({ tech_contacts: [{ email: 'john@inbox.test', - disclosed_attributes: %w[email] }] })) + disclosed_attributes: %w[email] }] }).deep_stringify_keys) visit whois_record_url(name: @whois_record.name) From d356d8f78559c8dfe958fc76901b51b85d0826ee Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:49:46 +0300 Subject: [PATCH 07/14] Fix the last to_s(:iso8601) left in the tests --- test/models/whois_record_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/models/whois_record_test.rb b/test/models/whois_record_test.rb index 9cc88d48..d56c0dba 100644 --- a/test/models/whois_record_test.rb +++ b/test/models/whois_record_test.rb @@ -72,7 +72,7 @@ def test_deserializes_domain_with_old_reg_deadline_format assert_equal 'shop.test', whois_record.domain.name assert_equal %w[active], whois_record.domain.statuses - assert_equal Time.parse('2020-06-09 23:59:59 +0300').try(:to_datetime).try(:to_s, :iso8601), + assert_equal Time.parse('2020-06-09 23:59:59 +0300').try(:to_datetime).try(:to_fs, :iso8601), whois_record.domain.registration_deadline end From cbbeff3393694b2e60a5b417a1f8a2935841cf42 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:52:26 +0300 Subject: [PATCH 08/14] Move the framework defaults to 8.1 The application was still initialising with the 6.0 defaults, so everything Rails changed its mind about between 6.0 and 8.1 was switched off. The 5.2 defaults file goes with it: every option in it was commented out, so it was a list of decisions nobody ever made. --- config/application.rb | 3 +- .../new_framework_defaults_5_2.rb | 38 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 config/initializers/new_framework_defaults_5_2.rb diff --git a/config/application.rb b/config/application.rb index 697b64ea..f24d0fab 100644 --- a/config/application.rb +++ b/config/application.rb @@ -8,8 +8,7 @@ module RestWhois class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 6.0 + config.load_defaults 8.1 # Authorize all hosts config.hosts.clear diff --git a/config/initializers/new_framework_defaults_5_2.rb b/config/initializers/new_framework_defaults_5_2.rb deleted file mode 100644 index c383d072..00000000 --- a/config/initializers/new_framework_defaults_5_2.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.2 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Make Active Record use stable #cache_key alongside new #cache_version method. -# This is needed for recyclable cache keys. -# Rails.application.config.active_record.cache_versioning = true - -# Use AES-256-GCM authenticated encryption for encrypted cookies. -# Also, embed cookie expiry in signed or encrypted cookies for increased security. -# -# This option is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 5.2. -# -# Existing cookies will be converted on read then written with the new scheme. -# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true - -# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages -# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. -# Rails.application.config.active_support.use_authenticated_message_encryption = true - -# Add default protection from forgery to ActionController::Base instead of in -# ApplicationController. -# Rails.application.config.action_controller.default_protect_from_forgery = true - -# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and -# 'f' after migrating old data. -# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - -# Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. -# Rails.application.config.active_support.use_sha1_digests = true - -# Make `form_with` generate id attributes for any generated HTML tags. -# Rails.application.config.action_view.form_with_generates_ids = true From d0f9599e9ab388174467298860b67cb4f78ee55a Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 11:54:42 +0300 Subject: [PATCH 09/14] Drop the test-only secret rewrite update_request_secret ran ContactRequest#update(secret:) whenever the environment was test, which attr_readonly has always dropped on the way to the database - the reload right after it read the original secret back. Since the 7.1 defaults an assignment to a readonly attribute raises instead of being ignored, so the dead line started taking the confirmation flow down with it. The two model tests that asserted the silent no-op now assert the exception, which is the same guarantee stated out loud. --- test/models/contact_request_test.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/models/contact_request_test.rb b/test/models/contact_request_test.rb index a9cf06a1..f9ee70d7 100644 --- a/test/models/contact_request_test.rb +++ b/test/models/contact_request_test.rb @@ -52,19 +52,21 @@ def test_new_request_generates_random_124_character_secret_on_creation def test_secret_cannot_be_changed @contact_request.save - @contact_request.update!(secret: 'foo') - @contact_request.reload - refute_equal('foo', @contact_request.secret) + assert_raises(ActiveRecord::ReadonlyAttributeError) do + @contact_request.update!(secret: 'foo') + end + refute_equal('foo', @contact_request.reload.secret) end def test_valid_to_cannot_be_changed @contact_request.save new_date = Time.parse('2018-01-01 00:00 UTC') - @contact_request.update!(valid_to: new_date) - @contact_request.reload - refute_equal(new_date, @contact_request.valid_to) + assert_raises(ActiveRecord::ReadonlyAttributeError) do + @contact_request.update!(valid_to: new_date) + end + refute_equal(new_date, @contact_request.reload.valid_to) end def test_can_be_confirmed_only_once From b8a82e5e3215ad15583d62713c4c5b8e3b04bed3 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 12:02:13 +0300 Subject: [PATCH 10/14] Allow the main page redirect to leave our host The 7.1 defaults refuse cross host redirects unless they say so explicitly. This one goes to main_page_url, which is server configuration and not anything the visitor can influence. --- app/controllers/contact_requests_controller.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/contact_requests_controller.rb b/app/controllers/contact_requests_controller.rb index 6da8e62a..988592ad 100644 --- a/app/controllers/contact_requests_controller.rb +++ b/app/controllers/contact_requests_controller.rb @@ -30,7 +30,10 @@ def create def redirect_to_main referer = ENV.fetch('main_page_url') { root_url } respond_to do |format| - format.html { redirect_to referer } + # Leaving our own host is the whole point of this button, and the address comes from the + # server configuration rather than from the request, so the open redirect protection that + # the 7.1 defaults turned on has nothing to protect here. + format.html { redirect_to referer, allow_other_host: true } end end From 24cdbf034dd35d8ae9ba5b2aa9f1e09dc06efe15 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 12:04:57 +0300 Subject: [PATCH 11/14] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef75704..12e7fb76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ + 27.08.2026 * Fixed stability issues on contact request https://github.com/internetee/rest-whois/pull/503 @@ -5,6 +6,9 @@ * Pinned Ruby back to 3.0.3 until Rails is upgraded https://github.com/internetee/rest-whois/pull/503 * Fixed contact request failures caused by registry replication delay https://github.com/internetee/rest-whois/pull/503 +21.08.2026 +* Rails 6.1 -> 8.1 and the zeitwerk autoloader on Ruby 3.4 https://github.com/internetee/rest-whois/pull/PRNUM + 08.22.2025 * Replacing codeclimate https://github.com/internetee/rest-whois/pull/434 From 68389389e541111dd25d1347bcbd6ff530a14622 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 21 Aug 2026 12:05:06 +0300 Subject: [PATCH 12/14] Point the changelog entry at the pull request --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e7fb76..8160d0d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * Fixed contact request failures caused by registry replication delay https://github.com/internetee/rest-whois/pull/503 21.08.2026 -* Rails 6.1 -> 8.1 and the zeitwerk autoloader on Ruby 3.4 https://github.com/internetee/rest-whois/pull/PRNUM +* Rails 6.1 -> 8.1 and the zeitwerk autoloader on Ruby 3.4 https://github.com/internetee/rest-whois/pull/505 08.22.2025 * Replacing codeclimate https://github.com/internetee/rest-whois/pull/434 From d1c1c57c341322f91903592ebf8f1e9f7c28ac6d Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Thu, 27 Aug 2026 11:51:40 +0300 Subject: [PATCH 13/14] Build nokogiri from source: the servers are older than its binary gem The deploy got past pg and died on nokogiri, loading it through sassc-rails: libm.so.6: version `GLIBC_2.29' not found (required by nokogiri-1.19.4-x86_64-linux-gnu/lib/nokogiri/3.4/nokogiri.so) Same story as pg: nokogiri's precompiled x86_64-linux-gnu gem requires glibc 2.29 or newer (nokogiri.org/tutorials/installing_nokogiri.html, checked via web 2026-08-27; the floor was raised in 1.18), and the deploy servers are older than that. force_ruby_platform makes Bundler ignore the binary and compile from source, which is what the gem's own error message recommends. nokogiri is a Rails dependency, not one we use directly, so it has to be named in the Gemfile just to carry the flag - and therefore also in DEPENDENCIES. Locking PLATFORMS to "ruby" was supposed to cover this, but it only holds if the install honours the lockfile: `bundle install` outside frozen/deployment mode adds the machine's own platform and re-resolves, which is where the native gem came from. Per-gem force_ruby_platform survives that, since it is a property of the Gemfile dependency rather than of the lockfile. Resolution is unchanged - the lockfile already held the ruby platform variant of nokogiri. Verified on Ruby 3.4.9 / Bundler 2.5.23 in a container: after `bundle lock --add-platform x86_64-linux`, ffi is the only gem left resolving to a native x86_64-linux-gnu build; nokogiri and pg stay on the ruby platform. ffi's binary needs no symbol above GLIBC_2.27, so it still loads on a 2.27/2.28 server, but it is next in line if the servers are older than that. Also brings DEPENDENCIES back in sync with the Gemfile: it asked for pg ~> 1.5.9 while both the Gemfile and the resolved version say 1.6.3, so the lockfile could not be satisfied as written. --- Gemfile | 9 +++++++++ Gemfile.lock | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 61d5e081..e6850abc 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,15 @@ gem 'figaro', '~> 1.3.0' gem 'jbuilder' gem 'passenger', '>= 5.3.2', require: 'phusion_passenger/rack_handler' +# Both are built from source on purpose: the deploy servers run a glibc older than 2.29, and the +# precompiled x86_64-linux gems that pg ships since 1.6 and nokogiri since 1.18 are linked against +# 2.29, so they load with "libm.so.6: version `GLIBC_2.29' not found". force_ruby_platform makes +# Bundler ignore those binaries and compile from source there, which is how both were installed +# before they started publishing Linux binaries. Needs bundler >= 2.3.18. nokogiri is listed here +# only for that flag - it is pulled in by Rails, not used directly. Drop both once the servers get +# a newer distribution; anything below glibc 2.29 is an out-of-support release and these two will +# not be the last gems to ship binaries it cannot load. +gem 'nokogiri', force_ruby_platform: true gem 'pg', '~> 1.6.3', force_ruby_platform: true gem 'rails', '~> 8.1.3' gem 'recaptcha', '~> 5.21', require: 'recaptcha/rails' diff --git a/Gemfile.lock b/Gemfile.lock index 29968fcf..71d4612b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -192,7 +192,6 @@ GEM rack (>= 1.6.13) rackup (>= 1.0.1) rake (>= 12.3.3) - pg (1.6.3) pp (0.6.4) prettyprint @@ -332,8 +331,9 @@ DEPENDENCIES listen (>= 3.0.5, < 3.10.1) mina (~> 1.2.4) minitest-mock + nokogiri passenger (>= 5.3.2) - pg (~> 1.5.9) + pg (~> 1.6.3) pry puma rails (~> 8.1.3) From 1c4f9c584e89521e564b7652977c48ce97004273 Mon Sep 17 00:00:00 2001 From: oleghasjanov Date: Fri, 28 Aug 2026 10:06:02 +0300 Subject: [PATCH 14/14] Restore the deploy fixes the conflict resolution dropped The branch was force-pushed back onto the pre-rebase line, and with it came back the state of four files as they were before the staging deploys of 2026-08-27. CI stops on its very first step: gem install bundler -v 2.6.9 ERROR: bundler-2.6.9 requires Ruby version >= 3.1.0. The current ruby version is 3.0.3.157. because .ruby-version went back to 3.0.3 while Gemfile.lock still says BUNDLED WITH 2.6.9 - and the Gemfile asks for Rails 8.1, which needs 3.2 anyway. This is the same trap the workflow comment warned about: the runner reads .ruby-version, so a stale number there does not fail loudly, it fails before the suite runs. Restored, all of it verified on staging before it was lost: - .ruby-version back to 3.4.9, which is what the servers run - gem "syslog", without which the app cannot boot on Ruby 3.4 - syslog is a bundled gem now and Bundler hides it unless the Gemfile asks - uglifier gone again, from the Gemfile and from production.rb: the app ships no JavaScript, and uglify-js died on the staging JS runtime with an empty Uglifier::Error during assets:precompile - the workflow comment explaining why the matrix reads .ruby-version Everything the force-push added - the contact request work, its tests, the dependabot and renovate changes, the changelog - is left untouched. Also untracks .repowise/sessions/sessions.db, a local index that slipped into the commit; .gitignore already lists .repowise/, so it will not come back. Lockfile regenerated rather than hand-edited: syslog 0.4.0 in, uglifier and execjs out, nothing else moved. Verified on Ruby 3.4.9 in a container that bundle lock --no-update reproduces it byte for byte, and that ffi remains the only gem resolving to a native x86_64-linux build. --- .github/workflows/ruby.yml | 3 +++ .repowise/sessions/sessions.db | Bin 20480 -> 0 bytes .ruby-version | 2 +- Gemfile | 4 +++- Gemfile.lock | 8 +++----- config/environments/production.rb | 4 ++-- 6 files changed, 12 insertions(+), 9 deletions(-) delete mode 100644 .repowise/sessions/sessions.db diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index fff289ea..6aaa04a7 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -20,6 +20,9 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04] + # Read from .ruby-version instead of a second copy of the number here. CI used to be + # pinned to 3.0.3 while .ruby-version said 3.4.9, so the suite stayed green on a Ruby + # the servers no longer ran and the breakage surfaced only on deploy. ruby: ['.ruby-version'] runs-on: ${{ matrix.os }} continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} diff --git a/.repowise/sessions/sessions.db b/.repowise/sessions/sessions.db deleted file mode 100644 index 1ee8ddabeaa4c1a038fe11d4c095696ee75da2a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI(O>fgM7zc1CYu5oQ-33+RQddZfN-b(>soD#ogD_2mF-j`YQ{*Nd4Wx;PU0yC6 zn1sZ)LfV1Puus5O;D}SP^aZt*cABQY6{n9a%d&rua&V%)er0-+)8jB2F-hxWhv+(a zOerBmS2eBbd5PPp^M?A>X5wX`PVRs0XLEl@YWs}byq!DUKFj`IHW>X7fB*y_009U< z00Izz00b^1@a@J%CSNFMCtR|D&xc|7-j2qBNP^UpuFE&?37{XFZMai_XRyTw2XbDn@m8T8qYdC)1p;1)yR{QyDP`Y zP%_^ht)A$pd+?&u?bGK*e|G8r7F(wA@=B#zYBO&XG~xv=|13WE>da@eu?#0|JK4Nl zOTzTYT`iL@7PasB37{9e{9I+@+fIB#fdB*`009U<00Izz00bZa0SG|gstR0> zH-_{7RZTDU1px>^00Izz00bZa0SG_<0uY!8#NYq5+)tt|6bL{70uX=z1Rwwb2tWV= z5P$##E~&sHEz`SmOW)Gld@vX^s}C#9R^6#I+Ji==-E6lj4_eg*YgKE!QEeWDLR#wo zAU(nNm>6n5YNzq{e=YZms0#%G5P$##AOHafKmY;|fB*y_0D;Rb&`PVf0M{ 5.21', require: 'recaptcha/rails' gem 'sassc', '~> 2.4' gem 'sassc-rails' gem 'simpleidn', '0.2.1' # For Punycode -gem 'uglifier' +# Ruby 3.4 demoted syslog from a default gem to a bundled one, so Bundler no longer puts it on the +# load path unless it is asked for. config/environments/production.rb logs through Syslog::Logger. +gem 'syslog' group :development do gem 'listen', '>= 3.0.5', '< 3.10.1' diff --git a/Gemfile.lock b/Gemfile.lock index 71d4612b..db578a3c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -126,8 +126,6 @@ GEM drb (2.2.3) erb (6.0.7) erubi (1.13.1) - execjs (2.10.2) - json (>= 2) ffi (1.17.4) figaro (1.3.0) thor (>= 0.14.0, < 2) @@ -289,14 +287,14 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) + syslog (0.4.0) + logger thor (1.5.0) tilt (2.9.0) timeout (0.6.1) tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.1) - execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext unf_ext (0.0.9.1) @@ -343,7 +341,7 @@ DEPENDENCIES selenium-webdriver simplecov simpleidn (= 0.2.1) - uglifier + syslog unicorn webmock diff --git a/config/environments/production.rb b/config/environments/production.rb index 9f81da8b..dbe72212 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -22,8 +22,8 @@ # Apache or NGINX already handles this. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - # Compress JavaScripts and CSS. - config.assets.js_compressor = Uglifier.new(harmony: true) + # No JavaScript compressor is configured: the application ships no JavaScript at all, so there is + # nothing to minify and no reason to require a JS runtime on the servers. # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed.