wordpress-deploy 1.0.0.alpha1 → 1.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +26 -1
  2. data/.rspec +1 -0
  3. data/.rvmrc +48 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +2 -5
  6. data/Gemfile.lock +40 -28
  7. data/Guardfile +24 -0
  8. data/README.md +22 -7
  9. data/bin/wp-deploy +1 -4
  10. data/lib/wordpress_deploy.rb +22 -48
  11. data/lib/wordpress_deploy/cli/helpers.rb +27 -14
  12. data/lib/wordpress_deploy/cli/utility.rb +86 -37
  13. data/lib/wordpress_deploy/database/mysql.rb +22 -136
  14. data/lib/wordpress_deploy/environment.rb +68 -0
  15. data/lib/wordpress_deploy/errors.rb +54 -0
  16. data/lib/wordpress_deploy/logger.rb +28 -50
  17. data/lib/wordpress_deploy/transfer_protocols/ftp.rb +305 -0
  18. data/lib/wordpress_deploy/version.rb +1 -1
  19. data/lib/wordpress_deploy/wordpress/configuration.rb +196 -0
  20. data/spec/data/ftp.yml +4 -0
  21. data/spec/data/wp-config-sample.php +90 -0
  22. data/spec/data/wp-config.yml +128 -0
  23. data/spec/database/mysql_spec.rb +93 -0
  24. data/spec/environment_spec.rb +35 -0
  25. data/spec/spec_helper.rb +36 -1
  26. data/spec/transfer_protocols/ftp_spec.rb +193 -0
  27. data/spec/wordpress/configuration_spec.rb +202 -0
  28. data/wordpress_deploy.gemspec +13 -10
  29. metadata +63 -47
  30. data/lib/wordpress_deploy/config.rb +0 -68
  31. data/lib/wordpress_deploy/database/base.rb +0 -53
  32. data/lib/wordpress_deploy/pipeline.rb +0 -110
  33. data/lib/wordpress_deploy/storage/base.rb +0 -99
  34. data/lib/wordpress_deploy/storage/ftp.rb +0 -133
  35. data/lib/wordpress_deploy/storage/local.rb +0 -82
  36. data/lib/wordpress_deploy/storage/scp.rb +0 -99
  37. data/lib/wordpress_deploy/storage/sftp.rb +0 -108
  38. data/spec/config_spec.rb +0 -16
@@ -1,53 +1,102 @@
1
- # encoding: utf-8
2
-
3
1
  ##
4
2
  # Build the WordpressDeploy Command Line Interface using Thor
3
+ #
5
4
  module WordpressDeploy
6
- module CLI
5
+ module Cli
7
6
  class Utility < Thor
8
7
  include Thor::Actions
9
8
 
10
- method_option :base, type: :string, aliases: ['-b']
11
- method_option :env, type: :string, required: true, default: 'production', aliases: ['-e']
9
+ # These options apply to all commands
10
+ class_option :root_dir, type: :string, default: '', aliases: '-r'
11
+ class_option :wp_dir, type: :string, default: '', aliases: '-w'
12
+ class_option :environment, type: :string, default: 'production', aliases: '-e'
13
+ class_option :verbose, type: :boolean, default: false, aliases: '-v'
14
+
15
+
12
16
  desc "generate", "Generate the wp-config.php file. Accepted environments are production or development."
13
17
  def generate
