Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
steps:
- name: Install system packages
timeout-minutes: 10
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libsqlite3-0 libvips curl ffmpeg
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libmysqlclient-dev libsqlite3-0 libvips curl ffmpeg

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
1 change: 1 addition & 0 deletions Brewfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
brew "imagemagick"
brew "mysql-client"
Comment thread
flavorjones marked this conversation as resolved.
brew "openslide"
brew "vips"
brew "gitleaks"
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ WORKDIR /rails

# Install base packages
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y ca-certificates curl libjemalloc2 libvips sqlite3 libssl-dev && \
apt-get install --no-install-recommends -y ca-certificates curl libjemalloc2 libmariadb3 libvips sqlite3 libssl-dev && \
ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

Expand All @@ -32,7 +32,7 @@ FROM base AS build

# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config && \
apt-get install --no-install-recommends -y build-essential default-libmysqlclient-dev git libyaml-dev pkg-config && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Install application gems
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ gem "solid_cache", "~> 1.0"
gem "solid_queue", "~> 1.7"
gem "sqlite3", ">= 2.0"
gem "thruster", require: false
gem "trilogy", "~> 2.13"
gem "mysql2", "~> 0.5"
gem "tzinfo-data" # Bundled zone data; the base image's tzdata package lags IANA

# Features
Expand Down
13 changes: 7 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ GEM
mocha (3.1.0)
ruby2_keywords (>= 0.0.5)
msgpack (1.8.5)
mysql2 (0.5.7)
bigdecimal
net-http-persistent (4.0.8)
connection_pool (>= 2.2.4, < 4)
net-imap (0.6.6)
Expand All @@ -323,7 +325,8 @@ GEM
net-ssh (>= 5.0.0, < 8.0.0)
net-smtp (0.5.1)
net-protocol
net-ssh (7.3.3)
net-ssh (7.3.8)
logger
nio4r (2.7.5)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
Expand Down Expand Up @@ -490,8 +493,6 @@ GEM
thruster (0.1.26-x86_64-darwin)
thruster (0.1.26-x86_64-linux)
timeout (0.6.1)
trilogy (2.13.1)
bigdecimal
tsort (0.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
Expand Down Expand Up @@ -553,6 +554,7 @@ DEPENDENCIES
mission_control-jobs
mittens
mocha
mysql2 (~> 0.5)
net-http-persistent
platform_agent
propshaft
Expand All @@ -573,7 +575,6 @@ DEPENDENCIES
stimulus-rails
surfguard!
thruster
trilogy (~> 2.13)
turbo-rails!
tzinfo-data
useragent!
Expand Down Expand Up @@ -680,14 +681,15 @@ CHECKSUMS
mittens (0.3.4) sha256=baa8e35c24bc06d6ed5c51633c800e8b6108ebcbd8efb1782d7146923797b383
mocha (3.1.0) sha256=75f42d69ebfb1f10b32489dff8f8431d37a418120ecdfc07afe3bc183d4e1d56
msgpack (1.8.5) sha256=97419eaa7b35dfe3e462c0e6c1a3dfdcbfc8756605c09e46e4c6daef127517ba
mysql2 (0.5.7) sha256=ba09ede515a0ae8a7192040a1b778c0fb0f025fa5877e9be895cd325fa5e9d7b
net-http-persistent (4.0.8) sha256=ef3de8319d691537b329053fae3a33195f8b070bbbfae8bf1a58c796081960e6
net-imap (0.6.6) sha256=96aa4ee50df3060203e649efc341f53480b791d49e150f2fdebf68beb141a8df
net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
net-scp (4.1.0) sha256=a99b0b92a1e5d360b0de4ffbf2dc0c91531502d3d4f56c28b0139a7c093d1a5d
net-sftp (4.0.0) sha256=65bb91c859c2f93b09826757af11b69af931a3a9155050f50d1b06d384526364
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
net-ssh (7.3.3) sha256=831def58b2c51dcef66ec00d29397d4f210de89c19fe78f95873ca30f386e86a
net-ssh (7.3.8) sha256=16bb1fccc4d9a580decdeea8ddb98e8544df29b7e6ac8cb94d4b379cfe4bee9f
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af
Expand Down Expand Up @@ -767,7 +769,6 @@ CHECKSUMS
thruster (0.1.26-x86_64-darwin) sha256=c7f7f0cbefd8030ea03bf88f6a988f595a0618a35a1a0d358e729c6898eb5b6a
thruster (0.1.26-x86_64-linux) sha256=3117a6ee430663f845a0457699fe9a05232dcc6c396e2cc83504de5a223c60e8
timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
trilogy (2.13.1) sha256=fbfb17c439b347e4240be70b80facf0f6f9c417b34c528179ff065b3d53635c9
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
turbo-rails (2.0.23)
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
Expand Down
13 changes: 7 additions & 6 deletions Gemfile.saas.lock
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ GEM
ruby2_keywords (>= 0.0.5)
msgpack (1.8.5)
multi_json (1.19.1)
mysql2 (0.5.7)
bigdecimal
net-http (0.9.1)
uri (>= 0.11.1)
net-http-persistent (4.0.8)
Expand All @@ -445,7 +447,8 @@ GEM
net-ssh (>= 5.0.0, < 8.0.0)
net-smtp (0.5.1)
net-protocol
net-ssh (7.3.3)
net-ssh (7.3.8)
logger
nio4r (2.7.5)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
Expand Down Expand Up @@ -668,8 +671,6 @@ GEM
thruster (0.1.26-x86_64-darwin)
thruster (0.1.26-x86_64-linux)
timeout (0.6.1)
trilogy (2.13.1)
bigdecimal
tsort (0.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
Expand Down Expand Up @@ -771,6 +772,7 @@ DEPENDENCIES
mission_control-jobs
mittens
mocha
mysql2 (~> 0.5)
net-http-persistent
platform_agent
prometheus-client-mmap (~> 1.3)
Expand All @@ -796,7 +798,6 @@ DEPENDENCIES
stimulus-rails
surfguard!
thruster
trilogy (~> 2.13)
turbo-rails!
tzinfo-data
useragent!
Expand Down Expand Up @@ -934,6 +935,7 @@ CHECKSUMS
mocha (3.1.0) sha256=75f42d69ebfb1f10b32489dff8f8431d37a418120ecdfc07afe3bc183d4e1d56
msgpack (1.8.5) sha256=97419eaa7b35dfe3e462c0e6c1a3dfdcbfc8756605c09e46e4c6daef127517ba
multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7
mysql2 (0.5.7) sha256=ba09ede515a0ae8a7192040a1b778c0fb0f025fa5877e9be895cd325fa5e9d7b
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
net-http-persistent (4.0.8) sha256=ef3de8319d691537b329053fae3a33195f8b070bbbfae8bf1a58c796081960e6
net-imap (0.6.6) sha256=96aa4ee50df3060203e649efc341f53480b791d49e150f2fdebf68beb141a8df
Expand All @@ -942,7 +944,7 @@ CHECKSUMS
net-scp (4.1.0) sha256=a99b0b92a1e5d360b0de4ffbf2dc0c91531502d3d4f56c28b0139a7c093d1a5d
net-sftp (4.0.0) sha256=65bb91c859c2f93b09826757af11b69af931a3a9155050f50d1b06d384526364
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
net-ssh (7.3.3) sha256=831def58b2c51dcef66ec00d29397d4f210de89c19fe78f95873ca30f386e86a
net-ssh (7.3.8) sha256=16bb1fccc4d9a580decdeea8ddb98e8544df29b7e6ac8cb94d4b379cfe4bee9f
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af
Expand Down Expand Up @@ -1040,7 +1042,6 @@ CHECKSUMS
thruster (0.1.26-x86_64-darwin) sha256=c7f7f0cbefd8030ea03bf88f6a988f595a0618a35a1a0d358e729c6898eb5b6a
thruster (0.1.26-x86_64-linux) sha256=3117a6ee430663f845a0457699fe9a05232dcc6c396e2cc83504de5a223c60e8
timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
trilogy (2.13.1) sha256=fbfb17c439b347e4240be70b80facf0f6f9c417b34c528179ff065b3d53635c9
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
turbo-rails (2.0.23)
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
Expand Down
2 changes: 1 addition & 1 deletion app/models/board/accessible.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def notifications_for_user(user)
#
# Notification->Event->Mention->Card and Notification->Event->Mention->Comment->Card are
# handled by destroying mentions_for_user.
uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy)
uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :mysql2)
board_id_binary = uuid_type.serialize(id)

