vlad 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|