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/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
- # Declare a remote host and its roles.
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
- # Returns the name of the host that the current task is executing on.
45
- # <tt>target_host</tt> can uniquely identify a particular task/host combination.
46
- def target_host
47
- Thread.current[:task].target_host
48
- end
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.0.0'
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
- # Loads tasks file +tasks_file+ and the vlad_tasks file.
68
- def self.load tasks_file = 'config/deploy.rb'
69
- Kernel.load tasks_file
70
- require 'vlad_tasks'
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
- Rake::RemoteTask.reset
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
@@ -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
- # Ideal scenarios:
15
- #
16
- # Initial:
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 a copy
55
- of the repository, exports it as the latest release, fixes up your
56
- symlinks, touches your assets, symlinks the latest revision to current and
57
- logs the update.".cleanup
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 #{directory}; #{rake} RAILS_ENV=#{rails_env} #{migrate_args} db:migrate"
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. This is useful for
126
- updating files piecemeal when you need to quickly deploy only a single
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
- if count >= releases.length then
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
@@ -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