wordmove 0.0.8 → 0.1.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/wordmove +2 -0
- data/lib/wordmove/assets/dump.php.erb +182 -0
- data/lib/wordmove/assets/import.php.erb +1076 -0
- data/lib/wordmove/cli.rb +27 -18
- data/lib/wordmove/deployer/base.rb +135 -0
- data/lib/wordmove/deployer/ftp.rb +117 -0
- data/lib/wordmove/deployer/ssh.rb +73 -0
- data/lib/wordmove/logger.rb +14 -22
- data/lib/wordmove/version.rb +1 -1
- data/wordmove.gemspec +4 -7
- metadata +13 -57
- data/lib/wordmove/deployer.rb +0 -149
data/lib/wordmove/cli.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'wordmove/generators/movefile'
|
3
|
-
require 'wordmove/deployer'
|
4
|
-
|
3
|
+
require 'wordmove/deployer/base'
|
5
4
|
|
6
5
|
module Wordmove
|
7
6
|
class CLI < Thor
|
@@ -12,27 +11,37 @@ module Wordmove
|
|
12
11
|
end
|
13
12
|
|
14
13
|
desc "pull", "Pulls WP data from remote host to the local machine"
|
15
|
-
method_option :
|
16
|
-
method_option :
|
17
|
-
method_option :
|
18
|
-
method_option :
|
19
|
-
method_option :verbose,
|
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 :config, :aliases => "-c"
|
21
21
|
def pull
|
22
|
-
deployer = Wordmove::Deployer.
|
23
|
-
|
22
|
+
deployer = Wordmove::Deployer::Base.deployer_for(options)
|
23
|
+
%w(db uploads themes plugins).map(&:to_sym).each do |task|
|
24
|
+
if options[task]
|
25
|
+
deployer.send("pull_#{task}")
|
26
|
+
end
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
30
|
desc "push", "Pushes WP data from local machine to remote host"
|
27
|
-
method_option :
|
28
|
-
method_option :
|
29
|
-
method_option :
|
30
|
-
method_option :
|
31
|
-
method_option :verbose,
|
32
|
-
method_option :
|
31
|
+
method_option :db, :aliases => "-d", :type => :boolean
|
32
|
+
method_option :uploads, :aliases => "-u", :type => :boolean
|
33
|
+
method_option :themes, :aliases => "-t", :type => :boolean
|
34
|
+
method_option :plugins, :aliases => "-p", :type => :boolean
|
35
|
+
method_option :verbose, :aliases => "-v", :type => :boolean
|
36
|
+
method_option :simulate, :aliases => "-s", :type => :boolean
|
37
|
+
method_option :config, :aliases => "-c"
|
33
38
|
def push
|
34
|
-
deployer = Wordmove::Deployer.
|
35
|
-
|
39
|
+
deployer = Wordmove::Deployer::Base.deployer_for(options)
|
40
|
+
%w(db uploads themes plugins).map(&:to_sym).each do |task|
|
41
|
+
if options[task]
|
42
|
+
deployer.send("push_#{task}")
|
43
|
+
end
|
44
|
+
end
|
36
45
|
end
|
37
46
|
|
38
47
|
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'wordmove/logger'
|
3
|
+
require 'escape'
|
4
|
+
|
5
|
+
module Wordmove
|
6
|
+
module Deployer
|
7
|
+
|
8
|
+
class Base
|
9
|
+
attr_reader :options
|
10
|
+
attr_reader :logger
|
11
|
+
|
12
|
+
def self.deployer_for(options)
|
13
|
+
options = fetch_movefile(options[:config]).merge(options)
|
14
|
+
recursive_symbolize_keys!(options)
|
15
|
+
if options[:remote][:ftp]
|
16
|
+
require 'wordmove/deployer/ftp'
|
17
|
+
FTP.new(options)
|
18
|
+
elsif options[:remote][:ssh]
|
19
|
+
require 'wordmove/deployer/ssh'
|
20
|
+
SSH.new(options)
|
21
|
+
else
|
22
|
+
raise Thor::Error, "No valid adapter found."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.fetch_movefile(path)
|
27
|
+
path ||= "Movefile"
|
28
|
+
unless File.exists?(path)
|
29
|
+
raise Thor::Error, "Could not find a valid Movefile"
|
30
|
+
end
|
31
|
+
YAML::load(File.open(path))
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(options = {})
|
35
|
+
@options = options
|
36
|
+
@logger = Logger.new(STDOUT)
|
37
|
+
@logger.level = Logger::DEBUG
|
38
|
+
end
|
39
|
+
|
40
|
+
def push_db;
|
41
|
+
logger.task "Pushing Database"
|
42
|
+
end
|
43
|
+
|
44
|
+
def pull_db
|
45
|
+
logger.task "Pulling Database"
|
46
|
+
end
|
47
|
+
|
48
|
+
def remote_get_directory(directory); end
|
49
|
+
def remote_put_directory(directory); end
|
50
|
+
|
51
|
+
%w(uploads themes plugins).each do |task|
|
52
|
+
define_method "push_#{task}" do
|
53
|
+
logger.task "Pushing #{task.titleize}"
|
54
|
+
remote_put_directory(local_wpcontent_path(task), remote_wpcontent_path(task))
|
55
|
+
end
|
56
|
+
|
57
|
+
define_method "pull_#{task}" do
|
58
|
+
logger.task "Pulling #{task.titleize}"
|
59
|
+
remote_get_directory(remote_wpcontent_path(task), local_wpcontent_path(task))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def run(command)
|
66
|
+
logger.task_step true, command
|
67
|
+
unless simulate?
|
68
|
+
system(command)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def download(url, local_path)
|
73
|
+
logger.task_step true, "download #{url}"
|
74
|
+
unless simulate?
|
75
|
+
open(local_path, 'w') do |file|
|
76
|
+
file << open(url).read
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def simulate?
|
82
|
+
options[:simulate]
|
83
|
+
end
|
84
|
+
|
85
|
+
def local_wpcontent_path(*args)
|
86
|
+
File.join(options[:local][:wordpress_path], "wp-content", *args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def remote_wpcontent_path(*args)
|
90
|
+
File.join(options[:remote][:wordpress_path], "wp-content", *args)
|
91
|
+
end
|
92
|
+
|
93
|
+
def remote_wpcontent_url(*args)
|
94
|
+
options[:remote][:vhost] + File.join("/wp-content", *args)
|
95
|
+
end
|
96
|
+
|
97
|
+
def adapt_sql(save_to_path, local, remote)
|
98
|
+
logger.task_step true, "adapt dump"
|
99
|
+
unless simulate?
|
100
|
+
File.open(save_to_path, 'a') do |file|
|
101
|
+
file.write "UPDATE wp_options SET option_value=\"#{remote[:vhost]}\" WHERE option_name=\"siteurl\" OR option_name=\"home\";\n"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def mysql_dump_command(options, save_to_path)
|
107
|
+
arguments = [ "mysqldump" ]
|
108
|
+
arguments << "--host=#{options[:host]}" if options[:host].present?
|
109
|
+
arguments << "--user=#{options[:user]}" if options[:user].present?
|
110
|
+
arguments << "--password=#{options[:password]}" if options[:password].present?
|
111
|
+
arguments << "--default-character-set=#{options[:charset]}" if options[:charset].present?
|
112
|
+
arguments << options[:name]
|
113
|
+
Escape.shell_command(arguments) + " > #{save_to_path}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def mysql_import_command(dump_path, options)
|
117
|
+
arguments = [ "mysql" ]
|
118
|
+
arguments << "--host=#{options[:host]}" if options[:host].present?
|
119
|
+
arguments << "--user=#{options[:user]}" if options[:user].present?
|
120
|
+
arguments << "--password=#{options[:password]}" if options[:password].present?
|
121
|
+
arguments << "--database=#{options[:name]}"
|
122
|
+
Escape.shell_command(arguments) + " < #{dump_path}"
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def self.recursive_symbolize_keys! hash
|
128
|
+
hash.symbolize_keys!
|
129
|
+
hash.values.select{|v| v.is_a? Hash}.each{|h| recursive_symbolize_keys!(h)}
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'wordmove/deployer/base'
|
2
|
+
require 'photocopier/ftp'
|
3
|
+
require 'erb'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
module Wordmove
|
7
|
+
module Deployer
|
8
|
+
class FTP < Base
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
super
|
12
|
+
ftp_options = options[:remote][:ftp]
|
13
|
+
@copier = Photocopier::FTP.new(ftp_options.merge(logger: logger))
|
14
|
+
end
|
15
|
+
|
16
|
+
def push_db
|
17
|
+
super
|
18
|
+
|
19
|
+
remote_import_script_path = remote_wpcontent_path("import.php")
|
20
|
+
local_dump_path = local_wpcontent_path("dump.sql")
|
21
|
+
remote_dump_path = remote_wpcontent_path("dump.sql")
|
22
|
+
|
23
|
+
# dump local mysql into file
|
24
|
+
run mysql_dump_command(options[:local][:database], local_dump_path)
|
25
|
+
# gsub sql
|
26
|
+
adapt_sql(local_dump_path, options[:local], options[:remote])
|
27
|
+
# upload it
|
28
|
+
remote_put(local_dump_path, remote_dump_path)
|
29
|
+
|
30
|
+
# generate a secure one-time password
|
31
|
+
one_time_password = SecureRandom.hex(40)
|
32
|
+
# generate import script
|
33
|
+
import_script = generate_import_script(options[:remote][:database], one_time_password)
|
34
|
+
# upload import script
|
35
|
+
remote_put(import_script, remote_import_script_path)
|
36
|
+
# run import script
|
37
|
+
import_url = "#{remote_wpcontent_url("import.php")}?shared_key=#{one_time_password}&start=1&foffset=0&totalqueries=0&fn=dump.sql"
|
38
|
+
download(import_url, local_dump_path + "_")
|
39
|
+
|
40
|
+
# remove script remotely
|
41
|
+
remote_delete(remote_import_script_path)
|
42
|
+
# remove dump remotely
|
43
|
+
remote_delete(remote_dump_path)
|
44
|
+
# and locally
|
45
|
+
run "rm #{local_dump_path}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def pull_db
|
49
|
+
super
|
50
|
+
|
51
|
+
remote_dump_script = remote_wpcontent_path("dump.php")
|
52
|
+
local_dump_path = local_wpcontent_path("dump.sql")
|
53
|
+
|
54
|
+
# generate a secure one-time password
|
55
|
+
one_time_password = SecureRandom.hex(40)
|
56
|
+
|
57
|
+
# generate dump script
|
58
|
+
dump_script = generate_dump_script(options[:remote][:database], one_time_password)
|
59
|
+
# upload the dump script
|
60
|
+
remote_put(dump_script, remote_dump_script)
|
61
|
+
# download the resulting dump (using the password)
|
62
|
+
dump_url = "#{remote_wpcontent_url("dump.php")}?shared_key=#{one_time_password}"
|
63
|
+
download(dump_url, local_dump_path)
|
64
|
+
|
65
|
+
# gsub sql
|
66
|
+
adapt_sql(local_dump_path, options[:remote], options[:local])
|
67
|
+
# import locally
|
68
|
+
run mysql_import_command(local_dump_path, options[:local][:database])
|
69
|
+
|
70
|
+
# remove it remotely
|
71
|
+
remote_delete(remote_dump_script)
|
72
|
+
# and locally
|
73
|
+
run "rm #{local_dump_path}"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
%w(get get_directory put_directory delete).each do |command|
|
79
|
+
define_method "remote_#{command}" do |*args|
|
80
|
+
logger.task_step false, "#{command}: #{args.join(" ")}"
|
81
|
+
unless simulate?
|
82
|
+
@copier.send(command, *args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def remote_put(thing, path)
|
88
|
+
if File.exists?(thing)
|
89
|
+
logger.task_step false, "copying #{thing} to #{path}"
|
90
|
+
else
|
91
|
+
logger.task_step false, "write #{path}"
|
92
|
+
end
|
93
|
+
unless simulate?
|
94
|
+
@copier.put(thing, path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def escape_php(string)
|
99
|
+
# replaces \ with \\
|
100
|
+
# replaces ' with \'
|
101
|
+
string.gsub('\\','\\\\\\').gsub(/[']/, '\\\\\'')
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_dump_script(db, password)
|
105
|
+
template = ERB.new File.read(File.join(File.dirname(__FILE__), "../assets/dump.php.erb"))
|
106
|
+
template.result(binding)
|
107
|
+
end
|
108
|
+
|
109
|
+
def generate_import_script(db, password)
|
110
|
+
template = ERB.new File.read(File.join(File.dirname(__FILE__), "../assets/import.php.erb"))
|
111
|
+
template.result(binding)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'wordmove/deployer/base'
|
2
|
+
require 'photocopier/ssh'
|
3
|
+
|
4
|
+
module Wordmove
|
5
|
+
module Deployer
|
6
|
+
class SSH < Base
|
7
|
+
def initialize(options)
|
8
|
+
super
|
9
|
+
ssh_options = options[:remote][:ssh]
|
10
|
+
@copier = Photocopier::SSH.new(ssh_options.merge(logger: logger))
|
11
|
+
end
|
12
|
+
|
13
|
+
def push_db
|
14
|
+
super
|
15
|
+
|
16
|
+
local_dump_path = local_wpcontent_path("dump.sql")
|
17
|
+
remote_dump_path = remote_wpcontent_path("dump.sql")
|
18
|
+
|
19
|
+
# dump local mysql into file
|
20
|
+
run mysql_dump_command(options[:local][:database], local_dump_path)
|
21
|
+
# gsub sql
|
22
|
+
adapt_sql(local_dump_path, options[:local], options[:remote])
|
23
|
+
# upload it
|
24
|
+
remote_put(local_dump_path, remote_dump_path)
|
25
|
+
# import it remotely
|
26
|
+
remote_run mysql_import_command(remote_dump_path, options[:remote][:database])
|
27
|
+
# remove it remotely
|
28
|
+
remote_delete(remote_dump_path)
|
29
|
+
# and locally
|
30
|
+
run "rm #{local_dump_path}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def pull_db
|
34
|
+
super
|
35
|
+
|
36
|
+
local_dump_path = local_wpcontent_path("dump.sql")
|
37
|
+
remote_dump_path = remote_wpcontent_path("dump.sql")
|
38
|
+
|
39
|
+
# dump remote db into file
|
40
|
+
remote_run mysql_dump_command(options[:remote][:database], remote_dump_path)
|
41
|
+
# download remote dump
|
42
|
+
remote_get(remote_dump_path, local_dump_path)
|
43
|
+
# gsub sql
|
44
|
+
adapt_sql(local_dump_path, options[:remote], options[:local])
|
45
|
+
# import locally
|
46
|
+
run mysql_import_command(local_dump_path, options[:local][:database])
|
47
|
+
# remove it remotely
|
48
|
+
remote_delete(remote_dump_path)
|
49
|
+
# and locally
|
50
|
+
run "rm #{local_dump_path}"
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
%w(get put get_directory put_directory delete).each do |command|
|
56
|
+
define_method "remote_#{command}" do |*args|
|
57
|
+
logger.task_step false, "#{command}: #{args.join(" ")}"
|
58
|
+
unless simulate?
|
59
|
+
@copier.send(command, *args)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def remote_run(command)
|
65
|
+
logger.task_step false, command
|
66
|
+
unless simulate?
|
67
|
+
@copier.session.exec! command
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/wordmove/logger.rb
CHANGED
@@ -1,31 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'colored'
|
4
|
+
require 'logger'
|
2
5
|
|
3
6
|
module Wordmove
|
4
|
-
class Logger
|
5
|
-
|
6
|
-
ERROR = 0
|
7
|
-
INFO = 1
|
8
|
-
VERBOSE = 2
|
9
|
-
|
10
|
-
attr_accessor :level
|
7
|
+
class Logger < ::Logger
|
11
8
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
9
|
+
def task(title)
|
10
|
+
puts ""
|
11
|
+
title = " ✓ #{title} "
|
12
|
+
puts "▬" * 2 + title.green + "▬" * (70 - title.length)
|
17
13
|
end
|
18
14
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def error(message)
|
28
|
-
log ERROR, message
|
15
|
+
def task_step(local_step, title)
|
16
|
+
if local_step
|
17
|
+
puts " local".cyan + " | ".black + title
|
18
|
+
else
|
19
|
+
puts " remote".yellow + " | ".black + title
|
20
|
+
end
|
29
21
|
end
|
30
22
|
|
31
23
|
end
|
data/lib/wordmove/version.rb
CHANGED
data/wordmove.gemspec
CHANGED
@@ -15,15 +15,12 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Wordmove::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency
|
19
|
-
gem.add_dependency
|
20
|
-
gem.add_dependency 'rake'
|
21
|
-
gem.add_dependency 'net-ssh'
|
22
|
-
gem.add_dependency 'net-scp'
|
18
|
+
gem.add_dependency "colored"
|
19
|
+
gem.add_dependency "rake"
|
23
20
|
gem.add_dependency "thor"
|
24
|
-
gem.add_dependency "activesupport"
|
21
|
+
gem.add_dependency "activesupport"
|
25
22
|
gem.add_dependency "i18n"
|
26
|
-
gem.add_dependency "
|
23
|
+
gem.add_dependency "photocopier"
|
27
24
|
|
28
25
|
gem.add_development_dependency "rspec"
|
29
26
|
gem.add_development_dependency "cucumber"
|