wordmove 0.1.0.alpha → 0.1.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mdown +18 -20
- data/lib/wordmove/assets/import.php.erb +2 -2
- data/lib/wordmove/deployer/base.rb +10 -7
- data/lib/wordmove/deployer/ftp.rb +2 -1
- data/lib/wordmove/deployer/ssh.rb +2 -1
- data/lib/wordmove/generators/Movefile +6 -11
- data/lib/wordmove/sql_mover.rb +59 -0
- data/lib/wordmove/version.rb +1 -1
- data/spec/fixtures/Movefile +3 -8
- metadata +3 -4
- data/lib/wordmove/hosts/local_host.rb +0 -41
- data/lib/wordmove/hosts/remote_host.rb +0 -135
data/README.mdown
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Wordmove
|
2
2
|
|
3
|
-
Wordmove is a nice little gem that lets you automatically mirror local Wordpress
|
3
|
+
Wordmove is a nice little gem that lets you automatically mirror local Wordpress
|
4
|
+
installations and DB data back and forth from your local development machine to
|
5
|
+
the remote staging server. SSH and FTP connections are both supported.
|
4
6
|
|
5
7
|
Think of it like Capistrano for Wordpress, complete with push/pull capabilities.
|
6
8
|
|
@@ -9,7 +11,7 @@ Think of it like Capistrano for Wordpress, complete with push/pull capabilities.
|
|
9
11
|
That's easy:
|
10
12
|
|
11
13
|
```
|
12
|
-
gem install wordmove
|
14
|
+
gem install wordmove --pre
|
13
15
|
```
|
14
16
|
|
15
17
|
## Usage
|
@@ -33,7 +35,7 @@ local:
|
|
33
35
|
wordpress_path: "~/dev/sites/your_site"
|
34
36
|
database:
|
35
37
|
name: "database_name"
|
36
|
-
|
38
|
+
user: "user"
|
37
39
|
password: "password"
|
38
40
|
host: "host"
|
39
41
|
remote:
|
@@ -46,34 +48,30 @@ remote:
|
|
46
48
|
- Movefile
|
47
49
|
database:
|
48
50
|
name: "database_name"
|
49
|
-
|
51
|
+
user: "user"
|
50
52
|
password: "password"
|
51
53
|
host: "host"
|
52
54
|
ssh:
|
53
|
-
|
55
|
+
user: "user"
|
54
56
|
password: "password" # optional, will use SSH public key if available
|
55
57
|
host: "host"
|
56
58
|
port: 2202 # optional
|
57
59
|
```
|
58
|
-
## About SSH authentication
|
59
|
-
|
60
60
|
### If you have your local SSH public key already installed on the remote machine.. (recommended)
|
61
|
-
Just
|
62
|
-
|
63
|
-
### Else..
|
64
|
-
You need to have [`sshpass`](http://sourceforge.net/projects/sshpass/) on your machine. On Linux, install it with your standard package manager. On Mac, you can have it via [`brew`](https://github.com/mxcl/homebrew):
|
61
|
+
Just not use the `remote.ssh.password` field on your `Movefile`. Easy peasy.
|
65
62
|
|
66
|
-
|
67
|
-
|
68
|
-
```
|
63
|
+
### If you want to specify SSH password on the Movefile
|
64
|
+
Please take a look at the various gotchas of the underlying [`photocopier` gem](https://github.com/stefanoverna/photocopier#password-gotchas).
|
69
65
|
|
70
|
-
|
66
|
+
### How the heck you are able to sync the DB via FTP?
|
67
|
+
Thanks for asking. We basically upload via FTP a PHP script that performs the various
|
68
|
+
import/export operations. This script then gets executed via HTTP. Don't worry
|
69
|
+
too much about security though: the script is deleted just after the usage,
|
70
|
+
and can only be executed by `wordmove`, as each time it requires a pre-shared
|
71
|
+
one-time-password to be run.
|
71
72
|
|
72
|
-
|
73
|
-
*
|
74
|
-
* Wordmove requires `mysqldump` and `mysql` to be present in the remote host and executable by the SSH user;
|
75
|
-
* Wordmove requires `rsync` to be present and executable in your machine;
|
76
|
-
* As said above, you may also need `sshpass` on your machine;
|
73
|
+
* The dump script is the [`MYSQL-dump` PHP package](https://github.com/dg/MySQL-dump) by David Grudl;
|
74
|
+
* The import script used is the [BigDump](http://www.ozerov.de/bigdump/) library;
|
77
75
|
|
78
76
|
## License
|
79
77
|
|
@@ -10,7 +10,7 @@ if ($_GET['shared_key'] != $shared_key) {
|
|
10
10
|
// Database configuration
|
11
11
|
|
12
12
|
$db_server = '<%= escape_php db[:host] %>';
|
13
|
-
$db_username = '<%= escape_php db[:
|
13
|
+
$db_username = '<%= escape_php db[:username] %>';
|
14
14
|
$db_password = '<%= escape_php db[:password] %>';
|
15
15
|
$db_name = '<%= escape_php db[:name] %>';
|
16
16
|
|
@@ -48,7 +48,7 @@ $comment[]='/*!'; // Or add your own string to leave out oth
|
|
48
48
|
// See http://dev.mysql.com/doc/refman/5.0/en/charset-charsets.html for the full list
|
49
49
|
// Change this if you have problems with non-latin letters
|
50
50
|
|
51
|
-
$db_connection_charset = '<%= db[:charset] || utf8';
|
51
|
+
$db_connection_charset = '<%= db[:charset] || 'utf8' %>';
|
52
52
|
|
53
53
|
// Default query delimiter: this character at the line end tells Bigdump where a SQL statement ends
|
54
54
|
// Can be changed by DELIMITER statement in the dump file (normally used when defining procedures/functions)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext'
|
2
2
|
require 'wordmove/logger'
|
3
|
+
require 'wordmove/sql_mover'
|
3
4
|
require 'escape'
|
4
5
|
|
5
6
|
module Wordmove
|
@@ -51,17 +52,21 @@ module Wordmove
|
|
51
52
|
%w(uploads themes plugins).each do |task|
|
52
53
|
define_method "push_#{task}" do
|
53
54
|
logger.task "Pushing #{task.titleize}"
|
54
|
-
remote_put_directory(local_wpcontent_path(task), remote_wpcontent_path(task))
|
55
|
+
remote_put_directory(local_wpcontent_path(task), remote_wpcontent_path(task), paths_to_exclude)
|
55
56
|
end
|
56
57
|
|
57
58
|
define_method "pull_#{task}" do
|
58
59
|
logger.task "Pulling #{task.titleize}"
|
59
|
-
remote_get_directory(remote_wpcontent_path(task), local_wpcontent_path(task))
|
60
|
+
remote_get_directory(remote_wpcontent_path(task), local_wpcontent_path(task), paths_to_exclude)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
64
|
protected
|
64
65
|
|
66
|
+
def paths_to_exclude
|
67
|
+
options[:remote][:exclude] || Array.new
|
68
|
+
end
|
69
|
+
|
65
70
|
def run(command)
|
66
71
|
logger.task_step true, command
|
67
72
|
unless simulate?
|
@@ -97,16 +102,14 @@ module Wordmove
|
|
97
102
|
def adapt_sql(save_to_path, local, remote)
|
98
103
|
logger.task_step true, "adapt dump"
|
99
104
|
unless simulate?
|
100
|
-
|
101
|
-
file.write "UPDATE wp_options SET option_value=\"#{remote[:vhost]}\" WHERE option_name=\"siteurl\" OR option_name=\"home\";\n"
|
102
|
-
end
|
105
|
+
SqlMover.new(save_to_path, local, remote).move!
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
106
109
|
def mysql_dump_command(options, save_to_path)
|
107
110
|
arguments = [ "mysqldump" ]
|
108
111
|
arguments << "--host=#{options[:host]}" if options[:host].present?
|
109
|
-
arguments << "--user=#{options[:
|
112
|
+
arguments << "--user=#{options[:username]}" if options[:username].present?
|
110
113
|
arguments << "--password=#{options[:password]}" if options[:password].present?
|
111
114
|
arguments << "--default-character-set=#{options[:charset]}" if options[:charset].present?
|
112
115
|
arguments << options[:name]
|
@@ -116,7 +119,7 @@ module Wordmove
|
|
116
119
|
def mysql_import_command(dump_path, options)
|
117
120
|
arguments = [ "mysql" ]
|
118
121
|
arguments << "--host=#{options[:host]}" if options[:host].present?
|
119
|
-
arguments << "--user=#{options[:
|
122
|
+
arguments << "--user=#{options[:username]}" if options[:username].present?
|
120
123
|
arguments << "--password=#{options[:password]}" if options[:password].present?
|
121
124
|
arguments << "--database=#{options[:name]}"
|
122
125
|
Escape.shell_command(arguments) + " < #{dump_path}"
|
@@ -10,7 +10,8 @@ module Wordmove
|
|
10
10
|
def initialize(options)
|
11
11
|
super
|
12
12
|
ftp_options = options[:remote][:ftp]
|
13
|
-
@copier = Photocopier::FTP.new(ftp_options
|
13
|
+
@copier = Photocopier::FTP.new(ftp_options)
|
14
|
+
@copier.logger = logger
|
14
15
|
end
|
15
16
|
|
16
17
|
def push_db
|
@@ -3,30 +3,25 @@ local:
|
|
3
3
|
wordpress_path: "~/dev/sites/your_site"
|
4
4
|
database:
|
5
5
|
name: "database_name"
|
6
|
-
username: "
|
6
|
+
username: "user"
|
7
7
|
password: "password"
|
8
|
-
host: "
|
8
|
+
host: "127.0.0.1"
|
9
9
|
remote:
|
10
10
|
vhost: "http://remote.com"
|
11
11
|
wordpress_path: "/var/www/your_site"
|
12
|
-
exclude:
|
13
|
-
- .git
|
14
|
-
- .DS_Store
|
15
|
-
- .sass-cache
|
16
|
-
- Movefile
|
17
12
|
database:
|
18
13
|
name: "database_name"
|
19
|
-
username: "
|
14
|
+
username: "user"
|
20
15
|
password: "password"
|
21
16
|
host: "host"
|
22
|
-
ssh:
|
17
|
+
ssh: # also ftp is allowed
|
23
18
|
host: "host"
|
24
|
-
|
19
|
+
user: "user"
|
25
20
|
password: "password" # password is optional, will use public keys if available.
|
26
21
|
# port: 22 # Port is optional
|
27
22
|
#
|
28
23
|
# gateway:
|
29
24
|
# host: "host"
|
30
|
-
#
|
25
|
+
# user: "user"
|
31
26
|
# password: "password" # password is optional, will use public keys if available.
|
32
27
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Wordmove
|
2
|
+
|
3
|
+
class SqlMover
|
4
|
+
|
5
|
+
attr_accessor :sql_content
|
6
|
+
attr_reader :sql_path, :source_config, :dest_config
|
7
|
+
|
8
|
+
def initialize(sql_path, source_config, dest_config)
|
9
|
+
@sql_path = sql_path
|
10
|
+
@source_config = source_config
|
11
|
+
@dest_config = dest_config
|
12
|
+
end
|
13
|
+
|
14
|
+
def sql_content
|
15
|
+
@sql_content ||= File.open(sql_path).read
|
16
|
+
end
|
17
|
+
|
18
|
+
def move!
|
19
|
+
replace_vhost!
|
20
|
+
replace_wordpress_path!
|
21
|
+
write_sql!
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace_vhost!
|
25
|
+
replace_field!(:vhost)
|
26
|
+
end
|
27
|
+
|
28
|
+
def replace_wordpress_path!
|
29
|
+
replace_field!(:wordpress_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def replace_field!(field_sym)
|
33
|
+
source_field = source_config[field_sym]
|
34
|
+
dest_field = dest_config[field_sym]
|
35
|
+
if source_field && dest_field
|
36
|
+
serialized_replace!(source_field, dest_field)
|
37
|
+
simple_replace!(source_field, dest_field)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def serialized_replace!(source_field, dest_field)
|
42
|
+
length_delta = source_field.length - dest_field.length
|
43
|
+
|
44
|
+
sql_content.gsub!(/s:(\d+):"#{Regexp.escape(source_field)}/) do |match|
|
45
|
+
source_length = $1.to_i
|
46
|
+
dest_length = source_length - length_delta
|
47
|
+
"s:#{dest_length}:\"#{dest_field}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def simple_replace!(source_field, dest_field)
|
52
|
+
sql_content.gsub!(source_field, dest_field)
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_sql!
|
56
|
+
File.open(sql_path, 'w') {|f| f.write(sql_content) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/wordmove/version.rb
CHANGED
data/spec/fixtures/Movefile
CHANGED
@@ -3,24 +3,19 @@ local:
|
|
3
3
|
wordpress_path: "~/dev/sites/your_site"
|
4
4
|
database:
|
5
5
|
name: "database_name"
|
6
|
-
|
6
|
+
user: "user"
|
7
7
|
password: "password"
|
8
8
|
host: "host"
|
9
9
|
remote:
|
10
10
|
vhost: "http://remote.com"
|
11
11
|
wordpress_path: "/var/www/your_site"
|
12
|
-
exclude:
|
13
|
-
- .git
|
14
|
-
- .DS_Store
|
15
|
-
- .sass-cache
|
16
|
-
- Movefile
|
17
12
|
database:
|
18
13
|
name: "database_name"
|
19
|
-
|
14
|
+
user: "user"
|
20
15
|
password: "password"
|
21
16
|
host: "host"
|
22
17
|
ssh:
|
23
|
-
|
18
|
+
user: "user"
|
24
19
|
password: "password"
|
25
20
|
host: "host"
|
26
21
|
port: 30000
|
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: 0.1.0.
|
4
|
+
version: 0.1.0.beta
|
5
5
|
prerelease: 6
|
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: 2012-
|
12
|
+
date: 2012-11-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colored
|
@@ -183,9 +183,8 @@ files:
|
|
183
183
|
- lib/wordmove/deployer/ssh.rb
|
184
184
|
- lib/wordmove/generators/Movefile
|
185
185
|
- lib/wordmove/generators/movefile.rb
|
186
|
-
- lib/wordmove/hosts/local_host.rb
|
187
|
-
- lib/wordmove/hosts/remote_host.rb
|
188
186
|
- lib/wordmove/logger.rb
|
187
|
+
- lib/wordmove/sql_mover.rb
|
189
188
|
- lib/wordmove/version.rb
|
190
189
|
- pkg/wordmove-0.0.1.gem
|
191
190
|
- pkg/wordmove-0.0.2.gem
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'escape'
|
2
|
-
|
3
|
-
module Wordmove
|
4
|
-
class LocalHost
|
5
|
-
|
6
|
-
attr_reader :options
|
7
|
-
attr_reader :logger
|
8
|
-
attr_reader :ssh_extras
|
9
|
-
|
10
|
-
def initialize(options = {})
|
11
|
-
@options = Hashie::Mash.new(options)
|
12
|
-
@logger = @options[:logger]
|
13
|
-
end
|
14
|
-
|
15
|
-
def run(*args)
|
16
|
-
command = shell_command(*args)
|
17
|
-
logger.verbose "Executing locally #{command}"
|
18
|
-
unless system(command)
|
19
|
-
raise Thor::Error, "Error executing \"#{command}\""
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def close
|
24
|
-
end
|
25
|
-
|
26
|
-
protected
|
27
|
-
|
28
|
-
def shell_command(*args)
|
29
|
-
options = args.extract_options!
|
30
|
-
command = Escape.shell_command(args)
|
31
|
-
if options[:stdin]
|
32
|
-
command += " < #{options[:stdin]}"
|
33
|
-
end
|
34
|
-
if options[:stdout]
|
35
|
-
command += " > #{options[:stdout]}"
|
36
|
-
end
|
37
|
-
command
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
require 'net/ssh'
|
2
|
-
require 'net/scp'
|
3
|
-
require 'net/ssh/gateway'
|
4
|
-
|
5
|
-
module Wordmove
|
6
|
-
class RemoteHost < LocalHost
|
7
|
-
|
8
|
-
alias :locally_run :run
|
9
|
-
|
10
|
-
attr_reader :session
|
11
|
-
|
12
|
-
def initialize(options = {})
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
16
|
-
def session
|
17
|
-
if options.ssh.nil?
|
18
|
-
raise Thor::Error, "No SSH credentials provided on Movefile!"
|
19
|
-
end
|
20
|
-
|
21
|
-
ssh_extras = {}
|
22
|
-
[ :port, :password ].each do |p|
|
23
|
-
ssh_extras.merge!( { p => options.ssh[p] } ) if options.ssh[p]
|
24
|
-
end
|
25
|
-
|
26
|
-
if options.ssh.gateway.nil?
|
27
|
-
logger.verbose "Connecting to #{options.ssh.host}..." unless @session.present?
|
28
|
-
@session ||= Net::SSH.start(options.ssh.host, options.ssh.username, ssh_extras)
|
29
|
-
else
|
30
|
-
logger.verbose "Connecting to #{options.ssh.host} through the gateway..." unless @session.present?
|
31
|
-
@session ||= gateway.ssh(options.ssh.host, options.ssh.username, ssh_extras)
|
32
|
-
end
|
33
|
-
|
34
|
-
@session
|
35
|
-
end
|
36
|
-
|
37
|
-
def gateway
|
38
|
-
if options.ssh.gateway.nil?
|
39
|
-
raise Thor::Error, "No SSH credentials provided on Movefile!"
|
40
|
-
end
|
41
|
-
|
42
|
-
ssh_extras = {}
|
43
|
-
[ :port, :password ].each do |p|
|
44
|
-
ssh_extras.merge!( { p => options.ssh.gateway[p] } ) if options.ssh.gateway[p]
|
45
|
-
end
|
46
|
-
|
47
|
-
logger.verbose "Connecting to #{options.ssh.gateway.host}..." unless @gateway.present?
|
48
|
-
@gateway ||= Net::SSH::Gateway.new(options.ssh.gateway.host, options.ssh.gateway.username, ssh_extras )
|
49
|
-
|
50
|
-
@gateway
|
51
|
-
end
|
52
|
-
|
53
|
-
def close
|
54
|
-
session.close
|
55
|
-
if options.ssh.gateway.present?
|
56
|
-
gateway.close(session.transport.port)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def upload_file(source_file, destination_file)
|
61
|
-
logger.verbose "Copying remote #{source_file} to #{destination_file}..."
|
62
|
-
session.scp.download! source_file, destination_file
|
63
|
-
end
|
64
|
-
|
65
|
-
def download_file(source_file, destination_file)
|
66
|
-
logger.verbose "Copying local #{source_file} to #{destination_file}..."
|
67
|
-
session.scp.upload! source_file, destination_file
|
68
|
-
end
|
69
|
-
|
70
|
-
def download_dir(source_dir, destination_dir)
|
71
|
-
destination_dir = ":#{destination_dir}"
|
72
|
-
destination_dir = "#{options.ssh.username}@#{destination_dir}" if options.ssh.username
|
73
|
-
rsync "#{source_dir}/", destination_dir
|
74
|
-
end
|
75
|
-
|
76
|
-
def upload_dir(source_dir, destination_dir)
|
77
|
-
source_dir = ":#{source_dir}/"
|
78
|
-
rsync source_dir, destination_dir
|
79
|
-
end
|
80
|
-
|
81
|
-
def run(*args)
|
82
|
-
command = shell_command(*args)
|
83
|
-
logger.verbose "Executing remotely #{command}"
|
84
|
-
session.exec!(command)
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def rsync(source_dir, destination_dir)
|
90
|
-
|
91
|
-
exclude_file = Tempfile.new('exclude')
|
92
|
-
exclude_file.write(options.exclude.join("\n"))
|
93
|
-
exclude_file.close
|
94
|
-
|
95
|
-
arguments = [ "-azLKO" ]
|
96
|
-
|
97
|
-
if options.ssh && (options.ssh.port || options.ssh.password || options.ssh.gateway)
|
98
|
-
|
99
|
-
remote_shell_arguments = []
|
100
|
-
|
101
|
-
if options.ssh.gateway
|
102
|
-
host = options.ssh.gateway.host
|
103
|
-
host = "#{options.ssh.gateway.username}@#{host}" if options.ssh.gateway.username
|
104
|
-
remote_shell_arguments << [ "ssh", host ]
|
105
|
-
if options.ssh.gateway.port
|
106
|
-
remote_shell_arguments << [ "-p", options.ssh.gateway.port ]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
remote_shell_arguments << [ "ssh" ]
|
111
|
-
|
112
|
-
if options.ssh.port
|
113
|
-
remote_shell_arguments << [ "-p", options.ssh.port ]
|
114
|
-
end
|
115
|
-
|
116
|
-
if options.ssh.password
|
117
|
-
remote_shell_arguments = [ "sshpass", "-p", options.ssh.password ] + remote_shell_arguments
|
118
|
-
end
|
119
|
-
|
120
|
-
host = options.ssh.host
|
121
|
-
host = "#{options.ssh.username}@#{host}" if options.ssh.username
|
122
|
-
remote_shell_arguments << host
|
123
|
-
|
124
|
-
arguments << [ "-e", remote_shell_arguments.join(" ") ]
|
125
|
-
end
|
126
|
-
|
127
|
-
arguments << [ "--exclude-from=#{exclude_file.path}", "--delete", source_dir, destination_dir ]
|
128
|
-
arguments.flatten!
|
129
|
-
locally_run "rsync", *arguments
|
130
|
-
|
131
|
-
exclude_file.unlink
|
132
|
-
end
|
133
|
-
|
134
|
-
end
|
135
|
-
end
|