wordmove 0.0.8 → 0.1.0.alpha

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.
@@ -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 :skip_db, :aliases => "-d", :type => :boolean
16
- method_option :skip_uploads, :aliases => "-u", :type => :boolean
17
- method_option :skip_themes, :aliases => "-t", :type => :boolean
18
- method_option :skip_plugins, :aliases => "-p", :type => :boolean
19
- method_option :verbose, :aliases => "-v", :type => :boolean
20
- method_option :config, :aliases => "-c"
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.new(options)
23
- deployer.pull
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 :skip_db, :aliases => "-d", :type => :boolean
28
- method_option :skip_uploads, :aliases => "-u", :type => :boolean
29
- method_option :skip_themes, :aliases => "-t", :type => :boolean
30
- method_option :skip_plugins, :aliases => "-p", :type => :boolean
31
- method_option :verbose, :aliases => "-v", :type => :boolean
32
- method_option :config, :aliases => "-c"
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.new(options)
35
- deployer.push
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
@@ -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 log(l, message)
13
- colors = [ :red, :green, :cyan ]
14
- if l <= level
15
- puts " " * [l-1, 0].max + message.send(colors[l])
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 info(message)
20
- log INFO, message
21
- end
22
-
23
- def verbose(message)
24
- log VERBOSE, message
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
@@ -1,3 +1,3 @@
1
1
  module Wordmove
2
- VERSION = "0.0.8"
2
+ VERSION = "0.1.0.alpha"
3
3
  end
@@ -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 'colored'
19
- gem.add_dependency 'escape'
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", "~> 3.0.0"
21
+ gem.add_dependency "activesupport"
25
22
  gem.add_dependency "i18n"
26
- gem.add_dependency "hashie"
23
+ gem.add_dependency "photocopier"
27
24
 
28
25
  gem.add_development_dependency "rspec"
29
26
  gem.add_development_dependency "cucumber"