From 2202ddf476aeff470dadbfe992aa224021b66933 Mon Sep 17 00:00:00 2001 From: Andrew Novoselac Date: Wed, 3 Jul 2024 15:47:22 -0400 Subject: [PATCH] Add mariadb options to application generator Until now, the database option has conflated the datbase adapter and the database itself. It's fine for postgres and sqlite, but for mysql2 and trilogy adapters we could either use mysql or mariadb as the database. This becomes a problem for generating a dev container, since we need to add a service to docker compose with the correct database. Previously, we gave the user a mysql database for the mysql option, and a mariadb database for the trilogy option. But that is no good as it is common to use a mysql database with trilogy. In this PR I disambiguate the adapter-database issue by introducing two new options, mariadb-mysql and mariadb-trilogy. When generating a dev container, the mysql and trilogy options will create a mysql database, and the maria specific options will generate a mariadb database. Users not generating a dev container do not need to worry about the new options and can continue using the original options, as this change only effects dev container setup. --- railties/CHANGELOG.md | 8 + railties/lib/rails/generators/database.rb | 166 +++++++++++------- .../generators/rails/app/app_generator.rb | 2 +- .../db/system/change/change_generator.rb | 2 +- .../test/commands/db_system_change_test.rb | 2 +- .../test/generators/app_generator_test.rb | 58 +++++- .../db_system_change_generator_test.rb | 6 +- 7 files changed, 173 insertions(+), 71 deletions(-) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 8e035afd5500a..2cce4f25c5c23 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,11 @@ +* Introduce `mariadb-mysql` and `mariadb-trilogy` database options for `rails new` + + When used with the `--devcontainer` flag, these options will use `mariadb` as the database for the + Dev Container. The original `mysql` and `trilogy` options will use `mysql`. Users who are not + generating a Dev Container do not need to use the new options. + + *Andrew Novoselac* + * Deprecate `::STATS_DIRECTORIES`. The global constant `STATS_DIRECTORIES` has been deprecated in favor of diff --git a/railties/lib/rails/generators/database.rb b/railties/lib/rails/generators/database.rb index e36a0da286e32..78f9f2e95f943 100644 --- a/railties/lib/rails/generators/database.rb +++ b/railties/lib/rails/generators/database.rb @@ -3,25 +3,91 @@ module Rails module Generators class Database - DATABASES = %w( mysql trilogy postgresql sqlite3 ) + DATABASES = %w( mysql trilogy postgresql sqlite3 mariadb-mysql mariadb-trilogy ) + + module MySQL + def name + "mysql" + end + + def port + 3306 + end + + def service + { + "image" => "mysql/mysql-server:8.0", + "restart" => "unless-stopped", + "environment" => { + "MYSQL_ALLOW_EMPTY_PASSWORD" => "true", + "MYSQL_ROOT_HOST" => "%" + }, + "volumes" => ["mysql-data:/var/lib/mysql"], + "networks" => ["default"], + } + end + + def socket + @socket ||= [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless Gem.win_platform? + end + + def host + "localhost" + end + end + + module MariaDB + def name + "mariadb" + end + + def port + 3306 + end + + def service + { + "image" => "mariadb:10.5", + "restart" => "unless-stopped", + "networks" => ["default"], + "volumes" => ["mariadb-data:/var/lib/mysql"], + "environment" => { + "MARIADB_ALLOW_EMPTY_ROOT_PASSWORD" => "true", + }, + } + end + end class << self def build(database_name) case database_name - when "mysql" then MySQL.new + when "mysql" then MySQL2.new when "postgresql" then PostgreSQL.new - when "trilogy" then MariaDB.new + when "trilogy" then Trilogy.new when "sqlite3" then SQLite3.new + when "mariadb-mysql" then MariaDBMySQL2.new + when "mariadb-trilogy" then MariaDBTrilogy.new else Null.new end end def all @all ||= [ - MySQL.new, + MySQL2.new, PostgreSQL.new, - MariaDB.new, SQLite3.new, + MariaDBMySQL2.new, + MariaDBTrilogy.new ] end end @@ -30,6 +96,10 @@ def name raise NotImplementedError end + def template + raise NotImplementedError + end + def service raise NotImplementedError end @@ -69,48 +139,11 @@ def volume "#{name}-data" end - module MySqlSocket - def socket - @socket ||= [ - "/tmp/mysql.sock", # default - "/var/run/mysqld/mysqld.sock", # debian/gentoo - "/var/tmp/mysql.sock", # freebsd - "/var/lib/mysql/mysql.sock", # fedora - "/opt/local/lib/mysql/mysql.sock", # fedora - "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql - "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 - "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 - "/opt/lampp/var/mysql/mysql.sock" # xampp for linux - ].find { |f| File.exist?(f) } unless Gem.win_platform? - end - - def host - "localhost" - end - end - - class MySQL < Database - include MySqlSocket - - def name - "mysql" - end - - def service - { - "image" => "mysql/mysql-server:8.0", - "restart" => "unless-stopped", - "environment" => { - "MYSQL_ALLOW_EMPTY_PASSWORD" => "true", - "MYSQL_ROOT_HOST" => "%" - }, - "volumes" => ["mysql-data:/var/lib/mysql"], - "networks" => ["default"], - } - end + class MySQL2 < Database + include MySQL - def port - 3306 + def template + "config/databases/mysql.yml" end def gem @@ -135,6 +168,10 @@ def name "postgres" end + def template + "config/databases/postgresql.yml" + end + def service { "image" => "postgres:16.1", @@ -169,27 +206,11 @@ def feature_name end end - class MariaDB < Database - include MySqlSocket - - def name - "mariadb" - end - - def service - { - "image" => "mariadb:10.5", - "restart" => "unless-stopped", - "networks" => ["default"], - "volumes" => ["mariadb-data:/var/lib/mysql"], - "environment" => { - "MARIADB_ALLOW_EMPTY_ROOT_PASSWORD" => "true", - }, - } - end + class Trilogy < Database + include MySQL - def port - 3306 + def template + "config/databases/trilogy.yml" end def gem @@ -214,6 +235,10 @@ def name "sqlite3" end + def template + "config/databases/sqlite3.yml" + end + def service nil end @@ -239,8 +264,17 @@ def feature_name end end + class MariaDBMySQL2 < MySQL2 + include MariaDB + end + + class MariaDBTrilogy < Trilogy + include MariaDB + end + class Null < Database def name; end + def template; end def service; end def port; end def volume; end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 5e9ebbabe220e..64003342c759f 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -206,7 +206,7 @@ def credentials_diff_enroll end def database_yml - template "config/databases/#{options[:database]}.yml", "config/database.yml" + template database.template, "config/database.yml" end def db diff --git a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb index e8c9d12e9fbfb..28ef35ca66d82 100644 --- a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb +++ b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb @@ -35,7 +35,7 @@ def initialize(*) end def edit_database_config - template("config/databases/#{options[:database]}.yml", "config/database.yml") + template(database.template, "config/database.yml") end def edit_gemfile diff --git a/railties/test/commands/db_system_change_test.rb b/railties/test/commands/db_system_change_test.rb index 29068cbbaa040..0f027a6d236e3 100644 --- a/railties/test/commands/db_system_change_test.rb +++ b/railties/test/commands/db_system_change_test.rb @@ -25,7 +25,7 @@ class Rails::Command::DbSystemChangeTest < ActiveSupport::TestCase assert_match <<~MSG.squish, output Invalid value for --to option. Supported preconfigurations are: - mysql, trilogy, postgresql, sqlite3. + mysql, trilogy, postgresql, sqlite3, mariadb-mysql, mariadb-trilogy. MSG end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 9eb16ccd4b55c..754b938f367e6 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -1369,9 +1369,65 @@ def test_devcontainer_mysql end end - def test_devcontainer_mariadb + def test_devcontainer_trilogy run_generator [ destination_root, "--devcontainer", "-d", "trilogy" ] + assert_compose_file do |compose_config| + assert_includes compose_config["services"]["rails-app"]["depends_on"], "mysql" + expected_mysql_config = { + "image" => "mysql/mysql-server:8.0", + "restart" => "unless-stopped", + "environment" => { + "MYSQL_ALLOW_EMPTY_PASSWORD" => "true", + "MYSQL_ROOT_HOST" => "%" + }, + "volumes" => ["mysql-data:/var/lib/mysql"], + "networks" => ["default"], + } + + assert_equal expected_mysql_config, compose_config["services"]["mysql"] + assert_includes compose_config["volumes"].keys, "mysql-data" + end + assert_devcontainer_json_file do |content| + assert_equal "mysql", content["containerEnv"]["DB_HOST"] + assert_includes(content["forwardPorts"], 3306) + end + assert_file("config/database.yml") do |content| + assert_match(/host: <%= ENV.fetch\("DB_HOST"\) \{ "localhost" } %>/, content) + end + end + + def test_devcontainer_mariadb_mysql + run_generator [ destination_root, "--devcontainer", "-d", "mariadb-mysql" ] + + assert_compose_file do |compose_config| + assert_includes compose_config["services"]["rails-app"]["depends_on"], "mariadb" + expected_mariadb_config = { + "image" => "mariadb:10.5", + "restart" => "unless-stopped", + "networks" => ["default"], + "volumes" => ["mariadb-data:/var/lib/mysql"], + "environment" => { + "MARIADB_ALLOW_EMPTY_ROOT_PASSWORD" => "true", + }, + } + + assert_equal expected_mariadb_config, compose_config["services"]["mariadb"] + assert_includes compose_config["volumes"].keys, "mariadb-data" + end + assert_devcontainer_json_file do |content| + assert_equal "mariadb", content["containerEnv"]["DB_HOST"] + assert_includes content["features"].keys, "ghcr.io/rails/devcontainer/features/mysql-client" + assert_includes(content["forwardPorts"], 3306) + end + assert_file("config/database.yml") do |content| + assert_match(/host: <%= ENV.fetch\("DB_HOST"\) \{ "localhost" } %>/, content) + end + end + + def test_devcontainer_mariadb_trilogy + run_generator [ destination_root, "--devcontainer", "-d", "mariadb-trilogy" ] + assert_compose_file do |compose_config| assert_includes compose_config["services"]["rails-app"]["depends_on"], "mariadb" expected_mariadb_config = { diff --git a/railties/test/generators/db_system_change_generator_test.rb b/railties/test/generators/db_system_change_generator_test.rb index 509d9a1a2ce4d..afff13b6730ba 100644 --- a/railties/test/generators/db_system_change_generator_test.rb +++ b/railties/test/generators/db_system_change_generator_test.rb @@ -28,7 +28,7 @@ class ChangeGeneratorTest < Rails::Generators::TestCase assert_match <<~MSG.squish, output Invalid value for --to option. Supported preconfigurations are: - mysql, trilogy, postgresql, sqlite3. + mysql, trilogy, postgresql, sqlite3, mariadb-mysql, mariadb-trilogy. MSG end @@ -159,6 +159,10 @@ class ChangeGeneratorTest < Rails::Generators::TestCase assert_match "curl libvips", content assert_no_match "default-libmysqlclient-dev", content end + end + + test "change to mariadb" do + run_generator ["--to", "mariadb-mysql"] assert_devcontainer_json_file do |content| assert_match "mariadb", content["containerEnv"]["DB_HOST"]