Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/tools/capistrano/lib/capistrano/recipes/deploy.rb

Revision 8919, 20.1 kB (checked in by minam, 2 years ago)

Fix incorrect reference to the 'setup' task (closes #10819)

Line 
1 require 'yaml'
2 require 'capistrano/recipes/deploy/scm'
3 require 'capistrano/recipes/deploy/strategy'
4
5 def _cset(name, *args, &block)
6   unless exists?(name)
7     set(name, *args, &block)
8   end
9 end
10
11 # =========================================================================
12 # These variables MUST be set in the client capfiles. If they are not set,
13 # the deploy will fail with an error.
14 # =========================================================================
15
16 _cset(:application) { abort "Please specify the name of your application, set :application, 'foo'" }
17 _cset(:repository)  { abort "Please specify the repository that houses your application's code, set :repository, 'foo'" }
18
19 # =========================================================================
20 # These variables may be set in the client capfile if their default values
21 # are not sufficient.
22 # =========================================================================
23
24 _cset :scm, :subversion
25 _cset :deploy_via, :checkout
26
27 _cset(:deploy_to) { "/u/apps/#{application}" }
28 _cset(:revision)  { source.head }
29
30 # =========================================================================
31 # These variables should NOT be changed unless you are very confident in
32 # what you are doing. Make sure you understand all the implications of your
33 # changes if you do decide to muck with these!
34 # =========================================================================
35
36 _cset(:source)            { Capistrano::Deploy::SCM.new(scm, self) }
37 _cset(:real_revision)     { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") { `#{cmd}` } } }
38
39 _cset(:strategy)          { Capistrano::Deploy::Strategy.new(deploy_via, self) }
40
41 _cset(:release_name)      { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") }
42
43 _cset :version_dir,       "releases"
44 _cset :shared_dir,        "shared"
45 _cset :current_dir,       "current"
46
47 _cset(:releases_path)     { File.join(deploy_to, version_dir) }
48 _cset(:shared_path)       { File.join(deploy_to, shared_dir) }
49 _cset(:current_path)      { File.join(deploy_to, current_dir) }
50 _cset(:release_path)      { File.join(releases_path, release_name) }
51
52 _cset(:releases)          { capture("ls -x #{releases_path}").split.sort }
53 _cset(:current_release)   { File.join(releases_path, releases.last) }
54 _cset(:previous_release)  { File.join(releases_path, releases[-2]) }
55
56 _cset(:current_revision)  { capture("cat #{current_path}/REVISION").chomp }
57 _cset(:latest_revision)   { capture("cat #{current_release}/REVISION").chomp }
58 _cset(:previous_revision) { capture("cat #{previous_release}/REVISION").chomp }
59
60 _cset(:run_method)        { fetch(:use_sudo, true) ? :sudo : :run }
61
62 # some tasks, like symlink, need to always point at the latest release, but
63 # they can also (occassionally) be called standalone. In the standalone case,
64 # the timestamped release_path will be inaccurate, since the directory won't
65 # actually exist. This variable lets tasks like symlink work either in the
66 # standalone case, or during deployment.
67 _cset(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release }
68
69 # =========================================================================
70 # These are helper methods that will be available to your recipes.
71 # =========================================================================
72
73 # Auxiliary helper method for the `deploy:check' task. Lets you set up your
74 # own dependencies.
75 def depend(location, type, *args)
76   deps = fetch(:dependencies, {})
77   deps[location] ||= {}
78   deps[location][type] ||= []
79   deps[location][type] << args
80   set :dependencies, deps
81 end
82
83 # Temporarily sets an environment variable, yields to a block, and restores
84 # the value when it is done.
85 def with_env(name, value)
86   saved, ENV[name] = ENV[name], value
87   yield
88 ensure
89   ENV[name] = saved
90 end
91
92 # =========================================================================
93 # These are the tasks that are available to help with deploying web apps,
94 # and specifically, Rails applications. You can have cap give you a summary
95 # of them with `cap -T'.
96 # =========================================================================
97
98 namespace :deploy do
99   desc <<-DESC
100     Deploys your project. This calls both `update' and `restart'. Note that \
101     this will generally only work for applications that have already been deployed \
102     once. For a "cold" deploy, you'll want to take a look at the `deploy:cold' \
103     task, which handles the cold start specifically.
104   DESC
105   task :default do
106     update
107     restart
108   end
109
110   desc <<-DESC
111     Prepares one or more servers for deployment. Before you can use any \
112     of the Capistrano deployment tasks with your project, you will need to \
113     make sure all of your servers have been prepared with `cap deploy:setup'. When \
114     you add a new server to your cluster, you can easily run the setup task \
115     on just that server by specifying the HOSTS environment variable:
116
117       $ cap HOSTS=new.server.com deploy:setup
118
119     It is safe to run this task on servers that have already been set up; it \
120     will not destroy any deployed revisions or data.
121   DESC
122   task :setup, :except => { :no_release => true } do
123     dirs = [deploy_to, releases_path, shared_path]
124     dirs += %w(system log pids).map { |d| File.join(shared_path, d) }
125     run "umask 02 && mkdir -p #{dirs.join(' ')}"
126   end
127
128   desc <<-DESC
129     Copies your project and updates the symlink. It does this in a \
130     transaction, so that if either `update_code' or `symlink' fail, all \
131     changes made to the remote servers will be rolled back, leaving your \
132     system in the same state it was in before `update' was invoked. Usually, \
133     you will want to call `deploy' instead of `update', but `update' can be \
134     handy if you want to deploy, but not immediately restart your application.
135   DESC
136   task :update do
137     transaction do
138       update_code
139       symlink
140     end
141   end
142
143   desc <<-DESC
144     Copies your project to the remote servers. This is the first stage \
145     of any deployment; moving your updated code and assets to the deployment \
146     servers. You will rarely call this task directly, however; instead, you \
147     should call the `deploy' task (to do a complete deploy) or the `update' \
148     task (if you want to perform the `restart' task separately).
149
150     You will need to make sure you set the :scm variable to the source \
151     control software you are using (it defaults to :subversion), and the \
152     :deploy_via variable to the strategy you want to use to deploy (it \
153     defaults to :checkout).
154   DESC
155   task :update_code, :except => { :no_release => true } do
156     on_rollback { run "rm -rf #{release_path}; true" }
157     strategy.deploy!
158     finalize_update
159   end
160
161   desc <<-DESC
162     [internal] Touches up the released code. This is called by update_code \
163     after the basic deploy finishes. It assumes a Rails project was deployed, \
164     so if you are deploying something else, you may want to override this \
165     task with your own environment's requirements.
166
167     This task will make the release group-writable (if the :group_writable \
168     variable is set to true, which is the default). It will then set up \
169     symlinks to the shared directory for the log, system, and tmp/pids \
170     directories, and will lastly touch all assets in public/images, \
171     public/stylesheets, and public/javascripts so that the times are \
172     consistent (so that asset timestamping works).
173   DESC
174   task :finalize_update, :except => { :no_release => true } do
175     run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true)
176
177     # mkdir -p is making sure that the directories are there for some SCM's that don't
178     # save empty folders
179     run <<-CMD
180       rm -rf #{latest_release}/log #{latest_release}/public/system #{latest_release}/tmp/pids &&
181       mkdir -p #{latest_release}/public &&
182       mkdir -p #{latest_release}/tmp &&
183       ln -s #{shared_path}/log #{latest_release}/log &&
184       ln -s #{shared_path}/system #{latest_release}/public/system &&
185       ln -s #{shared_path}/pids #{latest_release}/tmp/pids
186     CMD
187
188     stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
189     asset_paths = %w(images stylesheets javascripts).map { |p| "#{latest_release}/public/#{p}" }.join(" ")
190     run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
191   end
192
193   desc <<-DESC
194     Updates the symlink to the most recently deployed version. Capistrano works \
195     by putting each new release of your application in its own directory. When \
196     you deploy a new version, this task's job is to update the `current' symlink \
197     to point at the new version. You will rarely need to call this task \
198     directly; instead, use the `deploy' task (which performs a complete \
199     deploy, including `restart') or the 'update' task (which does everything \
200     except `restart').
201   DESC
202   task :symlink, :except => { :no_release => true } do
203     on_rollback { run "rm -f #{current_path}; ln -s #{previous_release} #{current_path}; true" }
204     run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}"
205   end
206
207   desc <<-DESC
208     Copy files to the currently deployed version. This is useful for updating \
209     files piecemeal, such as when you need to quickly deploy only a single \
210     file. Some files, such as updated templates, images, or stylesheets, \
211     might not require a full deploy, and especially in emergency situations \
212     it can be handy to just push the updates to production, quickly.
213
214     To use this task, specify the files and directories you want to copy as a \
215     comma-delimited list in the FILES environment variable. All directories \
216     will be processed recursively, with all files being pushed to the \
217     deployment servers. Any file or directory starting with a '.' character \
218     will be ignored.
219
220       $ cap deploy:upload FILES=templates,controller.rb
221   DESC
222   task :upload, :except => { :no_release => true } do
223     files = (ENV["FILES"] || "").
224       split(",").
225       map { |f| f.strip!; File.directory?(f) ? Dir["#{f}/**/*"] : f }.
226       flatten.
227       reject { |f| File.directory?(f) || File.basename(f)[0] == ?. }
228
229     abort "Please specify at least one file to update (via the FILES environment variable)" if files.empty?
230
231     files.each do |file|
232       content = File.open(file, "rb") { |f| f.read }
233       put content, File.join(current_path, file)
234     end
235   end
236
237   desc <<-DESC
238     Restarts your application. This works by calling the script/process/reaper \
239     script under the current path.
240    
241     By default, this will be invoked via sudo as the `app' user. If \
242     you wish to run it as a different user, set the :runner variable to \
243     that user. If you are in an environment where you can't use sudo, set \
244     the :use_sudo variable to false:
245    
246       set :use_sudo, false
247   DESC
248   task :restart, :roles => :app, :except => { :no_release => true } do
249     as = fetch(:runner, "app")
250     via = fetch(:run_method, :sudo)
251     invoke_command "#{current_path}/script/process/reaper", :via => via, :as => as
252   end
253
254   desc <<-DESC
255     Rolls back to the previously deployed version. The `current' symlink will \
256     be updated to point at the previously deployed version, and then the \
257     current release will be removed from the servers. You'll generally want \
258     to call `rollback' instead, as it performs a `restart' as well.
259   DESC
260   task :rollback_code, :except => { :no_release => true } do
261     if releases.length < 2
262       abort "could not rollback the code because there is no prior release"
263     else
264       run "rm #{current_path}; ln -s #{previous_release} #{current_path} && rm -rf #{current_release}"
265     end
266   end
267
268   desc <<-DESC
269     Rolls back to a previous version and restarts. This is handy if you ever \
270     discover that you've deployed a lemon; `cap rollback' and you're right \
271     back where you were, on the previously deployed version.
272   DESC
273   task :rollback do
274     rollback_code
275     restart
276   end
277
278   desc <<-DESC
279     Run the migrate rake task. By default, it runs this in most recently \
280     deployed version of the app. However, you can specify a different release \
281     via the migrate_target variable, which must be one of :latest (for the \
282     default behavior), or :current (for the release indicated by the \
283     `current' symlink). Strings will work for those values instead of symbols, \
284     too. You can also specify additional environment variables to pass to rake \
285     via the migrate_env variable. Finally, you can specify the full path to the \
286     rake executable by setting the rake variable. The defaults are:
287
288       set :rake,           "rake"
289       set :rails_env,      "production"
290       set :migrate_env,    ""
291       set :migrate_target, :latest
292   DESC
293   task :migrate, :roles => :db, :only => { :primary => true } do
294     rake = fetch(:rake, "rake")
295     rails_env = fetch(:rails_env, "production")
296     migrate_env = fetch(:migrate_env, "")
297     migrate_target = fetch(:migrate_target, :latest)
298
299     directory = case migrate_target.to_sym
300       when :current then current_path
301       when :latest  then current_release
302       else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
303       end
304
305     run "cd #{directory}; #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
306   end
307
308   desc <<-DESC
309     Deploy and run pending migrations. This will work similarly to the \
310     `deploy' task, but will also run any pending migrations (via the \
311     `deploy:migrate' task) prior to updating the symlink. Note that the \
312     update in this case it is not atomic, and transactions are not used, \
313     because migrations are not guaranteed to be reversible.
314   DESC
315   task :migrations do
316     set :migrate_target, :latest
317     update_code
318     migrate
319     symlink
320     restart
321   end
322
323   desc <<-DESC
324     Clean up old releases. By default, the last 5 releases are kept on each \
325     server (though you can change this with the keep_releases variable). All \
326     other deployed revisions are removed from the servers. By default, this \
327     will use sudo to clean up the old releases, but if sudo is not available \
328     for your environment, set the :use_sudo variable to false instead.
329   DESC
330   task :cleanup, :except => { :no_release => true } do
331     count = fetch(:keep_releases, 5).to_i
332     if count >= releases.length
333       logger.important "no old releases to clean up"
334     else
335       logger.info "keeping #{count} of #{releases.length} deployed releases"
336
337       directories = (releases - releases.last(count)).map { |release|
338         File.join(releases_path, release) }.join(" ")
339
340       invoke_command "rm -rf #{directories}", :via => run_method
341     end
342   end
343
344   desc <<-DESC
345     Test deployment dependencies. Checks things like directory permissions, \
346     necessary utilities, and so forth, reporting on the things that appear to \
347     be incorrect or missing. This is good for making sure a deploy has a \
348     chance of working before you actually run `cap deploy'.
349
350     You can define your own dependencies, as well, using the `depend' method:
351
352       depend :remote, :gem, "tzinfo", ">=0.3.3"
353       depend :local, :command, "svn"
354       depend :remote, :directory, "/u/depot/files"
355   DESC
356   task :check, :except => { :no_release => true } do
357     dependencies = strategy.check!
358
359     other = fetch(:dependencies, {})
360     other.each do |location, types|
361       types.each do |type, calls|
362         if type == :gem
363           dependencies.send(location).command(fetch(:gem_command, "gem")).or("`gem' command could not be found. Try setting :gem_command")
364         end
365
366         calls.each do |args|
367           dependencies.send(location).send(type, *args)
368         end
369       end
370     end
371
372     if dependencies.pass?
373       puts "You appear to have all necessary dependencies installed"
374     else
375       puts "The following dependencies failed. Please check them and try again:"
376       dependencies.reject { |d| d.pass? }.each do |d|
377         puts "--> #{d.message}"
378       end
379       abort
380     end
381   end
382
383   desc <<-DESC
384     Deploys and starts a `cold' application. This is useful if you have not \
385     deployed your application before, or if your application is (for some \
386     other reason) not currently running. It will deploy the code, run any \
387     pending migrations, and then instead of invoking `deploy:restart', it will \
388     invoke `deploy:start' to fire up the application servers.
389   DESC
390   task :cold do
391     update
392     migrate
393     start
394   end
395
396   desc <<-DESC
397     Start the application servers. This will attempt to invoke a script \
398     in your application called `script/spin', which must know how to start \
399     your application listeners. For Rails applications, you might just have \
400     that script invoke `script/process/spawner' with the appropriate \
401     arguments.
402
403     By default, the script will be executed via sudo as the `app' user. If \
404     you wish to run it as a different user, set the :runner variable to \
405     that user. If you are in an environment where you can't use sudo, set \
406     the :use_sudo variable to false.
407   DESC
408   task :start, :roles => :app do
409     as = fetch(:runner, "app")
410     via = fetch(:run_method, :sudo)
411     invoke_command "sh -c 'cd #{current_path} && nohup script/spin'", :via => via, :as => as
412   end
413
414   desc <<-DESC
415     Stop the application servers. This will call script/process/reaper for \
416     both the spawner process, and all of the application processes it has \
417     spawned. As such, it is fairly Rails specific and may need to be \
418     overridden for other systems.
419
420     By default, the script will be executed via sudo as the `app' user. If \
421     you wish to run it as a different user, set the :runner variable to \
422     that user. If you are in an environment where you can't use sudo, set \
423     the :use_sudo variable to false.
424   DESC
425   task :stop, :roles => :app do
426     as = fetch(:runner, "app")
427     via = fetch(:run_method, :sudo)
428
429     invoke_command "if [ -f #{current_path}/tmp/pids/dispatch.spawner.pid ]; then #{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid; fi", :via => via, :as => as
430     invoke_command "#{current_path}/script/process/reaper -a kill", :via => via, :as => as
431   end
432
433   namespace :pending do
434     desc <<-DESC
435       Displays the `diff' since your last deploy. This is useful if you want \
436       to examine what changes are about to be deployed. Note that this might \
437       not be supported on all SCM's.
438     DESC
439     task :diff, :except => { :no_release => true } do
440       system(source.local.diff(current_revision))
441     end
442
443     desc <<-DESC
444       Displays the commits since your last deploy. This is good for a summary \
445       of the changes that have occurred since the last deploy. Note that this \
446       might not be supported on all SCM's.
447     DESC
448     task :default, :except => { :no_release => true } do
449       from = source.next_revision(current_revision)
450       system(source.local.log(from))
451     end
452   end
453
454   namespace :web do
455     desc <<-DESC
456       Present a maintenance page to visitors. Disables your application's web \
457       interface by writing a "maintenance.html" file to each web server. The \
458       servers must be configured to detect the presence of this file, and if \
459       it is present, always display it instead of performing the request.
460
461       By default, the maintenance page will just say the site is down for \
462       "maintenance", and will be back "shortly", but you can customize the \
463       page by specifying the REASON and UNTIL environment variables:
464
465         $ cap deploy:web:disable \\
466               REASON="hardware upgrade" \\
467               UNTIL="12pm Central Time"
468
469       Further customization will require that you write your own task.
470     DESC
471     task :disable, :roles => :web, :except => { :no_release => true } do
472       require 'erb'
473       on_rollback { run "rm #{shared_path}/system/maintenance.html" }
474
475       reason = ENV['REASON']
476       deadline = ENV['UNTIL']
477
478       template = File.read(File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml"))
479       result = ERB.new(template).result(binding)
480
481       put result, "#{shared_path}/system/maintenance.html", :mode => 0644
482     end
483
484     desc <<-DESC
485       Makes the application web-accessible again. Removes the \
486       "maintenance.html" page generated by deploy:web:disable, which (if your \
487       web servers are configured correctly) will make your application \
488       web-accessible again.
489     DESC
490     task :enable, :roles => :web, :except => { :no_release => true } do
491       run "rm #{shared_path}/system/maintenance.html"
492     end
493   end
494 end
Note: See TracBrowser for help on using the browser.