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,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