wordpress-deploy 1.0.0.alpha1 → 1.0.0.alpha2

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.
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,68 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module WordpressDeploy
4
- module Config
5
- DEFAULTS = {
6
- :config_file => 'config.rb',
7
- :data_path => 'data',
8
- :log_path => 'log',
9
- :cache_path => '.cache',
10
- :tmp_path => '.tmp'
11
- }
12
-
13
- class << self
14
- # These paths are the basis for all scripts
15
- attr_reader :base_dir
16
- def base_dir
17
- @base_dir ||= Dir.pwd
18
- end
19
- attr_reader :config_dir
20
- def config_dir
21
- @config_dir ||= File.join(base_dir, "config")
22
- end
23
- attr_reader :sites_dir
24
- def sites_dir
25
- @sites_dir ||= File.join(base_dir, "site")
26
- end
27
-
28
- attr_reader :ftp_config
29
- def ftp_config
30
- @ftp_config ||= YAML.load_file(File.join(config_dir, "ftp.yml"))
31
- end
32
-
33
- attr_reader :wp_config
34
- def wp_config
35
- @wp_config ||= YAML.load_file(File.join(config_dir, "wp-config.yml"))
36
- end
37
-
38
- attr_reader :wp_config_sample
39
- def wp_config_sample
40
- @wp_config_sample ||= File.join(sites_dir, "wp-config-sample.php")
41
- end
42
- attr_reader :wp_config_output
43
- def wp_config_output
44
- @wp_config_output ||= File.join(sites_dir, "wp-config.php")
45
- end
46
-
47
- attr_reader :environment
48
- def environment
49
- @environment ||= "production"
50
- end
51
- alias :env :environment
52
-
53
- def environment=(new_env)
54
- @environment = new_env.downcase
55
- end
56
-
57
- # The Salting array
58
- def salt_array
59
- @salt_array ||= %w{0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ! @ # $ % ^ & * ( ) - ~ + = | / { } : ; , . ? < > [ ]}
60
- end
61
-
62
- def salt_keys
63
- @salt_keys ||= %w{AUTH_KEY SECURE_AUTH_KEY LOGGED_IN_KEY NONCE_KEY AUTH_SALT SECURE_AUTH_SALT LOGGED_IN_SALT NONCE_SALT}
64
- end
65
-
66
- end
67
- end
68
- end
@@ -1,53 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module WordpressDeploy
4
- module Database
5
- class Base
6
- include WordpressDeploy::CLI::Helpers
7
- include WordpressDeploy::Configuration::Helpers
8
-
9
- ##
10
- # Creates a new instance of the MongoDB database object
11
- # * Called using super(model) from subclasses *
12
- def initialize(model)
13
- @model = model
14
- load_defaults!
15
- end
16
-
17
- ##
18
- # Super method for all child (database) objects. Every database object's #perform!
19
- # method should call #super before anything else to prepare
20
- def perform!
21
- prepare!
22
- log!
23
- end
24
-
25
- private
26
-
27
- ##
28
- # Defines the @dump_path and ensures it exists by creating it
29
- def prepare!
30
- @dump_path = File.join(
31
- Config.tmp_path,
32
- @model.trigger,
33
- 'databases',
34
- self.class.name.split('::').last
35
- )
36
- FileUtils.mkdir_p(@dump_path)
37
- end
38
-
39
- ##
40
- # Return the database name, with WordpressDeploy namespace removed
41
- def database_name
42
- self.class.to_s.sub('WordpressDeploy::', '')
43
- end
44
-
45
- ##
46
- # Logs a message to the console and log file to inform
47
- # the client that WordpressDeploy is dumping the database
48
- def log!
49
- Logger.message "#{ database_name } started dumping and archiving '#{ name }'."
50
- end
51
- end
52
- end
53
- end
@@ -1,110 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module WordpressDeploy
4
- class Pipeline
5
- include WordpressDeploy::CLI::Helpers
6
-
7
- attr_reader :stderr, :errors
8
-
9
- def initialize
10
- @commands = []
11
- @errors = []
12
- @stderr = ''
13
- end
14
-
15
- ##
16
- # Adds a command to be executed in the pipeline.
17
- # Each command will be run in the order in which it was added,
18
- # with it's output being piped to the next command.
19
- def <<(command)
20
- @commands << command
21
- end
22
-
23
- ##
24
- # Runs the command line from `#pipeline` and collects STDOUT/STDERR.
25
- # STDOUT is then parsed to determine the exit status of each command.
26
- # For each command with a non-zero exit status, a SystemCallError is
27
- # created and added to @errors. All STDERR output is set in @stderr.
28
- #
29
- # Note that there is no accumulated STDOUT from the commands themselves.
30
- # Also, the last command should not attempt to write to STDOUT.
31
- # Any output on STDOUT from the final command will be sent to STDERR.
32
- # This in itself will not cause #run to fail, but will log warnings
33
- # when all commands exit with non-zero status.
34
- #
35
- # Use `#success?` to determine if all commands in the pipeline succeeded.
36
- # If `#success?` returns `false`, use `#error_messages` to get an error report.
37
- def run
38
- Open4.popen4(pipeline) do |pid, stdin, stdout, stderr|
39
- pipestatus = stdout.read.gsub("\n", '').split(':').sort
40
- pipestatus.each do |status|
41
- index, exitstatus = status.split('|').map(&:to_i)
42
- if exitstatus > 0
43
- command = command_name(@commands[index])
44
- @errors << SystemCallError.new(
45
- "'#{ command }' returned exit code: #{ exitstatus }", exitstatus
46
- )
47
- end
48
- end
49
- @stderr = stderr.read.strip
50
- end
51
- Logger.warn(stderr_messages) if success? && stderr_messages
52
- rescue Exception => e
53
- raise Errors::Pipeline::ExecutionError.wrap(e)
54
- end
55
-
56
- def success?
57
- @errors.empty?
58
- end
59
-
60
- ##
61
- # Returns a multi-line String, reporting all STDERR messages received
62
- # from the commands in the pipeline (if any), along with the SystemCallError
63
- # (Errno) message for each command which had a non-zero exit status.
64
- #
65
- # Each error is wrapped by WordpressDeploy::Errors to provide formatting.
66
- def error_messages
67
- @error_messages ||= (stderr_messages || '') +
68
- "The following system errors were returned:\n" +
69
- @errors.map {|err| Errors::Error.wrap(err).message }.join("\n")
70
- end
71
-
72
- private
73
-
74
- ##
75
- # Each command is added as part of the pipeline, grouped with an `echo`
76
- # command to pass along the command's index in @commands and it's exit status.
77
- # The command's STDERR is redirected to FD#4, and the `echo` command to
78
- # report the "index|exit status" is redirected to FD#3.
79
- # Each command's STDOUT will be connected to the STDIN of the next subshell.
80
- # The entire pipeline is run within a container group, which redirects
81
- # FD#3 to STDOUT and FD#4 to STDERR so these can be collected.
82
- # FD#1 is redirected to STDERR so that any output from the final command
83
- # on STDOUT will generate warnings, since the final command should not
84
- # attempt to write to STDOUT, as this would interfere with collecting
85
- # the exit statuses.
86
- #
87
- # There is no guarantee as to the order of this output, which is why the
88
- # command's index in @commands is passed along with it's exit status.
89
- # And, if multiple commands output messages on STDERR, those messages
90
- # may be interleaved. Interleaving of the "index|exit status" outputs
91
- # should not be an issue, given the small byte size of the data being written.
92
- def pipeline
93
- parts = []
94
- @commands.each_with_index do |command, index|
95
- parts << %Q[{ #{ command } 2>&4 ; echo "#{ index }|$?:" >&3 ; }]
96
- end
97
- %Q[{ #{ parts.join(' | ') } } 3>&1 1>&2 4>&2]
98
- end
99
-
100
- def stderr_messages
101
- @stderr_messages ||= @stderr.empty? ? false : <<-EOS.gsub(/^ +/, ' ')
102
- Pipeline STDERR Messages:
103
- (Note: may be interleaved if multiple commands returned error messages)
104
-
105
- #{ @stderr }
106
- EOS
107
- end
108
-
109
- end
110
- end
@@ -1,99 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module WordpressDeploy
4
- module Storage
5
- class Base
6
- include WordpressDeploy::Configuration::Helpers
7
-
8
- ##
9
- # Sets the limit to how many backups to keep in the remote location.
10
- # If exceeded, the oldest will be removed to make room for the newest
11
- attr_accessor :keep
12
-
13
- ##
14
- # (Optional)
15
- # User-defined string used to uniquely identify multiple storages of the
16
- # same type. This will be appended to the YAML storage file used for
17
- # cycling backups.
18
- attr_accessor :storage_id
19
-
20
- ##
21
- # Creates a new instance of the storage object
22
- # * Called with super(model, storage_id) from each subclass
23
- def initialize(model, storage_id = nil)
24
- load_defaults!
25
- @model = model
26
- @storage_id = storage_id
27
- end
28
-
29
- ##
30
- # Performs the backup transfer
31
- def perform!
32
- @package = @model.package
33
- transfer!
34
- cycle!
35
- end
36
-
37
- private
38
-
39
- ##
40
- # Provider defaults to false. Overridden when using a service-based
41
- # storage such as Amazon S3, Rackspace Cloud Files or Dropbox
42
- def provider
43
- false
44
- end
45
-
46
- ##
47
- # Each subclass must define a +path+ where remote files will be stored
48
- def path; end
49
-
50
- ##
51
- # Return the storage name, with optional storage_id
52
- def storage_name
53
- self.class.to_s.sub('WordpressDeploy::', '') +
54
- (storage_id ? " (#{storage_id})" : '')
55
- end
56
-
57
- ##
58
- # Returns the local path
59
- # This is where any Package to be transferred is located.
60
- def local_path
61
- Config.tmp_path
62
- end
63
-
64
- ##
65
- # Returns the remote path for the given Package
66
- # This is where the Package will be stored, or was previously stored.
67
- def remote_path_for(package)
68
- File.join(path, package.trigger, package.time)
69
- end
70
-
71
- ##
72
- # Yields two arguments to the given block: "local_file, remote_file"
73
- # The local_file is the full file name:
74
- # e.g. "2011.08.30.11.00.02.backup.tar.enc"
75
- # The remote_file is the full file name, minus the timestamp:
76
- # e.g. "backup.tar.enc"
77
- def files_to_transfer_for(package)
78
- package.filenames.each do |filename|
79
- yield filename, filename[20..-1]
80
- end
81
- end
82
- alias :transferred_files_for :files_to_transfer_for
83
-
84
- ##
85
- # Adds the current package being stored to the YAML cycle data file
86
- # and will remove any old Package file(s) when the storage limit
87
- # set by #keep is exceeded. Any errors raised while attempting to
88
- # remove older packages will be rescued and a warning will be logged
89
- # containing the original error message.
90
- def cycle!
91
- return unless keep.to_i > 0
92
- Logger.message "#{ storage_name }: Cycling Started..."
93
- Cycler.cycle!(self, @package)
94
- Logger.message "#{ storage_name }: Cycling Complete!"
95
- end
96
-
97
- end
98
- end
99
- end
@@ -1,133 +0,0 @@
1
- # encoding: utf-8
2
-
3
- ##
4
- # Only load the Net::FTP library/gem when the WordpressDeploy::Storage::FTP class is loaded
5
- require 'net/ftp'
6
- require 'pathname'
7
- require 'action_view'
8
-
9
- module WordpressDeploy
10
- module Storage
11
- class FTP < Base
12
- include ActionView::Helpers::NumberHelper
13
-
14
- ##
15
- # Server credentials
16
- attr_accessor :username, :password
17
-
18
- ##
19
- # Server IP Address and FTP port
20
- attr_accessor :ip, :port
21
- alias :hostname :ip
22
-
23
- ##
24
- # Path to store backups to
25
- attr_accessor :path
26
-
27
- ##
28
- # use passive mode?
29
- attr_accessor :passive_mode
30
-
31
- ##
32
- # the remote path
33
- attr_accessor :remote_path
34
-
35
- ##
36
- # Creates a new instance of the storage object
37
- def initialize(model, storage_id = nil, &block)
38
- super(model, storage_id)
39
-
40
- @hostname = hostname
41
- @username = username
42
- @password = password
43
- @remote_path = remote_path
44
- @remote_directores = []
45
-
46
- @port ||= 21
47
- @path ||= 'backups'
48
- @passive_mode ||= false
49
-
50
- instance_eval(&block) if block_given?
51
-
52
- @path = path.sub(/^\~\//, '')
53
- end
54
-
55
- private
56
-
57
- ##
58
- # Establishes a connection to the remote server
59
- #
60
- # Note:
61
- # Since the FTP port is defined as a constant in the Net::FTP class, and
62
- # might be required to change by the user, we dynamically remove and
63
- # re-add the constant with the provided port value
64
- def connection
65
- if Net::FTP.const_defined?(:FTP_PORT)
66
- Net::FTP.send(:remove_const, :FTP_PORT)
67
- end; Net::FTP.send(:const_set, :FTP_PORT, port)
68
-
69
- Net::FTP.open(ip, username, password) do |ftp|
70
- ftp.passive = true if passive_mode
71
- yield ftp
72
- end
73
- end
74
-
75
- ##
76
- # Transfers the archived file to the specified remote server
77
- def transfer!
78
- remote_path = remote_path_for(@package)
79
-
80
- connection do |ftp|
81
- create_remote_path(remote_path, ftp)
82
-
83
- files_to_transfer_for(@package) do |local_file, remote_file|
84
- Logger.message "#{storage_name} started transferring " +
85
- "'#{ local_file }' to '#{ ip }'."
86
- ftp.put(
87
- File.join(local_path, local_file),
88
- File.join(remote_path, remote_file)
89
- )
90
- end
91
- end
92
- end
93
-
94
- ##
95
- # Removes the transferred archive file(s) from the storage location.
96
- # Any error raised will be rescued during Cycling
97
- # and a warning will be logged, containing the error message.
98
- def remove!(package)
99
- remote_path = remote_path_for(package)
100
-
101
- connection do |ftp|
102
- transferred_files_for(package) do |local_file, remote_file|
103
- Logger.message "#{storage_name} started removing " +
104
- "'#{ local_file }' from '#{ ip }'."
105
-
106
- ftp.delete(File.join(remote_path, remote_file))
107
- end
108
-
109
- ftp.rmdir(remote_path)
110
- end
111
- end
112
-
113
- ##
114
- # Creates (if they don't exist yet) all the directories on the remote
115
- # server in order to upload the backup file. Net::FTP does not support
116
- # paths to directories that don't yet exist when creating new
117
- # directories. Instead, we split the parts up in to an array (for each
118
- # '/') and loop through that to create the directories one by one.
119
- # Net::FTP raises an exception when the directory it's trying to create
120
- # already exists, so we have rescue it
121
- def create_remote_path(remote_path, ftp)
122
- path_parts = Array.new
123
- remote_path.split('/').each do |path_part|
124
- path_parts << path_part
125
- begin
126
- ftp.mkdir(path_parts.join('/'))
127
- rescue Net::FTPPermError; end
128
- end
129
- end
130
-
131
- end
132
- end
133
- end