capistrano-wp 0.4.1

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.
@@ -0,0 +1,85 @@
1
+ require 'tempfile'
2
+
3
+ # These methods are used by recap tasks to run commands and detect when files have changed
4
+ # as part of a deployments
5
+
6
+ module CrowdFavorite::Support::CapistranoExtensions
7
+ # Run a command as the given user
8
+ def as_user(user, command, pwd = deploy_to)
9
+ sudo "su - #{user} -c 'cd #{pwd} && #{command}'"
10
+ end
11
+
12
+ # Run a command as root
13
+ def as_root(command, pwd = deploy_to)
14
+ as_user 'root', command, pwd
15
+ end
16
+
17
+ # Run a command as the application user
18
+ def as_app(command, pwd = deploy_to)
19
+ as_user application_user, command, pwd
20
+ end
21
+
22
+ # Put a string into a file as the application user
23
+ def put_as_app(string, path)
24
+ put string, "/tmp/cf-put-as-app"
25
+ as_app "cp /tmp/cf-put-as-app #{path} && chmod g+rw #{path}", "/"
26
+ ensure
27
+ run "rm /tmp/cf-put-as-app"
28
+ end
29
+
30
+ def editor
31
+ ENV['DEPLOY_EDITOR'] || ENV['EDITOR']
32
+ end
33
+
34
+ # Edit a file on the remote server, using a local editor
35
+ def edit_file(path)
36
+ if editor
37
+ as_app "touch #{path} && chmod g+rw #{path}"
38
+ local_path = Tempfile.new('deploy-edit').path
39
+ get(path, local_path)
40
+ CrowdFavorite::Support::ShellCommand.execute_interactive("#{editor} #{local_path}")
41
+ File.read(local_path)
42
+ else
43
+ abort "To edit a remote file, either the EDITOR or DEPLOY_EDITOR environment variables must be set"
44
+ end
45
+ end
46
+
47
+ # Run a git command in the `deploy_to` directory
48
+ def git(command)
49
+ run "cd #{deploy_to} && umask 002 && sg #{application_group} -c \"git #{command}\""
50
+ end
51
+
52
+ # Capture the result of a git command run within the `deploy_to` directory
53
+ def capture_git(command)
54
+ capture "cd #{deploy_to} && umask 002 && sg #{application_group} -c 'git #{command}'"
55
+ end
56
+
57
+ def exit_code(command)
58
+ capture("#{command} > /dev/null 2>&1; echo $?").strip
59
+ end
60
+
61
+ def exit_code_as_app(command, pwd = deploy_to)
62
+ capture(%|sudo -p 'sudo password: ' su - #{application_user} -c 'cd #{pwd} && #{command} > /dev/null 2>&1'; echo $?|).strip
63
+ end
64
+
65
+ # Find the latest tag from the repository. As `git tag` returns tags in order, and our release
66
+ # tags are timestamps, the latest tag will always be the last in the list.
67
+ def latest_tag_from_repository
68
+ result = capture_git("tag | tail -n1").strip
69
+ result.empty? ? nil : result
70
+ end
71
+
72
+ # Does the given file exist within the deployment directory?
73
+ def deployed_file_exists?(path, root_path = deploy_to)
74
+ exit_code("cd #{root_path} && [ -f #{path} ]") == "0"
75
+ end
76
+
77
+ # Has the given path been created or changed since the previous deployment? During the first
78
+ # successful deployment this will always return true.
79
+ def deployed_file_changed?(path)
80
+ return true unless latest_tag
81
+ exit_code("cd #{deploy_to} && git diff --exit-code #{latest_tag} origin/#{branch} #{path}") == "1"
82
+ end
83
+
84
+ Capistrano::Configuration.send :include, self
85
+ end
@@ -0,0 +1,47 @@
1
+ require 'capistrano'
2
+ require 'crowdfavorite/support/capistrano_extensions'
3
+
4
+ # This module is used to capture the definition of capistrano tasks, which makes it
5
+ # easier to test the behaviour of specific tasks without loading everything. If you
6
+ # are writing tests for a collection of tasks, you should put those tasks in a module
7
+ # and extend that module with `CrowdFavorite::Support::Namespace.
8
+ #
9
+ # You can look at some of the existing tasks (such as [env](../tasks/env.html)) and
10
+ # its corresponding specs for an example of this in practice.
11
+ #
12
+ # You should not need to use this module directly when using recap to deploy.
13
+
14
+ module CrowdFavorite::Support::Namespace
15
+ def self.default_config
16
+ @default_config
17
+ end
18
+
19
+ def self.default_config=(config)
20
+ @default_config = config
21
+ end
22
+
23
+ if Capistrano::Configuration.instance
24
+ self.default_config = Capistrano::Configuration.instance(:must_exist)
25
+ end
26
+
27
+ def capistrano_definitions
28
+ @capistrano_definitions ||= []
29
+ end
30
+
31
+ def namespace(name, &block)
32
+ capistrano_definitions << Proc.new do
33
+ namespace name do
34
+ instance_eval(&block)
35
+ end
36
+ end
37
+
38
+ load_into(CrowdFavorite::Support::Namespace.default_config) if CrowdFavorite::Support::Namespace.default_config
39
+ end
40
+
41
+ def load_into(configuration)
42
+ configuration.extend(self)
43
+ capistrano_definitions.each do |definition|
44
+ configuration.load(&definition)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,230 @@
1
+ require 'crowdfavorite/tasks'
2
+
3
+ module CrowdFavorite::Tasks::LocalChanges
4
+ extend CrowdFavorite::Support::Namespace
5
+
6
+ namespace :cf do
7
+ def _cset(name, *args, &block)
8
+ unless exists?(name)
9
+ set(name, *args, &block)
10
+ end
11
+ end
12
+
13
+ _cset(:comparison_target) { current_release rescue File.dirname(release_path) }
14
+ _cset(:hash_creation) {
15
+ # Find out what hashing mechanism we can use - shasum, sha1sum, openssl, or just an ls command.
16
+ # Unfortunate double-call of which handles some systems which output an error message on stdout
17
+ # when the program cannot be found.
18
+ creation = capture('((which shasum >/dev/null 2>&1 && which shasum) || (which sha1sum >/dev/null 2>&1 && which sha1sum) || (which openssl >/dev/null 2>&1 && which openssl) || echo "ls -ld")').to_s.strip
19
+ if creation.match(/openssl$/)
20
+ "#{creation} sha1 -r"
21
+ end
22
+ creation
23
+ }
24
+
25
+ _cset(:hash_directory) { shared_path }
26
+ _cset(:hash_suffix) { "_hash" }
27
+ _cset(:hash_compare_suffix) { "compare" }
28
+ _cset(:hashes) { capture("ls -xt #{File.join(hash_directory, '*' + hash_suffix)}").split.reverse }
29
+
30
+
31
+ def _snapshot_exists(path)
32
+ retcode = (capture("test -f " + Shellwords::escape(path) + "; echo $?").to_s.strip.to_i)
33
+ return retcode == 0 ? true : false
34
+ end
35
+
36
+ def _hash_path(release, extra = "")
37
+ File.join(hash_directory, release + hash_suffix + extra)
38
+ end
39
+
40
+ namespace :localchanges do
41
+ task :snapshot_deploy, :except => { :no_release => true } do
42
+ set(:snapshot_target, latest_release)
43
+ snapshot
44
+ unset(:snapshot_target)
45
+ end
46
+
47
+ desc "Snapshot the current release for later change detection."
48
+ task :snapshot, :except => { :no_release => true } do
49
+ target_release = File.basename(fetch(:snapshot_target, comparison_target))
50
+
51
+ target_path = File.join(releases_path, target_release)
52
+ default_hash_path = _hash_path(target_release) # File.join(shared_path, target_release + "_hash")
53
+ hash_path = fetch(:snapshot_hash_path, default_hash_path)
54
+
55
+ snapshot_exists = _snapshot_exists(hash_path)
56
+
57
+ if snapshot_exists and !fetch(:snapshot_force, false)
58
+ logger.info "A snapshot for release #{target_release} already exists."
59
+ next
60
+ end
61
+
62
+ run("find " + Shellwords::escape(target_path) + " -name .git -prune -o -name .svn -prune -o -type f -print0 | xargs -0 #{hash_creation} > " + Shellwords::escape(hash_path))
63
+
64
+ end
65
+
66
+ desc "Call this before a deploy to continue despite local changes made on the server."
67
+ task :allow_differences do
68
+ set(:snapshot_allow_differences, true)
69
+ end
70
+
71
+ task :forbid_differences do
72
+ set(:snapshot_allow_differences, false)
73
+ end
74
+
75
+ def _do_snapshot_compare()
76
+ if releases.length == 0
77
+ logger.info "no current release"
78
+ return false
79
+ end
80
+ release_name = File.basename(current_release)
81
+ set(:snapshot_target, current_release)
82
+ default_hash_path = _hash_path(release_name) # File.join(shared_path, release_name + "_hash")
83
+ snapshot_exists = _snapshot_exists(default_hash_path)
84
+ if !snapshot_exists
85
+ logger.info "no previous snapshot to compare against"
86
+ return false
87
+ end
88
+ set(:snapshot_hash_path, _hash_path(release_name, hash_compare_suffix)) # File.join(shared_path, release_name + "_hash_compare"))
89
+ set(:snapshot_force, true)
90
+ snapshot
91
+
92
+ # Hand-tooled diff-parsing - handles either shasum-style or ls -ld output
93
+ # Hashes store filename => [host, host, host]
94
+ left = {}
95
+ right = {}
96
+ changed = {}
97
+ run("diff " + default_hash_path + " " + snapshot_hash_path + " || true") do |channel, stream, data|
98
+ data.each_line do |line|
99
+ line.strip!
100
+ if line.match(/^\s*[<>]/)
101
+ parts = line.split(/\s+/)
102
+ if hash_creation.match(/ls -ld/)
103
+ # > -rw-rw-r-- 1 example example 41 Sep 19 14:58 index.php
104
+ parts.slice!(0, 9)
105
+ else
106
+ # < 198ed94e9f1e5c69e159e8ba6d4420bb9c039715 index.php
107
+ parts.slice!(0,2)
108
+ end
109
+
110
+ bucket = line.match(/^\s*</) ? left : right
111
+ filename = parts.join('')
112
+
113
+ bucket[filename] ||= []
114
+ bucket[filename].push(channel[:host])
115
+ end
116
+ end
117
+ end
118
+ if !(left.empty? && right.empty?)
119
+ left.each do |filename, servers|
120
+ if right.has_key?(filename)
121
+ servers.each do |host|
122
+ if right[filename].delete(host)
123
+ changed[filename] ||= []
124
+ changed[filename].push(host)
125
+ left[filename].delete(host)
126
+ end
127
+ end
128
+
129
+ left.delete(filename) if left[filename].empty?
130
+ right.delete(filename) if right[filename].empty?
131
+ end
132
+ end
133
+ end
134
+
135
+ unset(:snapshot_target)
136
+ unset(:snapshot_hash_path)
137
+ unset(:snapshot_force)
138
+ return {:left => left, :right => right, :changed => changed}
139
+ end
140
+
141
+ def _do_snapshot_diff(results, format = :full)
142
+ if !results
143
+ return false
144
+ end
145
+ if results[:left].empty? && results[:right].empty? && results[:changed].empty?
146
+ return false
147
+ end
148
+
149
+ if format == :basic || !(fetch(:strategy).class <= Capistrano::Deploy::Strategy.new(:remote).class)
150
+ logger.important "deleted: " + results[:left].inspect
151
+ logger.important "created: " + results[:right].inspect
152
+ logger.important "changed: " + results[:changed].inspect
153
+ return true
154
+ end
155
+
156
+ ## TODO: improve diff handling for remote_cache with .git copy_excluded
157
+ ## TODO: improve diff handling for remote_cache with .git not copy_excluded
158
+ ## TODO: improve diff handling for remote_cache with .svn copy_excluded
159
+ ## TODO: improve diff handling for remote_cache with .svn not copy_excluded
160
+ logger.important "deleted: " + results[:left].inspect
161
+ logger.important "created: " + results[:right].inspect
162
+ logger.important "changed: " + results[:changed].inspect
163
+ return true
164
+
165
+ todostuff = <<-'EOTODO'
166
+ File.join(shared_path, configuration[:repository_cache] || "cached-copy")
167
+ if fetch(:scm) == :git
168
+ if fetch(:copy_exclude, []).include?(".git")
169
+ run("echo 'gitdir: #{}
170
+
171
+ end
172
+
173
+
174
+ run("diff " + default_hash_path + " " + snapshot_hash_path + " || true") do |channel, stream, data|
175
+ data.each_line do |line|
176
+ line.strip!
177
+ if line.match(/^\s*[<>]/)
178
+ parts = line.split(/\s+/)
179
+ if hash_creation.match(/ls -ld/)
180
+ # > -rw-rw-r-- 1 example example 41 Sep 19 14:58 index.php
181
+ parts.slice!(0, 9)
182
+ else
183
+ # < 198ed94e9f1e5c69e159e8ba6d4420bb9c039715 index.php
184
+ parts.slice!(0,2)
185
+ end
186
+
187
+ bucket = line.match(/^\s*</) ? left : right
188
+ filename = parts.join('')
189
+
190
+ bucket[filename] ||= []
191
+ bucket[filename].push(channel[:host])
192
+ end
193
+ end
194
+ end
195
+ logger.important "oh hey, fully featured"
196
+ return true
197
+ EOTODO
198
+ end
199
+
200
+ desc "Check the current release for changes made on the server; abort if changes are detected."
201
+ task :compare, :except => { :no_release => true } do
202
+ results = _do_snapshot_compare()
203
+ if _do_snapshot_diff(results, :basic)
204
+ abort("Aborting: local changes detected in current release") unless fetch(:snapshot_allow_differences, false)
205
+ logger.important "Continuing deploy despite differences!"
206
+ end
207
+ end
208
+
209
+ desc "Check the current release for changes made on the server (and return detailed changes, if using a remote-cached git repo). Does not abort on changes."
210
+ task :diff, :except => { :no_release => true } do
211
+ results = _do_snapshot_compare()
212
+ _do_snapshot_diff(results, :full)
213
+ end
214
+
215
+ task :cleanup, :except => { :no_release => true } do
216
+ count = fetch(:keep_releases, 5).to_i
217
+ if count >= hashes.length
218
+ logger.info "no old hashes to clean up"
219
+ else
220
+ logger.info "keeping #{count} of #{hashes.length} release hashes"
221
+ hashpaths = (hashes - hashes.last(count)).map{ |thehash|
222
+ File.join(hash_directory, thehash) + " " + File.join(hash_directory, thehash + hash_compare_suffix)
223
+ }.join(" ")
224
+ try_sudo "rm -f #{hashpaths}"
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+
@@ -0,0 +1,247 @@
1
+ require 'crowdfavorite/tasks'
2
+ require 'Shellwords'
3
+
4
+ module CrowdFavorite::Tasks::WordPress
5
+ extend CrowdFavorite::Support::Namespace
6
+ def _combine_filehash base, update
7
+ if update.respond_to? :has_key?
8
+ update = [update]
9
+ end
10
+ new = {}
11
+ new.merge!(base)
12
+ update.each do |update_hash|
13
+ new.merge!(update_hash)
14
+ end
15
+ new
16
+ end
17
+ namespace :cf do
18
+ def _cset(name, *args, &block)
19
+ unless exists?(name)
20
+ set(name, *args, &block)
21
+ end
22
+ end
23
+
24
+ _cset :copy_exclude, [
25
+ ".git", ".gitignore", ".gitmodules",
26
+ ".DS_Store", ".svn",
27
+ "Capfile", "/config",
28
+ "capinfo.json",
29
+ ]
30
+
31
+ # wp_symlinks are symlinked from the shared directory into the release.
32
+ # Key is the shared file; value is the target location.
33
+ # :wp_symlinks overwrites :base_wp_symlinks; an empty target location
34
+ # means to skip linking the file.
35
+ _cset :base_wp_symlinks, {
36
+ "cache" => "wp-content/cache",
37
+ "uploads" => "wp-content/uploads",
38
+ "blogs.dir" => "wp-content/blogs.dir",
39
+ }
40
+
41
+ _cset :wp_symlinks, {}
42
+
43
+ # wp_configs are copied from the shared directory into the release.
44
+ # Key is the shared file; value is the target location.
45
+ # :wp_configs overwrites :base_wp_configs; an empty target location
46
+ # means to skip copying the file.
47
+ _cset :base_wp_configs, {
48
+ "db-config.php" => "wp-content/",
49
+ "advanced-cache.php" => "wp-content/",
50
+ "object-cache.php" => "wp-content/",
51
+ "*.html" => "/",
52
+ }
53
+
54
+ _cset :wp_configs, {}
55
+
56
+ # stage_specific_overrides are uploaded from the repo's config
57
+ # directory, if they exist for that stage. Files are named
58
+ # 'STAGE-filename.txt' locally and copied to 'filename.txt'
59
+ # on deploy for that stage.
60
+ # Key is the local file; value is the target location.
61
+ # :stage_specific_overrides overwrites :base_stage_specific_overrides;
62
+ # an empty target location means to skip uploading the file.
63
+ _cset :base_stage_specific_overrides, {
64
+ "local-config.php" => "local-config.php",
65
+ ".htaccess" => ".htaccess"
66
+ }
67
+
68
+ _cset :stage_specific_overrides, {}
69
+
70
+ before "deploy:finalize_update", "cf:wordpress:generate_config"
71
+ after "deploy:finalize_update", "cf:wordpress:touch_release"
72
+ after "cf:wordpress:generate_config", "cf:wordpress:link_symlinks"
73
+ after "cf:wordpress:link_symlinks", "cf:wordpress:copy_configs"
74
+ after "cf:wordpress:copy_configs", "cf:wordpress:install"
75
+ after "cf:wordpress:install", "cf:wordpress:do_stage_specific_overrides"
76
+ namespace :wordpress do
77
+
78
+ namespace :install do
79
+
80
+ desc <<-DESC
81
+ [internal] Installs WordPress with a remote svn cache
82
+ DESC
83
+ task :with_remote_cache, :except => { :no_release => true } do
84
+ wp = fetch(:wordpress_version, "trunk")
85
+ wp_target = fetch(:wp_path, release_path)
86
+ wp_stage = File.join(shared_path, "wordpress", wp)
87
+ # check out cache of wordpress code
88
+ run Shellwords::shelljoin(["test", "-e", wp_stage]) +
89
+ " || " + Shellwords::shelljoin(["svn", "co", "-q", "http://core.svn.wordpress.org/" + wp, wp_stage])
90
+ # update branches or trunk (no need to update tags)
91
+ run Shellwords::shelljoin(["svn", "up", "--force", "-q", wp_stage]) unless wp.start_with?("tags/")
92
+ # ensure a clean copy
93
+ run Shellwords::shelljoin(["svn", "revert", "-R", "-q", wp_stage])
94
+ # trailingslashit for rsync
95
+ wp_stage << '/' unless wp_stage[-1..-1] == '/'
96
+ # push wordpress into the right place (release_path by default, could be #{release_path}/wp)
97
+ run Shellwords::shelljoin(["rsync", "--exclude=.svn", "--ignore-existing", "-a", wp_stage, wp_target])
98
+ end
99
+
100
+ desc <<-DESC
101
+ [internal] Installs WordPress with a local svn cache/copy, compressing and uploading a snapshot
102
+ DESC
103
+ task :with_copy, :except => { :no_release => true } do
104
+ wp = fetch(:wordpress_version, "trunk")
105
+ wp_target = fetch(:wp_path, release_path)
106
+ Dir.mktmpdir do |tmp_dir|
107
+ tmpdir = fetch(:cf_database_store, tmp_dir)
108
+ wp = fetch(:wordpress_version, "trunk")
109
+ Dir.chdir(tmpdir) do
110
+ if !(wp.start_with?("tags/") || wp.start_with?("branches/") || wp == "trunk")
111
+ wp = "branches/#{wp}"
112
+ end
113
+ wp_stage = File.join(tmpdir, "wordpress", wp)
114
+ ["branches", "tags"].each do |wpsvntype|
115
+ system Shellwords::shelljoin(["mkdir", "-p", File.join(tmpdir, "wordpress", wpsvntype)])
116
+ end
117
+
118
+ puts "Getting WordPress #{wp} to #{wp_stage}"
119
+ system Shellwords::shelljoin(["test", "-e", wp_stage]) +
120
+ " || " + Shellwords::shelljoin(["svn", "co", "-q", "http://core.svn.wordpress.org/" + wp, wp_stage])
121
+ system Shellwords::shelljoin(["svn", "up", "--force", "-q", wp_stage]) unless wp.start_with?("tags/")
122
+ system Shellwords::shelljoin(["svn", "revert", "-R", "-q", wp_stage])
123
+ wp_stage << '/' unless wp_stage[-1..-1] == '/'
124
+ Dir.mktmpdir do |copy_dir|
125
+ comp = Struct.new(:extension, :compress_command, :decompress_command)
126
+ remote_tar = fetch(:copy_remote_tar, 'tar')
127
+ local_tar = fetch(:copy_local_tar, 'tar')
128
+ type = fetch(:copy_compression, :gzip)
129
+ compress = case type
130
+ when :gzip, :gz then comp.new("tar.gz", [local_tar, '-c -z --exclude .svn -f'], [remote_tar, '-x -k -z -f'])
131
+ when :bzip2, :bz2 then comp.new("tar.bz2", [local_tar, '-c -j --exclude .svn -f'], [remote_tar, '-x -k -j -f'])
132
+ when :zip then comp.new("zip", %w(zip -qyr), %w(unzip -q))
133
+ else raise ArgumentError, "invalid compression type #{type.inspect}"
134
+ end
135
+ compressed_filename = "wp-" + File.basename(fetch(:release_path)) + "." + compress.extension
136
+ local_file = File.join(copy_dir, compressed_filename)
137
+ puts "Compressing #{wp_stage} to #{local_file}"
138
+ Dir.chdir(wp_stage) do
139
+ system([compress.compress_command, local_file, '.'].join(' '))
140
+ end
141
+ remote_file = File.join(fetch(:copy_remote_dir, '/tmp'), File.basename(local_file))
142
+ puts "Pushing #{local_file} to #{remote_file} to deploy"
143
+ upload(local_file, remote_file)
144
+ wp_target = fetch(:wp_path, fetch(:release_path))
145
+ run("mkdir -p #{wp_target} && cd #{wp_target} && (#{compress.decompress_command.join(' ')} #{remote_file} || echo 'tar errors for normal conditions') && rm #{remote_file}")
146
+ end
147
+
148
+ end
149
+ end
150
+ end
151
+
152
+ desc <<-DESC
153
+ [internal] Installs WordPress to the application deploy point
154
+ DESC
155
+ task :default, :except => { :no_release => true } do
156
+ if fetch(:strategy).class <= Capistrano::Deploy::Strategy.new(:remote).class
157
+ with_remote_cache
158
+ else
159
+ with_copy
160
+ end
161
+ end
162
+ end
163
+
164
+ desc <<-DESC
165
+ [internal] (currently unused) Generate config files if appropriate
166
+ DESC
167
+ task :generate_config, :except => { :no_release => true } do
168
+ # live config lives in wp-config.php; dev config loaded with local-config.php
169
+ # this method does nothing for now
170
+ end
171
+
172
+ desc <<-DESC
173
+ [internal] Symlinks specified files (usually uploads/blogs.dir/cache directories)
174
+ DESC
175
+ task :link_symlinks, :except => { :no_release => true } do
176
+ symlinks = _combine_filehash(fetch(:base_wp_symlinks), fetch(:wp_symlinks))
177
+ symlinks.each do |src, targ|
178
+ next if targ.nil? || targ == false || targ.empty?
179
+ src = File.join(shared_path, src) unless src.include?(shared_path)
180
+ targ = File.join(release_path, targ) unless targ.include?(release_path)
181
+ run [
182
+ Shellwords::shelljoin(["test", "-e", src]),
183
+ Shellwords::shelljoin(["test", "-d", targ]),
184
+ Shellwords::shelljoin(["rm", "-rf", targ])
185
+ ].join(' && ') + " || true"
186
+ run Shellwords::shelljoin(["test", "-e", src]) + " && " + Shellwords::shelljoin(["ln", "-nsf", src, targ]) + " || true"
187
+ end
188
+ end
189
+
190
+ desc <<-DESC
191
+ [internal] Copies specified files (usually advanced-cache, object-cache, db-config)
192
+ DESC
193
+ task :copy_configs, :except => { :no_release => true } do
194
+ configs = _combine_filehash(fetch(:base_wp_configs), fetch(:wp_configs))
195
+ configs.each do |src, targ|
196
+ next if targ.nil? || targ == false || targ.empty?
197
+ src = File.join(shared_path, src) unless src.include?(shared_path)
198
+ targ = File.join(release_path, targ) unless targ.include?(release_path)
199
+ run "ls -d #{src} >/dev/null 2>&1 && cp -urp #{src} #{targ} || true"
200
+ #run Shellwords::shelljoin(["test", "-e", src]) + " && " + Shellwords::shelljoin(["cp", "-rp", src, targ]) + " || true"
201
+ end
202
+ end
203
+
204
+ desc <<-DESC
205
+ [internal] Pushes up local-config.php, .htaccess, others if they exist for that stage
206
+ DESC
207
+ task :do_stage_specific_overrides, :except => { :no_release => true } do
208
+ next unless fetch(:stage, false)
209
+ overrides = _combine_filehash(fetch(:base_stage_specific_overrides), fetch(:stage_specific_overrides))
210
+ overrides.each do |src, targ|
211
+ next if targ.nil? || targ == false || targ.empty?
212
+ src = File.join("config", "#{stage}-#{src}")
213
+ targ = File.join(release_path, targ) unless targ.include?(release_path)
214
+ if File.exist?(src)
215
+ upload(src, targ)
216
+ end
217
+ end
218
+ end
219
+
220
+ desc <<-DESC
221
+ [internal] Ensure the release path has an updated modified time for deploy:cleanup
222
+ DESC
223
+ task :touch_release, :except => { :no_release => true } do
224
+ run "touch '#{release_path}'"
225
+ end
226
+ end
227
+
228
+ #===========================================================================
229
+ # util / debugging code
230
+
231
+ namespace :debugging do
232
+
233
+ namespace :release_info do
234
+ desc <<-DESC
235
+ [internal] Debugging info about releases.
236
+ DESC
237
+
238
+ task :default do
239
+ %w{releases_path shared_path current_path release_path releases previous_release current_revision latest_revision previous_revision latest_release}.each do |var|
240
+ puts "#{var}: #{eval(var)}"
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+
@@ -0,0 +1,4 @@
1
+ require 'crowdfavorite'
2
+ module CrowdFavorite::Tasks
3
+ end
4
+
@@ -0,0 +1,3 @@
1
+ module CrowdFavorite
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,13 @@
1
+ # Default set of tasks for wordpress deploys, including all the before/after bindings
2
+
3
+ require 'crowdfavorite/tasks/wordpress'
4
+ require 'crowdfavorite/tasks/localchanges'
5
+
6
+ module CrowdFavorite::WordPress
7
+ extend CrowdFavorite::Support::Namespace
8
+ namespace :cf do
9
+ after "deploy:finalize_update", "cf:localchanges:snapshot_deploy"
10
+ before "deploy", "cf:localchanges:compare"
11
+ end
12
+ end
13
+
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+
3
+ module CrowdFavorite
4
+ module Support
5
+ autoload :Namespace, 'crowdfavorite/support/namespace'
6
+ end
7
+ autoload :Version, 'crowdfavorite/version'
8
+ end
9
+
data/spec/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color