14
- unless %w{production development}.include? options[:env]
15
- raise Errors::CLI::SystemCallError.wrap(e, <<-EOS)
16
- Failed to execute system command on #{ RUBY_PLATFORM }
17
- Command was: #{ command }
18
- EOS
19
- end
20
-
21
- Config.environment = options[:env]
22
- config = Config.wp_config[Config.env]
23
-
24
- # Salt the wp-config.php file if none are provided
25
- Config.salt_keys.each do |key|
26
- config[key] = Config.salt_array.sample(64).join("") unless config.has_key? key
27
- end
28
-
29
- out = File.open(Config.wp_config_output, 'w')
30
- File.open(Config.wp_config_sample, 'r') do |file|
31
- file.each_line do |line|
32
- match = /^define\(['"](?<parameter>\w*)['"]/.match(line)
33
- unless match.nil?
34
- param = match[:parameter]
35
- if config.has_key?(param)
36
- # The Wordpress Config file has the key
37
- # So now set the value from the config
38
- line.gsub!(/['"]((?!#{param})[\s\w\d\!\@\#\$\%\^\&\*\-\(\)]+)?['"]/, "'#{config[param]}'")
39
- end
40
- end
41
- out.puts(line)
42
- end
43
- end
44
- end
18
+ ##
19
+ # Set Logger into verbose mode (if the user requested it)
20
+ Logger.verbose = options[:verbose]
21
+
22
+ # Set environment options
23
+ Environment.set_options options
24
+
25
+ # Create a configuration file
26
+ config = Wordpress::Configuration.new
45
27
 
28
+ # Save the configuration file
29
+ config.save!
30
+ rescue => err
31
+ Logger.error Errors::Cli::Utility::Error.wrap(err)
46
32
 
47
- desc "deploy", "Deploy #{Config.sites_dir} via FTP to #{Config.ftp_config['hostname']}"
33
+ # Exit with an error
34
+ exit(1)
35
+ end
36
+
37
+ desc "deploy", "Deploy via FTP to configuration hostname."
48
38
  def deploy
39
+ ##
40
+ # Set Logger into verbose mode (if the user requested it)
41
+ Logger.verbose = options[:verbose]
42
+
43
+ # Set environment options
44
+ Environment.set_options options
45
+
46
+ # Create a new FTP client for sending the files
47
+ ftp_client = TransferProtocols::Ftp.new options[:environment]
48
+
49
+ # Now transmit the files
50
+ ftp_client.transmit!
51
+
52
+ rescue => err
53
+ Logger.error Errors::Cli::Utility::Error.wrap(err)
49
54
 
55
+ # Exit with an error
56
+ exit(1)
57
+ ensure
58
+ puts "Closing connection.".colorize(color: :red, background: :yellow) if ftp_client.close
59
+ end
60
+
61
+ desc "backup", "Pull down the remote files over FTP."
62
+ def backup
63
+ ##
64
+ # Set Logger into verbose mode (if the user requested it)
65
+ Logger.verbose = options[:verbose]
66
+
67
+ # Set environment options
68
+ Environment.set_options options
69
+
70
+ # Create a new FTP client for receiving the files
71
+ ftp_client = TransferProtocols::Ftp.new options[:environment]
72
+
73
+ # Now receive the files
74
+ ftp_client.receive!
75
+
76
+ rescue => err
77
+ Logger.error Errors::Cli::Utility::Error.wrap(err)
78
+
79
+ # Exit with an error
80
+ exit(1)
81
+ ensure
82
+ puts "Closing connection.".colorize(color: :red, background: :yellow) if ftp_client.close
83
+ end
84
+
85
+ desc "mirror", "Mirror database between two locations"
86
+ def mirror(from, to)
87
+ ##
88
+ # Set Logger into verbose mode (if the user requested it)
89
+ Logger.verbose = options[:verbose]
90
+
91
+ # Set environment options
92
+ Environment.set_options options
93
+ rescue => err
94
+ Logger.error Errors::Cli::Utility::Error.wrap(err)
95
+
96
+ # Exit with an error
97
+ exit(1)
50
98
  end
51
99
  end
52
100
  end
53
101
  end
102
+
@@ -1,159 +1,45 @@
1
- # encoding: utf-8
1
+ require 'tmpdir'
2
2
 
3
3
  module WordpressDeploy
4
4
  module Database
5
- class MySQL < Base
6
5
 
7
- ##
8
- # Name of the database that needs to get dumped
9
- # To dump all databases, set this to `:all` or leave blank.
10
- attr_accessor :name
11
-
12
- ##
13
- # Credentials for the specified database
14
- attr_accessor :username, :password
15
-
16
- ##
17
- # Connectivity options
18
- attr_accessor :host, :port, :socket
19
-
20
- ##
21
- # Tables to skip while dumping the database
22
- attr_accessor :skip_tables
23
-
24
- ##
25
- # Tables to dump, tables that aren't specified won't get dumped
26
- attr_accessor :only_tables
27
-
28
- ##
29
- # Additional "mysqldump" options
30
- attr_accessor :additional_options
6
+ class MySql
7
+ include WordpressDeploy::Cli::Helpers
31
8
 
32
- ##
33
- # Path to mysqldump utility (optional)
34
- attr_accessor :mysqldump_utility
35
-
36
- attr_deprecate :utility_path, :version => '3.0.21',
37
- :replacement => :mysqldump_utility
38
-
39
- ##
40
- # Creates a new instance of the MySQL adapter object
41
- def initialize(model, &block)
42
- super(model)
43
-
44
- @skip_tables ||= Array.new
45
- @only_tables ||= Array.new
46
- @additional_options ||= Array.new
47
-
48
- instance_eval(&block) if block_given?
9
+ attr_reader :configuration
49
10
 
50
- @name ||= :all
51
- @mysqldump_utility ||= utility(:mysqldump)
11
+ def initialize
12
+ @yaml = YAML.load_file(File.join(Environment.config_dir, "wp-config.yml"))
52
13
  end
53
14
 
54
- ##
55
- # Performs the mysqldump command and outputs the
56
- # data to the specified path based on the 'trigger'
57
- def perform!
58
- super
59
-
60
- pipeline = Pipeline.new
61
- dump_ext = 'sql'
62
-
63
- pipeline << mysqldump
64
- if @model.compressor
65
- @model.compressor.compress_with do |command, ext|
66
- pipeline << command
67
- dump_ext << ext
68
- end
69
- end
70
- pipeline << "cat > '#{ File.join(@dump_path, dump_filename) }.#{ dump_ext }'"
71
-
72
- pipeline.run
73
- if pipeline.success?
74
- Logger.message "#{ database_name } Complete!"
75
- else
76
- raise Errors::Database::PipelineError,
77
- "#{ database_name } Dump Failed!\n" +
78
- pipeline.error_messages
79
- end
80
- end
81
-
82
- private
83
-
84
- ##
85
- # Builds the full mysqldump string based on all attributes
86
15
  def mysqldump
87
- "#{ mysqldump_utility } #{ credential_options } #{ connectivity_options } " +
88
- "#{ user_options } #{ db_name } #{ tables_to_dump } #{ tables_to_skip }"
16
+ "#{utility("mysqldump")} #{arguments}"
89
17
  end
90
18
 
91
- ##
92
- # Returns the filename to use for dumping the database(s)
93
- def dump_filename
94
- dump_all? ? 'all-databases' : name
95
- end
96
-
97
- ##
98
- # Builds the credentials MySQL syntax to authenticate the user
99
- # to perform the database dumping process
100
- def credential_options
101
- %w[username password].map do |option|
102
- next if send(option).to_s.empty?
103
- "--#{option}='#{send(option)}'".gsub('--username', '--user')
104
- end.compact.join(' ')
105
- end
106
-
107
- ##
108
- # Builds the MySQL connectivity options syntax to connect the user
109
- # to perform the database dumping process
110
- def connectivity_options
111
- %w[host port socket].map do |option|
112
- next if send(option).to_s.empty?
113
- "--#{option}='#{send(option)}'"
114
- end.compact.join(' ')
19
+ def configuration=(new_config)
20
+ @configuration = new_config if new_config.instance_of? WordpressDeploy::Wordpress::Configuration
115
21
  end
116
22
 
117
- ##
118
- # Builds a MySQL compatible string for the additional options
119
- # specified by the user
120
- def user_options
121
- additional_options.join(' ')
122
- end
123
-
124
- ##
125
- # Returns the database name to use in the mysqldump command.
126
- # When dumping all databases, the database name is replaced
127
- # with the command option to dump all databases.
128
- def db_name
129
- dump_all? ? '--all-databases' : name
130
- end
23
+ private
131
24
 
132
- ##
133
- # Builds the MySQL syntax for specifying which tables to dump
134
- # during the dumping of the database
135
- def tables_to_dump
136
- only_tables.join(' ') unless dump_all?
137
- end
25
+ def arguments
26
+ host = configuration.host
27
+ port = configuration.port
28
+ username = configuration.DB_USER
29
+ password = configuration.DB_PASSWORD
30
+ db_name = configuration.DB_NAME
138
31
 
139
- ##
140
- # Builds the MySQL syntax for specifying which tables to skip
141
- # during the dumping of the database
142
- def tables_to_skip
143
- skip_tables.map do |table|
144
- table = (dump_all? || table['.']) ? table : "#{ name }.#{ table }"
145
- "--ignore-table='#{ table }'"
146
- end.join(' ')
32
+ "-P \"#{port}\" -h \"#{host}\" -u \"#{username}\" -p#{password} -B \"#{db_name}\""
147
33
  end
148
34
 
149
35
  ##
150
- # Return true if we're dumping all databases.
151
- # `name` will be set to :all if it is not set,
152
- # so this will be true by default
153
- def dump_all?
154
- name == :all
36
+ # A temporary directory for installing the executable to
37
+ def tmp_dir
38
+ @tmp_dir ||= Dir.mktmpdir
39
+ File.expand_path(@tmp_dir)
155
40
  end
156
41
 
157
42
  end
158
43
  end
159
44
  end
45
+
@@ -0,0 +1,68 @@
1
+ module WordpressDeploy
2
+ ##
3
+ # Environment defines all of the locations of input and
4
+ # output files. Specifically, the locations of the test
5
+ # definitions, the locations of the test results, and
6
+ # the locations of the build applications.
7
+ module Environment
8
+
9
+ ##
10
+ # Setup required paths based on the given options
11
+ def self.set_options(options = {})
12
+ options.each do |option, value|
13
+ method = "#{option}="
14
+ send(method, value) if respond_to? method and !value.empty?
15
+ end
16
+ end
17
+
18
+ def self.logging=(new_log)
19
+ # Only set @@logging if new_log is a boolean
20
+ if !!new_log == new_log
21
+ @@logging = new_log
22
+ else
23
+ @@logging = false
24
+ end
25
+ end
26
+
27
+ def self.logging?
28
+ @@logging ||= false
29
+ @@logging
30
+ end
31
+
32
+ def self.root_dir=(new_root)
33
+ @@root_dir = new_root
34
+ end
35
+
36
+ def self.root_dir
37
+ @@root_dir ||= Dir.pwd
38
+ File.expand_path @@root_dir
39
+ end
40
+
41
+ def self.config_dir=(new_config_dir)
42
+ @@config_dir = new_config_dir
43
+ end
44
+
45
+ def self.config_dir
46
+ @@config_dir ||= "config"
47
+ File.expand_path File.join(root_dir, @@config_dir)
48
+ end
49
+
50
+ def self.wp_dir=(new_wp_dir)
51
+ @@wp_dir = new_wp_dir
52
+ end
53
+
54
+ def self.wp_dir
55
+ @@wp_dir ||= "site"
56
+ File.expand_path File.join(root_dir, @@wp_dir)
57
+ end
58
+
59
+ def self.log_file
60
+ @@log_file ||= "#{Time.now.strftime("%Y_%m_%d_%H_%M_%S")}.log"
61
+ File.expand_path File.join(root_dir, @@log_file)
62
+ end
63
+
64
+ def self.clean!
65
+ FileUtils.rm Dir.glob(File.join(root_dir, "*.log"))
66
+ end
67
+ end
68
+ end
@@ -1,7 +1,61 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module WordpressDeploy
4
+ ##
5
+ # - automatically defines module namespaces referenced under WordpressDeploy::Errors
6
+ # - any constant name referenced that ends with 'Error' will be created
7
+ # as a subclass of WordpressDeploy::Errors::Error
8
+ # e.g.
9
+ # err = WordpressDeploy::Errors::Foo::Bar::FooError.new('error message')
10
+ # err.message => "Foo::Bar::FooError: error message"
11
+ #
12
+ module ErrorsHelper
13
+ def const_missing(const)
14
+ if const.to_s.end_with?('Error')
15
+ module_eval("class #{const} < WordpressDeploy::Errors::Error; end")
16
+ else
17
+ module_eval("module #{const}; extend WordpressDeploy::ErrorsHelper; end")
18
+ end
19
+ const_get(const)
20
+ end
21
+ end
22
+
23
+ ##
24
+ # provides cascading errors with formatted messages
25
+ # see the specs for details
26
+ #
27
+ # e.g.
28
+ # module WordpressDeploy
29
+ # begin
30
+ # begin
31
+ # begin
32
+ # raise Errors::ZoneAError, 'an error occurred in Zone A'
33
+ # rescue => err
34
+ # raise Errors::ZoneBError.wrap(err, <<-EOS)
35
+ # an error occurred in Zone B
36
+ #
37
+ # the following error should give a reason
38
+ # EOS
39
+ # end
40
+ # rescue => err
41
+ # raise Errors::ZoneCError.wrap(err)
42
+ # end
43
+ # rescue => err
44
+ # puts Errors::ZoneDError.wrap(err, 'an error occurred in Zone D')
45
+ # end
46
+ # end
47
+ #
48
+ # Outputs:
49
+ # ZoneDError: an error occurred in Zone D
50
+ # Reason: ZoneCError
51
+ # ZoneBError: an error occurred in Zone B
52
+ #
53
+ # the following error should give a reason
54
+ # Reason: ZoneAError
55
+ # an error occurred in Zone A
56
+ #
4
57
  module Errors
58
+ extend ErrorsHelper
5
59
 
6
60
  class Error < StandardError
7
61