vlad 1.0.0 → 1.1.0
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/History.txt +26 -1
- data/Manifest.txt +4 -1
- data/README.txt +9 -12
- data/Rakefile +26 -2
- data/doco/faq.txt +75 -0
- data/doco/getting_started.txt +18 -4
- data/doco/migration.txt +22 -0
- data/doco/variables.txt +38 -6
- data/lib/rake_remote_task.rb +274 -134
- data/lib/vlad.rb +62 -51
- data/lib/vlad/apache.rb +37 -0
- data/lib/{vlad_tasks.rb → vlad/core.rb} +21 -135
- data/lib/vlad/mongrel.rb +65 -0
- data/lib/vlad/perforce.rb +5 -9
- data/lib/vlad/subversion.rb +8 -4
- data/test/test_rake_remote_task.rb +6 -1
- data/test/test_vlad.rb +8 -16
- data/test/test_vlad_perforce.rb +0 -1
- data/test/test_vlad_subversion.rb +2 -1
- data/test/vlad_test_case.rb +17 -7
- metadata +9 -5
data/lib/vlad.rb
CHANGED
@@ -1,76 +1,87 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'rake'
|
3
2
|
require 'thread'
|
4
3
|
require 'rake_remote_task'
|
5
4
|
|
6
|
-
|
7
|
-
# Equivalent to <tt>role</tt>, but shorter for multiple roles.
|
8
|
-
def host host_name, *roles
|
9
|
-
Rake::RemoteTask.host host_name, *roles
|
10
|
-
end
|
11
|
-
|
12
|
-
# Declare a Vlad task that will execute on all hosts by default.
|
13
|
-
# To limit that task to specific roles, use:
|
14
|
-
# remote_task :example, :roles => [:app, :web] do
|
15
|
-
def remote_task name, options = {}, &b
|
16
|
-
Rake::RemoteTask.remote_task name, options, &b
|
17
|
-
end
|
18
|
-
|
19
|
-
# Declare a role and assign a remote host to it.
|
20
|
-
# Equivalent to the <tt>host</tt> method; provided for capistrano compatibility.
|
21
|
-
def role role_name, host, args = {}
|
22
|
-
Rake::RemoteTask.role role_name, host, args
|
23
|
-
end
|
24
|
-
|
25
|
-
# Execute the given command on the <tt>target_host</tt> for the current task.
|
26
|
-
def run *args, &b
|
27
|
-
Thread.current[:task].run(*args, &b)
|
28
|
-
end
|
29
|
-
|
30
|
-
# rsync the given files to <tt>target_host</tt>.
|
31
|
-
def rsync local, remote
|
32
|
-
Thread.current[:task].rsync local, remote
|
33
|
-
end
|
34
|
-
|
35
|
-
# Declare a variable called +name+ and assign it a value.
|
36
|
-
# A globally-visible method with the name of the variable is defined.
|
37
|
-
# If a block is given, it will be called when the variable is first accessed.
|
38
|
-
# Subsequent references to the variable will always return the same value.
|
39
|
-
# Raises <tt>ArgumentError</tt> if the +name+ would conflict with an existing method.
|
40
|
-
def set name, val = nil, &b
|
41
|
-
Rake::RemoteTask.set name, val, &b
|
42
|
-
end
|
5
|
+
$TESTING ||= false
|
43
6
|
|
44
|
-
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
7
|
+
##
|
8
|
+
# Vlad the Deployer - Pragmatic application deployment automation, without mercy.
|
9
|
+
#
|
10
|
+
# Please read doco/getting_started.txt or http://rubyhitsquad.com/
|
11
|
+
#
|
12
|
+
# === Basic scenario:
|
13
|
+
#
|
14
|
+
# 1. rake vlad:setup (first time only)
|
15
|
+
# 2. rake vlad:update
|
16
|
+
# 3. rake vlad:migrate (optional)
|
17
|
+
# 4. rake vlad:start
|
49
18
|
|
50
19
|
module Vlad
|
51
20
|
|
21
|
+
##
|
52
22
|
# This is the version of Vlad you are running.
|
53
|
-
VERSION = '1.
|
23
|
+
VERSION = '1.1.0'
|
54
24
|
|
25
|
+
##
|
55
26
|
# Base error class for all Vlad errors.
|
56
27
|
class Error < RuntimeError; end
|
57
28
|
|
29
|
+
##
|
58
30
|
# Raised when you have incorrectly configured Vlad.
|
59
31
|
class ConfigurationError < Error; end
|
60
32
|
|
33
|
+
##
|
61
34
|
# Raised when a remote command fails.
|
62
35
|
class CommandFailedError < Error; end
|
63
36
|
|
37
|
+
##
|
64
38
|
# Raised when an environment variable hasn't been set.
|
65
39
|
class FetchError < Error; end
|
66
40
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
41
|
+
##
|
42
|
+
# Loads tasks file +tasks_file+ and various recipe styles as a hash
|
43
|
+
# of category/style pairs. Recipes default to:
|
44
|
+
#
|
45
|
+
# :app => :mongrel
|
46
|
+
# :config => 'config/deploy.rb'
|
47
|
+
# :core => :core
|
48
|
+
# :scm => :subversion
|
49
|
+
# :web => :apache
|
50
|
+
#
|
51
|
+
# You can override individual values and/or set to nil to
|
52
|
+
# deactivate. :config will get loaded last to ensure that user
|
53
|
+
# variables override default values.
|
54
|
+
#
|
55
|
+
# And by all means, feel free to skip this entirely if it doesn't
|
56
|
+
# fit for you. All it does is a fancy-pants require. Require
|
57
|
+
# whatever files you need as you see fit straight from your
|
58
|
+
# Rakefile. YAY for simple and clean!
|
59
|
+
def self.load options = {}
|
60
|
+
options = {:config => options} if String === options
|
61
|
+
|
62
|
+
recipes = {
|
63
|
+
:app => :mongrel,
|
64
|
+
:config => 'config/deploy.rb',
|
65
|
+
:core => :core,
|
66
|
+
:scm => :subversion,
|
67
|
+
:web => :apache,
|
68
|
+
}.merge(options)
|
69
|
+
|
70
|
+
recipes.each do |flavor, recipe|
|
71
|
+
next if recipe.nil? or flavor == :config
|
72
|
+
require "vlad/#{recipe}"
|
73
|
+
end
|
74
|
+
|
75
|
+
Kernel.load recipes[:config]
|
71
76
|
end
|
72
|
-
|
73
77
|
end
|
74
78
|
|
75
|
-
|
76
|
-
|
79
|
+
class String #:nodoc:
|
80
|
+
def cleanup
|
81
|
+
if ENV['FULL'] then
|
82
|
+
gsub(/\s+/, ' ').strip
|
83
|
+
else
|
84
|
+
self[/\A.*?\./]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/vlad/apache.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'vlad'
|
2
|
+
|
3
|
+
namespace :vlad do
|
4
|
+
##
|
5
|
+
# Apache web server
|
6
|
+
|
7
|
+
set :web_command, "apachectl"
|
8
|
+
|
9
|
+
desc "(Re)Start the web servers"
|
10
|
+
|
11
|
+
remote_task :start_web, :roles => :web do
|
12
|
+
run "#{web_command} restart"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Stop the web servers"
|
16
|
+
|
17
|
+
remote_task :stop_web, :roles => :web do
|
18
|
+
run "#{web_command} stop"
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Everything HTTP.
|
23
|
+
|
24
|
+
desc "(Re)Start the web and app servers"
|
25
|
+
|
26
|
+
remote_task :start do
|
27
|
+
Rake::Task['vlad:start_app'].invoke
|
28
|
+
Rake::Task['vlad:start_web'].invoke
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Stop the web and app servers"
|
32
|
+
|
33
|
+
remote_task :stop do
|
34
|
+
Rake::Task['vlad:stop_app'].invoke
|
35
|
+
Rake::Task['vlad:stop_web'].invoke
|
36
|
+
end
|
37
|
+
end
|
@@ -1,30 +1,10 @@
|
|
1
1
|
require 'vlad'
|
2
2
|
|
3
|
-
class String #:nodoc:
|
4
|
-
def cleanup
|
5
|
-
if ENV['FULL'] then
|
6
|
-
gsub(/\s+/, ' ').strip
|
7
|
-
else
|
8
|
-
self[/\A.*?\./]
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
3
|
##
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# 1) rake vlad:setup
|
19
|
-
# 2) rake vlad:update
|
20
|
-
# 3) rake vlad:migrate
|
21
|
-
# 4) rake vlad:start
|
22
|
-
#
|
23
|
-
# Subsequent:
|
24
|
-
#
|
25
|
-
# 1) rake vlad:update
|
26
|
-
# 2) rake vlad:migrate (optional)
|
27
|
-
# 3) rake vlad:start
|
4
|
+
# used by update, out here so we can ensure all threads have the same value
|
5
|
+
def now
|
6
|
+
@now ||= Time.now.utc.strftime("%Y%m%d%H%M.%S")
|
7
|
+
end
|
28
8
|
|
29
9
|
namespace :vlad do
|
30
10
|
desc "Show the vlad setup. This is all the default variables for vlad
|
@@ -32,15 +12,20 @@ namespace :vlad do
|
|
32
12
|
|
33
13
|
task :debug do
|
34
14
|
require 'yaml'
|
15
|
+
|
16
|
+
# force them into values
|
17
|
+
Rake::RemoteTask.env.keys.each do |key|
|
18
|
+
next if key =~ /_release|releases|sudo_password/
|
19
|
+
Rake::RemoteTask.fetch key
|
20
|
+
end
|
21
|
+
|
35
22
|
puts "# Environment:"
|
23
|
+
puts
|
36
24
|
y Rake::RemoteTask.env
|
37
25
|
puts "# Roles:"
|
38
26
|
y Rake::RemoteTask.roles
|
39
27
|
end
|
40
28
|
|
41
|
-
# used by update, out here so we can ensure all threads have the same value
|
42
|
-
now = Time.now.utc.strftime("%Y%m%d%H%M.%S")
|
43
|
-
|
44
29
|
desc "Setup your servers. Before you can use any of the deployment
|
45
30
|
tasks with your project, you will need to make sure all of your
|
46
31
|
servers have been prepared with 'rake vlad:setup'. It is safe to
|
@@ -51,10 +36,10 @@ namespace :vlad do
|
|
51
36
|
Rake::Task['vlad:setup_app'].invoke
|
52
37
|
end
|
53
38
|
|
54
|
-
desc "Updates your application server to the latest revision. Syncs
|
55
|
-
of the repository, exports it as the latest release, fixes
|
56
|
-
|
57
|
-
|
39
|
+
desc "Updates your application server to the latest revision. Syncs
|
40
|
+
a copy of the repository, exports it as the latest release, fixes
|
41
|
+
up your symlinks, symlinks the latest revision to current and logs
|
42
|
+
the update.".cleanup
|
58
43
|
|
59
44
|
remote_task :update, :roles => :app do
|
60
45
|
symlink = false
|
@@ -71,12 +56,8 @@ namespace :vlad do
|
|
71
56
|
"ln -s #{shared_path}/pids #{latest_release}/tmp/pids",
|
72
57
|
].join(" && ")
|
73
58
|
|
74
|
-
asset_paths = %w(images stylesheets javascripts).map { |p| "#{latest_release}/public/#{p}" }.join(" ")
|
75
|
-
run "find #{asset_paths} -exec touch -t #{now} {} ';'; true"
|
76
|
-
|
77
59
|
symlink = true
|
78
60
|
run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}"
|
79
|
-
# Rake::Task["vlad:migrate"].invoke
|
80
61
|
|
81
62
|
run "echo #{now} $USER #{'head'} #{File.basename release_path} >> #{deploy_to}/revisions.log" # FIX shouldn't be head
|
82
63
|
rescue => e
|
@@ -92,7 +73,6 @@ namespace :vlad do
|
|
92
73
|
directory by setting :migrate_target to :current. Additional environment
|
93
74
|
variables can be passed to rake via the migrate_env variable.".cleanup
|
94
75
|
|
95
|
-
# HACK :only => { :primary => true }
|
96
76
|
# No application files are on the DB machine, also migrations should only be
|
97
77
|
# run once.
|
98
78
|
remote_task :migrate, :roles => :app do
|
@@ -104,7 +84,7 @@ namespace :vlad do
|
|
104
84
|
else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
|
105
85
|
end
|
106
86
|
|
107
|
-
run "cd #{
|
87
|
+
run "cd #{current_path}; #{rake_cmd} RAILS_ENV=#{rails_env} db:migrate #{migrate_args}"
|
108
88
|
end
|
109
89
|
|
110
90
|
desc "Invoke a single command on every remote server. This is useful for
|
@@ -122,9 +102,9 @@ namespace :vlad do
|
|
122
102
|
puts run(command)
|
123
103
|
end
|
124
104
|
|
125
|
-
desc "Copy files to the currently deployed version
|
126
|
-
updating files piecemeal when you
|
127
|
-
file.
|
105
|
+
desc "Copy arbitrary files to the currently deployed version using
|
106
|
+
FILES=a,b,c. This is useful for updating files piecemeal when you
|
107
|
+
need to quickly deploy only a single file.
|
128
108
|
|
129
109
|
To use this task, specify the files and directories you want to copy as a
|
130
110
|
comma-delimited list in the FILES environment variable. All directories
|
@@ -172,7 +152,7 @@ namespace :vlad do
|
|
172
152
|
|
173
153
|
remote_task :cleanup do
|
174
154
|
count = keep_releases
|
175
|
-
|
155
|
+
unless count >= releases.length then
|
176
156
|
puts "no old releases to clean up"
|
177
157
|
else
|
178
158
|
puts "keeping #{count} of #{releases.length} deployed releases"
|
@@ -185,98 +165,4 @@ namespace :vlad do
|
|
185
165
|
end
|
186
166
|
end
|
187
167
|
|
188
|
-
##
|
189
|
-
# Mongrel app server
|
190
|
-
|
191
|
-
set :mongrel_address, "127.0.0.1"
|
192
|
-
set :mongrel_clean, false
|
193
|
-
set :mongrel_command, 'mongrel_rails'
|
194
|
-
set :mongrel_conf, "#{shared_path}/mongrel_cluster.conf"
|
195
|
-
set :mongrel_config_script, nil
|
196
|
-
set :mongrel_environment, "production"
|
197
|
-
set :mongrel_group, nil
|
198
|
-
set :mongrel_log_file, nil
|
199
|
-
set :mongrel_pid_file, nil
|
200
|
-
set :mongrel_port, 8000
|
201
|
-
set :mongrel_prefix, nil
|
202
|
-
set :mongrel_servers, 2
|
203
|
-
set :mongrel_user, nil
|
204
|
-
|
205
|
-
desc "Prepares application servers for deployment. Mongrel configuration is set via the mongrel_*
|
206
|
-
variables.".cleanup
|
207
|
-
|
208
|
-
remote_task :setup_app, :roles => :app do
|
209
|
-
dirs = [deploy_to, releases_path, scm_path, shared_path]
|
210
|
-
dirs += %w(system log pids).map { |d| File.join(shared_path, d) }
|
211
|
-
run "umask 02 && mkdir -p #{dirs.join(' ')}"
|
212
|
-
|
213
|
-
cmd = [
|
214
|
-
"#{mongrel_command} cluster::configure",
|
215
|
-
"-N #{mongrel_servers}",
|
216
|
-
"-p #{mongrel_port}",
|
217
|
-
"-e #{mongrel_environment}",
|
218
|
-
"-a #{mongrel_address}",
|
219
|
-
"-c #{current_path}",
|
220
|
-
"-C #{mongrel_conf}",
|
221
|
-
("-P #{mongrel_pid_file}" if mongrel_pid_file),
|
222
|
-
("-l #{mongrel_log_file}" if mongrel_log_file),
|
223
|
-
("--user #{mongrel_user}" if mongrel_user),
|
224
|
-
("--group #{mongrel_group}" if mongrel_group),
|
225
|
-
("--prefix #{mongrel_prefix}" if mongrel_prefix),
|
226
|
-
("-S #{mongrel_config_script}" if mongrel_config_script),
|
227
|
-
].compact.join ' '
|
228
|
-
|
229
|
-
run cmd
|
230
|
-
end
|
231
|
-
|
232
|
-
desc "Restart the app servers"
|
233
|
-
|
234
|
-
remote_task :start_app, :roles => :app do
|
235
|
-
cmd = "#{mongrel_command} cluster::restart -C #{mongrel_conf}"
|
236
|
-
cmd << ' --clean' if mongrel_clean
|
237
|
-
run cmd
|
238
|
-
end
|
239
|
-
|
240
|
-
desc "Stop the app servers"
|
241
|
-
|
242
|
-
remote_task :stop_app, :roles => :app do
|
243
|
-
cmd = "#{mongrel_command} cluster::stop -C #{mongrel_conf}"
|
244
|
-
cmd << ' --clean' if mongrel_clean
|
245
|
-
run cmd
|
246
|
-
end
|
247
|
-
|
248
|
-
##
|
249
|
-
# Apache web server
|
250
|
-
|
251
|
-
set :web_command, "apachectl"
|
252
|
-
|
253
|
-
desc "Restart the web servers"
|
254
|
-
|
255
|
-
remote_task :start_web, :roles => :web do
|
256
|
-
run "#{web_command} restart"
|
257
|
-
end
|
258
|
-
|
259
|
-
desc "Stop the web servers"
|
260
|
-
|
261
|
-
remote_task :stop_web, :roles => :web do
|
262
|
-
run "#{web_command} stop"
|
263
|
-
end
|
264
|
-
|
265
|
-
##
|
266
|
-
# Everything HTTP.
|
267
|
-
|
268
|
-
desc "Restart the web and app servers"
|
269
|
-
|
270
|
-
remote_task :start do
|
271
|
-
Rake::Task['vlad:start_app'].invoke
|
272
|
-
Rake::Task['vlad:start_web'].invoke
|
273
|
-
end
|
274
|
-
|
275
|
-
desc "Stop the web and app servers"
|
276
|
-
|
277
|
-
remote_task :stop do
|
278
|
-
Rake::Task['vlad:stop_app'].invoke
|
279
|
-
Rake::Task['vlad:stop_web'].invoke
|
280
|
-
end
|
281
|
-
|
282
168
|
end # namespace vlad
|
data/lib/vlad/mongrel.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'vlad'
|
2
|
+
|
3
|
+
namespace :vlad do
|
4
|
+
##
|
5
|
+
# Mongrel app server
|
6
|
+
|
7
|
+
set :mongrel_address, "127.0.0.1"
|
8
|
+
set :mongrel_clean, false
|
9
|
+
set :mongrel_command, 'mongrel_rails'
|
10
|
+
set(:mongrel_conf) { "#{shared_path}/mongrel_cluster.conf" }
|
11
|
+
set :mongrel_config_script, nil
|
12
|
+
set :mongrel_environment, "production"
|
13
|
+
set :mongrel_group, nil
|
14
|
+
set :mongrel_log_file, nil
|
15
|
+
set :mongrel_pid_file, nil
|
16
|
+
set :mongrel_port, 8000
|
17
|
+
set :mongrel_prefix, nil
|
18
|
+
set :mongrel_servers, 2
|
19
|
+
set :mongrel_user, nil
|
20
|
+
|
21
|
+
desc "Prepares application servers for deployment. Mongrel
|
22
|
+
configuration is set via the mongrel_* variables.".cleanup
|
23
|
+
|
24
|
+
remote_task :setup_app, :roles => :app do
|
25
|
+
dirs = [deploy_to, releases_path, scm_path, shared_path]
|
26
|
+
dirs += %w(system log pids).map { |d| File.join(shared_path, d) }
|
27
|
+
run "umask 02 && mkdir -p #{dirs.join(' ')}"
|
28
|
+
|
29
|
+
cmd = [
|
30
|
+
"#{mongrel_command} cluster::configure",
|
31
|
+
"-N #{mongrel_servers}",
|
32
|
+
"-p #{mongrel_port}",
|
33
|
+
"-e #{mongrel_environment}",
|
34
|
+
"-a #{mongrel_address}",
|
35
|
+
"-c #{current_path}",
|
36
|
+
"-C #{mongrel_conf}",
|
37
|
+
("-P #{mongrel_pid_file}" if mongrel_pid_file),
|
38
|
+
("-l #{mongrel_log_file}" if mongrel_log_file),
|
39
|
+
("--user #{mongrel_user}" if mongrel_user),
|
40
|
+
("--group #{mongrel_group}" if mongrel_group),
|
41
|
+
("--prefix #{mongrel_prefix}" if mongrel_prefix),
|
42
|
+
("-S #{mongrel_config_script}" if mongrel_config_script),
|
43
|
+
].compact.join ' '
|
44
|
+
|
45
|
+
run cmd
|
46
|
+
end
|
47
|
+
|
48
|
+
def mongrel(cmd) # :nodoc:
|
49
|
+
cmd = "#{mongrel_command} #{cmd} -C #{mongrel_conf}"
|
50
|
+
cmd << ' --clean' if mongrel_clean
|
51
|
+
cmd
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Restart the app servers"
|
55
|
+
|
56
|
+
remote_task :start_app, :roles => :app do
|
57
|
+
run mongrel("cluster::restart")
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Stop the app servers"
|
61
|
+
|
62
|
+
remote_task :stop_app, :roles => :app do
|
63
|
+
run mongrel("cluster::stop")
|
64
|
+
end
|
65
|
+
end
|