user.notifications
Expand Down
2 changes: 1 addition & 1 deletion app/models/search/record.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Search::Record < ApplicationRecord
if connection.adapter_name == "Trilogy"
if connection.adapter_name == "Mysql2"
self.abstract_class = true # MySQL uses sharded tables; shard models set their own table_name
else
self.table_name = "search_records"
Expand Down
2 changes: 1 addition & 1 deletion config/database.mysql.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
default: &default
adapter: trilogy
adapter: mysql2
Comment thread
flavorjones marked this conversation as resolved.
host: <%= ENV.fetch("MYSQL_HOST", "127.0.0.1") %>
port: <%= ENV.fetch("MYSQL_PORT", "3306") %>
username: <%= ENV.fetch("MYSQL_USER", "root") %>
Expand Down
5 changes: 5 additions & 0 deletions config/initializers/mysql_ssl_mode.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# mysql2 built against MariaDB Connector/C, as in the Docker image, ignores verify_ca and
# connects without checking the server certificate.
if ENV["MYSQL_SSL_MODE"].to_s.casecmp?("verify_ca")
raise "MYSQL_SSL_MODE=verify_ca is not supported. Use verify_identity, which checks the server certificate and its host name."
end
4 changes: 2 additions & 2 deletions config/initializers/uuid_primary_keys.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module MysqlUuidAdapter
# Override lookup_cast_type to recognize binary(16) as UUID type
def lookup_cast_type(sql_type)
if sql_type == "binary(16)"
ActiveRecord::Type.lookup(:uuid, adapter: :trilogy)
ActiveRecord::Type.lookup(:uuid, adapter: :mysql2)
else
super
end
Expand Down Expand Up @@ -116,7 +116,7 @@ def uuid(name, **options)
ActiveRecord::ConnectionAdapters::TableDefinition.prepend(TableDefinitionUuidSupport)
end

