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.
Files changed (41) hide show
  1. db_sync_tool/__main__.py +7 -252
  2. db_sync_tool/cli.py +733 -0
  3. db_sync_tool/database/process.py +94 -111
  4. db_sync_tool/database/utility.py +339 -121
  5. db_sync_tool/info.py +1 -1
  6. db_sync_tool/recipes/drupal.py +87 -12
  7. db_sync_tool/recipes/laravel.py +7 -6
  8. db_sync_tool/recipes/parsing.py +102 -0
  9. db_sync_tool/recipes/symfony.py +17 -28
  10. db_sync_tool/recipes/typo3.py +33 -54
  11. db_sync_tool/recipes/wordpress.py +13 -12
  12. db_sync_tool/remote/client.py +206 -71
  13. db_sync_tool/remote/file_transfer.py +303 -0
  14. db_sync_tool/remote/rsync.py +18 -15
  15. db_sync_tool/remote/system.py +2 -3
  16. db_sync_tool/remote/transfer.py +51 -47
  17. db_sync_tool/remote/utility.py +29 -30
  18. db_sync_tool/sync.py +52 -28
  19. db_sync_tool/utility/config.py +367 -0
  20. db_sync_tool/utility/config_resolver.py +573 -0
  21. db_sync_tool/utility/console.py +779 -0
  22. db_sync_tool/utility/exceptions.py +32 -0
  23. db_sync_tool/utility/helper.py +155 -148
  24. db_sync_tool/utility/info.py +53 -20
  25. db_sync_tool/utility/log.py +55 -31
  26. db_sync_tool/utility/logging_config.py +410 -0
  27. db_sync_tool/utility/mode.py +85 -150
  28. db_sync_tool/utility/output.py +122 -51
  29. db_sync_tool/utility/parser.py +33 -53
  30. db_sync_tool/utility/pure.py +93 -0
  31. db_sync_tool/utility/security.py +79 -0
  32. db_sync_tool/utility/system.py +277 -194
  33. db_sync_tool/utility/validation.py +2 -9
  34. db_sync_tool_kmi-3.0.2.dist-info/METADATA +99 -0
  35. db_sync_tool_kmi-3.0.2.dist-info/RECORD +44 -0
  36. {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/WHEEL +1 -1
  37. db_sync_tool_kmi-2.11.6.dist-info/METADATA +0 -276
  38. db_sync_tool_kmi-2.11.6.dist-info/RECORD +0 -34
  39. {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/entry_points.txt +0 -0
  40. {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info/licenses}/LICENSE +0 -0
  41. {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: future_fstrings -*-
3
2
 
4
3
  """
5
4
  rsync script
@@ -7,6 +6,7 @@ rsync script
7
6
 
8
7
  import re
9
8
  from db_sync_tool.utility import mode, system, output
9
+ from db_sync_tool.utility.console import get_output_manager
10
10
 
11
11
  # Default options for rsync command
12
12
  # https://wiki.ubuntuusers.de/rsync/
@@ -31,8 +31,10 @@ def get_password_environment(client):
31
31
  if not client:
32
32
  return ''
33
33
 
34
- if system.config['use_sshpass'] and not 'ssh_key' in system.config[client] and 'password' in system.config[client]:
35
- return f'SSHPASS=\'{system.config[client]["password"]}\' '
34
+ cfg = system.get_typed_config()
35
+ client_cfg = cfg.get_client(client)
36
+ if cfg.use_sshpass and not client_cfg.ssh_key and client_cfg.password:
37
+ return f'SSHPASS=\'{client_cfg.password}\' '
36
38
  return ''
37
39
 
38
40
 
@@ -42,20 +44,19 @@ def get_authorization(client):
42
44
  :param client: String
43
45
  :return: String
44
46
  """
45
- _ssh_key = None
46
47
  if not client:
47
48
  return ''
48
49
 
49
- if 'ssh_key' in system.config[client]:
50
- _ssh_key = system.config[mode.Client.ORIGIN]['ssh_key']
51
-
52
- _ssh_port = system.config[client]['port'] if 'port' in system.config[client] else 22
50
+ cfg = system.get_typed_config()
51
+ client_cfg = cfg.get_client(client)
52
+ _ssh_key = client_cfg.ssh_key
53
+ _ssh_port = client_cfg.port
53
54
 
54
55
  if _ssh_key is None:
55
- if system.config['use_sshpass'] and get_password_environment(client):
56
+ if cfg.use_sshpass and get_password_environment(client):
56
57
  # In combination with SSHPASS environment variable
57
58
  # https://www.redhat.com/sysadmin/ssh-automation-sshpass
58
- return f'--rsh="sshpass -e ssh -p{_ssh_port} -o StrictHostKeyChecking=no -l {system.config[client]["user"]}"'
59
+ return f'--rsh="sshpass -e ssh -p{_ssh_port} -o StrictHostKeyChecking=no -l {client_cfg.user}"'
59
60
  else:
60
61
  return f'-e "ssh -p{_ssh_port} -o StrictHostKeyChecking=no"'
61
62
  else:
@@ -70,7 +71,9 @@ def get_host(client):
70
71
  :return: String
71
72
  """
72
73
  if mode.is_remote(client):
73
- return f'{system.config[client]["user"]}@{system.config[client]["host"]}:'
74
+ cfg = system.get_typed_config()
75
+ client_cfg = cfg.get_client(client)
76
+ return f'{client_cfg.user}@{client_cfg.host}:'
74
77
  return ''
75
78
 
76
79
 
@@ -79,9 +82,10 @@ def get_options():
79
82
  Prepare rsync options with stored default options and provided addtional options
80
83
  :return: String
81
84
  """
85
+ cfg = system.get_typed_config()
82
86
  _options = f'{" ".join(default_options)}'
83
- if not system.config['use_rsync_options'] is None:
84
- _options += f'{system.config["use_rsync_options"]}'
87
+ if cfg.use_rsync_options is not None:
88
+ _options += f'{cfg.use_rsync_options}'
85
89
  return _options
86
90
 
87
91
 
@@ -91,8 +95,7 @@ def read_stats(stats):
91
95
  :param stats: String
92
96
  :return:
93
97
  """
94
- if system.config['verbose']:
95
- print(f'{output.Subject.DEBUG}{output.CliFormat.BLACK}{stats}{output.CliFormat.ENDC}')
98
+ get_output_manager().debug(stats)
96
99
 
97
100
  _file_size = parse_string(stats, r'Total transferred file size:\s*([\d.]+[MKG]?)')
98
101
 
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: future_fstrings -*-
3
2
 
4
3
  """
5
4
  System script
6
5
  """
7
6
 
8
- import sys
9
7
  from db_sync_tool.utility import mode, output, helper
8
+ from db_sync_tool.utility.exceptions import DbSyncError
10
9
  from db_sync_tool.remote import client as remote_client
11
10
 
12
11
 
@@ -38,7 +37,7 @@ def run_ssh_command(command, ssh_client=remote_client.ssh_client_origin, client=
38
37
 
39
38
  if err and exit_status != 0:
40
39
  helper.run_script(client=client, script='error')
41
- sys.exit(output.message(output.Subject.ERROR, err, False))
40
+ raise DbSyncError(err)
42
41
  elif err:
43
42
  output.message(output.Subject.WARNING, err, True)
44
43
 
@@ -1,11 +1,9 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: future_fstrings -*-
3
2
 
4
3
  """
5
4
  Transfer script
6
5
  """
7
6
 
8
- import sys
9
7
  from db_sync_tool.utility import mode, system, helper, output
10
8
  from db_sync_tool.database import utility as database_utility
11
9
  from db_sync_tool.remote import utility, client, rsync
@@ -16,6 +14,7 @@ def transfer_origin_database_dump():
16
14
  Transfer the origin database dump files
17
15
  :return:
18
16
  """
17
+ cfg = system.get_typed_config()
19
18
  if not mode.is_import():
20
19
  if mode.get_sync_mode() == mode.SyncMode.RECEIVER:
21
20
  get_origin_database_dump(helper.get_dump_dir(mode.Client.TARGET))
@@ -31,7 +30,7 @@ def transfer_origin_database_dump():
31
30
  put_origin_database_dump(system.default_local_sync_path)
32
31
  elif mode.get_sync_mode() == mode.SyncMode.SYNC_REMOTE or mode.get_sync_mode() == mode.SyncMode.SYNC_LOCAL:
33
32
  system.check_target_configuration()
34
- elif system.config['is_same_client']:
33
+ elif cfg.is_same_client:
35
34
  utility.remove_origin_database_dump(True)
36
35
  else:
37
36
  system.check_target_configuration()
@@ -43,6 +42,7 @@ def get_origin_database_dump(target_path):
43
42
  :param target_path: String
44
43
  :return:
45
44
  """
45
+ cfg = system.get_typed_config()
46
46
  output.message(
47
47
  output.Subject.ORIGIN,
48
48
  'Downloading database dump',
@@ -51,16 +51,16 @@ def get_origin_database_dump(target_path):
51
51
  if mode.get_sync_mode() != mode.SyncMode.PROXY:
52
52
  helper.check_and_create_dump_dir(mode.Client.TARGET, target_path)
53
53
 
54
- if not system.config['dry_run']:
55
- _remotepath = helper.get_dump_dir(mode.Client.ORIGIN) + database_utility.database_dump_file_name + '.tar.gz'
54
+ if not cfg.dry_run:
55
+ _remotepath = database_utility.get_dump_gz_path(mode.Client.ORIGIN)
56
56
  _localpath = target_path
57
57
 
58
- if system.config['use_rsync']:
58
+ if cfg.use_rsync:
59
59
  rsync.run_rsync_command(
60
60
  remote_client=mode.Client.ORIGIN,
61
61
  origin_path=_remotepath,
62
62
  target_path=_localpath,
63
- origin_ssh=system.config[mode.Client.ORIGIN]['user'] + '@' + system.config[mode.Client.ORIGIN]['host']
63
+ origin_ssh=cfg.origin.user + '@' + cfg.origin.host
64
64
  )
65
65
  else:
66
66
  #
@@ -68,29 +68,46 @@ def get_origin_database_dump(target_path):
68
68
  # https://github.com/paramiko/paramiko/issues/60
69
69
  #
70
70
  sftp = get_sftp_client(client.ssh_client_origin)
71
- sftp.get(helper.get_dump_dir(mode.Client.ORIGIN) + database_utility.database_dump_file_name + '.tar.gz',
72
- target_path + database_utility.database_dump_file_name + '.tar.gz', download_status)
71
+ sftp.get(database_utility.get_dump_gz_path(mode.Client.ORIGIN),
72
+ target_path + database_utility.database_dump_file_name + '.gz', download_status)
73
73
  sftp.close()
74
- if not system.config['mute']:
75
- print('')
76
74
 
77
75
  utility.remove_origin_database_dump()
78
76
 
79
77
 
78
+ def _transfer_status(sent, size, direction, subject_override=None):
79
+ """
80
+ Print transfer progress status.
81
+
82
+ :param sent: Bytes transferred
83
+ :param size: Total bytes
84
+ :param direction: 'downloaded' or 'uploaded'
85
+ :param subject_override: Optional subject prefix override
86
+ """
87
+ cfg = system.get_typed_config()
88
+ if cfg.mute:
89
+ return
90
+
91
+ from db_sync_tool.utility.console import get_output_manager
92
+ output_manager = get_output_manager()
93
+
94
+ # Track transfer size for final summary
95
+ if sent == size:
96
+ output_manager.track_stat("size", size)
97
+
98
+ # Use OutputManager for progress display
99
+ msg = "Downloading" if direction == "downloaded" else "Uploading"
100
+ output_manager.progress(sent, size, msg)
101
+
102
+
80
103
  def download_status(sent, size):
81
104
  """
82
- Printing the download status information
83
- :param sent: Float
84
- :param size: Float
85
- :return:
105
+ Callback for SFTP download progress.
106
+
107
+ :param sent: Bytes transferred
108
+ :param size: Total bytes
86
109
  """
87
- if not system.config['mute']:
88
- sent_mb = round(float(sent) / 1024 / 1024, 1)
89
- size = round(float(size) / 1024 / 1024, 1)
90
- sys.stdout.write(
91
- output.Subject.ORIGIN + output.CliFormat.BLACK + '[REMOTE]' + output.CliFormat.ENDC + " Status: {0} MB of {1} MB downloaded".
92
- format(sent_mb, size, ))
93
- sys.stdout.write('\r')
110
+ _transfer_status(sent, size, 'downloaded')
94
111
 
95
112
 
96
113
  def put_origin_database_dump(origin_path):
@@ -99,6 +116,7 @@ def put_origin_database_dump(origin_path):
99
116
  :param origin_path: String
100
117
  :return:
101
118
  """
119
+ cfg = system.get_typed_config()
102
120
  if mode.get_sync_mode() == mode.SyncMode.PROXY:
103
121
  _subject = output.Subject.LOCAL
104
122
  else:
@@ -111,16 +129,16 @@ def put_origin_database_dump(origin_path):
111
129
  )
112
130
  helper.check_and_create_dump_dir(mode.Client.TARGET, helper.get_dump_dir(mode.Client.TARGET))
113
131
 
114
- if not system.config['dry_run']:
115
- _localpath = origin_path + database_utility.database_dump_file_name + '.tar.gz'
132
+ if not cfg.dry_run:
133
+ _localpath = origin_path + database_utility.database_dump_file_name + '.gz'
116
134
  _remotepath = helper.get_dump_dir(mode.Client.TARGET) + '/'
117
135
 
118
- if system.config['use_rsync']:
136
+ if cfg.use_rsync:
119
137
  rsync.run_rsync_command(
120
138
  remote_client=mode.Client.TARGET,
121
139
  origin_path=_localpath,
122
140
  target_path=_remotepath,
123
- target_ssh=system.config[mode.Client.TARGET]['user'] + '@' + system.config[mode.Client.TARGET]['host']
141
+ target_ssh=cfg.target.user + '@' + cfg.target.host
124
142
  )
125
143
  else:
126
144
  #
@@ -128,35 +146,21 @@ def put_origin_database_dump(origin_path):
128
146
  # https://github.com/paramiko/paramiko/issues/60
129
147
  #
130
148
  sftp = get_sftp_client(client.ssh_client_target)
131
- sftp.put(origin_path + database_utility.database_dump_file_name + '.tar.gz',
132
- helper.get_dump_dir(mode.Client.TARGET) + database_utility.database_dump_file_name + '.tar.gz',
149
+ sftp.put(origin_path + database_utility.database_dump_file_name + '.gz',
150
+ database_utility.get_dump_gz_path(mode.Client.TARGET),
133
151
  upload_status)
134
152
  sftp.close()
135
- if not system.config['mute']:
136
- print('')
137
153
 
138
154
 
139
155
 
140
156
  def upload_status(sent, size):
141
157
  """
142
- Printing the upload status information
143
- :param sent: Float
144
- :param size: Float
145
- :return:
146
- """
147
- if not system.config['mute']:
148
- sent_mb = round(float(sent) / 1024 / 1024, 1)
149
- size = round(float(size) / 1024 / 1024, 1)
158
+ Callback for SFTP upload progress.
150
159
 
151
- if (mode.get_sync_mode() == mode.SyncMode.PROXY):
152
- _subject = output.Subject.LOCAL
153
- else:
154
- _subject = output.Subject.ORIGIN + output.CliFormat.BLACK + '[LOCAL]' + output.CliFormat.ENDC
155
-
156
- sys.stdout.write(
157
- _subject + " Status: {0} MB of {1} MB uploaded".
158
- format(sent_mb, size, ))
159
- sys.stdout.write('\r')
160
+ :param sent: Bytes transferred
161
+ :param size: Total bytes
162
+ """
163
+ _transfer_status(sent, size, 'uploaded')
160
164
 
161
165
 
162
166
  def get_sftp_client(ssh_client):
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: future_fstrings -*-
3
2
 
4
3
  """
5
4
  Utility script
@@ -9,7 +8,6 @@ import os
9
8
  import paramiko
10
9
  from db_sync_tool.utility import mode, system, helper, output
11
10
  from db_sync_tool.database import utility as database_utility
12
- from db_sync_tool.remote import client as remote_client
13
11
 
14
12
 
15
13
  def remove_origin_database_dump(keep_compressed_file=False):
@@ -24,34 +22,33 @@ def remove_origin_database_dump(keep_compressed_file=False):
24
22
  True
25
23
  )
26
24
 
27
- if system.config['dry_run']:
25
+ cfg = system.get_typed_config()
26
+ if cfg.dry_run:
28
27
  return
29
28
 
30
- _file_path = helper.get_dump_dir(mode.Client.ORIGIN) + database_utility.database_dump_file_name
31
- if mode.is_origin_remote():
32
- mode.run_command(
33
- helper.get_command(mode.Client.ORIGIN, 'rm') + ' ' + _file_path,
34
- mode.Client.ORIGIN
35
- )
36
- if not keep_compressed_file:
29
+ _gz_path = database_utility.get_dump_gz_path(mode.Client.ORIGIN)
30
+
31
+ # With streaming compression, only .gz file exists on origin (no separate .sql)
32
+ if not keep_compressed_file:
33
+ if mode.is_origin_remote():
37
34
  mode.run_command(
38
- helper.get_command(mode.Client.ORIGIN, 'rm') + ' ' + _file_path + '.tar.gz',
35
+ helper.get_command(mode.Client.ORIGIN, 'rm') + ' ' + _gz_path,
39
36
  mode.Client.ORIGIN
40
37
  )
41
- else:
42
- os.remove(_file_path)
43
- if not keep_compressed_file:
44
- os.remove(f'{_file_path}.tar.gz')
38
+ else:
39
+ if os.path.isfile(_gz_path):
40
+ os.remove(_gz_path)
45
41
 
46
42
  if keep_compressed_file:
47
- if 'keep_dumps' in system.config[mode.Client.ORIGIN]:
43
+ origin_cfg = cfg.origin
44
+ if origin_cfg.keep_dumps is not None:
48
45
  helper.clean_up_dump_dir(mode.Client.ORIGIN,
49
46
  helper.get_dump_dir(mode.Client.ORIGIN) + '*',
50
- system.config[mode.Client.ORIGIN]['keep_dumps'])
47
+ origin_cfg.keep_dumps)
51
48
 
52
49
  output.message(
53
50
  output.Subject.INFO,
54
- f'Database dump file is saved to: {_file_path}.tar.gz',
51
+ f'Database dump file is saved to: {_gz_path}',
55
52
  True,
56
53
  True
57
54
  )
@@ -62,17 +59,22 @@ def remove_target_database_dump():
62
59
  Removing the target database dump files
63
60
  :return:
64
61
  """
65
- _file_path = helper.get_dump_dir(mode.Client.TARGET) + database_utility.database_dump_file_name
62
+ cfg = system.get_typed_config()
63
+ _file_path = database_utility.get_dump_file_path(mode.Client.TARGET)
64
+ _gz_file_path = database_utility.get_dump_gz_path(mode.Client.TARGET)
66
65
 
67
66
  #
68
67
  # Move dump to specified directory
69
68
  #
70
- if system.config['keep_dump']:
69
+ if cfg.keep_dump:
71
70
  helper.create_local_temporary_data_dir()
72
- _keep_dump_path = system.default_local_sync_path + database_utility.database_dump_file_name
71
+ # Copy the .gz file (streaming compression means only .gz exists)
72
+ # database_dump_file_name is guaranteed to be set at this point
73
+ _dump_name = database_utility.database_dump_file_name or ''
74
+ _keep_dump_path = system.default_local_sync_path + _dump_name + '.gz'
73
75
  mode.run_command(
74
76
  helper.get_command('target',
75
- 'cp') + ' ' + _file_path + ' ' + _keep_dump_path,
77
+ 'cp') + ' ' + _gz_file_path + ' ' + _keep_dump_path,
76
78
  mode.Client.TARGET
77
79
  )
78
80
  output.message(
@@ -92,23 +94,20 @@ def remove_target_database_dump():
92
94
  True
93
95
  )
94
96
 
95
- if system.config['dry_run']:
97
+ if cfg.dry_run:
96
98
  return
97
99
 
98
100
  if mode.is_target_remote():
101
+ # Remove both decompressed .sql and compressed .gz
99
102
  mode.run_command(
100
- helper.get_command(mode.Client.TARGET, 'rm') + ' ' + _file_path,
101
- mode.Client.TARGET
102
- )
103
- mode.run_command(
104
- helper.get_command(mode.Client.TARGET, 'rm') + ' ' + _file_path + '.tar.gz',
103
+ helper.get_command(mode.Client.TARGET, 'rm') + ' -f ' + _file_path + ' ' + _gz_file_path,
105
104
  mode.Client.TARGET
106
105
  )
107
106
  else:
108
107
  if os.path.isfile(_file_path):
109
108
  os.remove(_file_path)
110
- if os.path.isfile(f'{_file_path}.tar.gz'):
111
- os.remove(f'{_file_path}.tar.gz')
109
+ if os.path.isfile(_gz_file_path):
110
+ os.remove(_gz_file_path)
112
111
 
113
112
 
114
113
  def check_keys_from_ssh_agent():
db_sync_tool/sync.py CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: future_fstrings -*-
3
2
  """
4
3
  Sync script
5
4
  """
6
5
 
7
- from db_sync_tool.utility import system, helper, info
8
- from db_sync_tool.database import process
9
- from db_sync_tool.remote import transfer, client as remote_client
6
+ import sys
7
+ from db_sync_tool.utility import system, helper, info, output
8
+ from db_sync_tool.utility.exceptions import DbSyncError
9
+ from db_sync_tool.database import process, utility as database_utility
10
+ from db_sync_tool.remote import transfer, client as remote_client, file_transfer
10
11
 
11
12
 
12
13
  class Sync:
@@ -29,6 +30,8 @@ class Sync:
29
30
  use_rsync=False,
30
31
  use_rsync_options=None,
31
32
  reverse=False,
33
+ with_files=False,
34
+ files_only=False,
32
35
  config=None,
33
36
  args=None):
34
37
  """
@@ -47,34 +50,55 @@ class Sync:
47
50
  :param use_rsync:
48
51
  :param use_rsync_options:
49
52
  :param reverse:
53
+ :param with_files:
54
+ :param files_only:
50
55
  :param config:
51
56
  :param args:
52
57
  """
53
58
  if config is None:
54
59
  config = {}
55
60
 
56
- info.print_header(mute)
57
- system.check_args_options(
58
- config_file,
59
- verbose,
60
- yes,
61
- mute,
62
- dry_run,
63
- import_file,
64
- dump_name,
65
- keep_dump,
66
- host_file,
67
- clear,
68
- force_password,
69
- use_rsync,
70
- use_rsync_options,
71
- reverse
72
- )
73
- system.get_configuration(config, args)
74
- system.check_authorizations()
75
- process.create_origin_database_dump()
76
- transfer.transfer_origin_database_dump()
77
- process.import_database_dump()
78
- helper.clean_up()
79
- remote_client.close_ssh_clients()
61
+ info.print_header(mute, verbose)
62
+ try:
63
+ system.check_args_options(
64
+ config_file,
65
+ verbose,
66
+ yes,
67
+ mute,
68
+ dry_run,
69
+ import_file,
70
+ dump_name,
71
+ keep_dump,
72
+ host_file,
73
+ clear,
74
+ force_password,
75
+ use_rsync,
76
+ use_rsync_options,
77
+ reverse,
78
+ with_files,
79
+ files_only
80
+ )
81
+ system.get_configuration(config, args)
82
+ system.check_authorizations()
83
+
84
+ cfg = system.get_typed_config()
85
+
86
+ # Database sync (skip if --files-only)
87
+ if not cfg.files_only:
88
+ process.create_origin_database_dump()
89
+ transfer.transfer_origin_database_dump()
90
+ process.import_database_dump()
91
+
92
+ # File sync (only if --with-files or --files-only)
93
+ file_transfer.transfer_files()
94
+
95
+ helper.clean_up()
96
+ except DbSyncError as e:
97
+ output.message(output.Subject.ERROR, str(e), do_print=True)
98
+ sys.exit(1)
99
+ finally:
100
+ # Always clean up sensitive credential files, even on error
101
+ database_utility.cleanup_mysql_config_files()
102
+ remote_client.close_ssh_clients()
103
+ file_transfer.cleanup()
80
104
  info.print_footer()