From ab6d4430baa33a05f1aa66e776cc2a5bcaa0ede8 Mon Sep 17 00:00:00 2001 From: Adam Cooke Date: Wed, 1 Oct 2025 16:42:39 +0100 Subject: [PATCH] chore: upgrade to rails 7.1 and ruby 3.4 (#3457) --- .github/workflows/ci.yml | 4 +- .ruby-version | 2 +- Dockerfile | 12 +- Gemfile | 17 +- Gemfile.lock | 262 ++++++++++-------- app/models/credential.rb | 2 +- app/models/webhook_request.rb | 2 +- config/environments/development.rb | 2 +- config/environments/production.rb | 2 +- config/environments/test.rb | 2 +- config/initializers/secret_key.rb | 4 +- spec/models/server_spec.rb | 2 + spec/rails_helper.rb | 2 - .../services/webhook_delivery_service_spec.rb | 7 +- 14 files changed, 184 insertions(+), 138 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f55343f..50109b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,10 +52,10 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: docker-compose pull + - run: docker compose pull env: POSTAL_IMAGE: ghcr.io/postalserver/postal:ci-${{ github.sha }} - - run: docker-compose run postal sh -c 'bundle exec rspec' + - run: docker compose run postal sh -c 'bundle exec rspec' env: POSTAL_IMAGE: ghcr.io/postalserver/postal:ci-${{ github.sha }} diff --git a/.ruby-version b/.ruby-version index be94e6f..1cf8253 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.2 +3.4.6 diff --git a/Dockerfile b/Dockerfile index 4b66484..287e474 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,13 @@ -FROM ruby:3.2.2-bullseye AS base +FROM ruby:3.4.6-bookworm AS base SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - software-properties-common dirmngr apt-transport-https \ - && (curl -sL https://deb.nodesource.com/setup_20.x | bash -) \ - && rm -rf /var/lib/apt/lists/* +RUN (curl -sL https://deb.nodesource.com/setup_20.x | bash -) # Install main dependencies RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ - netcat \ + netcat-openbsd \ curl \ libmariadb-dev \ libcap2-bin \ @@ -31,7 +27,7 @@ RUN mkdir -p /opt/postal/app /opt/postal/config WORKDIR /opt/postal/app # Install bundler -RUN gem install bundler -v 2.5.6 --no-doc +RUN gem install bundler -v 2.7.2 --no-doc # Install the latest and active gem dependencies and re-run # the appropriate commands to handle installs. diff --git a/Gemfile b/Gemfile index 70a30f7..678c379 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ # frozen_string_literal: true source "https://rubygems.org" +gem "abbrev" gem "authie" gem "autoprefixer-rails" gem "bcrypt" @@ -17,14 +18,17 @@ gem "jwt" gem "kaminari" gem "klogger-logger" gem "konfig-config", "~> 3.0" +gem "logger" gem "mail" +gem "mutex_m" gem "mysql2" gem "nifty-utils" gem "nilify_blanks" gem "nio4r" +gem "ostruct" gem "prometheus-client" gem "puma" -gem "rails", "= 7.0.8.1" +gem "rails", "= 7.1.5.2" gem "resolv" gem "secure_headers" gem "sentry-rails" @@ -47,12 +51,15 @@ end group :development do gem "annotate" - gem "database_cleaner", require: false - gem "factory_bot_rails", require: false - gem "rspec", require: false - gem "rspec-rails", require: false gem "rubocop" gem "rubocop-rails" +end + +group :test do + gem "database_cleaner-active_record" + gem "factory_bot_rails" + gem "rspec" + gem "rspec-rails" gem "shoulda-matchers" gem "timecop" gem "webmock" diff --git a/Gemfile.lock b/Gemfile.lock index eb93732..920a0fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,70 +1,83 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.1) - actionpack (= 7.0.8.1) - activesupport (= 7.0.8.1) + abbrev (0.1.2) + actioncable (7.1.5.2) + actionpack (= 7.1.5.2) + activesupport (= 7.1.5.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.1) - actionpack (= 7.0.8.1) - activejob (= 7.0.8.1) - activerecord (= 7.0.8.1) - activestorage (= 7.0.8.1) - activesupport (= 7.0.8.1) + zeitwerk (~> 2.6) + actionmailbox (7.1.5.2) + actionpack (= 7.1.5.2) + activejob (= 7.1.5.2) + activerecord (= 7.1.5.2) + activestorage (= 7.1.5.2) + activesupport (= 7.1.5.2) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.1) - actionpack (= 7.0.8.1) - actionview (= 7.0.8.1) - activejob (= 7.0.8.1) - activesupport (= 7.0.8.1) + actionmailer (7.1.5.2) + actionpack (= 7.1.5.2) + actionview (= 7.1.5.2) + activejob (= 7.1.5.2) + activesupport (= 7.1.5.2) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.8.1) - actionview (= 7.0.8.1) - activesupport (= 7.0.8.1) - rack (~> 2.0, >= 2.2.4) + rails-dom-testing (~> 2.2) + actionpack (7.1.5.2) + actionview (= 7.1.5.2) + activesupport (= 7.1.5.2) + nokogiri (>= 1.8.5) + racc + 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 (7.0.8.1) - actionpack (= 7.0.8.1) - activerecord (= 7.0.8.1) - activestorage (= 7.0.8.1) - activesupport (= 7.0.8.1) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.5.2) + actionpack (= 7.1.5.2) + activerecord (= 7.1.5.2) + activestorage (= 7.1.5.2) + activesupport (= 7.1.5.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.1) - activesupport (= 7.0.8.1) + actionview (7.1.5.2) + activesupport (= 7.1.5.2) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.8.1) - activesupport (= 7.0.8.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.1.5.2) + activesupport (= 7.1.5.2) globalid (>= 0.3.6) - activemodel (7.0.8.1) - activesupport (= 7.0.8.1) - activerecord (7.0.8.1) - activemodel (= 7.0.8.1) - activesupport (= 7.0.8.1) - activestorage (7.0.8.1) - actionpack (= 7.0.8.1) - activejob (= 7.0.8.1) - activerecord (= 7.0.8.1) - activesupport (= 7.0.8.1) + activemodel (7.1.5.2) + activesupport (= 7.1.5.2) + activerecord (7.1.5.2) + activemodel (= 7.1.5.2) + activesupport (= 7.1.5.2) + timeout (>= 0.4.0) + activestorage (7.1.5.2) + actionpack (= 7.1.5.2) + activejob (= 7.1.5.2) + activerecord (= 7.1.5.2) + activesupport (= 7.1.5.2) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.8.1) + activesupport (7.1.5.2) + base64 + benchmark (>= 0.3) + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) + mutex_m + securerandom (>= 0.3) tzinfo (~> 2.0) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) @@ -74,13 +87,14 @@ GEM rake (>= 10.4, < 14.0) ast (2.4.2) attr_required (1.0.2) - authie (4.1.3) - activerecord (>= 6.1, < 8.0) + authie (5.0.0) + activerecord (>= 6.1, < 9.0) autoprefixer-rails (10.4.13.0) execjs (~> 2) - base64 (0.2.0) + base64 (0.3.0) bcrypt (3.1.20) - bigdecimal (3.1.6) + benchmark (0.4.1) + bigdecimal (3.2.3) bindata (2.5.0) builder (3.2.4) chronic (0.10.2) @@ -91,26 +105,25 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.5) + connection_pool (2.5.4) crack (1.0.0) bigdecimal rexml crass (1.0.6) - database_cleaner (2.0.2) - database_cleaner-active_record (>= 2, < 3) - database_cleaner-active_record (2.1.0) + database_cleaner-active_record (2.2.2) activerecord (>= 5.a) - database_cleaner-core (~> 2.0.0) + database_cleaner-core (~> 2.0) database_cleaner-core (2.0.1) date (3.3.4) - diff-lcs (1.5.0) + diff-lcs (1.6.2) domain_name (0.6.20240107) dotenv (3.0.2) - dynamic_form (1.3.1) - actionview (> 5.2.0) - activemodel (> 5.2.0) + drb (2.2.3) + dynamic_form (1.2.0) email_validator (2.2.4) activemodel + erb (5.0.2) erubi (1.12.0) execjs (2.7.0) factory_bot (6.4.6) @@ -127,7 +140,7 @@ GEM ffi (1.15.5) gelf (3.1.0) json - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) haml (6.3.0) temple (>= 0.8.2) @@ -136,8 +149,13 @@ GEM hashdiff (1.1.0) hashie (5.0.0) highline (2.1.0) - i18n (1.14.1) + i18n (1.14.7) concurrent-ruby (~> 1.0) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -170,6 +188,7 @@ GEM rouge (>= 3.30, < 5.0) konfig-config (3.0.0) hashie + logger (1.7.0) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -178,11 +197,10 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) - method_source (1.0.0) + marcel (1.1.0) mini_mime (1.1.5) - mini_portile2 (2.8.5) - minitest (5.22.2) + minitest (5.25.5) + mutex_m (0.3.0) mysql2 (0.5.6) net-http (0.4.1) uri @@ -200,16 +218,13 @@ GEM activerecord (>= 4.0.0) activesupport (>= 4.0.0) nio4r (2.7.0) - nokogiri (1.16.2) - mini_portile2 (~> 2.8.2) + nokogiri (1.18.10-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.16.2-aarch64-linux) + nokogiri (1.18.10-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.2-arm64-darwin) + nokogiri (1.18.10-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.2-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.16.2-x86_64-linux) + nokogiri (1.18.10-x86_64-linux-gnu) racc (~> 1.4) omniauth (2.1.2) hashie (>= 3.4.6) @@ -234,15 +249,22 @@ GEM tzinfo validate_url webfinger (~> 2.0) + ostruct (0.6.3) parallel (1.22.1) parser (3.2.1.1) ast (~> 2.4.1) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) prometheus-client (4.2.2) + psych (5.2.6) + date + stringio public_suffix (5.0.4) puma (6.4.2) nio4r (~> 2.0) - racc (1.7.3) - rack (2.2.8.1) + racc (1.8.1) + rack (3.2.1) rack-oauth2 (2.2.1) activesupport attr_required @@ -250,25 +272,31 @@ GEM faraday-follow_redirects json-jwt (>= 1.11.0) rack (>= 2.1.0) - rack-protection (3.2.0) + rack-protection (4.1.1) base64 (>= 0.1.0) - rack (~> 2.2, >= 2.2.4) + logger (>= 1.6.0) + rack (>= 3.0.0, < 4) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.8.1) - actioncable (= 7.0.8.1) - actionmailbox (= 7.0.8.1) - actionmailer (= 7.0.8.1) - actionpack (= 7.0.8.1) - actiontext (= 7.0.8.1) - actionview (= 7.0.8.1) - activejob (= 7.0.8.1) - activemodel (= 7.0.8.1) - activerecord (= 7.0.8.1) - activestorage (= 7.0.8.1) - activesupport (= 7.0.8.1) + rackup (2.2.1) + rack (>= 3) + rails (7.1.5.2) + actioncable (= 7.1.5.2) + actionmailbox (= 7.1.5.2) + actionmailer (= 7.1.5.2) + actionpack (= 7.1.5.2) + actiontext (= 7.1.5.2) + actionview (= 7.1.5.2) + activejob (= 7.1.5.2) + activemodel (= 7.1.5.2) + activerecord (= 7.1.5.2) + activestorage (= 7.1.5.2) + activesupport (= 7.1.5.2) bundler (>= 1.15.0) - railties (= 7.0.8.1) + railties (= 7.1.5.2) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -276,31 +304,37 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.8.1) - actionpack (= 7.0.8.1) - activesupport (= 7.0.8.1) - method_source + railties (7.1.5.2) + actionpack (= 7.1.5.2) + activesupport (= 7.1.5.2) + irb + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.1.0) + rdoc (6.14.2) + erb + psych (>= 4.0.0) regexp_parser (2.7.0) + reline (0.6.2) + io-console (~> 0.5) resolv (0.3.0) rexml (3.2.5) rouge (4.2.0) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.1) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.4) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) + rspec-support (~> 3.13.0) rspec-rails (6.1.1) actionpack (>= 6.1) activesupport (>= 6.1) @@ -309,7 +343,7 @@ GEM rspec-expectations (~> 3.12) rspec-mocks (~> 3.12) rspec-support (~> 3.12) - rspec-support (3.12.0) + rspec-support (3.13.6) rubocop (1.48.1) json (~> 2.3) parallel (~> 1.10) @@ -338,6 +372,7 @@ GEM sprockets-rails tilt secure_headers (6.5.0) + securerandom (0.4.1) sentry-rails (5.16.1) railties (>= 5.0) sentry-ruby (~> 5.16.1) @@ -352,6 +387,7 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) + stringio (3.1.7) swd (2.0.3) activesupport (>= 3) attr_required (>= 0.0.5) @@ -361,7 +397,7 @@ GEM thor (1.3.0) tilt (2.3.0) timecop (0.9.8) - timeout (0.4.1) + timeout (0.4.3) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) @@ -383,7 +419,8 @@ GEM crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) webrick (1.8.1) - websocket-driver (0.7.6) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) zeitwerk (2.6.13) @@ -391,18 +428,18 @@ GEM PLATFORMS aarch64-linux arm64-darwin - ruby x86_64-darwin x86_64-linux DEPENDENCIES + abbrev annotate authie autoprefixer-rails bcrypt chronic coffee-rails (~> 5.0) - database_cleaner + database_cleaner-active_record domain_name dotenv dynamic_form @@ -417,16 +454,19 @@ DEPENDENCIES kaminari klogger-logger konfig-config (~> 3.0) + logger mail + mutex_m mysql2 nifty-utils nilify_blanks nio4r omniauth-rails_csrf_protection omniauth_openid_connect + ostruct prometheus-client puma - rails (= 7.0.8.1) + rails (= 7.1.5.2) resolv rspec rspec-rails @@ -443,4 +483,4 @@ DEPENDENCIES webrick BUNDLED WITH - 2.5.6 + 2.7.2 diff --git a/app/models/credential.rb b/app/models/credential.rb index 8062eb5..98b2ddc 100644 --- a/app/models/credential.rb +++ b/app/models/credential.rb @@ -31,7 +31,7 @@ class Credential < ApplicationRecord validate :validate_key_cannot_be_changed validate :validate_key_for_smtp_ip - serialize :options, Hash + serialize :options, type: Hash before_validation :generate_key diff --git a/app/models/webhook_request.rb b/app/models/webhook_request.rb index 7609b5a..dfbf63e 100644 --- a/app/models/webhook_request.rb +++ b/app/models/webhook_request.rb @@ -34,7 +34,7 @@ class WebhookRequest < ApplicationRecord validates :url, presence: true validates :event, presence: true - serialize :payload, Hash + serialize :payload, type: Hash class << self diff --git a/config/environments/development.rb b/config/environments/development.rb index b13af62..77e32a1 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -6,7 +6,7 @@ Rails.application.configure do # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. - config.cache_classes = false + config.enable_reloading = true # Do not eager load code on boot. config.eager_load = false diff --git a/config/environments/production.rb b/config/environments/production.rb index 386e938..ba4f1b1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -4,7 +4,7 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. - config.cache_classes = true + config.enable_reloading = false # Eager load code on boot. This eager loads most of Rails and # your application in memory, allowing both threaded web servers diff --git a/config/environments/test.rb b/config/environments/test.rb index f88be04..da3785d 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -7,7 +7,7 @@ Rails.application.configure do # 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 # and recreated between test runs. Don't rely on the data there! - config.cache_classes = true + config.enable_reloading = false # Do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that diff --git a/config/initializers/secret_key.rb b/config/initializers/secret_key.rb index d45e166..87a9bdf 100644 --- a/config/initializers/secret_key.rb +++ b/config/initializers/secret_key.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true if Postal::Config.rails.secret_key - Rails.application.secrets.secret_key_base = Postal::Config.rails.secret_key + Rails.application.credentials.secret_key_base = Postal::Config.rails.secret_key else warn "No secret key was specified in the Postal config file. Using one for just this session" - Rails.application.secrets.secret_key_base = SecureRandom.hex(128) + Rails.application.credentials.secret_key_base = SecureRandom.hex(128) end diff --git a/spec/models/server_spec.rb b/spec/models/server_spec.rb index 5712b7b..a81fb9a 100644 --- a/spec/models/server_spec.rb +++ b/spec/models/server_spec.rb @@ -120,6 +120,8 @@ describe Server do end describe "deletion" do + let(:server) { create(:server) } + it "removes the database" do expect(server.message_db.provisioner).to receive(:drop).once server.provision_database = true diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 526651e..bf2c9ae 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -10,14 +10,12 @@ require "rspec/rails" require "spec_helper" require "factory_bot" require "timecop" -require "database_cleaner" require "webmock/rspec" require "shoulda-matchers" DatabaseCleaner.allow_remote_database_url = true ActiveRecord::Base.logger = Logger.new("/dev/null") -Dir[File.expand_path("factories/*.rb", __dir__)].each { |f| require f } Dir[File.expand_path("helpers/**/*.rb", __dir__)].each { |f| require f } ActionMailer::Base.delivery_method = :test diff --git a/spec/services/webhook_delivery_service_spec.rb b/spec/services/webhook_delivery_service_spec.rb index 4386c0f..14b0f59 100644 --- a/spec/services/webhook_delivery_service_spec.rb +++ b/spec/services/webhook_delivery_service_spec.rb @@ -59,8 +59,11 @@ RSpec.describe WebhookDeliveryService do end it "updates the last used at time on the webhook" do - service.call - expect(webhook.reload.last_used_at).to be_within(1.second).of(Time.current) + frozen_time = Time.current.change(usec: 0) + Timecop.freeze(frozen_time) do + service.call + expect(webhook.reload.last_used_at).to eq(frozen_time) + end end end