ActiveSupport.on_load(:active_record_trilogyadapter) do
ActiveSupport.on_load(:active_record_mysql2adapter) do
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.prepend(MysqlUuidAdapter)
ActiveRecord::ConnectionAdapters::MySQL::SchemaDumper.prepend(SchemaDumperUuidType)
end
Expand Down
8 changes: 4 additions & 4 deletions lib/rails_ext/active_record_sql_redaction.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Trilogy runs without prepared statements, so a value reaches the SQL text as a quoted literal. The
# MySQL runs without prepared statements, so a value reaches the SQL text as a quoted literal. The
# sql.active_record notification carries that text to every consumer, and with Rails.event in debug mode
# (config/application.rb) it reaches the log: in SaaS mode, the structured log line for the request or
# job, and from there Loki. Sentry breadcrumbs and spans read it too, and a database error can quote a
Expand Down Expand Up @@ -72,7 +72,7 @@ def mentions_redacted_table?(text)
def filter_error(error)
filtered = \
if server_error_code(error)
[ error.error_code, FILTERED_ERROR ].join(": ")
[ error.error_number, FILTERED_ERROR ].join(": ")
else
redact_error_message(error.message)
end
Expand All @@ -89,7 +89,7 @@ def redact_error_message(message)

private
def server_error_code(error)
error.respond_to?(:error_code) && error.error_code
error.respond_to?(:error_number) && error.error_number
end

def reference_to(table)
Expand Down Expand Up @@ -123,7 +123,7 @@ module ActiveRecordSqlRedaction::ErrorTranslation
private
def translate_exception_class(native_error, sql, binds)
if ActiveRecordSqlRedaction.redacted_statement?(sql) && \
ActiveRecordSqlRedaction::UNFILTERED_ERROR_CODES.exclude?(native_error.try(:error_code))
ActiveRecordSqlRedaction::UNFILTERED_ERROR_CODES.exclude?(native_error.try(:error_number))
ActiveRecordSqlRedaction.filter_error(native_error)
end

Expand Down
14 changes: 0 additions & 14 deletions lib/rails_ext/active_record_trilogy_quote_string.rb

This file was deleted.

4 changes: 2 additions & 2 deletions lib/rails_ext/active_record_uuid_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ def cast(value)
end
end

# Register the UUID type for Trilogy (MySQL) and SQLite3 adapters
ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :trilogy)
# Register the UUID type for the Mysql2 and SQLite3 adapters
ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :mysql2)
ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :sqlite3)
4 changes: 2 additions & 2 deletions saas/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ FROM base AS build

# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install -y --no-install-recommends -y build-essential pkg-config git libvips libyaml-dev libssl-dev && \
apt-get install -y --no-install-recommends -y build-essential default-libmysqlclient-dev pkg-config git libvips libyaml-dev libssl-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Install application gems
Expand Down Expand Up @@ -63,7 +63,7 @@ FROM base
# library (ActiveStorage::VIPS_AVAILABLE=false), and config/initializers/vips.rb only demands it
# when no cell is registered.
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y libsqlite3-0 sqlite3 libjemalloc-dev && \
apt-get install --no-install-recommends -y libmariadb3 libsqlite3-0 sqlite3 libjemalloc-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# The eMudhra intermediate CAs' private keys leaked in October 2026, and they chain to these two roots.
Expand Down
3 changes: 2 additions & 1 deletion saas/config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
%>

default: &default
adapter: trilogy
adapter: mysql2
host: <%= ENV.fetch "FIZZY_DB_HOST", "127.0.0.1" %>
port: <%= ENV.fetch "FIZZY_DB_PORT", 3306 %>
username: root # mysql2 falls back to the OS user; production names its own
pool: 50
timeout: 5000
<% unless ENV["MIGRATE"].present? %>
Expand Down
4 changes: 2 additions & 2 deletions saas/test/integration/structured_log_redaction_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def perform(identity)
ActiveRecord::Base.cache { 2.times { assert MagicLink.find_by(code: CODE) } }
assert_raises(ActiveRecord::RecordNotUnique) { identities(:kevin).magic_links.new(code: CODE, expires_at: 1.minute.from_now).save!(validate: false) }
transaction.finish
Sentry.capture_exception(ActiveRecord::RecordNotUnique.new("Trilogy::ProtocolError: 1062: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'"))
Sentry.capture_exception(ActiveRecord::RecordNotUnique.new("Mysql2::Error: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'"))

assert sentry_events.many?
assert_no_secret CODE, sentry_events.map { |event| event.to_h.to_json }
Expand All @@ -139,7 +139,7 @@ def perform(identity)
unclosed = Sentry::Breadcrumb.new(category: "sql.active_record", data: { sql: "SELECT * FROM magic_links WHERE purpose = '0' AND code = '#{CODE}" })
assert_no_secret CODE, [ config.before_breadcrumb.call(unclosed, {}).data[:sql] ]

error = ActiveRecord::RecordNotUnique.new("Trilogy::ProtocolError: 1062: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'")
error = ActiveRecord::RecordNotUnique.new("Mysql2::Error: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'")
event = Sentry::ErrorEvent.new(configuration: config).tap { |event| event.add_exception_interface(error, mechanism: Sentry::Mechanism.new) }
assert_no_secret CODE, [ config.before_send.call(event, {}).exception.values.map(&:value).inspect ]

Expand Down
31 changes: 31 additions & 0 deletions test/initializers/mysql_ssl_mode_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require "test_helper"

class MysqlSslModeTest < ActiveSupport::TestCase
test "verify_ca is refused" do
%w[ verify_ca VERIFY_CA ].each do |mode|
with_mysql_ssl_mode(mode) do
error = assert_raises(RuntimeError) { load_initializer }
assert_match "verify_identity", error.message
end
end
end

test "other modes are allowed" do
[ nil, "disabled", "required", "verify_identity" ].each do |mode|
with_mysql_ssl_mode(mode) { assert_nothing_raised { load_initializer } }
end
end

private
def with_mysql_ssl_mode(mode)
original = ENV["MYSQL_SSL_MODE"]
ENV["MYSQL_SSL_MODE"] = mode
yield
ensure
ENV["MYSQL_SSL_MODE"] = original
end

def load_initializer
load Rails.root.join("config/initializers/mysql_ssl_mode.rb").to_s
end
end
23 changes: 23 additions & 0 deletions test/lib/database_adapter_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require "test_helper"

class DatabaseAdapterTest < ActiveSupport::TestCase
unless Fizzy.db_adapter.sqlite?
test "MySQL connects with mysql2" do
assert_equal "Mysql2", connection.adapter_name
end

test "MySQL connects in utf8mb4" do
assert_equal "utf8mb4", connection.select_value("SELECT @@character_set_connection")
assert_equal "🗂️", connection.select_value("SELECT #{connection.quote("🗂️")}")
end

test "MySQL reads binary(16) columns as UUIDs" do
assert_equal :uuid, Card.columns_hash["id"].type
end
end

private
def connection
ActiveRecord::Base.connection
end
end
Loading
Loading