exwiw 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a93af3fb75c47b5f8e477716a595c3dd2909e23285c3c5838a965adf640d2db6
4
- data.tar.gz: c98c7197342539af1faa5fce7c4acc759566cca3b96ea07585cc99e78a297572
3
+ metadata.gz: ef76bef2bd961b6bf50afcdd7f28e87e5c78ab587385519bbbfc3d66e3438c6d
4
+ data.tar.gz: 99c7d8cb5250fd3cfb7951a586d575dd76b3dbfa20f6c9aa5125643ce7bd12cb
5
5
  SHA512:
6
- metadata.gz: b7beec90b827baf014bbe16dcb6954374885ee40b2a3be13a17bb354cf51a88611d0dcfbe0273d00f987dc07032f61264d9cd160a9aac1fa5e2608cbceea0ee0
7
- data.tar.gz: 41c69598a4564af9f6fad2108673101abb0a544f8f721032d9804d860caeaa440d0934b31c14038a988cc39cc98ef7594d5f8530ba7fcee76bdc0b4ab77f62cb
6
+ metadata.gz: 271ef2c7496ab0d6d118a0e27195d0b411f8642914cc0a0eca68abc3d47df02b8876261c40933790f99a637f6e2eb1f63e8fc2ce2f618315151c3622370c7a1a
7
+ data.tar.gz: 356b3c7e579c980a38ab649e2c986bbb33310f4906593ff504f14ed2839f3d3249e60337fe355d8b32ddf2e72e6439b07bfa5da5845d194cceaa2a0d933a21b8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.1.8] - 2026-10-29
6
+
7
+ ### Added
8
+
9
+ - **`--ssl-mode` and `--ssl-ca` connect to MySQL over TLS.** exwiw passed no TLS options to the driver or to `mysqldump`, so with a client library that does not use TLS by default (MariaDB Connector/C, for one) every command failed against a server with `require_secure_transport=ON` — `Connections using insecure transport are prohibited`. The two options take the MySQL client's values (`disabled`, `preferred`, `required`, `verify_ca`, `verify_identity`) and a CA file, and apply to the `mysql2` / `trilogy` connection and to `mysqldump`; MariaDB's `mysqldump`, which has no `--ssl-mode`, gets the equivalent `--ssl` / `--ssl-verify-server-cert` flags, and where MariaDB's client library or `mysqldump` lacks a mode, `verify_ca` also checks the host name and `preferred` leaves the client default. Without the options nothing changes. mysql adapter only.
10
+
5
11
  ## [1.1.7] - 2026-10-01
6
12
 
7
13
  ### Added
