cloner 0.13.0 → 0.14.0
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 +4 -4
- data/.env +5 -0
- data/.ruby-version +1 -1
- data/CLAUDE.md +58 -0
- data/README.md +166 -0
- data/compose.yml +15 -0
- data/lib/cloner/ar.rb +7 -7
- data/lib/cloner/docker_compose.rb +283 -0
- data/lib/cloner/internal.rb +1 -0
- data/lib/cloner/mongodb.rb +32 -2
- data/lib/cloner/mysql.rb +65 -15
- data/lib/cloner/postgres.rb +63 -15
- data/lib/cloner/version.rb +1 -1
- data/lib/cloner.rb +1 -0
- data/lib/generators/cloner_generator.rb +18 -4
- data/lib/generators/templates/cloner_docker_compose.thor.erb +154 -0
- data/test/dl_compose_test.thor +158 -0
- data/test/test_docker_compose.rb +213 -0
- data/test/tmp/dump/cloner.bak +1 -0
- metadata +15 -7
data/lib/cloner/mysql.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
module Cloner::MySQL
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
2
4
|
def my_local_auth
|
3
|
-
if
|
5
|
+
if local_db_config['password'].blank?
|
4
6
|
""
|
5
7
|
else
|
6
|
-
"--password='#{
|
8
|
+
"--password='#{local_db_config['password']}'"
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
10
12
|
def my_remote_auth
|
11
|
-
if
|
13
|
+
if remote_db_config['password'].blank?
|
12
14
|
""
|
13
15
|
else
|
14
|
-
"--password='#{
|
16
|
+
"--password='#{remote_db_config['password']}'"
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
@@ -26,6 +28,35 @@ module Cloner::MySQL
|
|
26
28
|
def my_bin_path(util)
|
27
29
|
util
|
28
30
|
end
|
31
|
+
|
32
|
+
def my_local_bin_path(util)
|
33
|
+
if local_docker_compose? && local_docker_compose_service
|
34
|
+
# For mysql restore, we pipe data in, so we don't wrap it
|
35
|
+
return util if util == "mysql"
|
36
|
+
|
37
|
+
# Build docker compose exec command
|
38
|
+
compose_cmd = local_docker_compose_exec(
|
39
|
+
local_docker_compose_service,
|
40
|
+
util,
|
41
|
+
no_tty: true
|
42
|
+
)
|
43
|
+
return compose_cmd
|
44
|
+
end
|
45
|
+
my_bin_path(util)
|
46
|
+
end
|
47
|
+
|
48
|
+
def my_remote_bin_path(util)
|
49
|
+
if remote_docker_compose? && remote_docker_compose_service
|
50
|
+
# Build docker compose exec command for remote
|
51
|
+
compose_cmd = remote_docker_compose_exec(
|
52
|
+
remote_docker_compose_service,
|
53
|
+
util,
|
54
|
+
no_tty: true
|
55
|
+
)
|
56
|
+
return compose_cmd
|
57
|
+
end
|
58
|
+
my_bin_path(util)
|
59
|
+
end
|
29
60
|
|
30
61
|
def my_dump_remote
|
31
62
|
puts "backup remote DB via ssh"
|
@@ -33,9 +64,9 @@ module Cloner::MySQL
|
|
33
64
|
ssh.exec!("rm -R #{e remote_dump_path}")
|
34
65
|
ret = ssh_exec!(ssh, "mkdir -p #{e remote_dump_path}")
|
35
66
|
check_ssh_err(ret)
|
36
|
-
host =
|
37
|
-
port =
|
38
|
-
dump = "#{
|
67
|
+
host = remote_db_config['host'].present? ? " --host #{e remote_db_config['host']}" : ""
|
68
|
+
port = remote_db_config['port'].present? ? " --port #{e remote_db_config['port']}" : ""
|
69
|
+
dump = "#{my_remote_bin_path 'mysqldump'} #{my_dump_param} --user #{e remote_db_config['username']} #{my_remote_auth}#{host}#{port} #{e remote_db_config['database']} > #{e(remote_dump_path + '/'+db_file_name+'.sql')}"
|
39
70
|
puts dump if verbose?
|
40
71
|
ret = ssh_exec!(ssh, dump)
|
41
72
|
check_ssh_err(ret)
|
@@ -44,15 +75,34 @@ module Cloner::MySQL
|
|
44
75
|
|
45
76
|
def my_dump_restore
|
46
77
|
puts "restoring DB"
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
78
|
+
|
79
|
+
if local_docker_compose? && local_docker_compose_service
|
80
|
+
# Docker compose restore - pipe the SQL file to docker compose exec
|
81
|
+
host = local_db_config['host'].present? ? " --host #{e local_db_config['host']}" : ""
|
82
|
+
port = local_db_config['port'].present? ? " --port #{e local_db_config['port']}" : ""
|
83
|
+
|
84
|
+
compose_path = local_docker_compose_path
|
85
|
+
compose_file = local_docker_compose_file
|
86
|
+
service = local_docker_compose_service
|
87
|
+
|
88
|
+
# MySQL requires password to be passed differently in Docker
|
89
|
+
restore = "cat #{e(my_path + '/'+db_file_name+'.sql')} | (cd #{e compose_path} && docker compose -f #{e compose_file} exec -T #{e service} mysql #{my_restore_param} --user #{e local_db_config['username']} #{my_local_auth}#{host}#{port} #{e ar_to})"
|
90
|
+
puts restore if verbose?
|
91
|
+
system(restore)
|
92
|
+
ret = $?.to_i
|
93
|
+
else
|
94
|
+
# Standard restore
|
95
|
+
host = local_db_config['host'].present? ? " --host #{e local_db_config['host']}" : ""
|
96
|
+
port = local_db_config['port'].present? ? " --port #{e local_db_config['port']}" : ""
|
97
|
+
restore = "#{my_local_bin_path 'mysql'} #{my_restore_param} --user #{e local_db_config['username']} #{my_local_auth}#{host}#{port} #{e ar_to} < #{e(my_path + '/'+db_file_name+'.sql')}"
|
98
|
+
puts restore if verbose?
|
99
|
+
pipe = IO.popen(restore)
|
100
|
+
while (line = pipe.gets)
|
101
|
+
print line if verbose?
|
102
|
+
end
|
103
|
+
ret = $?.to_i
|
54
104
|
end
|
55
|
-
|
105
|
+
|
56
106
|
if ret != 0
|
57
107
|
puts "Error: local command exited with #{ret}"
|
58
108
|
end
|
data/lib/cloner/postgres.rb
CHANGED
@@ -2,18 +2,18 @@ module Cloner::Postgres
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
def pg_local_auth
|
5
|
-
if
|
5
|
+
if local_db_config['password'].blank?
|
6
6
|
""
|
7
7
|
else
|
8
|
-
"PGPASSWORD='#{
|
8
|
+
"PGPASSWORD='#{local_db_config['password']}' "
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
def pg_remote_auth
|
13
|
-
if
|
13
|
+
if remote_db_config['password'].blank?
|
14
14
|
""
|
15
15
|
else
|
16
|
-
"PGPASSWORD='#{
|
16
|
+
"PGPASSWORD='#{remote_db_config['password']}' "
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -37,10 +37,39 @@ module Cloner::Postgres
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def pg_local_bin_path(util)
|
40
|
+
if local_docker_compose? && local_docker_compose_service
|
41
|
+
# For pg_restore, we pipe data in, so we don't wrap it in docker compose
|
42
|
+
return util if util == "pg_restore"
|
43
|
+
|
44
|
+
# Build docker compose exec command
|
45
|
+
env_vars = {}
|
46
|
+
env_vars['PGPASSWORD'] = local_db_config['password'] if local_db_config['password'].present?
|
47
|
+
|
48
|
+
compose_cmd = local_docker_compose_exec(
|
49
|
+
local_docker_compose_service,
|
50
|
+
"env PGPASSWORD='#{local_db_config['password']}' #{util}",
|
51
|
+
env: env_vars,
|
52
|
+
no_tty: true
|
53
|
+
)
|
54
|
+
return compose_cmd
|
55
|
+
end
|
40
56
|
pg_bin_path(util)
|
41
57
|
end
|
42
58
|
|
43
59
|
def pg_remote_bin_path(util)
|
60
|
+
if remote_docker_compose? && remote_docker_compose_service
|
61
|
+
# Build docker compose exec command for remote
|
62
|
+
env_vars = {}
|
63
|
+
env_vars['PGPASSWORD'] = remote_db_config['password'] if remote_db_config['password'].present?
|
64
|
+
|
65
|
+
compose_cmd = remote_docker_compose_exec(
|
66
|
+
remote_docker_compose_service,
|
67
|
+
"env PGPASSWORD='#{remote_db_config['password']}' #{util}",
|
68
|
+
env: env_vars,
|
69
|
+
no_tty: true
|
70
|
+
)
|
71
|
+
return compose_cmd
|
72
|
+
end
|
44
73
|
pg_bin_path(util)
|
45
74
|
end
|
46
75
|
|
@@ -50,9 +79,9 @@ module Cloner::Postgres
|
|
50
79
|
ssh.exec!("rm -R #{e remote_dump_path}")
|
51
80
|
ret = ssh_exec!(ssh, "mkdir -p #{e remote_dump_path}")
|
52
81
|
check_ssh_err(ret)
|
53
|
-
host =
|
54
|
-
port =
|
55
|
-
dump = pg_remote_auth + "#{pg_remote_bin_path 'pg_dump'} #{pg_dump_param} -U #{e
|
82
|
+
host = remote_db_config['host'].present? ? " -h #{e remote_db_config['host']}" : ""
|
83
|
+
port = remote_db_config['port'].present? ? " -p #{e remote_db_config['port']}" : ""
|
84
|
+
dump = pg_remote_auth + "#{pg_remote_bin_path 'pg_dump'} #{pg_dump_param} -U #{e remote_db_config['username']}#{host}#{port} #{e remote_db_config['database']} > #{e(remote_dump_path + '/'+db_file_name+'.bak')}"
|
56
85
|
puts dump if verbose?
|
57
86
|
ret = ssh_exec!(ssh, dump)
|
58
87
|
check_ssh_err(ret)
|
@@ -61,15 +90,34 @@ module Cloner::Postgres
|
|
61
90
|
|
62
91
|
def pg_dump_restore
|
63
92
|
puts "restoring DB"
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
93
|
+
|
94
|
+
if local_docker_compose? && local_docker_compose_service
|
95
|
+
# Docker compose restore - pipe the backup file to docker compose exec
|
96
|
+
host = local_db_config['host'].present? ? " -h #{e local_db_config['host']}" : ""
|
97
|
+
port = local_db_config['port'].present? ? " -p #{e local_db_config['port']}" : ""
|
98
|
+
|
99
|
+
env_str = local_db_config['password'].present? ? "--env PGPASSWORD=#{e local_db_config['password']}" : ""
|
100
|
+
compose_path = local_docker_compose_path
|
101
|
+
compose_file = local_docker_compose_file
|
102
|
+
service = local_docker_compose_service
|
103
|
+
|
104
|
+
restore = "cat #{e(pg_path + '/'+db_file_name+'.bak')} | (cd #{e compose_path} && docker compose -f #{e compose_file} exec -T #{env_str} #{e service} pg_restore #{pg_restore_param} -U #{e local_db_config['username']}#{host}#{port} -d #{e ar_to})"
|
105
|
+
puts restore if verbose?
|
106
|
+
system(restore)
|
107
|
+
ret = $?.to_i
|
108
|
+
else
|
109
|
+
# Standard restore
|
110
|
+
host = local_db_config['host'].present? ? " -h #{e local_db_config['host']}" : ""
|
111
|
+
port = local_db_config['port'].present? ? " -p #{e local_db_config['port']}" : ""
|
112
|
+
restore = pg_local_auth + "#{pg_local_bin_path 'pg_restore'} #{pg_restore_param} -U #{e local_db_config['username']}#{host}#{port} -d #{e ar_to} #{e(pg_path + '/'+db_file_name+'.bak')}"
|
113
|
+
puts restore if verbose?
|
114
|
+
pipe = IO.popen(restore)
|
115
|
+
while (line = pipe.gets)
|
116
|
+
print line if verbose?
|
117
|
+
end
|
118
|
+
ret = $?.to_i
|
71
119
|
end
|
72
|
-
|
120
|
+
|
73
121
|
if ret != 0
|
74
122
|
puts "Error: local command exited with #{ret}"
|
75
123
|
end
|
data/lib/cloner/version.rb
CHANGED
data/lib/cloner.rb
CHANGED
@@ -2,13 +2,16 @@ class ClonerGenerator < Rails::Generators::Base
|
|
2
2
|
source_root File.expand_path('templates', __dir__)
|
3
3
|
|
4
4
|
class_option :extend, default: false, type: :boolean, aliases: '-e'
|
5
|
+
class_option :docker_compose, default: false, type: :boolean, aliases: '-d', desc: 'Include Docker Compose configuration examples'
|
5
6
|
|
6
7
|
desc "This generator create lib/tasks/dl.thor"
|
7
8
|
def create_task_file
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
if options[:docker_compose]
|
10
|
+
create_docker_compose_task_file
|
11
|
+
elsif options[:extend]
|
11
12
|
create_extended_task_file
|
13
|
+
else
|
14
|
+
create_default_task_file
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
@@ -19,7 +22,18 @@ class ClonerGenerator < Rails::Generators::Base
|
|
19
22
|
|
20
23
|
def create_extended_task_file
|
21
24
|
say 'Create extend file'
|
22
|
-
|
25
|
+
module_parent_name = if Rails::VERSION::MAJOR >= 6
|
26
|
+
Rails.application.class.module_parent_name
|
27
|
+
else
|
28
|
+
Rails.application.class.parent_name
|
29
|
+
end
|
30
|
+
@username = module_parent_name.downcase
|
23
31
|
template 'cloner_extend.thor.erb', 'lib/tasks/dl.thor'
|
24
32
|
end
|
33
|
+
|
34
|
+
def create_docker_compose_task_file
|
35
|
+
say 'Create Docker Compose file with examples'
|
36
|
+
@username = Rails.application.class.parent_name.downcase
|
37
|
+
template 'cloner_docker_compose.thor.erb', 'lib/tasks/dl.thor'
|
38
|
+
end
|
25
39
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'cloner'
|
2
|
+
|
3
|
+
class Dl < Cloner::Base
|
4
|
+
no_commands do
|
5
|
+
def rails_path
|
6
|
+
File.expand_path("../../../config/environment", __FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
def stages
|
10
|
+
@_stages ||= {
|
11
|
+
# TODO: Add new stages here if you needed
|
12
|
+
production: {
|
13
|
+
# TODO: Fix production settings
|
14
|
+
ssh_host: 'production.example.com',
|
15
|
+
ssh_user: '<%= @username %>',
|
16
|
+
# Docker Compose settings for remote
|
17
|
+
docker_compose: true,
|
18
|
+
docker_compose_service: 'db', # service name in compose.yml
|
19
|
+
docker_compose_path: '/data/<%= @username %>/app/current'
|
20
|
+
},
|
21
|
+
staging: {
|
22
|
+
# TODO: Fix staging settings
|
23
|
+
ssh_host: 'staging.example.com',
|
24
|
+
ssh_user: '<%= @username %>',
|
25
|
+
# Docker Compose settings for remote
|
26
|
+
docker_compose: true,
|
27
|
+
docker_compose_service: 'db',
|
28
|
+
docker_compose_path: '/data/<%= @username %>/app/current'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def ssh_host
|
34
|
+
stages.dig(options[:from].to_sym, :ssh_host)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ssh_user
|
38
|
+
stages.dig(options[:from].to_sym, :ssh_user)
|
39
|
+
end
|
40
|
+
|
41
|
+
def remote_dump_path
|
42
|
+
# TODO: Fix remote dump path
|
43
|
+
'/data/<%= @username %>/dump'
|
44
|
+
end
|
45
|
+
|
46
|
+
def remote_app_path
|
47
|
+
# TODO: Fix remote app path
|
48
|
+
'/data/<%= @username %>/app/current'
|
49
|
+
end
|
50
|
+
|
51
|
+
# Docker Compose configuration for remote
|
52
|
+
def remote_docker_compose?
|
53
|
+
stages.dig(options[:from].to_sym, :docker_compose) || false
|
54
|
+
end
|
55
|
+
|
56
|
+
def remote_docker_compose_service
|
57
|
+
stages.dig(options[:from].to_sym, :docker_compose_service)
|
58
|
+
end
|
59
|
+
|
60
|
+
def remote_docker_compose_path
|
61
|
+
stages.dig(options[:from].to_sym, :docker_compose_path) || remote_app_path
|
62
|
+
end
|
63
|
+
|
64
|
+
# Docker Compose configuration for local (if needed)
|
65
|
+
def local_docker_compose?
|
66
|
+
# TODO: Set to true if you use Docker Compose locally
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def local_docker_compose_service
|
71
|
+
# TODO: Set your local Docker Compose service name
|
72
|
+
'db'
|
73
|
+
end
|
74
|
+
|
75
|
+
def local_docker_compose_path
|
76
|
+
# TODO: Set your local Docker Compose path
|
77
|
+
Rails.root.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
# Override for PostgreSQL with Docker Compose (example)
|
81
|
+
# def pg_remote_bin_path(util)
|
82
|
+
# if remote_docker_compose? && remote_docker_compose_service
|
83
|
+
# # Example: Read .env file for credentials
|
84
|
+
# env_vars = read_remote_env_file
|
85
|
+
# "cd #{e remote_docker_compose_path} && docker compose exec --no-TTY --env PGPASSWORD='#{env_vars['DB_PASSWORD']}' #{e remote_docker_compose_service} #{util}"
|
86
|
+
# else
|
87
|
+
# super
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
|
91
|
+
# Helper to read remote .env file (example)
|
92
|
+
# def read_remote_env_file
|
93
|
+
# env_content = ""
|
94
|
+
# do_ssh do |ssh|
|
95
|
+
# env_content = ssh.exec!("cat #{e remote_app_path}/.env")
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# # Parse .env content
|
99
|
+
# env_vars = {}
|
100
|
+
# env_content.each_line do |line|
|
101
|
+
# next if line.strip.empty? || line.strip.start_with?('#')
|
102
|
+
# key, value = line.strip.split('=', 2)
|
103
|
+
# next unless key && value
|
104
|
+
# # Remove quotes if present
|
105
|
+
# value = value.gsub(/^["']|["']$/, '')
|
106
|
+
# env_vars[key] = value
|
107
|
+
# end
|
108
|
+
# env_vars
|
109
|
+
# end
|
110
|
+
end
|
111
|
+
|
112
|
+
class_option :from,
|
113
|
+
default: 'production',
|
114
|
+
type: :string,
|
115
|
+
desc: 'stage name where cloner get data'
|
116
|
+
class_option :skip_database,
|
117
|
+
default: false,
|
118
|
+
type: :boolean,
|
119
|
+
aliases: '-D',
|
120
|
+
desc: 'skip clone database'
|
121
|
+
class_option :skip_files,
|
122
|
+
default: false,
|
123
|
+
type: :boolean,
|
124
|
+
aliases: '-F',
|
125
|
+
desc: 'skip clone files'
|
126
|
+
|
127
|
+
desc "download", "clone files and DB from production"
|
128
|
+
def download
|
129
|
+
load_env
|
130
|
+
say "Clone from: #{options[:from]}", :green
|
131
|
+
|
132
|
+
if remote_docker_compose?
|
133
|
+
say "Using Docker Compose on remote (service: #{remote_docker_compose_service})", :blue
|
134
|
+
end
|
135
|
+
|
136
|
+
if local_docker_compose?
|
137
|
+
say "Using Docker Compose locally (service: #{local_docker_compose_service})", :blue
|
138
|
+
end
|
139
|
+
|
140
|
+
if options[:skip_database]
|
141
|
+
say "Skip clone database!", :yellow
|
142
|
+
else
|
143
|
+
clone_db
|
144
|
+
end
|
145
|
+
|
146
|
+
if options[:skip_files]
|
147
|
+
say "Skip clone files!", :yellow
|
148
|
+
else
|
149
|
+
# TODO: Fix folders for synchronization here
|
150
|
+
rsync_public("ckeditor_assets")
|
151
|
+
rsync_public("uploads")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require_relative '../lib/cloner'
|
2
|
+
require 'pathname'
|
3
|
+
require 'net/ssh'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
|
7
|
+
class ::Rails
|
8
|
+
def self.root
|
9
|
+
Pathname.new(File.expand_path("../", __FILE__))
|
10
|
+
end
|
11
|
+
def self.env
|
12
|
+
"development"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class DlComposeTest < Cloner::Base
|
17
|
+
no_commands do
|
18
|
+
def rails_path
|
19
|
+
File.expand_path("../", __FILE__)
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_env
|
23
|
+
require 'net/ssh'
|
24
|
+
require 'shellwords'
|
25
|
+
require 'active_support/core_ext/object'
|
26
|
+
end
|
27
|
+
|
28
|
+
# SSH configuration
|
29
|
+
def ssh_host
|
30
|
+
'rscx.ru'
|
31
|
+
end
|
32
|
+
|
33
|
+
def ssh_user
|
34
|
+
'root'
|
35
|
+
end
|
36
|
+
|
37
|
+
def env_from
|
38
|
+
"production"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remote paths
|
42
|
+
def remote_dump_path
|
43
|
+
'/root/compose/aichaos/tmp_dump'
|
44
|
+
end
|
45
|
+
|
46
|
+
def remote_app_path
|
47
|
+
'/root/compose/aichaos'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Docker Compose configuration for remote
|
51
|
+
def remote_docker_compose?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def remote_docker_compose_service
|
56
|
+
'db' # Assuming the PostgreSQL service is named 'db'
|
57
|
+
end
|
58
|
+
|
59
|
+
def remote_docker_compose_path
|
60
|
+
remote_app_path
|
61
|
+
end
|
62
|
+
|
63
|
+
# Docker Compose configuration for local
|
64
|
+
def local_docker_compose?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def local_docker_compose_service
|
69
|
+
'testdb'
|
70
|
+
end
|
71
|
+
|
72
|
+
def local_docker_compose_path
|
73
|
+
Rails.root.parent.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
# Override to read credentials from remote .env file
|
77
|
+
def read_ar_r_conf
|
78
|
+
# Read from remote .env file
|
79
|
+
env_content = ""
|
80
|
+
do_ssh do |ssh|
|
81
|
+
env_content = ssh.exec!("cat #{e remote_app_path}/.env")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Parse .env content
|
85
|
+
env_vars = {}
|
86
|
+
env_content.each_line do |line|
|
87
|
+
next if line.strip.empty? || line.strip.start_with?('#')
|
88
|
+
key, value = line.strip.split('=', 2)
|
89
|
+
next unless key && value
|
90
|
+
# Remove quotes if present
|
91
|
+
value = value.gsub(/^["']|["']$/, '')
|
92
|
+
env_vars[key] = value
|
93
|
+
end
|
94
|
+
|
95
|
+
{
|
96
|
+
adapter: "postgresql",
|
97
|
+
host: env_vars['DB_HOST'] || 'db',
|
98
|
+
port: env_vars['DB_PORT'] || '5432',
|
99
|
+
database: env_vars['DB_NAME'] || 'aichaos',
|
100
|
+
username: env_vars['DB_USER'] || 'aichaos',
|
101
|
+
password: env_vars['DB_PASSWORD'] || ''
|
102
|
+
}.stringify_keys
|
103
|
+
end
|
104
|
+
|
105
|
+
# Local database configuration
|
106
|
+
def ar_conf
|
107
|
+
{
|
108
|
+
adapter: "postgresql",
|
109
|
+
host: "localhost",
|
110
|
+
port: "5432",
|
111
|
+
database: "test_development",
|
112
|
+
username: "testuser",
|
113
|
+
password: "testpass"
|
114
|
+
}.stringify_keys
|
115
|
+
end
|
116
|
+
|
117
|
+
def ar_to
|
118
|
+
ar_conf['database']
|
119
|
+
end
|
120
|
+
|
121
|
+
def db_file_name
|
122
|
+
"cloner_test"
|
123
|
+
end
|
124
|
+
|
125
|
+
# Override pg_bin_path to handle Docker Compose
|
126
|
+
def pg_remote_bin_path(util)
|
127
|
+
if remote_docker_compose? && remote_docker_compose_service
|
128
|
+
env_vars = read_ar_r_conf
|
129
|
+
"cd #{e remote_app_path} && docker compose exec --no-TTY --env PGPASSWORD='#{env_vars['password']}' #{e remote_docker_compose_service} #{util}"
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def verbose?
|
136
|
+
true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
desc "test", "Test cloning from remote Docker Compose PostgreSQL"
|
141
|
+
def test
|
142
|
+
load_env
|
143
|
+
|
144
|
+
puts "Testing Docker Compose PostgreSQL cloning..."
|
145
|
+
puts "Remote: #{ssh_user}@#{ssh_host}:#{remote_app_path}"
|
146
|
+
puts "Local: #{local_docker_compose_path}"
|
147
|
+
|
148
|
+
# Start local Docker Compose
|
149
|
+
puts "\nStarting local Docker Compose..."
|
150
|
+
system("cd #{e local_docker_compose_path} && docker compose up -d")
|
151
|
+
sleep 5 # Wait for database to be ready
|
152
|
+
|
153
|
+
# Clone the database
|
154
|
+
clone_db
|
155
|
+
|
156
|
+
puts "\nTest completed!"
|
157
|
+
end
|
158
|
+
end
|