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.
- data/.document +5 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +60 -0
- data/LICENSE.txt +387 -0
- data/README.md +52 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/capistrano-wp.gemspec +85 -0
- data/lib/capistrano/crowdfavorite/wordpress.rb +2 -0
- data/lib/capistrano-wp.rb +3 -0
- data/lib/crowdfavorite/support/capistrano_extensions.rb +85 -0
- data/lib/crowdfavorite/support/namespace.rb +47 -0
- data/lib/crowdfavorite/tasks/localchanges.rb +230 -0
- data/lib/crowdfavorite/tasks/wordpress.rb +247 -0
- data/lib/crowdfavorite/tasks.rb +4 -0
- data/lib/crowdfavorite/version.rb +3 -0
- data/lib/crowdfavorite/wordpress.rb +13 -0
- data/lib/crowdfavorite.rb +9 -0
- data/spec/.rspec +1 -0
- data/spec/capistrano-wp_spec.rb +58 -0
- data/spec/localchanges_spec.rb +32 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/capistrano.rb +6 -0
- metadata +218 -0
@@ -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,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
|
+
|
data/spec/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|