data/README.md CHANGED
@@ -21,6 +21,8 @@ You usually want `require: false` on the Gemfile entry. Without bundler, run `ge
21
21
 
22
22
  For MySQL, exwiw uses the `mysql2` gem if it is available and `trilogy` otherwise; pass `--adapter=mysql` either way. Set `EXWIW_MYSQL_DRIVER=trilogy` (or `mysql2`) to choose one. `trilogy` helps when `mysql2` is linked against a client library that cannot load the server's auth plugin, such as a MySQL 9.x client connecting to a server that uses `mysql_native_password`.
23
23
 
24
+ To connect to MySQL over TLS, pass `--ssl-mode` (`disabled`, `preferred`, `required`, `verify_ca` or `verify_identity`) and `--ssl-ca=<CA file>`. The server certificate is verified only with `verify_ca` or `verify_identity`; `--ssl-ca` is rejected without `--ssl-mode`. Both apply to the driver connection and to `mysqldump`. Without them, exwiw uses the client library's default, which may connect without TLS and then fail against a server with `require_secure_transport=ON`. MariaDB's client library and `mysqldump` do not have every mode, so with them `verify_ca` also checks the host name and `preferred` leaves the client default (`mysqldump` gets `--ssl` / `--ssl-verify-server-cert` in place of `--ssl-mode`).
25
+
24
26
  ## Usage
25
27
 
26
28
  exwiw has three subcommands:
@@ -103,6 +103,7 @@ module Exwiw
103
103
  # MariaDB's mysqldump does not recognise the MySQL-specific
104
104
  # --set-gtid-purged flag and exits with code 7.
105
105
  gtid_flags = mariadb_mysqldump?(mysqldump_bin) ? [] : ['--set-gtid-purged=OFF']
106
+ ssl_flags = mysqldump_ssl_flags(mariadb: mariadb_mysqldump?(mysqldump_bin))
106
107
 
107
108
  cmd = [
108
109
  mysqldump_bin,
@@ -115,6 +116,7 @@ module Exwiw
115
116
  '--skip-set-charset',
116
117
  '--no-tablespaces', # skip tablespace query that requires PROCESS privilege (unavailable on managed MySQL like RDS)
117
118
  *gtid_flags,
119
+ *ssl_flags,
118
120
  '--compact',
119
121
  @connection_config.database_name,
120
122
  ]
@@ -437,6 +439,23 @@ module Exwiw
437
439
  end
438
440
  end
439
441
 
442
+ private def mysqldump_ssl_flags(mariadb:)
443
+ mode = @connection_config.ssl_mode
444
+ ca_flags = @connection_config.ssl_ca ? ["--ssl-ca=#{@connection_config.ssl_ca}"] : []
445
+ return ca_flags + (mode ? ["--ssl-mode=#{mode.upcase}"] : []) unless mariadb
446
+
447
+ # MariaDB's client has no --ssl-mode, and from 11.4 verifies the server
448
+ # certificate by default, so `required` must opt out to stay unverified.
449
+ mode_flags =
450
+ case mode
451
+ when nil, "preferred" then []
452
+ when "disabled" then ["--skip-ssl"]
453
+ when "required" then ["--ssl", "--skip-ssl-verify-server-cert"]
454
+ else ["--ssl", "--ssl-verify-server-cert"]
455
+ end
456
+ ca_flags + mode_flags
457
+ end
458
+
440
459
  private def connection
441
460
  @connection ||= MysqlClient.new(@connection_config)
442
461
  end
@@ -20,6 +20,16 @@ module Exwiw
20
20
 
21
21
  DRIVERS = [:mysql2, :trilogy].freeze
22
22
 
23
+ SSL_MODES = %w[disabled preferred required verify_ca verify_identity].freeze
24
+
25
+ TRILOGY_SSL_MODE_CONSTANTS = {
26
+ "disabled" => :SSL_DISABLED,
27
+ "preferred" => :SSL_PREFERRED_NOVERIFY,
28
+ "required" => :SSL_REQUIRED_NOVERIFY,
29
+ "verify_ca" => :SSL_VERIFY_CA,
30
+ "verify_identity" => :SSL_VERIFY_IDENTITY,
31
+ }.freeze
32
+
23
33
  # Pick the available driver, preferring mysql2 (exwiw's historical default).
24
34
  # require returns false when already loaded, so this is safe to call repeatedly.
25
35
  #
@@ -188,10 +198,31 @@ module Exwiw
188
198
  }.compact
189
199
 
190
200
  case @driver
191
- when :mysql2 then Mysql2::Client.new(options)
192
- when :trilogy then Trilogy.new(options)
201
+ when :mysql2 then Mysql2::Client.new(options.merge(mysql2_ssl_options))
202
+ when :trilogy then Trilogy.new(options.merge(trilogy_ssl_options))
193
203
  end
194
204
  end
205
+
206
+ private def mysql2_ssl_options
207
+ { ssl_mode: mysql2_ssl_mode, sslca: @connection_config.ssl_ca }.compact
208
+ end
209
+
210
+ # mysql2 linked against MariaDB Connector/C defines unsupported modes as 0 and
211
+ # ignores them with a warning, so fall back to the nearest mode it supports.
212
+ private def mysql2_ssl_mode
213
+ mode = @connection_config.ssl_mode
214
+ return mode if mode.nil? || !Mysql2::Client.const_get("SSL_MODE_#{mode.upcase}").zero?
215
+
216
+ "verify_identity" if mode == "verify_ca"
217
+ end
218
+
219
+ private def trilogy_ssl_options
220
+ mode = @connection_config.ssl_mode
221
+ {
222
+ ssl_mode: mode && Trilogy.const_get(TRILOGY_SSL_MODE_CONSTANTS.fetch(mode)),
223
+ ssl_ca: @connection_config.ssl_ca,
224
+ }.compact
225
+ end
195
226
  end
196
227
  end
197
228
  end
data/lib/exwiw/cli.rb CHANGED
@@ -65,7 +65,7 @@ module Exwiw
65
65
  # Database connection settings are environment-specific (and sometimes
66
66
  # secret-adjacent), so they must be passed via CLI/env, never the committed
67
67
  # config file. `adapter` is the one connection-ish key allowed in config.
68
- REJECTED_CONNECTION_KEYS = %w[host port user database uri password].freeze
68
+ REJECTED_CONNECTION_KEYS = %w[host port user database uri password ssl_mode ssl_ca].freeze
69
69
 
70
70
  # Keys that only make sense for `export`. They are skipped when merging config
71
71
  # for `explain` so a shared config file does not trip validate_explain_only!,
@@ -103,6 +103,8 @@ module Exwiw
103
103
  @database_user = nil
104
104
  @database_password = ENV["DATABASE_PASSWORD"]
105
105
  @connection_uri = nil
106
+ @ssl_mode = nil
107
+ @ssl_ca = nil
106
108
  @output_dir = nil
107
109
  @schema_dir = nil
108
110
  @from_db = false
@@ -148,6 +150,8 @@ module Exwiw
148
150
  database_name: @database_name,
149
151
  uri: @connection_uri,
150
152
  mongodb_query_timeout_ms: @mongodb_query_timeout_ms,
153
+ ssl_mode: @ssl_mode,
154
+ ssl_ca: @ssl_ca,
151
155
  )
152
156
 
153
157
  dump_target = DumpTarget.new(
@@ -324,6 +328,7 @@ module Exwiw
324
328
  resolve_scope_column!
325
329
  resolve_ids_field!
326
330
  resolve_uri_option!
331
+ resolve_ssl_options!
327
332
  resolve_parallel_workers!
328
333
  resolve_mongodb_query_timeout_ms!
329
334
 
@@ -654,6 +659,31 @@ module Exwiw
654
659
  end
655
660
  end
656
661
 
662
+ private def resolve_ssl_options!
663
+ return if @ssl_mode.nil? && @ssl_ca.nil?
664
+
665
+ if @database_adapter != "mysql"
666
+ $stderr.puts "--ssl-mode / --ssl-ca are only supported by the mysql adapter"
667
+ exit 1
668
+ end
669
+
670
+ if @ssl_ca&.empty?
671
+ $stderr.puts "--ssl-ca must be a path to a CA certificate file"
672
+ exit 1
673
+ end
674
+
675
+ if @ssl_mode.nil?
676
+ $stderr.puts "--ssl-ca requires --ssl-mode (verify_ca or verify_identity to verify the server)"
677
+ exit 1
678
+ end
679
+
680
+ @ssl_mode = @ssl_mode.downcase
681
+ unless Adapter::MysqlClient::SSL_MODES.include?(@ssl_mode)
682
+ $stderr.puts "Invalid --ssl-mode. Available options are: #{Adapter::MysqlClient::SSL_MODES.join(', ')}"
683
+ exit 1
684
+ end
685
+ end
686
+
657
687
  # `--parallel-workers` opts into the MongoDB fork-parallel dump schedule
658
688
  # (docs/mongodb-dump-parallelism-2x-notes.md). It is mongodb-only (the SQL
659
689
  # adapters shell out to their own dumpers) and must be a positive integer;
@@ -919,6 +949,8 @@ module Exwiw
919
949
  end
920
950
  opts.on("-a", "--adapter=ADAPTER", "Database adapter: mysql, sqlite, postgresql, mongodb (aliases: mysql2, sqlite3)") { |v| @database_adapter = v }
921
951
  opts.on("--uri=URI", "Full MongoDB connection URI (mongodb:// or mongodb+srv://). mongodb adapter only; takes precedence over --host/--port/--user. TLS, replicaSet, authSource and credentials are read from the URI.") { |v| @connection_uri = v }
952
+ opts.on("--ssl-mode=MODE", "TLS mode for the connection and mysqldump: #{Adapter::MysqlClient::SSL_MODES.join(', ')}. mysql adapter only; defaults to the client library's behavior.") { |v| @ssl_mode = v }
953
+ opts.on("--ssl-ca=PATH", "CA certificate file used to verify the server. mysql adapter only.") { |v| @ssl_ca = v }
922
954
  opts.on("--database=DATABASE", "Target database name") { |v| @database_name = v }
923
955
  opts.on("--target-table=[TABLE]", "Target table for extraction. If omitted with --ids, run in scope-column mode (SQL adapters; the schema must declare `scope_column:`). If omitted without --ids, dump all tables.") { |v| @target_table_name = v }
924
956
  opts.on("--target-collection=[COLLECTION]", "Alias of --target-table for the mongodb adapter.") { |v| @target_collection_name = v }
data/lib/exwiw/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Exwiw
4
- VERSION = "1.1.7"
4
+ VERSION = "1.1.8"
5
5
  end
data/lib/exwiw.rb CHANGED
@@ -116,5 +116,7 @@ module Exwiw
116
116
  # source: the server aborts the operation past the deadline. nil leaves it
117
117
  # unset (no timeout). A per-collection `query_timeout_ms` overrides it.
118
118
  # mongodb adapter only.
119
- ConnectionConfig = Struct.new(:adapter, :host, :port, :user, :password, :database_name, :uri, :mongodb_query_timeout_ms, keyword_init: true)
119
+ #
120
+ # `ssl_mode` / `ssl_ca` apply to both the driver connection and mysqldump. mysql adapter only.
121
+ ConnectionConfig = Struct.new(:adapter, :host, :port, :user, :password, :database_name, :uri, :mongodb_query_timeout_ms, :ssl_mode, :ssl_ca, keyword_init: true)
120
122
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exwiw
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.7
4
+ version: 1.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shia