wordmove 1.0.4 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +13 -13
- data/README.mdown +23 -16
- data/lib/wordmove/cli.rb +18 -14
- data/lib/wordmove/deployer/base.rb +42 -15
- data/lib/wordmove/deployer/ftp.rb +45 -33
- data/lib/wordmove/deployer/ssh.rb +30 -26
- data/lib/wordmove/generators/Movefile +17 -12
- data/lib/wordmove/sql_mover.rb +11 -4
- data/lib/wordmove/version.rb +1 -1
- data/spec/sql_mover_spec.rb +14 -5
- data/wordmove.gemspec +1 -1
- metadata +5 -5
- data/lib/wordmove/deployer.rb +0 -166
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
wordmove (
|
4
|
+
wordmove (1.0.5)
|
5
5
|
activesupport
|
6
6
|
colored
|
7
7
|
i18n
|
8
|
-
photocopier
|
8
|
+
photocopier (>= 0.0.6)
|
9
9
|
rake
|
10
10
|
thor
|
11
11
|
|
12
12
|
GEM
|
13
13
|
remote: http://rubygems.org/
|
14
14
|
specs:
|
15
|
-
activesupport (3.2.
|
15
|
+
activesupport (3.2.12)
|
16
16
|
i18n (~> 0.6)
|
17
17
|
multi_json (~> 1.0)
|
18
18
|
aruba (0.5.1)
|
@@ -33,22 +33,22 @@ GEM
|
|
33
33
|
ffi (1.2.0)
|
34
34
|
gherkin (2.11.5)
|
35
35
|
json (>= 1.4.6)
|
36
|
-
i18n (0.6.
|
36
|
+
i18n (0.6.4)
|
37
37
|
json (1.7.5)
|
38
|
-
multi_json (1.
|
39
|
-
net-scp (1.0
|
40
|
-
net-ssh (>=
|
41
|
-
net-ssh (2.6.
|
42
|
-
net-ssh-gateway (1.
|
43
|
-
net-ssh (>=
|
44
|
-
photocopier (0.0.
|
38
|
+
multi_json (1.7.0)
|
39
|
+
net-scp (1.1.0)
|
40
|
+
net-ssh (>= 2.6.5)
|
41
|
+
net-ssh (2.6.6)
|
42
|
+
net-ssh-gateway (1.2.0)
|
43
|
+
net-ssh (>= 2.6.5)
|
44
|
+
photocopier (0.0.6)
|
45
45
|
activesupport
|
46
46
|
escape
|
47
47
|
i18n
|
48
48
|
net-scp
|
49
49
|
net-ssh
|
50
50
|
net-ssh-gateway
|
51
|
-
rake (10.0.
|
51
|
+
rake (10.0.3)
|
52
52
|
rspec (2.12.0)
|
53
53
|
rspec-core (~> 2.12.0)
|
54
54
|
rspec-expectations (~> 2.12.0)
|
@@ -57,7 +57,7 @@ GEM
|
|
57
57
|
rspec-expectations (2.12.0)
|
58
58
|
diff-lcs (~> 1.1.3)
|
59
59
|
rspec-mocks (2.12.0)
|
60
|
-
thor (0.
|
60
|
+
thor (0.17.0)
|
61
61
|
|
62
62
|
PLATFORMS
|
63
63
|
ruby
|
data/README.mdown
CHANGED
@@ -47,25 +47,31 @@ local:
|
|
47
47
|
name: "database_name"
|
48
48
|
user: "user"
|
49
49
|
password: "password"
|
50
|
-
host: "
|
51
|
-
|
50
|
+
host: "127.0.0.1"
|
51
|
+
staging:
|
52
52
|
vhost: "http://remote.com"
|
53
53
|
wordpress_path: "/var/www/your_site"
|
54
|
-
exclude:
|
55
|
-
- .git
|
56
|
-
- .DS_Store
|
57
|
-
- .sass-cache
|
58
|
-
- Movefile
|
59
54
|
database:
|
60
55
|
name: "database_name"
|
61
56
|
user: "user"
|
62
57
|
password: "password"
|
63
58
|
host: "host"
|
64
|
-
ssh:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
# ssh:
|
60
|
+
# host: "host"
|
61
|
+
# user: "user"
|
62
|
+
# password: "password" # password is optional, will use public keys if available.
|
63
|
+
# port: 22 # Port is optional
|
64
|
+
# gateway: # Gateway is optional
|
65
|
+
# host: "host"
|
66
|
+
# user: "user"
|
67
|
+
# password: "password" # password is optional, will use public keys if available.
|
68
|
+
# ftp:
|
69
|
+
# user: "user"
|
70
|
+
# password: "password"
|
71
|
+
# host: "host"
|
72
|
+
# passive: true
|
73
|
+
# production: # multiple environments can be specified
|
74
|
+
# [...]
|
69
75
|
```
|
70
76
|
|
71
77
|
### If you have your local SSH public key already installed on the remote machine.. (recommended)
|
@@ -81,9 +87,10 @@ too much about security though: the script is deleted just after the usage,
|
|
81
87
|
and can only be executed by `wordmove`, as each time it requires a pre-shared
|
82
88
|
one-time-password to be run.
|
83
89
|
|
84
|
-
### If you want to specify both relative and absolute wordpress path (FTP paths anyone?)
|
85
|
-
Just add
|
86
|
-
|
90
|
+
### If you want to specify both relative and absolute wordpress path (FTP relative paths anyone?)
|
91
|
+
Just add in the remote section of the Movefile a `wordpress_absolute_path` field
|
92
|
+
specifying the absolute path (you may need to recover this from the `__FILE__` constant),
|
93
|
+
while using `wordpress_path` for the relative folder path.
|
87
94
|
|
88
95
|
### If you want to specify a passive FTP connection
|
89
96
|
Add to the YAML config a `passive` flag set to `true`.
|
@@ -95,7 +102,7 @@ Add to the YAML config a `passive` flag set to `true`.
|
|
95
102
|
|
96
103
|
(The MIT License)
|
97
104
|
|
98
|
-
Copyright ©
|
105
|
+
Copyright © 2013 weLaika
|
99
106
|
|
100
107
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
101
108
|
|
data/lib/wordmove/cli.rb
CHANGED
@@ -11,13 +11,15 @@ module Wordmove
|
|
11
11
|
end
|
12
12
|
|
13
13
|
desc "pull", "Pulls WP data from remote host to the local machine"
|
14
|
-
method_option :db,
|
15
|
-
method_option :uploads,
|
16
|
-
method_option :themes,
|
17
|
-
method_option :plugins,
|
18
|
-
method_option :verbose,
|
19
|
-
method_option :simulate,
|
20
|
-
method_option :
|
14
|
+
method_option :db, :aliases => "-d", :type => :boolean
|
15
|
+
method_option :uploads, :aliases => "-u", :type => :boolean
|
16
|
+
method_option :themes, :aliases => "-t", :type => :boolean
|
17
|
+
method_option :plugins, :aliases => "-p", :type => :boolean
|
18
|
+
method_option :verbose, :aliases => "-v", :type => :boolean
|
19
|
+
method_option :simulate, :aliases => "-s", :type => :boolean
|
20
|
+
method_option :no_adapt, :type => :boolean
|
21
|
+
method_option :environment, :aliases => "-e"
|
22
|
+
method_option :config, :aliases => "-c"
|
21
23
|
def pull
|
22
24
|
deployer = Wordmove::Deployer::Base.deployer_for(options)
|
23
25
|
%w(db uploads themes plugins).map(&:to_sym).each do |task|
|
@@ -28,13 +30,15 @@ module Wordmove
|
|
28
30
|
end
|
29
31
|
|
30
32
|
desc "push", "Pushes WP data from local machine to remote host"
|
31
|
-
method_option :
|
32
|
-
method_option :
|
33
|
-
method_option :themes,
|
34
|
-
method_option :plugins,
|
35
|
-
method_option :verbose,
|
36
|
-
method_option :simulate,
|
37
|
-
method_option :
|
33
|
+
method_option :uploads, :aliases => "-u", :type => :boolean
|
34
|
+
method_option :db, :aliases => "-d", :type => :boolean
|
35
|
+
method_option :themes, :aliases => "-t", :type => :boolean
|
36
|
+
method_option :plugins, :aliases => "-p", :type => :boolean
|
37
|
+
method_option :verbose, :aliases => "-v", :type => :boolean
|
38
|
+
method_option :simulate, :aliases => "-s", :type => :boolean
|
39
|
+
method_option :no_adapt, :type => :boolean
|
40
|
+
method_option :environment, :aliases => "-e"
|
41
|
+
method_option :config, :aliases => "-c"
|
38
42
|
def push
|
39
43
|
deployer = Wordmove::Deployer::Base.deployer_for(options)
|
40
44
|
%w(db uploads themes plugins).map(&:to_sym).each do |task|
|
@@ -9,16 +9,26 @@ module Wordmove
|
|
9
9
|
class Base
|
10
10
|
attr_reader :options
|
11
11
|
attr_reader :logger
|
12
|
+
attr_reader :environment
|
12
13
|
|
13
|
-
def self.deployer_for(
|
14
|
-
options = fetch_movefile(
|
14
|
+
def self.deployer_for(cli_options)
|
15
|
+
options = fetch_movefile(cli_options[:config])
|
16
|
+
available_enviroments = options.keys.map(&:to_sym) - [ :local ]
|
17
|
+
options.merge!(cli_options)
|
15
18
|
recursive_symbolize_keys!(options)
|
16
|
-
|
19
|
+
|
20
|
+
if available_enviroments.size > 1 && options[:environment].nil?
|
21
|
+
raise "You need to specify an environment with --environment parameter"
|
22
|
+
end
|
23
|
+
|
24
|
+
environment = (options[:environment] || available_enviroments.first).to_sym
|
25
|
+
|
26
|
+
if options[environment][:ftp]
|
17
27
|
require 'wordmove/deployer/ftp'
|
18
|
-
FTP.new(options)
|
19
|
-
elsif options[
|
28
|
+
FTP.new(environment, options)
|
29
|
+
elsif options[environment][:ssh]
|
20
30
|
require 'wordmove/deployer/ssh'
|
21
|
-
SSH.new(options)
|
31
|
+
SSH.new(environment, options)
|
22
32
|
else
|
23
33
|
raise Thor::Error, "No valid adapter found."
|
24
34
|
end
|
@@ -32,7 +42,8 @@ module Wordmove
|
|
32
42
|
YAML::load(File.open(path))
|
33
43
|
end
|
34
44
|
|
35
|
-
def initialize(options = {})
|
45
|
+
def initialize(environment, options = {})
|
46
|
+
@environment = environment.to_sym
|
36
47
|
@options = options
|
37
48
|
@logger = Logger.new(STDOUT)
|
38
49
|
@logger.level = Logger::DEBUG
|
@@ -64,18 +75,19 @@ module Wordmove
|
|
64
75
|
protected
|
65
76
|
|
66
77
|
def paths_to_exclude
|
67
|
-
|
78
|
+
remote_options[:exclude] || Array.new
|
68
79
|
end
|
69
80
|
|
70
81
|
def run(command)
|
71
82
|
logger.task_step true, command
|
72
83
|
unless simulate?
|
73
84
|
system(command)
|
85
|
+
raise "Return code reports an error" unless $?.success?
|
74
86
|
end
|
75
87
|
end
|
76
88
|
|
77
89
|
def download(url, local_path)
|
78
|
-
logger.task_step true, "download #{url}"
|
90
|
+
logger.task_step true, "download #{url} > #{local_path}"
|
79
91
|
unless simulate?
|
80
92
|
open(local_path, 'w') do |file|
|
81
93
|
file << open(url).read
|
@@ -88,21 +100,23 @@ module Wordmove
|
|
88
100
|
end
|
89
101
|
|
90
102
|
def local_wpcontent_path(*args)
|
91
|
-
File.join(
|
103
|
+
File.join(local_options[:wordpress_path], "wp-content", *args)
|
92
104
|
end
|
93
105
|
|
94
106
|
def remote_wpcontent_path(*args)
|
95
|
-
File.join(
|
107
|
+
File.join(remote_options[:wordpress_path], "wp-content", *args)
|
96
108
|
end
|
97
109
|
|
98
110
|
def remote_wpcontent_url(*args)
|
99
|
-
|
111
|
+
remote_options[:vhost] + File.join("/wp-content", *args)
|
100
112
|
end
|
101
113
|
|
102
114
|
def adapt_sql(save_to_path, local, remote)
|
103
|
-
|
104
|
-
|
105
|
-
|
115
|
+
unless options[:no_adapt]
|
116
|
+
logger.task_step true, "adapt dump"
|
117
|
+
unless simulate?
|
118
|
+
SqlMover.new(save_to_path, local, remote).move!
|
119
|
+
end
|
106
120
|
end
|
107
121
|
end
|
108
122
|
|
@@ -125,6 +139,19 @@ module Wordmove
|
|
125
139
|
Escape.shell_command(arguments) + " < #{dump_path}"
|
126
140
|
end
|
127
141
|
|
142
|
+
def save_local_db(local_dump_path)
|
143
|
+
# dump local mysql into file
|
144
|
+
run mysql_dump_command(local_options[:database], local_dump_path)
|
145
|
+
end
|
146
|
+
|
147
|
+
def remote_options
|
148
|
+
options[environment].clone
|
149
|
+
end
|
150
|
+
|
151
|
+
def local_options
|
152
|
+
options[:local].clone
|
153
|
+
end
|
154
|
+
|
128
155
|
private
|
129
156
|
|
130
157
|
def self.recursive_symbolize_keys! hash
|
@@ -7,9 +7,9 @@ module Wordmove
|
|
7
7
|
module Deployer
|
8
8
|
class FTP < Base
|
9
9
|
|
10
|
-
def initialize(options)
|
10
|
+
def initialize(environment, options)
|
11
11
|
super
|
12
|
-
ftp_options =
|
12
|
+
ftp_options = remote_options[:ftp]
|
13
13
|
@copier = Photocopier::FTP.new(ftp_options)
|
14
14
|
@copier.logger = logger
|
15
15
|
end
|
@@ -17,29 +17,20 @@ module Wordmove
|
|
17
17
|
def push_db
|
18
18
|
super
|
19
19
|
|
20
|
-
remote_import_script_path = remote_wpcontent_path("import.php")
|
21
20
|
local_dump_path = local_wpcontent_path("dump.sql")
|
22
21
|
remote_dump_path = remote_wpcontent_path("dump.sql")
|
22
|
+
local_backup_path = local_wpcontent_path("remote-backup-#{Time.now.to_i}.sql")
|
23
|
+
|
24
|
+
download_remote_db(local_backup_path)
|
25
|
+
save_local_db(local_dump_path)
|
23
26
|
|
24
|
-
# dump local mysql into file
|
25
|
-
run mysql_dump_command(options[:local][:database], local_dump_path)
|
26
27
|
# gsub sql
|
27
|
-
adapt_sql(local_dump_path,
|
28
|
+
adapt_sql(local_dump_path, local_options, remote_options)
|
28
29
|
# upload it
|
29
30
|
remote_put(local_dump_path, remote_dump_path)
|
30
31
|
|
31
|
-
|
32
|
-
one_time_password = SecureRandom.hex(40)
|
33
|
-
# generate import script
|
34
|
-
import_script = generate_import_script(options[:remote][:database], one_time_password)
|
35
|
-
# upload import script
|
36
|
-
remote_put(import_script, remote_import_script_path)
|
37
|
-
# run import script
|
38
|
-
import_url = "#{remote_wpcontent_url("import.php")}?shared_key=#{one_time_password}&start=1&foffset=0&totalqueries=0&fn=dump.sql"
|
39
|
-
download(import_url, local_dump_path + "_")
|
32
|
+
import_remote_dump
|
40
33
|
|
41
|
-
# remove script remotely
|
42
|
-
remote_delete(remote_import_script_path)
|
43
34
|
# remove dump remotely
|
44
35
|
remote_delete(remote_dump_path)
|
45
36
|
# and locally
|
@@ -48,28 +39,17 @@ module Wordmove
|
|
48
39
|
|
49
40
|
def pull_db
|
50
41
|
super
|
51
|
-
|
52
|
-
remote_dump_script = remote_wpcontent_path("dump.php")
|
53
42
|
local_dump_path = local_wpcontent_path("dump.sql")
|
43
|
+
local_backup_path = local_wpcontent_path("local-backup-#{Time.now.to_i}.sql")
|
54
44
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
# generate dump script
|
59
|
-
dump_script = generate_dump_script(options[:remote][:database], one_time_password)
|
60
|
-
# upload the dump script
|
61
|
-
remote_put(dump_script, remote_dump_script)
|
62
|
-
# download the resulting dump (using the password)
|
63
|
-
dump_url = "#{remote_wpcontent_url("dump.php")}?shared_key=#{one_time_password}"
|
64
|
-
download(dump_url, local_dump_path)
|
45
|
+
save_local_db(local_backup_path)
|
46
|
+
download_remote_db(local_dump_path)
|
65
47
|
|
66
48
|
# gsub sql
|
67
|
-
adapt_sql(local_dump_path,
|
49
|
+
adapt_sql(local_dump_path, remote_options, local_options)
|
68
50
|
# import locally
|
69
|
-
run mysql_import_command(local_dump_path,
|
51
|
+
run mysql_import_command(local_dump_path, local_options[:database])
|
70
52
|
|
71
|
-
# remove it remotely
|
72
|
-
remote_delete(remote_dump_script)
|
73
53
|
# and locally
|
74
54
|
run "rm #{local_dump_path}"
|
75
55
|
end
|
@@ -112,6 +92,38 @@ module Wordmove
|
|
112
92
|
template.result(binding)
|
113
93
|
end
|
114
94
|
|
95
|
+
def download_remote_db(local_dump_path)
|
96
|
+
remote_dump_script = remote_wpcontent_path("dump.php")
|
97
|
+
# generate a secure one-time password
|
98
|
+
one_time_password = SecureRandom.hex(40)
|
99
|
+
# generate dump script
|
100
|
+
dump_script = generate_dump_script(remote_options[:database], one_time_password)
|
101
|
+
# upload the dump script
|
102
|
+
remote_put(dump_script, remote_dump_script)
|
103
|
+
# download the resulting dump (using the password)
|
104
|
+
dump_url = "#{remote_wpcontent_url("dump.php")}?shared_key=#{one_time_password}"
|
105
|
+
download(dump_url, local_dump_path)
|
106
|
+
# remove it remotely
|
107
|
+
remote_delete(remote_dump_script)
|
108
|
+
end
|
109
|
+
|
110
|
+
def import_remote_dump
|
111
|
+
temp_path = local_wpcontent_path("temp.txt")
|
112
|
+
remote_import_script_path = remote_wpcontent_path("import.php")
|
113
|
+
# generate a secure one-time password
|
114
|
+
one_time_password = SecureRandom.hex(40)
|
115
|
+
# generate import script
|
116
|
+
import_script = generate_import_script(remote_options[:database], one_time_password)
|
117
|
+
# upload import script
|
118
|
+
remote_put(import_script, remote_import_script_path)
|
119
|
+
# run import script
|
120
|
+
import_url = "#{remote_wpcontent_url("import.php")}?shared_key=#{one_time_password}&start=1&foffset=0&totalqueries=0&fn=dump.sql"
|
121
|
+
download(import_url, temp_path)
|
122
|
+
run "rm #{temp_path}"
|
123
|
+
# remove script remotely
|
124
|
+
remote_delete(remote_import_script_path)
|
125
|
+
end
|
126
|
+
|
115
127
|
end
|
116
128
|
end
|
117
129
|
end
|
@@ -4,9 +4,9 @@ require 'photocopier/ssh'
|
|
4
4
|
module Wordmove
|
5
5
|
module Deployer
|
6
6
|
class SSH < Base
|
7
|
-
def initialize(options)
|
7
|
+
def initialize(environment, options)
|
8
8
|
super
|
9
|
-
ssh_options =
|
9
|
+
ssh_options = remote_options[:ssh]
|
10
10
|
@copier = Photocopier::SSH.new(ssh_options)
|
11
11
|
@copier.logger = logger
|
12
12
|
end
|
@@ -15,19 +15,12 @@ module Wordmove
|
|
15
15
|
super
|
16
16
|
|
17
17
|
local_dump_path = local_wpcontent_path("dump.sql")
|
18
|
-
|
18
|
+
local_backup_path = local_wpcontent_path("remote-backup-#{Time.now.to_i}.sql")
|
19
|
+
download_remote_db(local_backup_path)
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
adapt_sql(local_dump_path, options[:local], options[:remote])
|
24
|
-
# upload it
|
25
|
-
remote_put(local_dump_path, remote_dump_path)
|
26
|
-
# import it remotely
|
27
|
-
remote_run mysql_import_command(remote_dump_path, options[:remote][:database])
|
28
|
-
# remove it remotely
|
29
|
-
remote_delete(remote_dump_path)
|
30
|
-
# and locally
|
21
|
+
save_local_db(local_dump_path)
|
22
|
+
adapt_sql(local_dump_path, local_options, remote_options)
|
23
|
+
import_remote_dump(local_dump_path)
|
31
24
|
run "rm #{local_dump_path}"
|
32
25
|
end
|
33
26
|
|
@@ -36,18 +29,12 @@ module Wordmove
|
|
36
29
|
|
37
30
|
local_dump_path = local_wpcontent_path("dump.sql")
|
38
31
|
remote_dump_path = remote_wpcontent_path("dump.sql")
|
32
|
+
local_backup_path = local_wpcontent_path("local-backup-#{Time.now.to_i}.sql")
|
33
|
+
save_local_db(local_backup_path)
|
39
34
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
remote_get(remote_dump_path, local_dump_path)
|
44
|
-
# gsub sql
|
45
|
-
adapt_sql(local_dump_path, options[:remote], options[:local])
|
46
|
-
# import locally
|
47
|
-
run mysql_import_command(local_dump_path, options[:local][:database])
|
48
|
-
# remove it remotely
|
49
|
-
remote_delete(remote_dump_path)
|
50
|
-
# and locally
|
35
|
+
download_remote_db(local_dump_path)
|
36
|
+
adapt_sql(local_dump_path, remote_options, local_options)
|
37
|
+
run mysql_import_command(local_dump_path, local_options[:database])
|
51
38
|
run "rm #{local_dump_path}"
|
52
39
|
end
|
53
40
|
|
@@ -65,10 +52,27 @@ module Wordmove
|
|
65
52
|
def remote_run(command)
|
66
53
|
logger.task_step false, command
|
67
54
|
unless simulate?
|
68
|
-
@copier.
|
55
|
+
stdout, stderr, exit_code = @copier.exec! command
|
56
|
+
raise "Error code #{exit_code} returned by command \"#{cmd}\": #{stderr}" unless exit_code.zero?
|
69
57
|
end
|
70
58
|
end
|
71
59
|
|
60
|
+
def download_remote_db(local_dump_path)
|
61
|
+
remote_dump_path = remote_wpcontent_path("dump.sql")
|
62
|
+
# dump remote db into file
|
63
|
+
remote_run mysql_dump_command(remote_options[:database], remote_dump_path)
|
64
|
+
# download remote dump
|
65
|
+
remote_get(remote_dump_path, local_dump_path)
|
66
|
+
remote_delete(remote_dump_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def import_remote_dump(local_dump_path)
|
70
|
+
remote_dump_path = remote_wpcontent_path("dump.sql")
|
71
|
+
remote_put(local_dump_path, remote_dump_path)
|
72
|
+
remote_run mysql_import_command(remote_dump_path, remote_options[:database])
|
73
|
+
remote_delete(remote_dump_path)
|
74
|
+
end
|
75
|
+
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -6,7 +6,7 @@ local:
|
|
6
6
|
user: "user"
|
7
7
|
password: "password"
|
8
8
|
host: "127.0.0.1"
|
9
|
-
|
9
|
+
staging:
|
10
10
|
vhost: "http://remote.com"
|
11
11
|
wordpress_path: "/var/www/your_site"
|
12
12
|
database:
|
@@ -14,14 +14,19 @@ remote:
|
|
14
14
|
user: "user"
|
15
15
|
password: "password"
|
16
16
|
host: "host"
|
17
|
-
ssh:
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
# ssh:
|
18
|
+
# host: "host"
|
19
|
+
# user: "user"
|
20
|
+
# password: "password" # password is optional, will use public keys if available.
|
21
|
+
# port: 22 # Port is optional
|
22
|
+
# gateway: # Gateway is optional
|
23
|
+
# host: "host"
|
24
|
+
# user: "user"
|
25
|
+
# password: "password" # password is optional, will use public keys if available.
|
26
|
+
# ftp:
|
27
|
+
# user: "user"
|
28
|
+
# password: "password"
|
29
|
+
# host: "host"
|
30
|
+
# passive: true
|
31
|
+
# production: # multiple environments can be specified
|
32
|
+
# [...]
|
data/lib/wordmove/sql_mover.rb
CHANGED
@@ -43,10 +43,17 @@ module Wordmove
|
|
43
43
|
def serialized_replace!(source_field, dest_field)
|
44
44
|
length_delta = source_field.length - dest_field.length
|
45
45
|
|
46
|
-
sql_content.gsub!(/s:(\d+):([\\'"]+)
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
sql_content.gsub!(/s:(\d+):([\\'"]+)(.*?)\2;/) do |match|
|
47
|
+
length = $1.to_i
|
48
|
+
delimiter = $2
|
49
|
+
string = $3
|
50
|
+
|
51
|
+
string.gsub!(/#{Regexp.escape(source_field)}/) do |match|
|
52
|
+
length -= length_delta
|
53
|
+
dest_field
|
54
|
+
end
|
55
|
+
|
56
|
+
%(s:#{length}:#{delimiter}#{string}#{delimiter};)
|
50
57
|
end
|
51
58
|
end
|
52
59
|
|
data/lib/wordmove/version.rb
CHANGED
data/spec/sql_mover_spec.rb
CHANGED
@@ -82,21 +82,30 @@ describe Wordmove::SqlMover do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
context ".serialized_replace!" do
|
85
|
-
let(:content) { '
|
85
|
+
let(:content) { 'a:3:{i:0;s:20:"http://dump.com/spam";i:1;s:6:"foobar";i:2;s:22:"http://dump.com/foobar";}' }
|
86
86
|
let(:sql) { Tempfile.new('sql').tap do |d| d.write(content); d.close end }
|
87
87
|
let(:sql_path) { sql.path }
|
88
88
|
|
89
89
|
it "should replace source vhost with dest vhost" do
|
90
90
|
sql_mover.serialized_replace!('http://dump.com', 'http://shrubbery.com')
|
91
|
-
sql_mover.sql_content.should == '
|
91
|
+
sql_mover.sql_content.should == 'a:3:{i:0;s:25:"http://shrubbery.com/spam";i:1;s:6:"foobar";i:2;s:27:"http://shrubbery.com/foobar";}'
|
92
92
|
end
|
93
93
|
|
94
94
|
context "given multiple types of string quoting" do
|
95
|
-
let(:content) { "s:20:\\\"http://dump.com/spam\\\";s:6:'foobar';s:22:'http://dump.com/foobar';s:8:'sausages'" }
|
95
|
+
let(:content) { "a:3:{s:20:\\\"http://dump.com/spam\\\";s:6:'foobar';s:22:'http://dump.com/foobar';s:8:'sausages';}" }
|
96
96
|
|
97
|
-
it "
|
97
|
+
it "should handle replacing just as well" do
|
98
98
|
sql_mover.serialized_replace!('http://dump.com', 'http://shrubbery.com')
|
99
|
-
sql_mover.sql_content.should == "s:25:\\\"http://shrubbery.com/spam\\\";s:6:'foobar';s:27:'http://shrubbery.com/foobar';s:8:'sausages'"
|
99
|
+
sql_mover.sql_content.should == "a:3:{s:25:\\\"http://shrubbery.com/spam\\\";s:6:'foobar';s:27:'http://shrubbery.com/foobar';s:8:'sausages';}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "given multiple occurences in the same string" do
|
104
|
+
let(:content) { 'a:1:{i:0;s:52:"ni http://dump.com/spam ni http://dump.com/foobar ni";}' }
|
105
|
+
|
106
|
+
it "should replace all occurences" do
|
107
|
+
sql_mover.serialized_replace!('http://dump.com', 'http://shrubbery.com')
|
108
|
+
sql_mover.sql_content.should == 'a:1:{i:0;s:62:"ni http://shrubbery.com/spam ni http://shrubbery.com/foobar ni";}'
|
100
109
|
end
|
101
110
|
end
|
102
111
|
end
|
data/wordmove.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_dependency "thor"
|
21
21
|
gem.add_dependency "activesupport"
|
22
22
|
gem.add_dependency "i18n"
|
23
|
-
gem.add_dependency "photocopier"
|
23
|
+
gem.add_dependency "photocopier", ">= 0.0.6"
|
24
24
|
|
25
25
|
gem.add_development_dependency "rspec"
|
26
26
|
gem.add_development_dependency "cucumber"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wordmove
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colored
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - ! '>='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
101
|
+
version: 0.0.6
|
102
102
|
type: :runtime
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +106,7 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 0.0.6
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: rspec
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -180,7 +180,6 @@ files:
|
|
180
180
|
- lib/wordmove/assets/dump.php.erb
|
181
181
|
- lib/wordmove/assets/import.php.erb
|
182
182
|
- lib/wordmove/cli.rb
|
183
|
-
- lib/wordmove/deployer.rb
|
184
183
|
- lib/wordmove/deployer/base.rb
|
185
184
|
- lib/wordmove/deployer/ftp.rb
|
186
185
|
- lib/wordmove/deployer/ssh.rb
|
@@ -230,3 +229,4 @@ test_files:
|
|
230
229
|
- spec/fixtures/Movefile
|
231
230
|
- spec/spec_helper.rb
|
232
231
|
- spec/sql_mover_spec.rb
|
232
|
+
has_rdoc:
|
data/lib/wordmove/deployer.rb
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext'
|
2
|
-
require 'hashie'
|
3
|
-
require 'wordmove/hosts/local_host'
|
4
|
-
require 'wordmove/hosts/remote_host'
|
5
|
-
require 'wordmove/logger'
|
6
|
-
require 'wordmove/sql_mover'
|
7
|
-
|
8
|
-
module Wordmove
|
9
|
-
|
10
|
-
class Deployer
|
11
|
-
|
12
|
-
attr_reader :options
|
13
|
-
attr_reader :logger
|
14
|
-
|
15
|
-
def initialize(options = {})
|
16
|
-
@options = Hashie::Mash.new(options)
|
17
|
-
@logger = Logger.new
|
18
|
-
@logger.level = options.verbose ? Logger::VERBOSE : Logger::INFO
|
19
|
-
end
|
20
|
-
|
21
|
-
def push
|
22
|
-
informative_errors do
|
23
|
-
unless options.skip_db
|
24
|
-
logger.info "Pushing the DB..."
|
25
|
-
push_db
|
26
|
-
end
|
27
|
-
|
28
|
-
remotely do |host|
|
29
|
-
%w(uploads themes plugins).each do |step|
|
30
|
-
unless options.send("skip_#{step}")
|
31
|
-
logger.info "Pushing wp-content/#{step}..."
|
32
|
-
host.download_dir local_wpcontent_path(step), remote_wpcontent_path(step)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def pull
|
40
|
-
informative_errors do
|
41
|
-
unless options.skip_db
|
42
|
-
logger.info "Pulling the DB..."
|
43
|
-
pull_db
|
44
|
-
end
|
45
|
-
|
46
|
-
remotely do |host|
|
47
|
-
%w(uploads themes plugins).each do |step|
|
48
|
-
unless options.send("skip_#{step}")
|
49
|
-
logger.info "Pulling wp-content/#{step}..."
|
50
|
-
host.upload_dir remote_wpcontent_path(step), local_wpcontent_path(step)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def push_db
|
60
|
-
local_mysql_dump_path = local_wpcontent_path("database_dump.sql")
|
61
|
-
remote_mysql_dump_path = remote_wpcontent_path("database_dump.sql")
|
62
|
-
|
63
|
-
locally do |host|
|
64
|
-
host.run "mysqldump", "--host=#{config.local.database.host}", "--user=#{config.local.database.user}", "--password=#{config.local.database.password}", config.local.database.name, :stdout => local_mysql_dump_path
|
65
|
-
if options.adapt_sql
|
66
|
-
Wordmove::SqlMover.new(local_mysql_dump_path, config.local, config.remote).move!
|
67
|
-
else
|
68
|
-
File.open(local_mysql_dump_path, 'a') do |file|
|
69
|
-
file.write "UPDATE #{options_table} SET option_value=\"#{config.remote.vhost}\" WHERE option_name=\"siteurl\" OR option_name=\"home\";\n"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
remotely do |host|
|
75
|
-
host.download_file local_mysql_dump_path, remote_mysql_dump_path
|
76
|
-
host.run "mysql", "--user=#{config.remote.database.user}", "--password=#{config.remote.database.password}", "--host=#{config.remote.database.host}", "--database=#{config.remote.database.name}", :stdin => remote_mysql_dump_path
|
77
|
-
host.run "rm", remote_mysql_dump_path
|
78
|
-
end
|
79
|
-
|
80
|
-
locally do |host|
|
81
|
-
host.run "rm", local_mysql_dump_path
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
|
86
|
-
def pull_db
|
87
|
-
local_mysql_dump_path = local_wpcontent_path("database_dump.sql")
|
88
|
-
remote_mysql_dump_path = remote_wpcontent_path("database_dump.sql")
|
89
|
-
|
90
|
-
remotely do |host|
|
91
|
-
host.run "mysqldump", "--host=#{config.remote.database.host}", "--user=#{config.remote.database.user}", "--password=#{config.remote.database.password}", config.remote.database.name, :stdout => remote_mysql_dump_path
|
92
|
-
host.upload_file remote_mysql_dump_path, local_mysql_dump_path
|
93
|
-
end
|
94
|
-
|
95
|
-
locally do |host|
|
96
|
-
if options.adapt_sql
|
97
|
-
Wordmove::SqlMover.new(local_mysql_dump_path, config.remote, config.local).move!
|
98
|
-
else
|
99
|
-
File.open(local_mysql_dump_path, 'a') do |file|
|
100
|
-
file.write "UPDATE #{options_table} SET option_value=\"#{config.local.vhost}\" WHERE option_name=\"siteurl\" OR option_name=\"home\";\n"
|
101
|
-
end
|
102
|
-
end
|
103
|
-
host.run "mysql", "--user=#{config.local.database.user}", "--password=#{config.local.database.password}", "--host=#{config.local.database.host}", "--database=#{config.local.database.name}", :stdin => local_mysql_dump_path
|
104
|
-
host.run "rm", local_mysql_dump_path
|
105
|
-
end
|
106
|
-
|
107
|
-
remotely do |host|
|
108
|
-
host.run "rm", remote_mysql_dump_path
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
def config
|
114
|
-
if @config.blank?
|
115
|
-
config_path = @options[:config] || "Movefile"
|
116
|
-
unless File.exists? config_path
|
117
|
-
raise Thor::Error, "Could not find a valid Movefile"
|
118
|
-
end
|
119
|
-
@config = Hashie::Mash.new(YAML::load(File.open(config_path)))
|
120
|
-
end
|
121
|
-
@config
|
122
|
-
end
|
123
|
-
|
124
|
-
def table_prefix
|
125
|
-
config.table_prefix || "wp_"
|
126
|
-
end
|
127
|
-
|
128
|
-
def options_table
|
129
|
-
table_prefix + "options"
|
130
|
-
end
|
131
|
-
|
132
|
-
def local_wpcontent_path(*args)
|
133
|
-
File.join(config.local.wordpress_path, "wp-content", *args)
|
134
|
-
end
|
135
|
-
|
136
|
-
def remote_wpcontent_path(*args)
|
137
|
-
File.join(config.remote.wordpress_path, "wp-content", *args)
|
138
|
-
end
|
139
|
-
|
140
|
-
def locally
|
141
|
-
host = LocalHost.new(config.local.merge(:logger => @logger))
|
142
|
-
yield host
|
143
|
-
host.close
|
144
|
-
end
|
145
|
-
|
146
|
-
def remotely
|
147
|
-
host = RemoteHost.new(config.remote.merge(:logger => @logger))
|
148
|
-
yield host
|
149
|
-
host.close
|
150
|
-
end
|
151
|
-
|
152
|
-
def informative_errors
|
153
|
-
yield
|
154
|
-
rescue Timeout::Error
|
155
|
-
logger.error "Connection timed out!"
|
156
|
-
puts "Timed out"
|
157
|
-
rescue Errno::EHOSTUNREACH
|
158
|
-
logger.error "Host unreachable!"
|
159
|
-
rescue Errno::ECONNREFUSED
|
160
|
-
logger.error "Connection refused!"
|
161
|
-
rescue Net::SSH::AuthenticationFailed
|
162
|
-
logger.error "SSH authentification failure, please double check the SSH credentials on your Movefile!"
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
166
|
-
end
|