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
32 changes: 28 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ on:
pull_request:

jobs:
build:
name: ${{ format('Build ({0}, {1}, {2})', matrix.mysql, matrix.distribution, matrix.ruby) }}
mysql:
name: ${{ format('MySQL {0} ({1}, Ruby {2})', matrix.db_version, matrix.distribution, matrix.ruby) }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
mysql: ["8.0", "8.4"]
db_version: ["8.0", "8.4", "9.5"]
distribution: ["debian:bookworm", "ubuntu:noble", "ubuntu:jammy", "ubuntu:focal"]
ruby: ["3.3", "3.4"]
steps:
Expand All @@ -29,7 +29,31 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run tests
env:
MYSQL_VERSION: ${{ matrix.mysql }}
DB_VENDOR: mysql
DB_VERSION: ${{ matrix.db_version }}
DISTRIBUTION: ${{ matrix.distribution }}
RUBY_VERSION: ${{ matrix.ruby }}
run: script/cibuild

mariadb:
name: ${{ format('MariaDB {0} ({1}, Ruby {2})', matrix.db_version, matrix.distribution, matrix.ruby) }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
db_version: ["10.6", "10.11", "11.4", "11.8", "12.1"]
distribution: ["debian:bookworm", "ubuntu:noble", "ubuntu:jammy", "ubuntu:focal"]
ruby: ["3.3", "3.4"]
steps:
- uses: actions/checkout@v6
- name: docker login
run: echo $GITHUB_TOKEN | docker login ghcr.io --username trilogy --password-stdin
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run tests
env:
DB_VENDOR: mariadb
DB_VERSION: ${{ matrix.db_version }}
DISTRIBUTION: ${{ matrix.distribution }}
RUBY_VERSION: ${{ matrix.ruby }}
run: script/cibuild
112 changes: 104 additions & 8 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ permissions:
contents: read

name: macOS

on:
push:
branches:
Expand All @@ -11,31 +12,71 @@ on:
pull_request:

jobs:
test:
name: Test
test-mysql:
name: Test (MySQL ${{ matrix.mysql }})
runs-on: macos-latest
strategy:
matrix:
mysql: ["8.0", "8.4"]
mysql: ["8.0", "8.4", "9.5"]
steps:
- uses: actions/checkout@v6
- name: Setup MySQL
run: |
brew install mysql@${{ matrix.mysql }}
# Apply macOS-specific config if it exists (e.g., 8.4 needs mysql_native_password=ON)
# Homebrew MySQL reads config from $(brew --prefix)/etc/my.cnf
if [[ -f "test/mysql/conf.d/${{ matrix.mysql }}/macos.cnf" ]]; then
cat test/mysql/conf.d/${{ matrix.mysql }}/macos.cnf >> $(brew --prefix)/etc/my.cnf
fi
(unset CI; brew postinstall mysql@${{ matrix.mysql }})
brew services start mysql@${{ matrix.mysql }}
sleep 5
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot -e 'CREATE DATABASE test'
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot < test/mysql/docker-entrypoint-initdb.d/caching_sha2_password_user.sql
- name: Build
run: CFLAGS="-I$(brew --prefix openssl@1.1)/include" LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" make all test/test
- name: test
run: test/test
test-ruby:
name: Test Ruby

test-mariadb:
name: Test (MariaDB ${{ matrix.mariadb }})
runs-on: macos-latest
strategy:
matrix:
mysql: ["8.0"]
mariadb: ["10.6", "10.11", "11.4", "11.8"]
steps:
- uses: actions/checkout@v6
- name: Setup MariaDB
run: |
brew install mariadb@${{ matrix.mariadb }}
# Apply macOS-specific config if it exists
if [[ -f "test/mariadb/conf.d/${{ matrix.mariadb }}/macos.cnf" ]]; then
cat test/mariadb/conf.d/${{ matrix.mariadb }}/macos.cnf >> $(brew --prefix)/etc/my.cnf
fi
(unset CI; brew postinstall mariadb@${{ matrix.mariadb }})
brew services start mariadb@${{ matrix.mariadb }}
sleep 5
# MariaDB uses unix_socket auth for root by default, so use sudo
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e 'CREATE DATABASE IF NOT EXISTS test'
# Create a test user for C tests (root uses unix_socket which doesn't work for TCP)
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e "CREATE USER IF NOT EXISTS 'trilogy'@'127.0.0.1' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON test.* TO 'trilogy'@'127.0.0.1';"
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e "CREATE USER IF NOT EXISTS 'trilogy'@'localhost' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON test.* TO 'trilogy'@'localhost';"
- name: Build
run: CFLAGS="-I$(brew --prefix openssl@1.1)/include" LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" make all test/test
- name: test
env:
MYSQL_HOST: "127.0.0.1"
MYSQL_USER: trilogy
MYSQL_PASS: password
MYSQL_DB: test
run: test/test

test-ruby-mysql:
name: Test Ruby (MySQL ${{ matrix.mysql }}, Ruby ${{ matrix.ruby }})
runs-on: macos-latest
strategy:
matrix:
mysql: ["8.0", "8.4", "9.5"]
ruby: ["3.0", "3.1", "3.2", "3.3", "3.4"]
steps:
- uses: actions/checkout@v6
Expand All @@ -47,12 +88,20 @@ jobs:
MYSQL_VERSION: ${{ matrix.mysql }}
run: |
brew install mysql@${{ matrix.mysql }}
# Apply macOS-specific config if it exists (e.g., 8.4 needs mysql_native_password=ON)
# Homebrew MySQL reads config from $(brew --prefix)/etc/my.cnf
if [[ -f "test/mysql/conf.d/${{ matrix.mysql }}/macos.cnf" ]]; then
cat test/mysql/conf.d/${{ matrix.mysql }}/macos.cnf >> $(brew --prefix)/etc/my.cnf
fi
(unset CI; brew postinstall mysql@${{ matrix.mysql }})
brew services start mysql@${{ matrix.mysql }}
sleep 5
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot -e 'CREATE DATABASE test'
[[ "$MYSQL_VERSION" == "8.0" ]] && $(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot < test/mysql/docker-entrypoint-initdb.d/caching_sha2_password_user.sql
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot < test/mysql/docker-entrypoint-initdb.d/native_password_user.sql
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot < test/mysql/docker-entrypoint-initdb.d/caching_sha2_password_user.sql
# mysql_native_password plugin was removed in MySQL 9.x
if [[ ! "${{ matrix.mysql }}" =~ ^9 ]]; then
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot -e "CREATE USER 'native'@'%'; GRANT ALL PRIVILEGES ON test.* TO 'native'@'%'; ALTER USER 'native'@'%' IDENTIFIED WITH mysql_native_password BY 'password';"
fi
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot < test/mysql/docker-entrypoint-initdb.d/x509_user.sql
$(brew --prefix mysql@${{ matrix.mysql }})/bin/mysql -uroot < test/mysql/docker-entrypoint-initdb.d/cleartext_user.sql
- name: Install dependencies
Expand All @@ -63,3 +112,50 @@ jobs:
run: |
cd contrib/ruby
bundle exec rake

test-ruby-mariadb:
name: Test Ruby (MariaDB ${{ matrix.mariadb }}, Ruby ${{ matrix.ruby }})
runs-on: macos-latest
strategy:
matrix:
mariadb: ["10.6", "10.11", "11.4", "11.8"]
ruby: ["3.0", "3.1", "3.2", "3.3", "3.4"]
steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Setup MariaDB
env:
MARIADB_VERSION: ${{ matrix.mariadb }}
run: |
brew install mariadb@${{ matrix.mariadb }}
# Apply macOS-specific config if it exists
if [[ -f "test/mariadb/conf.d/${{ matrix.mariadb }}/macos.cnf" ]]; then
cat test/mariadb/conf.d/${{ matrix.mariadb }}/macos.cnf >> $(brew --prefix)/etc/my.cnf
fi
(unset CI; brew postinstall mariadb@${{ matrix.mariadb }})
brew services start mariadb@${{ matrix.mariadb }}
sleep 5
# MariaDB uses unix_socket auth for root by default, so use sudo
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e 'CREATE DATABASE IF NOT EXISTS test'
# Create a test user that can connect via TCP (root uses unix_socket which doesn't work for TCP)
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e "CREATE USER IF NOT EXISTS 'trilogy'@'127.0.0.1' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON *.* TO 'trilogy'@'127.0.0.1' WITH GRANT OPTION;"
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e "CREATE USER IF NOT EXISTS 'trilogy'@'localhost' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON *.* TO 'trilogy'@'localhost' WITH GRANT OPTION;"
# MariaDB uses IDENTIFIED VIA instead of IDENTIFIED WITH
sudo $(brew --prefix mariadb@${{ matrix.mariadb }})/bin/mariadb -e "CREATE USER IF NOT EXISTS 'native'@'%'; GRANT ALL PRIVILEGES ON test.* TO 'native'@'%'; ALTER USER 'native'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('password');"
# Note: x509_user.sql and cleartext_user.sql are not used for MariaDB
# - x509 tests require custom client certificates (not available without generate_keys.sh)
# - cleartext_plugin_server requires auth_test_plugin.so (MySQL-specific)
- name: Install dependencies
run: |
cd contrib/ruby
bundle --without benchmark
- name: Run tests
env:
MYSQL_HOST: "127.0.0.1"
MYSQL_USER: trilogy
MYSQL_PASS: password
run: |
cd contrib/ruby
bundle exec rake
7 changes: 6 additions & 1 deletion contrib/ruby/ext/trilogy-ruby/cext.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,12 @@ static void auth_switch(struct trilogy_ctx *ctx, trilogy_handshake_t *handshake)
}

if (rc != TRILOGY_AGAIN) {
handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
if (rc == TRILOGY_UNSUPPORTED) {
handle_trilogy_error(ctx, rc, "trilogy_auth_recv: caching_sha2_password requires either TCP with TLS or a unix socket");
}
else {
handle_trilogy_error(ctx, rc, "trilogy_auth_recv");
}
}

rc = trilogy_sock_wait_read(ctx->conn.socket);
Expand Down
50 changes: 38 additions & 12 deletions contrib/ruby/test/auth_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,49 @@

class AuthTest < TrilogyTest
def setup
client = new_tcp_client

plugin_exists = client.query("SELECT name FROM mysql.plugin WHERE name = 'cleartext_plugin_server'").rows.first
unless plugin_exists
client.query("INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin.so'")
# Only try to install cleartext plugin on MySQL (MariaDB doesn't have auth_test_plugin.so)
if has_cleartext_plugin_available?
client = new_tcp_client
plugin_exists = client.query("SELECT name FROM mysql.plugin WHERE name = 'cleartext_plugin_server'").rows.first
unless plugin_exists
client.query("INSTALL PLUGIN cleartext_plugin_server SONAME 'auth_test_plugin.so'")
end
end

super
end

def has_cleartext_plugin_available?
# MariaDB doesn't ship auth_test_plugin.so, only MySQL does
!is_mariadb?
end

def has_caching_sha2?
server_version = new_tcp_client.server_version
server_version.split(".", 2)[0].to_i >= 8
# MySQL 8+ has caching_sha2_password
# MariaDB server-side caching_sha2_password was added in 12.1 (Community) / 11.8 (Enterprise)
# Ref: https://mariadb.com/docs/server/reference/clientserver-protocol/1-connecting/caching_sha2_password-authentication-plugin
if is_mariadb?
# MariaDB version format is like "10.6.18-MariaDB" or "11.4.5-MariaDB"
version_parts = server_version.split("-").first.split(".")
major = version_parts[0].to_i
# Only available in MariaDB 12.1+ for Community Server (we test against Community images)
major >= 12
else
server_version.split(".", 2)[0].to_i >= 8
end
end

def has_native_password_plugin?
new_tcp_client.query("SELECT PLUGIN_NAME FROM information_schema.plugins WHERE PLUGIN_NAME = 'mysql_native_password'").count > 0
rescue Trilogy::Error
false
end

def test_connect_native_with_password
return skip unless has_native_password_plugin?
# mysql_native_password user creation has issues on MariaDB (connection reset during ALTER USER)
return skip("mysql_native_password test not supported on MariaDB") if is_mariadb?
create_and_delete_test_user(username: "native", auth_plugin: "mysql_native_password") do
client = new_tcp_client username: "native", password: "password"

Expand All @@ -31,9 +58,6 @@ def test_connect_caching_sha2_with_password
return skip unless has_caching_sha2?
create_and_delete_test_user(username: "caching_sha2", auth_plugin: "caching_sha2_password") do

# Ensure correct setup
assert_equal [["caching_sha2_password"]], new_tcp_client.query("SELECT plugin FROM mysql.user WHERE user = 'caching_sha2'").rows

client = new_tcp_client username: "caching_sha2", password: "password"

refute_nil client
Expand Down Expand Up @@ -64,9 +88,6 @@ def test_connect_without_ssl_or_unix_socket_caching_sha2_raises
return skip unless has_caching_sha2?

create_and_delete_test_user(username: "caching_sha2", auth_plugin: "caching_sha2_password") do
# Ensure correct setup
assert_equal [["caching_sha2_password"]], new_tcp_client.query("SELECT plugin FROM mysql.user WHERE user = 'caching_sha2'").rows

options = {
host: DEFAULT_HOST,
port: DEFAULT_PORT,
Expand All @@ -86,6 +107,9 @@ def test_connect_without_ssl_or_unix_socket_caching_sha2_raises
end

def test_connection_error_native
return skip unless has_native_password_plugin?
# mysql_native_password user creation has issues on MariaDB (connection reset during ALTER USER)
return skip("mysql_native_password test not supported on MariaDB") if is_mariadb?
create_and_delete_test_user(username: "native", auth_plugin: "mysql_native_password") do

err = assert_raises Trilogy::ConnectionError do
Expand All @@ -109,6 +133,7 @@ def test_connection_error_caching_sha2
end

def test_cleartext_auth_plugin_with_password
return skip unless has_cleartext_plugin_available?
create_and_delete_test_user(username: "cleartext_user", auth_plugin: "cleartext_plugin_server") do
client = new_tcp_client username: "cleartext_user", password: "password", enable_cleartext_plugin: true
refute_nil client
Expand All @@ -118,6 +143,7 @@ def test_cleartext_auth_plugin_with_password
end

def test_cleartext_auth_plugin_disabled
return skip unless has_cleartext_plugin_available?
create_and_delete_test_user(username: "cleartext_user", password: "", auth_plugin: "cleartext_plugin_server") do

assert_raises Trilogy::AuthPluginError do
Expand Down
7 changes: 4 additions & 3 deletions contrib/ruby/test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -760,8 +760,8 @@ def test_server_info
def test_connect_by_multiple_names
return skip unless ["127.0.0.1", "localhost"].include?(DEFAULT_HOST)

Trilogy.new(host: "127.0.0.1")
Trilogy.new(host: "localhost")
Trilogy.new(host: "127.0.0.1", username: DEFAULT_USER, password: DEFAULT_PASS)
Trilogy.new(host: "localhost", username: DEFAULT_USER, password: DEFAULT_PASS)
end

PADDED_QUERY_TEMPLATE = "SELECT LENGTH('%s')"
Expand Down Expand Up @@ -1091,7 +1091,8 @@ def test_no_character_encoding
assert_equal "utf8mb4", client.query("SELECT @@character_set_client").first.first
assert_equal "utf8mb4", client.query("SELECT @@character_set_results").first.first
assert_equal "utf8mb4", client.query("SELECT @@character_set_connection").first.first
assert_equal "utf8mb4_general_ci", client.query("SELECT @@collation_connection").first.first
collation = client.query("SELECT @@collation_connection").first.first
assert collation.start_with?("utf8mb4_"), "Expected utf8mb4 collation, got #{collation}"
end

def test_bad_character_encoding
Expand Down
3 changes: 2 additions & 1 deletion contrib/ruby/test/ssl_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ def test_raise_proper_invalid_ssl_state
end

def ca_cert_path
ENV["TRILOGY_TEST_CERTS"]
path = ENV["TRILOGY_TEST_CERTS"]
path if path && File.exist?("#{path}/ca.pem")
end

def test_trilogy_ssl_verify_ca_without_ca
Expand Down
Loading