db-sync-tool-kmi 2.11.6__py3-none-any.whl → 3.0.2__py3-none-any.whl
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.
- db_sync_tool/__main__.py +7 -252
- db_sync_tool/cli.py +733 -0
- db_sync_tool/database/process.py +94 -111
- db_sync_tool/database/utility.py +339 -121
- db_sync_tool/info.py +1 -1
- db_sync_tool/recipes/drupal.py +87 -12
- db_sync_tool/recipes/laravel.py +7 -6
- db_sync_tool/recipes/parsing.py +102 -0
- db_sync_tool/recipes/symfony.py +17 -28
- db_sync_tool/recipes/typo3.py +33 -54
- db_sync_tool/recipes/wordpress.py +13 -12
- db_sync_tool/remote/client.py +206 -71
- db_sync_tool/remote/file_transfer.py +303 -0
- db_sync_tool/remote/rsync.py +18 -15
- db_sync_tool/remote/system.py +2 -3
- db_sync_tool/remote/transfer.py +51 -47
- db_sync_tool/remote/utility.py +29 -30
- db_sync_tool/sync.py +52 -28
- db_sync_tool/utility/config.py +367 -0
- db_sync_tool/utility/config_resolver.py +573 -0
- db_sync_tool/utility/console.py +779 -0
- db_sync_tool/utility/exceptions.py +32 -0
- db_sync_tool/utility/helper.py +155 -148
- db_sync_tool/utility/info.py +53 -20
- db_sync_tool/utility/log.py +55 -31
- db_sync_tool/utility/logging_config.py +410 -0
- db_sync_tool/utility/mode.py +85 -150
- db_sync_tool/utility/output.py +122 -51
- db_sync_tool/utility/parser.py +33 -53
- db_sync_tool/utility/pure.py +93 -0
- db_sync_tool/utility/security.py +79 -0
- db_sync_tool/utility/system.py +277 -194
- db_sync_tool/utility/validation.py +2 -9
- db_sync_tool_kmi-3.0.2.dist-info/METADATA +99 -0
- db_sync_tool_kmi-3.0.2.dist-info/RECORD +44 -0
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/WHEEL +1 -1
- db_sync_tool_kmi-2.11.6.dist-info/METADATA +0 -276
- db_sync_tool_kmi-2.11.6.dist-info/RECORD +0 -34
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/entry_points.txt +0 -0
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info/licenses}/LICENSE +0 -0
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/top_level.txt +0 -0
db_sync_tool/cli.py
ADDED
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Typer-based CLI for db_sync_tool.
|
|
5
|
+
|
|
6
|
+
This module provides a modern CLI using typer with type annotations,
|
|
7
|
+
automatic documentation, and rich help formatting.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import logging
|
|
12
|
+
import traceback
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
import typer
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
|
|
19
|
+
from db_sync_tool import sync
|
|
20
|
+
from db_sync_tool.utility.config_resolver import ConfigResolver
|
|
21
|
+
from db_sync_tool.utility.console import init_output_manager
|
|
22
|
+
from db_sync_tool.utility.exceptions import ConfigError, NoConfigFoundError
|
|
23
|
+
from db_sync_tool.utility.logging_config import init_logging
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OutputFormat(str, Enum):
|
|
27
|
+
"""Output format options."""
|
|
28
|
+
|
|
29
|
+
interactive = "interactive"
|
|
30
|
+
ci = "ci"
|
|
31
|
+
json = "json"
|
|
32
|
+
quiet = "quiet"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
app = typer.Typer(
|
|
36
|
+
name="db_sync_tool",
|
|
37
|
+
help="A tool for automatic database synchronization from and to host systems.",
|
|
38
|
+
add_completion=True,
|
|
39
|
+
rich_markup_mode="rich",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.command()
|
|
44
|
+
def main(
|
|
45
|
+
# === Positional Arguments ===
|
|
46
|
+
origin: Annotated[
|
|
47
|
+
str | None,
|
|
48
|
+
typer.Argument(help="Origin database defined in host file"),
|
|
49
|
+
] = None,
|
|
50
|
+
target: Annotated[
|
|
51
|
+
str | None,
|
|
52
|
+
typer.Argument(help="Target database defined in host file"),
|
|
53
|
+
] = None,
|
|
54
|
+
# === Configuration Files ===
|
|
55
|
+
config_file: Annotated[
|
|
56
|
+
str | None,
|
|
57
|
+
typer.Option(
|
|
58
|
+
"--config-file",
|
|
59
|
+
"-f",
|
|
60
|
+
help="Path to configuration file",
|
|
61
|
+
rich_help_panel="Configuration",
|
|
62
|
+
),
|
|
63
|
+
] = None,
|
|
64
|
+
host_file: Annotated[
|
|
65
|
+
str | None,
|
|
66
|
+
typer.Option(
|
|
67
|
+
"--host-file",
|
|
68
|
+
"-o",
|
|
69
|
+
help="Using an additional hosts file for merging hosts information",
|
|
70
|
+
rich_help_panel="Configuration",
|
|
71
|
+
),
|
|
72
|
+
] = None,
|
|
73
|
+
log_file: Annotated[
|
|
74
|
+
str | None,
|
|
75
|
+
typer.Option(
|
|
76
|
+
"--log-file",
|
|
77
|
+
"-l",
|
|
78
|
+
help="File path for creating an additional log file",
|
|
79
|
+
rich_help_panel="Configuration",
|
|
80
|
+
),
|
|
81
|
+
] = None,
|
|
82
|
+
json_log: Annotated[
|
|
83
|
+
bool,
|
|
84
|
+
typer.Option(
|
|
85
|
+
"--json-log",
|
|
86
|
+
"-jl",
|
|
87
|
+
help="Use JSON format for log file output (structured logging)",
|
|
88
|
+
rich_help_panel="Configuration",
|
|
89
|
+
),
|
|
90
|
+
] = False,
|
|
91
|
+
# === Output Options ===
|
|
92
|
+
verbose: Annotated[
|
|
93
|
+
int,
|
|
94
|
+
typer.Option(
|
|
95
|
+
"--verbose",
|
|
96
|
+
"-v",
|
|
97
|
+
count=True,
|
|
98
|
+
help="Enable verbose output (-v) or debug output (-vv)",
|
|
99
|
+
rich_help_panel="Output",
|
|
100
|
+
),
|
|
101
|
+
] = 0,
|
|
102
|
+
mute: Annotated[
|
|
103
|
+
bool,
|
|
104
|
+
typer.Option(
|
|
105
|
+
"--mute",
|
|
106
|
+
"-m",
|
|
107
|
+
help="Mute console output",
|
|
108
|
+
rich_help_panel="Output",
|
|
109
|
+
),
|
|
110
|
+
] = False,
|
|
111
|
+
quiet: Annotated[
|
|
112
|
+
bool,
|
|
113
|
+
typer.Option(
|
|
114
|
+
"--quiet",
|
|
115
|
+
"-q",
|
|
116
|
+
help="Suppress all output except errors (shorthand for --output=quiet)",
|
|
117
|
+
rich_help_panel="Output",
|
|
118
|
+
),
|
|
119
|
+
] = False,
|
|
120
|
+
output: Annotated[
|
|
121
|
+
OutputFormat,
|
|
122
|
+
typer.Option(
|
|
123
|
+
"--output",
|
|
124
|
+
help="Output format",
|
|
125
|
+
rich_help_panel="Output",
|
|
126
|
+
),
|
|
127
|
+
] = OutputFormat.interactive,
|
|
128
|
+
# === Execution Options ===
|
|
129
|
+
yes: Annotated[
|
|
130
|
+
bool,
|
|
131
|
+
typer.Option(
|
|
132
|
+
"--yes",
|
|
133
|
+
"-y",
|
|
134
|
+
help="Skip user confirmation for database import",
|
|
135
|
+
rich_help_panel="Execution",
|
|
136
|
+
),
|
|
137
|
+
] = False,
|
|
138
|
+
dry_run: Annotated[
|
|
139
|
+
bool,
|
|
140
|
+
typer.Option(
|
|
141
|
+
"--dry-run",
|
|
142
|
+
"-dr",
|
|
143
|
+
help="Testing process without running database export, transfer or import",
|
|
144
|
+
rich_help_panel="Execution",
|
|
145
|
+
),
|
|
146
|
+
] = False,
|
|
147
|
+
reverse: Annotated[
|
|
148
|
+
bool,
|
|
149
|
+
typer.Option(
|
|
150
|
+
"--reverse",
|
|
151
|
+
"-r",
|
|
152
|
+
help="Reverse origin and target hosts",
|
|
153
|
+
rich_help_panel="Execution",
|
|
154
|
+
),
|
|
155
|
+
] = False,
|
|
156
|
+
force_password: Annotated[
|
|
157
|
+
bool,
|
|
158
|
+
typer.Option(
|
|
159
|
+
"--force-password",
|
|
160
|
+
"-fpw",
|
|
161
|
+
help="Force password user query",
|
|
162
|
+
rich_help_panel="Execution",
|
|
163
|
+
),
|
|
164
|
+
] = False,
|
|
165
|
+
# === Database Dump Options ===
|
|
166
|
+
import_file: Annotated[
|
|
167
|
+
str | None,
|
|
168
|
+
typer.Option(
|
|
169
|
+
"--import-file",
|
|
170
|
+
"-i",
|
|
171
|
+
help="Import database from a specific file dump",
|
|
172
|
+
rich_help_panel="Database Dump",
|
|
173
|
+
),
|
|
174
|
+
] = None,
|
|
175
|
+
dump_name: Annotated[
|
|
176
|
+
str | None,
|
|
177
|
+
typer.Option(
|
|
178
|
+
"--dump-name",
|
|
179
|
+
"-dn",
|
|
180
|
+
help='Set a specific dump file name (default is "_[dbname]_[date]")',
|
|
181
|
+
rich_help_panel="Database Dump",
|
|
182
|
+
),
|
|
183
|
+
] = None,
|
|
184
|
+
keep_dump: Annotated[
|
|
185
|
+
str | None,
|
|
186
|
+
typer.Option(
|
|
187
|
+
"--keep-dump",
|
|
188
|
+
"-kd",
|
|
189
|
+
help="Skip target import and save dump file in the given directory",
|
|
190
|
+
rich_help_panel="Database Dump",
|
|
191
|
+
),
|
|
192
|
+
] = None,
|
|
193
|
+
clear_database: Annotated[
|
|
194
|
+
bool,
|
|
195
|
+
typer.Option(
|
|
196
|
+
"--clear-database",
|
|
197
|
+
"-cd",
|
|
198
|
+
help="Drop all tables before importing to get a clean database",
|
|
199
|
+
rich_help_panel="Database Dump",
|
|
200
|
+
),
|
|
201
|
+
] = False,
|
|
202
|
+
tables: Annotated[
|
|
203
|
+
str | None,
|
|
204
|
+
typer.Option(
|
|
205
|
+
"--tables",
|
|
206
|
+
"-ta",
|
|
207
|
+
help="Define specific tables to export, e.g. --tables=table1,table2",
|
|
208
|
+
rich_help_panel="Database Dump",
|
|
209
|
+
),
|
|
210
|
+
] = None,
|
|
211
|
+
where: Annotated[
|
|
212
|
+
str | None,
|
|
213
|
+
typer.Option(
|
|
214
|
+
"--where",
|
|
215
|
+
"-w",
|
|
216
|
+
help="Additional where clause for mysql dump to sync only selected rows",
|
|
217
|
+
rich_help_panel="Database Dump",
|
|
218
|
+
),
|
|
219
|
+
] = None,
|
|
220
|
+
additional_mysqldump_options: Annotated[
|
|
221
|
+
str | None,
|
|
222
|
+
typer.Option(
|
|
223
|
+
"--additional-mysqldump-options",
|
|
224
|
+
"-amo",
|
|
225
|
+
help="Additional mysqldump options",
|
|
226
|
+
rich_help_panel="Database Dump",
|
|
227
|
+
),
|
|
228
|
+
] = None,
|
|
229
|
+
# === Framework ===
|
|
230
|
+
framework_type: Annotated[
|
|
231
|
+
str | None,
|
|
232
|
+
typer.Option(
|
|
233
|
+
"--type",
|
|
234
|
+
"-t",
|
|
235
|
+
help="Define the framework type [TYPO3, Symfony, Drupal, Wordpress, Laravel]",
|
|
236
|
+
rich_help_panel="Framework",
|
|
237
|
+
),
|
|
238
|
+
] = None,
|
|
239
|
+
# === Transfer Options ===
|
|
240
|
+
use_rsync: Annotated[
|
|
241
|
+
bool,
|
|
242
|
+
typer.Option(
|
|
243
|
+
"--use-rsync",
|
|
244
|
+
"-ur",
|
|
245
|
+
help="Use rsync as transfer method",
|
|
246
|
+
rich_help_panel="Transfer",
|
|
247
|
+
),
|
|
248
|
+
] = False,
|
|
249
|
+
use_rsync_options: Annotated[
|
|
250
|
+
str | None,
|
|
251
|
+
typer.Option(
|
|
252
|
+
"--use-rsync-options",
|
|
253
|
+
"-uro",
|
|
254
|
+
help="Additional rsync options",
|
|
255
|
+
rich_help_panel="Transfer",
|
|
256
|
+
),
|
|
257
|
+
] = None,
|
|
258
|
+
# === File Transfer Options ===
|
|
259
|
+
with_files: Annotated[
|
|
260
|
+
bool,
|
|
261
|
+
typer.Option(
|
|
262
|
+
"--with-files",
|
|
263
|
+
"-wf",
|
|
264
|
+
help="Enable file synchronization (requires 'files' section in config)",
|
|
265
|
+
rich_help_panel="File Transfer",
|
|
266
|
+
),
|
|
267
|
+
] = False,
|
|
268
|
+
files_only: Annotated[
|
|
269
|
+
bool,
|
|
270
|
+
typer.Option(
|
|
271
|
+
"--files-only",
|
|
272
|
+
"-fo",
|
|
273
|
+
help="Sync only files, skip database synchronization",
|
|
274
|
+
rich_help_panel="File Transfer",
|
|
275
|
+
),
|
|
276
|
+
] = False,
|
|
277
|
+
# === Target Client Options ===
|
|
278
|
+
target_path: Annotated[
|
|
279
|
+
str | None,
|
|
280
|
+
typer.Option(
|
|
281
|
+
"--target-path",
|
|
282
|
+
"-tp",
|
|
283
|
+
help="File path to target database credential file",
|
|
284
|
+
rich_help_panel="Target Client",
|
|
285
|
+
),
|
|
286
|
+
] = None,
|
|
287
|
+
target_name: Annotated[
|
|
288
|
+
str | None,
|
|
289
|
+
typer.Option(
|
|
290
|
+
"--target-name",
|
|
291
|
+
"-tn",
|
|
292
|
+
help="Providing a name for the target system",
|
|
293
|
+
rich_help_panel="Target Client",
|
|
294
|
+
),
|
|
295
|
+
] = None,
|
|
296
|
+
target_host: Annotated[
|
|
297
|
+
str | None,
|
|
298
|
+
typer.Option(
|
|
299
|
+
"--target-host",
|
|
300
|
+
"-th",
|
|
301
|
+
help="SSH host to target system",
|
|
302
|
+
rich_help_panel="Target Client",
|
|
303
|
+
),
|
|
304
|
+
] = None,
|
|
305
|
+
target_user: Annotated[
|
|
306
|
+
str | None,
|
|
307
|
+
typer.Option(
|
|
308
|
+
"--target-user",
|
|
309
|
+
"-tu",
|
|
310
|
+
help="SSH user for target system",
|
|
311
|
+
rich_help_panel="Target Client",
|
|
312
|
+
),
|
|
313
|
+
] = None,
|
|
314
|
+
target_password: Annotated[
|
|
315
|
+
str | None,
|
|
316
|
+
typer.Option(
|
|
317
|
+
"--target-password",
|
|
318
|
+
"-tpw",
|
|
319
|
+
help="SSH password for target system",
|
|
320
|
+
rich_help_panel="Target Client",
|
|
321
|
+
),
|
|
322
|
+
] = None,
|
|
323
|
+
target_key: Annotated[
|
|
324
|
+
str | None,
|
|
325
|
+
typer.Option(
|
|
326
|
+
"--target-key",
|
|
327
|
+
"-tk",
|
|
328
|
+
help="File path to SSH key for target system",
|
|
329
|
+
rich_help_panel="Target Client",
|
|
330
|
+
),
|
|
331
|
+
] = None,
|
|
332
|
+
target_port: Annotated[
|
|
333
|
+
int | None,
|
|
334
|
+
typer.Option(
|
|
335
|
+
"--target-port",
|
|
336
|
+
"-tpo",
|
|
337
|
+
help="SSH port for target system",
|
|
338
|
+
rich_help_panel="Target Client",
|
|
339
|
+
),
|
|
340
|
+
] = None,
|
|
341
|
+
target_dump_dir: Annotated[
|
|
342
|
+
str | None,
|
|
343
|
+
typer.Option(
|
|
344
|
+
"--target-dump-dir",
|
|
345
|
+
"-tdd",
|
|
346
|
+
help="Directory path for database dump file on target system",
|
|
347
|
+
rich_help_panel="Target Client",
|
|
348
|
+
),
|
|
349
|
+
] = None,
|
|
350
|
+
target_keep_dumps: Annotated[
|
|
351
|
+
int | None,
|
|
352
|
+
typer.Option(
|
|
353
|
+
"--target-keep-dumps",
|
|
354
|
+
"-tkd",
|
|
355
|
+
help="Keep dump file count for target system",
|
|
356
|
+
rich_help_panel="Target Client",
|
|
357
|
+
),
|
|
358
|
+
] = None,
|
|
359
|
+
target_db_name: Annotated[
|
|
360
|
+
str | None,
|
|
361
|
+
typer.Option(
|
|
362
|
+
"--target-db-name",
|
|
363
|
+
"-tdn",
|
|
364
|
+
help="Database name for target system",
|
|
365
|
+
rich_help_panel="Target Client - Database",
|
|
366
|
+
),
|
|
367
|
+
] = None,
|
|
368
|
+
target_db_host: Annotated[
|
|
369
|
+
str | None,
|
|
370
|
+
typer.Option(
|
|
371
|
+
"--target-db-host",
|
|
372
|
+
"-tdh",
|
|
373
|
+
help="Database host for target system",
|
|
374
|
+
rich_help_panel="Target Client - Database",
|
|
375
|
+
),
|
|
376
|
+
] = None,
|
|
377
|
+
target_db_user: Annotated[
|
|
378
|
+
str | None,
|
|
379
|
+
typer.Option(
|
|
380
|
+
"--target-db-user",
|
|
381
|
+
"-tdu",
|
|
382
|
+
help="Database user for target system",
|
|
383
|
+
rich_help_panel="Target Client - Database",
|
|
384
|
+
),
|
|
385
|
+
] = None,
|
|
386
|
+
target_db_password: Annotated[
|
|
387
|
+
str | None,
|
|
388
|
+
typer.Option(
|
|
389
|
+
"--target-db-password",
|
|
390
|
+
"-tdpw",
|
|
391
|
+
help="Database password for target system",
|
|
392
|
+
rich_help_panel="Target Client - Database",
|
|
393
|
+
),
|
|
394
|
+
] = None,
|
|
395
|
+
target_db_port: Annotated[
|
|
396
|
+
int | None,
|
|
397
|
+
typer.Option(
|
|
398
|
+
"--target-db-port",
|
|
399
|
+
"-tdpo",
|
|
400
|
+
help="Database port for target system",
|
|
401
|
+
rich_help_panel="Target Client - Database",
|
|
402
|
+
),
|
|
403
|
+
] = None,
|
|
404
|
+
target_after_dump: Annotated[
|
|
405
|
+
str | None,
|
|
406
|
+
typer.Option(
|
|
407
|
+
"--target-after-dump",
|
|
408
|
+
"-tad",
|
|
409
|
+
help="Additional dump file to insert after regular database import",
|
|
410
|
+
rich_help_panel="Target Client",
|
|
411
|
+
),
|
|
412
|
+
] = None,
|
|
413
|
+
# === Origin Client Options ===
|
|
414
|
+
origin_path: Annotated[
|
|
415
|
+
str | None,
|
|
416
|
+
typer.Option(
|
|
417
|
+
"--origin-path",
|
|
418
|
+
"-op",
|
|
419
|
+
help="File path to origin database credential file",
|
|
420
|
+
rich_help_panel="Origin Client",
|
|
421
|
+
),
|
|
422
|
+
] = None,
|
|
423
|
+
origin_name: Annotated[
|
|
424
|
+
str | None,
|
|
425
|
+
typer.Option(
|
|
426
|
+
"--origin-name",
|
|
427
|
+
"-on",
|
|
428
|
+
help="Providing a name for the origin system",
|
|
429
|
+
rich_help_panel="Origin Client",
|
|
430
|
+
),
|
|
431
|
+
] = None,
|
|
432
|
+
origin_host: Annotated[
|
|
433
|
+
str | None,
|
|
434
|
+
typer.Option(
|
|
435
|
+
"--origin-host",
|
|
436
|
+
"-oh",
|
|
437
|
+
help="SSH host to origin system",
|
|
438
|
+
rich_help_panel="Origin Client",
|
|
439
|
+
),
|
|
440
|
+
] = None,
|
|
441
|
+
origin_user: Annotated[
|
|
442
|
+
str | None,
|
|
443
|
+
typer.Option(
|
|
444
|
+
"--origin-user",
|
|
445
|
+
"-ou",
|
|
446
|
+
help="SSH user for origin system",
|
|
447
|
+
rich_help_panel="Origin Client",
|
|
448
|
+
),
|
|
449
|
+
] = None,
|
|
450
|
+
origin_password: Annotated[
|
|
451
|
+
str | None,
|
|
452
|
+
typer.Option(
|
|
453
|
+
"--origin-password",
|
|
454
|
+
"-opw",
|
|
455
|
+
help="SSH password for origin system",
|
|
456
|
+
rich_help_panel="Origin Client",
|
|
457
|
+
),
|
|
458
|
+
] = None,
|
|
459
|
+
origin_key: Annotated[
|
|
460
|
+
str | None,
|
|
461
|
+
typer.Option(
|
|
462
|
+
"--origin-key",
|
|
463
|
+
"-ok",
|
|
464
|
+
help="File path to SSH key for origin system",
|
|
465
|
+
rich_help_panel="Origin Client",
|
|
466
|
+
),
|
|
467
|
+
] = None,
|
|
468
|
+
origin_port: Annotated[
|
|
469
|
+
int | None,
|
|
470
|
+
typer.Option(
|
|
471
|
+
"--origin-port",
|
|
472
|
+
"-opo",
|
|
473
|
+
help="SSH port for origin system",
|
|
474
|
+
rich_help_panel="Origin Client",
|
|
475
|
+
),
|
|
476
|
+
] = None,
|
|
477
|
+
origin_dump_dir: Annotated[
|
|
478
|
+
str | None,
|
|
479
|
+
typer.Option(
|
|
480
|
+
"--origin-dump-dir",
|
|
481
|
+
"-odd",
|
|
482
|
+
help="Directory path for database dump file on origin system",
|
|
483
|
+
rich_help_panel="Origin Client",
|
|
484
|
+
),
|
|
485
|
+
] = None,
|
|
486
|
+
origin_keep_dumps: Annotated[
|
|
487
|
+
int | None,
|
|
488
|
+
typer.Option(
|
|
489
|
+
"--origin-keep-dumps",
|
|
490
|
+
"-okd",
|
|
491
|
+
help="Keep dump file count for origin system",
|
|
492
|
+
rich_help_panel="Origin Client",
|
|
493
|
+
),
|
|
494
|
+
] = None,
|
|
495
|
+
origin_db_name: Annotated[
|
|
496
|
+
str | None,
|
|
497
|
+
typer.Option(
|
|
498
|
+
"--origin-db-name",
|
|
499
|
+
"-odn",
|
|
500
|
+
help="Database name for origin system",
|
|
501
|
+
rich_help_panel="Origin Client - Database",
|
|
502
|
+
),
|
|
503
|
+
] = None,
|
|
504
|
+
origin_db_host: Annotated[
|
|
505
|
+
str | None,
|
|
506
|
+
typer.Option(
|
|
507
|
+
"--origin-db-host",
|
|
508
|
+
"-odh",
|
|
509
|
+
help="Database host for origin system",
|
|
510
|
+
rich_help_panel="Origin Client - Database",
|
|
511
|
+
),
|
|
512
|
+
] = None,
|
|
513
|
+
origin_db_user: Annotated[
|
|
514
|
+
str | None,
|
|
515
|
+
typer.Option(
|
|
516
|
+
"--origin-db-user",
|
|
517
|
+
"-odu",
|
|
518
|
+
help="Database user for origin system",
|
|
519
|
+
rich_help_panel="Origin Client - Database",
|
|
520
|
+
),
|
|
521
|
+
] = None,
|
|
522
|
+
origin_db_password: Annotated[
|
|
523
|
+
str | None,
|
|
524
|
+
typer.Option(
|
|
525
|
+
"--origin-db-password",
|
|
526
|
+
"-odpw",
|
|
527
|
+
help="Database password for origin system",
|
|
528
|
+
rich_help_panel="Origin Client - Database",
|
|
529
|
+
),
|
|
530
|
+
] = None,
|
|
531
|
+
origin_db_port: Annotated[
|
|
532
|
+
int | None,
|
|
533
|
+
typer.Option(
|
|
534
|
+
"--origin-db-port",
|
|
535
|
+
"-odpo",
|
|
536
|
+
help="Database port for origin system",
|
|
537
|
+
rich_help_panel="Origin Client - Database",
|
|
538
|
+
),
|
|
539
|
+
] = None,
|
|
540
|
+
) -> None:
|
|
541
|
+
"""
|
|
542
|
+
Synchronize a database from origin to target system.
|
|
543
|
+
|
|
544
|
+
Examples:
|
|
545
|
+
db_sync_tool -f config.yaml
|
|
546
|
+
db_sync_tool production local -o hosts.yaml
|
|
547
|
+
db_sync_tool -f config.yaml -v -y
|
|
548
|
+
"""
|
|
549
|
+
# Initialize output manager first (needed for config resolver output)
|
|
550
|
+
output_format = "quiet" if quiet else output.value
|
|
551
|
+
init_output_manager(format=output_format, verbose=verbose, mute=mute or quiet)
|
|
552
|
+
|
|
553
|
+
# Config resolution: use ConfigResolver if no explicit config file or host file
|
|
554
|
+
resolved_config = None
|
|
555
|
+
resolved_origin = origin
|
|
556
|
+
resolved_target = target
|
|
557
|
+
|
|
558
|
+
if config_file is None and import_file is None and host_file is None:
|
|
559
|
+
# Use ConfigResolver for auto-discovery
|
|
560
|
+
# Skip if host_file is provided (use original host linking mechanism)
|
|
561
|
+
console = Console()
|
|
562
|
+
resolver = ConfigResolver(console=console)
|
|
563
|
+
|
|
564
|
+
# Check if we should use auto-discovery
|
|
565
|
+
# Allow interactive mode only if running in a TTY and not in quiet/mute mode
|
|
566
|
+
interactive = console.is_terminal and not (quiet or mute)
|
|
567
|
+
|
|
568
|
+
try:
|
|
569
|
+
resolved = resolver.resolve(
|
|
570
|
+
config_file=None,
|
|
571
|
+
origin=origin,
|
|
572
|
+
target=target,
|
|
573
|
+
interactive=interactive,
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
if resolved.config_file:
|
|
577
|
+
config_file = str(resolved.config_file)
|
|
578
|
+
|
|
579
|
+
# Store resolved configs for later merging
|
|
580
|
+
if resolved.origin_config or resolved.target_config:
|
|
581
|
+
resolved_config = resolved
|
|
582
|
+
# Clear origin/target args since we're using resolved configs
|
|
583
|
+
resolved_origin = None
|
|
584
|
+
resolved_target = None
|
|
585
|
+
|
|
586
|
+
except NoConfigFoundError:
|
|
587
|
+
# No auto-discovery config found, fall through to original behavior
|
|
588
|
+
# This is expected when no .db-sync-tool/ or ~/.db-sync-tool/ exists
|
|
589
|
+
pass
|
|
590
|
+
except ConfigError:
|
|
591
|
+
# Config was found but has errors (invalid YAML, missing host, etc.)
|
|
592
|
+
# Re-raise to let the user know about the problem
|
|
593
|
+
raise
|
|
594
|
+
except Exception as e:
|
|
595
|
+
# Unexpected error during config resolution
|
|
596
|
+
# Log with details in verbose mode, then re-raise
|
|
597
|
+
logger = logging.getLogger('db_sync_tool')
|
|
598
|
+
error_msg = f"Unexpected error during config resolution: {e}"
|
|
599
|
+
if verbose >= 2:
|
|
600
|
+
logger.error(f"{error_msg}\n{traceback.format_exc()}")
|
|
601
|
+
elif verbose >= 1:
|
|
602
|
+
logger.error(error_msg)
|
|
603
|
+
raise
|
|
604
|
+
|
|
605
|
+
# Build args namespace for compatibility with existing code
|
|
606
|
+
args = _build_args_namespace(
|
|
607
|
+
origin=resolved_origin,
|
|
608
|
+
target=resolved_target,
|
|
609
|
+
config_file=config_file,
|
|
610
|
+
host_file=host_file,
|
|
611
|
+
log_file=log_file,
|
|
612
|
+
json_log=json_log,
|
|
613
|
+
verbose=verbose,
|
|
614
|
+
mute=mute,
|
|
615
|
+
quiet=quiet,
|
|
616
|
+
output=output,
|
|
617
|
+
yes=yes,
|
|
618
|
+
dry_run=dry_run,
|
|
619
|
+
reverse=reverse,
|
|
620
|
+
force_password=force_password,
|
|
621
|
+
import_file=import_file,
|
|
622
|
+
dump_name=dump_name,
|
|
623
|
+
keep_dump=keep_dump,
|
|
624
|
+
clear_database=clear_database,
|
|
625
|
+
tables=tables,
|
|
626
|
+
where=where,
|
|
627
|
+
additional_mysqldump_options=additional_mysqldump_options,
|
|
628
|
+
framework_type=framework_type,
|
|
629
|
+
use_rsync=use_rsync,
|
|
630
|
+
use_rsync_options=use_rsync_options,
|
|
631
|
+
with_files=with_files,
|
|
632
|
+
files_only=files_only,
|
|
633
|
+
target_path=target_path,
|
|
634
|
+
target_name=target_name,
|
|
635
|
+
target_host=target_host,
|
|
636
|
+
target_user=target_user,
|
|
637
|
+
target_password=target_password,
|
|
638
|
+
target_key=target_key,
|
|
639
|
+
target_port=target_port,
|
|
640
|
+
target_dump_dir=target_dump_dir,
|
|
641
|
+
target_keep_dumps=target_keep_dumps,
|
|
642
|
+
target_db_name=target_db_name,
|
|
643
|
+
target_db_host=target_db_host,
|
|
644
|
+
target_db_user=target_db_user,
|
|
645
|
+
target_db_password=target_db_password,
|
|
646
|
+
target_db_port=target_db_port,
|
|
647
|
+
target_after_dump=target_after_dump,
|
|
648
|
+
origin_path=origin_path,
|
|
649
|
+
origin_name=origin_name,
|
|
650
|
+
origin_host=origin_host,
|
|
651
|
+
origin_user=origin_user,
|
|
652
|
+
origin_password=origin_password,
|
|
653
|
+
origin_key=origin_key,
|
|
654
|
+
origin_port=origin_port,
|
|
655
|
+
origin_dump_dir=origin_dump_dir,
|
|
656
|
+
origin_keep_dumps=origin_keep_dumps,
|
|
657
|
+
origin_db_name=origin_db_name,
|
|
658
|
+
origin_db_host=origin_db_host,
|
|
659
|
+
origin_db_user=origin_db_user,
|
|
660
|
+
origin_db_password=origin_db_password,
|
|
661
|
+
origin_db_port=origin_db_port,
|
|
662
|
+
resolved_config=resolved_config,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
# Store json_log in system.config for use by log.py and other modules
|
|
666
|
+
# Import here to avoid circular imports at module level
|
|
667
|
+
from db_sync_tool.utility import system as sys_module
|
|
668
|
+
sys_module.config['json_log'] = json_log
|
|
669
|
+
|
|
670
|
+
# Initialize structured logging
|
|
671
|
+
init_logging(
|
|
672
|
+
verbose=verbose,
|
|
673
|
+
mute=mute or quiet,
|
|
674
|
+
log_file=log_file,
|
|
675
|
+
json_logging=json_log,
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
# Call sync with typed args
|
|
679
|
+
sync.Sync(
|
|
680
|
+
config_file=config_file,
|
|
681
|
+
verbose=verbose,
|
|
682
|
+
yes=yes,
|
|
683
|
+
mute=mute or quiet,
|
|
684
|
+
dry_run=dry_run,
|
|
685
|
+
import_file=import_file,
|
|
686
|
+
dump_name=dump_name,
|
|
687
|
+
keep_dump=keep_dump,
|
|
688
|
+
host_file=host_file,
|
|
689
|
+
clear=clear_database,
|
|
690
|
+
force_password=force_password,
|
|
691
|
+
use_rsync=use_rsync,
|
|
692
|
+
use_rsync_options=use_rsync_options,
|
|
693
|
+
reverse=reverse,
|
|
694
|
+
with_files=with_files,
|
|
695
|
+
files_only=files_only,
|
|
696
|
+
args=args,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def _build_args_namespace(**kwargs: object) -> argparse.Namespace:
|
|
701
|
+
"""
|
|
702
|
+
Build an argparse-compatible namespace from typer arguments.
|
|
703
|
+
|
|
704
|
+
This enables backward compatibility with existing code that
|
|
705
|
+
expects an argparse.Namespace object.
|
|
706
|
+
"""
|
|
707
|
+
args = argparse.Namespace()
|
|
708
|
+
|
|
709
|
+
# Map typer parameter names to argparse attribute names
|
|
710
|
+
name_mapping = {
|
|
711
|
+
"framework_type": "type",
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
for key, value in kwargs.items():
|
|
715
|
+
# Map to argparse name if different
|
|
716
|
+
attr_name = name_mapping.get(key, key)
|
|
717
|
+
|
|
718
|
+
# Handle enum conversion
|
|
719
|
+
if hasattr(value, "value"):
|
|
720
|
+
setattr(args, attr_name, value.value)
|
|
721
|
+
else:
|
|
722
|
+
setattr(args, attr_name, value)
|
|
723
|
+
|
|
724
|
+
return args
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
def run() -> None:
|
|
728
|
+
"""Entry point for typer CLI."""
|
|
729
|
+
app()
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
if __name__ == "__main__":
|
|
733
|
+
run()
|