Permalink
Browse files

Make the task description parser a bit smarter, so that heredocs can …

…be used for task descriptions again

git-svn-id: http://svn.rubyonrails.org/rails/tools/capistrano@6495 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 669bb75 commit 0e774ac0d009427aeecd7be4bcdb0e1ca8b69967 @jamis jamis committed Mar 30, 2007
View
332 lib/capistrano/recipes/deploy.rb
@@ -49,70 +49,81 @@
set(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
namespace :deploy do
-desc "Deploys your project. This calls both `update' and `restart'. Note that \
-this will generally only work for applications that have already been deployed \
-once. For a \"cold\" deploy, you'll want to take a look at the `cold_deploy' \
-task, which handles the cold start specifically."
+ desc <<-DESC
+ Deploys your project. This calls both `update' and `restart'. Note that \
+ this will generally only work for applications that have already been deployed \
+ once. For a "cold" deploy, you'll want to take a look at the `cold_deploy' \
+ task, which handles the cold start specifically.
+ DESC
task :default do
update
restart
end
-desc "Prepares one or more servers for deployment. Before you can use any \
-of the Capistrano deployment tasks with your project, you will need to make \
-sure all of your servers have been prepared with `cap setup'. When you add a \
-new server to your cluster, you can easily run the setup task on just that \
-server by specifying the HOSTS environment variable:
+ desc <<-DESC
+ Prepares one or more servers for deployment. Before you can use any \
+ of the Capistrano deployment tasks with your project, you will need to \
+ make sure all of your servers have been prepared with `cap setup'. When \
+ you add a new server to your cluster, you can easily run the setup task \
+ on just that server by specifying the HOSTS environment variable:
- $ cap HOSTS=new.server.com setup
+ $ cap HOSTS=new.server.com setup
-It is safe to run this task on servers that have already been set up; it will \
-not destroy any deployed revisions or data."
+ It is safe to run this task on servers that have already been set up; it \
+ will not destroy any deployed revisions or data.
+ DESC
task :setup, :except => { :no_release => true } do
dirs = [deploy_to, releases_path, shared_path]
dirs += %w(system log pids).map { |d| File.join(shared_path, d) }
run "umask 02 && mkdir -p #{dirs.join(' ')}"
end
-desc "Copies your project and updates the symlink. It does this in a transaction, \
-so that if either `update_code' or `symlink' fail, all changes made to the \
-remote servers will be rolled back, leaving your system in the same state it \
-was in before `update' was invoked. Usually, you will want to call `deploy' \
-instead of `update', but `update' can be handy if you want to deploy, but not \
-immediately restart your application."
+ desc <<-DESC
+ Copies your project and updates the symlink. It does this in a \
+ transaction, so that if either `update_code' or `symlink' fail, all \
+ changes made to the remote servers will be rolled back, leaving your \
+ system in the same state it was in before `update' was invoked. Usually, \
+ you will want to call `deploy' instead of `update', but `update' can be \
+ handy if you want to deploy, but not immediately restart your application.
+ DESC
task :update do
transaction do
update_code
symlink
end
end
-desc "Copies your project to the remote servers. This is the first stage \
-of any deployment; moving your updated code and assets to the deployment \
-servers. You will rarely call this task directly, however; instead, you should \
-call the `deploy' task (to do a complete deploy) or the `update' task (if you \
-want to perform the `restart' task separately).
-
-You will need to make sure you set the :scm variable to the source control \
-software you are using (it defaults to :subversion), and the :deploy_via \
-variable to the strategy you want to use to deploy (it defaults to :checkout)."
+ desc <<-DESC
+ Copies your project to the remote servers. This is the first stage \
+ of any deployment; moving your updated code and assets to the deployment \
+ servers. You will rarely call this task directly, however; instead, you \
+ should call the `deploy' task (to do a complete deploy) or the `update' \
+ task (if you want to perform the `restart' task separately).
+
+ You will need to make sure you set the :scm variable to the source \
+ control software you are using (it defaults to :subversion), and the \
+ :deploy_via variable to the strategy you want to use to deploy (it \
+ defaults to :checkout).
+ DESC
task :update_code, :except => { :no_release => true } do
on_rollback { run "rm -rf #{release_path}; true" }
strategy.deploy!
finalize_update
end
-desc "[internal] Touches up the released code. This is called by update_code \
-after the basic deploy finishes. It assumes a Rails project was deployed, so \
-if you are deploying something else, you may want to override this task with \
-your own environment's requirements.
-
-This task will make the release group-writable (if the :group_writable \
-variable is set to true, which is the default). It will then set up symlinks \
-to the shared directory for the log, system, and tmp/pids directories, and \
-will lastly touch all assets in public/images, public/stylesheets, and \
-public/javascripts so that the times are consistent (so that asset timestamping \
-works)."
+ desc <<-DESC
+ [internal] Touches up the released code. This is called by update_code \
+ after the basic deploy finishes. It assumes a Rails project was deployed, \
+ so if you are deploying something else, you may want to override this \
+ task with your own environment's requirements.
+
+ This task will make the release group-writable (if the :group_writable \
+ variable is set to true, which is the default). It will then set up \
+ symlinks to the shared directory for the log, system, and tmp/pids \
+ directories, and will lastly touch all assets in public/images, \
+ public/stylesheets, and public/javascripts so that the times are \
+ consistent (so that asset timestamping works).
+ DESC
task :finalize_update, :except => { :no_release => true } do
run "chmod -R g+w #{release_path}" if fetch(:group_writable, true)
@@ -128,29 +139,35 @@
run "find #{asset_paths} -exec touch -t #{stamp} {} \\;; true", :env => { "TZ" => "UTC" }
end
-desc "Updates the symlink to the deployed version. Capistrano works by putting \
-each new release of your application in its own directory. When you deploy a \
-new version, this task's job is to update the `current' symlink to point at \
-the new version. You will rarely need to call this task directly; instead, use \
-the `deploy' task (which performs a complete deploy, including `restart') or \
-the 'update' task (which does everything except `restart')."
+ desc <<-DESC
+ Updates the symlink to the deployed version. Capistrano works by putting \
+ each new release of your application in its own directory. When you \
+ deploy a new version, this task's job is to update the `current' symlink \
+ to point at the new version. You will rarely need to call this task \
+ directly; instead, use the `deploy' task (which performs a complete \
+ deploy, including `restart') or the 'update' task (which does everything \
+ except `restart').
+ DESC
task :symlink, :except => { :no_release => true } do
on_rollback { run "rm -f #{current_path}; ln -s #{previous_release} #{current_path}; true" }
run "rm -f #{current_path} && ln -s #{release_path} #{current_path}"
end
-desc "Copy files to the currently deployed version. This is useful for updating \
-files piecemeal, such as when you need to quickly deploy only a single file. \
-Some files, such as updated templates, images, or stylesheets, might not require \
-a full deploy, and especially in emergency situations it can be handy to just \
-push the updates to production, quickly.
-
-To use this task, specify the files and directories you want to copy as a \
-comma-delimited list in the FILES environment variable. All directories will \
-be processed recursively, with all files being pushed to the deployment \
-servers. Any file or directory starting with a '.' character will be ignored.
-
- $ cap deploy:upload FILES=templates,controller.rb"
+ desc <<-DESC
+ Copy files to the currently deployed version. This is useful for updating \
+ files piecemeal, such as when you need to quickly deploy only a single \
+ file. Some files, such as updated templates, images, or stylesheets, \
+ might not require a full deploy, and especially in emergency situations \
+ it can be handy to just push the updates to production, quickly.
+
+ To use this task, specify the files and directories you want to copy as a \
+ comma-delimited list in the FILES environment variable. All directories \
+ will be processed recursively, with all files being pushed to the \
+ deployment servers. Any file or directory starting with a '.' character \
+ will be ignored.
+
+ $ cap deploy:upload FILES=templates,controller.rb
+ DESC
task :upload, :except => { :no_release => true } do
files = (ENV["FILES"] || "").
split(",").
@@ -165,21 +182,25 @@
end
end
-desc "Restarts your application. This works by calling the script/process/reaper \
-script under the current path. By default, this will be invoked via sudo, but \
-if you are in an environment where sudo is not an option, or is not allowed, \
-you can indicate that restarts should use `run' instead by setting the \
-`use_sudo' variable to false:
+ desc <<-DESC
+ Restarts your application. This works by calling the script/process/reaper \
+ script under the current path. By default, this will be invoked via sudo, \
+ but if you are in an environment where sudo is not an option, or is not \
+ allowed, you can indicate that restarts should use `run' instead by \
+ setting the `use_sudo' variable to false:
- set :use_sudo, false"
+ set :use_sudo, false
+ DESC
task :restart, :roles => :app, :except => { :no_release => true } do
invoke_command "#{current_path}/script/process/reaper", :via => run_method
end
-desc "Rolls back to the previously deployed version. The `current' symlink will \
-be updated to point at the previously deployed version, and then the current \
-release will be removed from the servers. You'll generally want to call \
-`rollback' instead, as it performs a `restart' as well."
+ desc <<-DESC
+ Rolls back to the previously deployed version. The `current' symlink will \
+ be updated to point at the previously deployed version, and then the \
+ current release will be removed from the servers. You'll generally want \
+ to call `rollback' instead, as it performs a `restart' as well.
+ DESC
task :rollback_code, :except => { :no_release => true } do
if releases.length < 2
abort "could not rollback the code because there is no prior release"
@@ -188,28 +209,32 @@
end
end
-desc "Rolls back to a previous version and restarts. This is handy if you ever \
-discover that you've deployed a lemon; `cap rollback' and you're right back \
-where you were, on the previously deployed version."
+ desc <<-DESC
+ Rolls back to a previous version and restarts. This is handy if you ever \
+ discover that you've deployed a lemon; `cap rollback' and you're right \
+ back where you were, on the previously deployed version.
+ DESC
task :rollback do
rollback_code
restart
end
-desc "Run the migrate rake task. By default, it runs this in most recently \
-deployed version of the app. However, you can specify a different release \
-via the migrate_target variable, which must be one of :latest (for the \
-default behavior), or :current (for the release indicated by the `current' \
-symlink). latest release to be deployed with the update_code task). Strings will work \
-for those values instead of symbols, too. You can also specify additional \
-environment variables to pass to rake via the migrate_env variable. Finally, \
-you can specify the full path to the rake executable by setting the rake \
-variable. The defaults are:
-
- set :rake, \"rake\"
- set :rails_env, \"production\"
- set :migrate_env, \"\"
- set :migrate_target, :latest"
+ desc <<-DESC
+ Run the migrate rake task. By default, it runs this in most recently \
+ deployed version of the app. However, you can specify a different release \
+ via the migrate_target variable, which must be one of :latest (for the \
+ default behavior), or :current (for the release indicated by the \
+ `current' symlink). latest release to be deployed with the update_code \
+ task). Strings will work for those values instead of symbols, too. You \
+ can also specify additional environment variables to pass to rake via the \
+ migrate_env variable. Finally, you can specify the full path to the rake \
+ executable by setting the rake variable. The defaults are:
+
+ set :rake, "rake"
+ set :rails_env, "production"
+ set :migrate_env, ""
+ set :migrate_target, :latest
+ DESC
task :migrate, :roles => :db, :only => { :primary => true } do
rake = fetch(:rake, "rake")
rails_env = fetch(:rails_env, "production")
@@ -225,11 +250,13 @@
run "cd #{directory}; #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
end
-desc "Deploy and run pending migrations. This will work similarly to the \
-`deploy' task, but will also run any pending migrations (via the `deploy:migrate' \
-task) prior to updating the symlink. Note that the update in this case it is \
-not atomic, and transactions are not used, because migrations are not \
-guaranteed to be reversible."
+ desc <<-DESC
+ Deploy and run pending migrations. This will work similarly to the \
+ `deploy' task, but will also run any pending migrations (via the \
+ `deploy:migrate' task) prior to updating the symlink. Note that the \
+ update in this case it is not atomic, and transactions are not used, \
+ because migrations are not guaranteed to be reversible.
+ DESC
task :migrations do
set :migrate_target, :latest
update_code
@@ -238,11 +265,13 @@
restart
end
-desc "Clean up old releases. By default, the last 5 releases are kept on each \
-server (though you can change this with the keep_releases variable). All other \
-deployed revisions are removed from the servers. By default, this will use \
-sudo to clean up the old releases, but if sudo is not available for your \
-environment, set the :use_sudo variable to false instead."
+ desc <<-DESC
+ Clean up old releases. By default, the last 5 releases are kept on each \
+ server (though you can change this with the keep_releases variable). All \
+ other deployed revisions are removed from the servers. By default, this \
+ will use sudo to clean up the old releases, but if sudo is not available \
+ for your environment, set the :use_sudo variable to false instead.
+ DESC
task :cleanup, :except => { :no_release => true } do
count = fetch(:keep_releases, 5).to_i
if count >= releases.length
@@ -257,10 +286,12 @@
end
end
-desc "Test deployment dependencies. Checks things like directory permissions, \
-necessary utilities, and so forth, reporting on the things that appear to be \
-incorrect or missing. This is good for making sure a deploy has a chance of \
-working before you actually run `cap deploy'!"
+ desc <<-DESC
+ Test deployment dependencies. Checks things like directory permissions, \
+ necessary utilities, and so forth, reporting on the things that appear to \
+ be incorrect or missing. This is good for making sure a deploy has a \
+ chance of working before you actually run `cap deploy'.
+ DESC
task :check, :except => { :no_release => true } do
dependencies = strategy.check!
if dependencies.pass?
@@ -273,41 +304,48 @@
end
end
-desc "Deploys and starts a `cold' application. This is useful if you have not \
-deployed your application before, or if your application is (for some other \
-reason) not currently running. It will deploy the code, and then instead of \
-invoking `deploy:restart', it will invoke `deploy:app:start' to fire up the \
-application servers."
+ desc <<-DESC
+ Deploys and starts a `cold' application. This is useful if you have not \
+ deployed your application before, or if your application is (for some \
+ other reason) not currently running. It will deploy the code, and then \
+ instead of invoking `deploy:restart', it will invoke `deploy:app:start' \
+ to fire up the application servers.
+ DESC
task :cold do
update
app.start
end
namespace :app do
-desc "Start the application servers. This will attempt to invoke a script \
-in your application called `script/spin', which must know how to start your \
-application listeners. For Rails applications, you might just have that script \
-invoke `script/process/spawner' with the appropriate arguments.
-
-By default, the script will be executed via sudo as the `app' user. If you \
-wish to run it as a different user, set the :runner variable to that user. \
-If you are in an environment where you can't use sudo, set the :use_sudo \
-variable to false."
+ desc <<-DESC
+ Start the application servers. This will attempt to invoke a script \
+ in your application called `script/spin', which must know how to start \
+ your application listeners. For Rails applications, you might just have \
+ that script invoke `script/process/spawner' with the appropriate \
+ arguments.
+
+ By default, the script will be executed via sudo as the `app' user. If \
+ you wish to run it as a different user, set the :runner variable to \
+ that user. If you are in an environment where you can't use sudo, set \
+ the :use_sudo variable to false.
+ DESC
task :start, :roles => :app do
as = fetch(:runner, "app")
via = fetch(:run_method, :sudo)
invoke_command "sh -c 'cd #{current_path} && nohup script/spin'", :via => via, :as => as
end
-desc "Stop the application servers. This will call script/process/reaper for \
-both the spawner process, and all of the application processes it has spawned. \
-As such, it is fairly Rails specific and may need to be overridden for other \
-systems.
-
-By default, the script will be executed via sudo as the `app' user. If you \
-wish to run it as a different user, set the :runner variable to that user. \
-If you are in an environment where you can't use sudo, set the :use_sudo \
-variable to false."
+ desc <<-DESC
+ Stop the application servers. This will call script/process/reaper for \
+ both the spawner process, and all of the application processes it has \
+ spawned. As such, it is fairly Rails specific and may need to be \
+ overridden for other systems.
+
+ By default, the script will be executed via sudo as the `app' user. If \
+ you wish to run it as a different user, set the :runner variable to \
+ that user. If you are in an environment where you can't use sudo, set \
+ the :use_sudo variable to false.
+ DESC
task :stop, :roles => :app do
as = fetch(:runner, "app")
via = fetch(:run_method, :sudo)
@@ -318,35 +356,42 @@
end
namespace :pending do
-desc "Displays the `diff' since your last deploy. This is useful if you want \
-to examine what changes are about to be deployed. Note that this might not be \
-supported on all SCM's."
+ desc <<-DESC
+ Displays the `diff' since your last deploy. This is useful if you want \
+ to examine what changes are about to be deployed. Note that this might \
+ not be supported on all SCM's.
+ DESC
task :diff, :except => { :no_release => true } do
system(source.diff(current_revision))
end
-desc "Displays the commits since your last deploy. This is good for a summary \
-of the changes that have occurred since the last deploy. Note that this might \
-not be supported on all SCM's."
+ desc <<-DESC
+ Displays the commits since your last deploy. This is good for a summary \
+ of the changes that have occurred since the last deploy. Note that this \
+ might not be supported on all SCM's.
+ DESC
task :default, :except => { :no_release => true } do
system(source.log(current_revision))
end
end
namespace :web do
-desc "Present a maintenance page to visitors. Disables your application's web \
-interface by writing a \"maintenance.html\" file to each web server. The \
-servers must be configured to detect the presence of this file, and if it is \
-present, always display it instead of performing the request.
-
-By default, the maintenance page will just say the site is down for \
-\"maintenance\", and will be back \"shortly\", but you can customize the page \
-by specifying the REASON and UNTIL environment variables:
-
- $ cap deploy:web:disable BECAUSE=\"hardware upgrade\" \\
- UNTIL=\"12pm Central Time\"
-
-Further customization will require that you write your own task."
+ desc <<-DESC
+ Present a maintenance page to visitors. Disables your application's web \
+ interface by writing a "maintenance.html" file to each web server. The \
+ servers must be configured to detect the presence of this file, and if \
+ it is present, always display it instead of performing the request.
+
+ By default, the maintenance page will just say the site is down for \
+ "maintenance", and will be back "shortly", but you can customize the \
+ page by specifying the REASON and UNTIL environment variables:
+
+ $ cap deploy:web:disable \\
+ BECAUSE="hardware upgrade" \\
+ UNTIL="12pm Central Time"
+
+ Further customization will require that you write your own task.
+ DESC
task :disable, :roles => :web, :except => { :no_release => true } do
require 'erb'
on_rollback { run "rm #{shared_path}/system/maintenance.html" }
@@ -360,9 +405,12 @@
put result, "#{shared_path}/system/maintenance.html", :mode => 0644
end
-desc "Makes the application web-accessible again. Removes the \"maintenance.html\" \
-page generated by deploy:web:disable, which (if your web servers are \
-configured correclty) will make your application web-accessible again."
+ desc <<-DESC
+ Makes the application web-accessible again. Removes the \
+ "maintenance.html" page generated by deploy:web:disable, which (if your \
+ web servers are configured correctly) will make your application \
+ web-accessible again.
+ DESC
task :enable, :roles => :web, :except => { :no_release => true } do
run "rm #{shared_path}/system/maintenance.html"
end
View
36 lib/capistrano/recipes/standard.rb
@@ -1,33 +1,33 @@
desc <<-DESC
-Invoke a single command on the remote servers. This is useful for performing \
-one-off commands that may not require a full task to be written for them. \
-Simply specify the command to execute via the COMMAND environment variable. \
-To execute the command only on certain roles, specify the ROLES environment \
-variable as a comma-delimited list of role names. Alternatively, you can \
-specify the HOSTS environment variable as a comma-delimited list of hostnames \
-to execute the task on those hosts, explicitly. Lastly, if you want to \
-execute the command via sudo, specify a non-empty value for the SUDO \
-environment variable.
+ Invoke a single command on the remote servers. This is useful for performing \
+ one-off commands that may not require a full task to be written for them. \
+ Simply specify the command to execute via the COMMAND environment variable. \
+ To execute the command only on certain roles, specify the ROLES environment \
+ variable as a comma-delimited list of role names. Alternatively, you can \
+ specify the HOSTS environment variable as a comma-delimited list of hostnames \
+ to execute the task on those hosts, explicitly. Lastly, if you want to \
+ execute the command via sudo, specify a non-empty value for the SUDO \
+ environment variable.
-Sample usage:
+ Sample usage:
- $ cap COMMAND=uptime HOSTS=foo.capistano.test invoke
- $ cap ROLES=app,web SUDO=1 COMMAND="tail -f /var/log/messages" invoke
+ $ cap COMMAND=uptime HOSTS=foo.capistano.test invoke
+ $ cap ROLES=app,web SUDO=1 COMMAND="tail -f /var/log/messages" invoke
DESC
task :invoke do
method = ENV["SUDO"] ? :sudo : :run
invoke_command(ENV["COMMAND"], :via => method)
end
desc <<-DESC
-Begin an interactive Capistrano session. This gives you an interactive \
-terminal from which to execute tasks and commands on all of your servers. \
-(This is still an experimental feature, and is subject to change without \
-notice!)
+ Begin an interactive Capistrano session. This gives you an interactive \
+ terminal from which to execute tasks and commands on all of your servers. \
+ (This is still an experimental feature, and is subject to change without \
+ notice!)
-Sample usage:
+ Sample usage:
- $ cap shell
+ $ cap shell
DESC
task :shell do
require 'capistrano/shell'
View
14 lib/capistrano/recipes/upgrade.rb
@@ -2,12 +2,14 @@
# Capistrano 2.x.
namespace :upgrade do
-desc "Migrate from the revisions log to REVISION. Capistrano 1.x recorded each \
-deployment to a revisions.log file. Capistrano 2.x is cleaner, and just puts \
-a REVISION file in the root of the deployed revision. This task migrates \
-from the revisions.log used in Capistrano 1.x, to the REVISION tag file used \
-in Capistrano 2.x. It is non-destructive and may be safely run any number of \
-times."
+ desc <<-DESC
+ Migrate from the revisions log to REVISION. Capistrano 1.x recorded each \
+ deployment to a revisions.log file. Capistrano 2.x is cleaner, and just \
+ puts a REVISION file in the root of the deployed revision. This task \
+ migrates from the revisions.log used in Capistrano 1.x, to the REVISION \
+ tag file used in Capistrano 2.x. It is non-destructive and may be safely \
+ run any number of times.
+ DESC
task :revisions do
revisions = capture("cat #{deploy_to}/revisions.log")
View
16 lib/capistrano/task_definition.rb
@@ -41,9 +41,19 @@ def description(rebuild=false)
@description = nil if rebuild
@description ||= begin
description = options[:desc] || ""
- description.strip.
- gsub(/\r\n/, "\n").
- gsub(/\\\n/, " ")
+
+ indentation = description[/\A\s+/]
+ if indentation
+ reformatted_description = ""
+ description.strip.each_line do |line|
+ line = line.chomp.sub(/^#{indentation}/, "")
+ line = line.gsub(/#{indentation}\s*/, " ") if line[/^\S/]
+ reformatted_description << line << "\n"
+ end
+ description = reformatted_description
+ end
+
+ description.strip.gsub(/\r\n/, "\n")
end
end
View
28 test/task_definition_test.rb
@@ -108,6 +108,34 @@ def test_description_should_normalize_newlines
assert_equal "a\nb\nc", new_task(:testing, @namespace, :desc => "a\nb\r\nc").description
end
+ def test_description_should_detect_and_remove_indentation
+ desc = <<-DESC
+ Here is some indented text \
+ and I want all of this to \
+ run together on a single line, \
+ without any extraneous spaces.
+
+ additional indentation will
+ be preserved.
+ DESC
+
+ task = new_task(:testing, @namespace, :desc => desc)
+ assert_equal "Here is some indented text and I want all of this to run together on a single line, without any extraneous spaces.\n\n additional indentation will\n be preserved.", task.description
+ end
+
+ def test_description_munging_should_be_sensitive_to_code_blocks
+ desc = <<-DESC
+ Here is a line \
+ wrapped with spacing in it.
+
+ foo bar
+ baz bang
+ DESC
+
+ task = new_task(:testing, @namespace, :desc => desc)
+ assert_equal "Here is a line wrapped with spacing in it.\n\n foo bar\n baz bang", task.description
+ end
+
def test_task_brief_description_should_return_first_sentence_in_description
desc = "This is the task. It does all kinds of things."
task = new_task(:testing, @namespace, :desc => desc)

0 comments on commit 0e774ac

Please sign in to comment.