capistrano 1.4.2 → 2.0.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.
Files changed (113) hide show
  1. data/CHANGELOG +140 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README +22 -14
  4. data/bin/cap +1 -8
  5. data/bin/capify +77 -0
  6. data/examples/sample.rb +10 -109
  7. data/lib/capistrano.rb +1 -0
  8. data/lib/capistrano/callback.rb +41 -0
  9. data/lib/capistrano/cli.rb +17 -317
  10. data/lib/capistrano/cli/execute.rb +82 -0
  11. data/lib/capistrano/cli/help.rb +102 -0
  12. data/lib/capistrano/cli/help.txt +53 -0
  13. data/lib/capistrano/cli/options.rb +183 -0
  14. data/lib/capistrano/cli/ui.rb +28 -0
  15. data/lib/capistrano/command.rb +62 -29
  16. data/lib/capistrano/configuration.rb +25 -226
  17. data/lib/capistrano/configuration/actions/file_transfer.rb +35 -0
  18. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  19. data/lib/capistrano/configuration/actions/invocation.rb +127 -0
  20. data/lib/capistrano/configuration/callbacks.rb +148 -0
  21. data/lib/capistrano/configuration/connections.rb +159 -0
  22. data/lib/capistrano/configuration/execution.rb +126 -0
  23. data/lib/capistrano/configuration/loading.rb +112 -0
  24. data/lib/capistrano/configuration/namespaces.rb +190 -0
  25. data/lib/capistrano/configuration/roles.rb +51 -0
  26. data/lib/capistrano/configuration/servers.rb +75 -0
  27. data/lib/capistrano/configuration/variables.rb +127 -0
  28. data/lib/capistrano/errors.rb +15 -0
  29. data/lib/capistrano/extensions.rb +27 -8
  30. data/lib/capistrano/gateway.rb +54 -29
  31. data/lib/capistrano/logger.rb +11 -11
  32. data/lib/capistrano/recipes/compat.rb +32 -0
  33. data/lib/capistrano/recipes/deploy.rb +483 -0
  34. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  35. data/lib/capistrano/recipes/deploy/local_dependency.rb +46 -0
  36. data/lib/capistrano/recipes/deploy/remote_dependency.rb +65 -0
  37. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  38. data/lib/capistrano/recipes/deploy/scm/base.rb +180 -0
  39. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  40. data/lib/capistrano/recipes/deploy/scm/cvs.rb +151 -0
  41. data/lib/capistrano/recipes/deploy/scm/darcs.rb +85 -0
  42. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +129 -0
  43. data/lib/capistrano/recipes/deploy/scm/perforce.rb +126 -0
  44. data/lib/capistrano/recipes/deploy/scm/subversion.rb +103 -0
  45. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  46. data/lib/capistrano/recipes/deploy/strategy/base.rb +64 -0
  47. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  48. data/lib/capistrano/recipes/deploy/strategy/copy.rb +143 -0
  49. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  50. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  51. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +47 -0
  52. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +53 -0
  53. data/lib/capistrano/recipes/standard.rb +26 -276
  54. data/lib/capistrano/recipes/templates/maintenance.rhtml +1 -1
  55. data/lib/capistrano/recipes/upgrade.rb +33 -0
  56. data/lib/capistrano/server_definition.rb +51 -0
  57. data/lib/capistrano/shell.rb +125 -81
  58. data/lib/capistrano/ssh.rb +80 -36
  59. data/lib/capistrano/task_definition.rb +69 -0
  60. data/lib/capistrano/upload.rb +146 -0
  61. data/lib/capistrano/version.rb +13 -17
  62. data/test/cli/execute_test.rb +132 -0
  63. data/test/cli/help_test.rb +139 -0
  64. data/test/cli/options_test.rb +226 -0
  65. data/test/cli/ui_test.rb +28 -0
  66. data/test/cli_test.rb +17 -0
  67. data/test/command_test.rb +284 -25
  68. data/test/configuration/actions/file_transfer_test.rb +40 -0
  69. data/test/configuration/actions/inspect_test.rb +62 -0
  70. data/test/configuration/actions/invocation_test.rb +195 -0
  71. data/test/configuration/callbacks_test.rb +206 -0
  72. data/test/configuration/connections_test.rb +288 -0
  73. data/test/configuration/execution_test.rb +159 -0
  74. data/test/configuration/loading_test.rb +119 -0
  75. data/test/configuration/namespace_dsl_test.rb +283 -0
  76. data/test/configuration/roles_test.rb +47 -0
  77. data/test/configuration/servers_test.rb +90 -0
  78. data/test/configuration/variables_test.rb +180 -0
  79. data/test/configuration_test.rb +60 -212
  80. data/test/deploy/scm/base_test.rb +55 -0
  81. data/test/deploy/strategy/copy_test.rb +146 -0
  82. data/test/extensions_test.rb +69 -0
  83. data/test/fixtures/cli_integration.rb +5 -0
  84. data/test/fixtures/custom.rb +2 -2
  85. data/test/gateway_test.rb +167 -0
  86. data/test/logger_test.rb +123 -0
  87. data/test/server_definition_test.rb +108 -0
  88. data/test/shell_test.rb +64 -0
  89. data/test/ssh_test.rb +67 -154
  90. data/test/task_definition_test.rb +101 -0
  91. data/test/upload_test.rb +131 -0
  92. data/test/utils.rb +31 -39
  93. data/test/version_test.rb +24 -0
  94. metadata +145 -98
  95. data/THANKS +0 -4
  96. data/lib/capistrano/actor.rb +0 -567
  97. data/lib/capistrano/generators/rails/deployment/deployment_generator.rb +0 -25
  98. data/lib/capistrano/generators/rails/deployment/templates/capistrano.rake +0 -49
  99. data/lib/capistrano/generators/rails/deployment/templates/deploy.rb +0 -122
  100. data/lib/capistrano/generators/rails/loader.rb +0 -20
  101. data/lib/capistrano/scm/base.rb +0 -61
  102. data/lib/capistrano/scm/baz.rb +0 -118
  103. data/lib/capistrano/scm/bzr.rb +0 -70
  104. data/lib/capistrano/scm/cvs.rb +0 -129
  105. data/lib/capistrano/scm/darcs.rb +0 -27
  106. data/lib/capistrano/scm/mercurial.rb +0 -83
  107. data/lib/capistrano/scm/perforce.rb +0 -139
  108. data/lib/capistrano/scm/subversion.rb +0 -128
  109. data/lib/capistrano/transfer.rb +0 -97
  110. data/lib/capistrano/utils.rb +0 -26
  111. data/test/actor_test.rb +0 -402
  112. data/test/scm/cvs_test.rb +0 -196
  113. data/test/scm/subversion_test.rb +0 -145
@@ -0,0 +1,64 @@
1
+ require 'capistrano/recipes/deploy/dependencies'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module Strategy
6
+
7
+ # This class defines the abstract interface for all Capistrano
8
+ # deployment strategies. Subclasses must implement at least the
9
+ # #deploy! method.
10
+ class Base
11
+ attr_reader :configuration
12
+
13
+ # Instantiates a strategy with a reference to the given configuration.
14
+ def initialize(config={})
15
+ @configuration = config
16
+ end
17
+
18
+ # Executes the necessary commands to deploy the revision of the source
19
+ # code identified by the +revision+ variable. Additionally, this
20
+ # should write the value of the +revision+ variable to a file called
21
+ # REVISION, in the base of the deployed revision. This file is used by
22
+ # other tasks, to perform diffs and such.
23
+ def deploy!
24
+ raise NotImplementedError, "`deploy!' is not implemented by #{self.class.name}"
25
+ end
26
+
27
+ # Performs a check on the remote hosts to determine whether everything
28
+ # is setup such that a deploy could succeed.
29
+ def check!
30
+ Dependencies.new(configuration) do |d|
31
+ d.remote.directory(configuration[:releases_path]).or("`#{configuration[:releases_path]}' does not exist. Please run `cap deploy:setup'.")
32
+ d.remote.writable(configuration[:deploy_to]).or("You do not have permissions to write to `#{configuration[:deploy_to]}'.")
33
+ d.remote.writable(configuration[:releases_path]).or("You do not have permissions to write to `#{configuration[:releases_path]}'.")
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ # This is to allow helper methods like "run" and "put" to be more
40
+ # easily accessible to strategy implementations.
41
+ def method_missing(sym, *args, &block)
42
+ if configuration.respond_to?(sym)
43
+ configuration.send(sym, *args, &block)
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def logger
52
+ @logger ||= configuration[:logger] || Capistrano::Logger.new(:output => STDOUT)
53
+ end
54
+
55
+ # The revision to deploy. Must return a real revision identifier,
56
+ # and not a pseudo-id.
57
+ def revision
58
+ configuration[:real_revision]
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,20 @@
1
+ require 'capistrano/recipes/deploy/strategy/remote'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module Strategy
6
+
7
+ # Implements the deployment strategy which does an SCM checkout on each
8
+ # target host. This is the default deployment strategy for Capistrano.
9
+ class Checkout < Remote
10
+ protected
11
+
12
+ # Returns the SCM's checkout command for the revision to deploy.
13
+ def command
14
+ @command ||= source.checkout(revision, configuration[:release_path])
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,143 @@
1
+ require 'capistrano/recipes/deploy/strategy/base'
2
+ require 'fileutils'
3
+ require 'tempfile' # Dir.tmpdir
4
+
5
+ module Capistrano
6
+ module Deploy
7
+ module Strategy
8
+
9
+ # This class implements the strategy for deployments which work
10
+ # by preparing the source code locally, compressing it, copying the
11
+ # file to each target host, and uncompressing it to the deployment
12
+ # directory.
13
+ #
14
+ # By default, the SCM checkout command is used to obtain the local copy
15
+ # of the source code. If you would rather use the export operation,
16
+ # you can set the :copy_strategy variable to :export.
17
+ #
18
+ # This deployment strategy supports a special variable,
19
+ # :copy_compression, which must be one of :gzip, :bz2, or
20
+ # :zip, and which specifies how the source should be compressed for
21
+ # transmission to each host.
22
+ class Copy < Base
23
+ # Obtains a copy of the source code locally (via the #command method),
24
+ # compresses it to a single file, copies that file to all target
25
+ # servers, and uncompresses it on each of them into the deployment
26
+ # directory.
27
+ def deploy!
28
+ logger.debug "getting (via #{copy_strategy}) revision #{revision} to #{destination}"
29
+ system(command)
30
+ File.open(File.join(destination, "REVISION"), "w") { |f| f.puts(revision) }
31
+
32
+ logger.trace "compressing #{destination} to #{filename}"
33
+ Dir.chdir(tmpdir) { system(compress(File.basename(destination), File.basename(filename)).join(" ")) }
34
+
35
+ put File.read(filename), remote_filename
36
+ run "cd #{configuration[:releases_path]} && #{decompress(remote_filename).join(" ")} && rm #{remote_filename}"
37
+ ensure
38
+ FileUtils.rm filename rescue nil
39
+ FileUtils.rm_rf destination rescue nil
40
+ end
41
+
42
+ def check!
43
+ super.check do |d|
44
+ d.local.command(source.local.command)
45
+ d.local.command(compress(nil, nil).first)
46
+ d.remote.command(decompress(nil).first)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # Returns the basename of the release_path, which will be used to
53
+ # name the local copy and archive file.
54
+ def destination
55
+ @destination ||= File.join(tmpdir, File.basename(configuration[:release_path]))
56
+ end
57
+
58
+ # Returns the value of the :copy_strategy variable, defaulting to
59
+ # :checkout if it has not been set.
60
+ def copy_strategy
61
+ @copy_strategy ||= configuration.fetch(:copy_strategy, :checkout)
62
+ end
63
+
64
+ # Should return the command(s) necessary to obtain the source code
65
+ # locally.
66
+ def command
67
+ @command ||= case copy_strategy
68
+ when :checkout
69
+ source.checkout(revision, destination)
70
+ when :export
71
+ source.export(revision, destination)
72
+ end
73
+ end
74
+
75
+ # Returns the name of the file that the source code will be
76
+ # compressed to.
77
+ def filename
78
+ @filename ||= File.join(tmpdir, "#{File.basename(destination)}.#{compression_extension}")
79
+ end
80
+
81
+ # The directory to which the copy should be checked out
82
+ def tmpdir
83
+ @tmpdir ||= configuration[:copy_dir] || Dir.tmpdir
84
+ end
85
+
86
+ # The directory on the remote server to which the archive should be
87
+ # copied
88
+ def remote_dir
89
+ @remote_dir ||= configuration[:copy_remote_dir] || "/tmp"
90
+ end
91
+
92
+ # The location on the remote server where the file should be
93
+ # temporarily stored.
94
+ def remote_filename
95
+ @remote_filename ||= File.join(remote_dir, File.basename(filename))
96
+ end
97
+
98
+ # The compression method to use, defaults to :gzip.
99
+ def compression
100
+ configuration[:copy_compression] || :gzip
101
+ end
102
+
103
+ # Returns the file extension used for the compression method in
104
+ # question.
105
+ def compression_extension
106
+ case compression
107
+ when :gzip, :gz then "tar.gz"
108
+ when :bzip2, :bz2 then "tar.bz2"
109
+ when :zip then "zip"
110
+ else raise ArgumentError, "invalid compression type #{compression.inspect}"
111
+ end
112
+ end
113
+
114
+ # Returns the command necessary to compress the given directory
115
+ # into the given file. The command is returned as an array, where
116
+ # the first element is the utility to be used to perform the compression.
117
+ def compress(directory, file)
118
+ case compression
119
+ when :gzip, :gz then ["tar", "czf", file, directory]
120
+ when :bzip2, :bz2 then ["tar", "cjf", file, directory]
121
+ when :zip then ["zip", "-qr", file, directory]
122
+ else raise ArgumentError, "invalid compression type #{compression.inspect}"
123
+ end
124
+ end
125
+
126
+ # Returns the command necessary to decompress the given file,
127
+ # relative to the current working directory. It must also
128
+ # preserve the directory structure in the file. The command is returned
129
+ # as an array, where the first element is the utility to be used to
130
+ # perform the decompression.
131
+ def decompress(file)
132
+ case compression
133
+ when :gzip, :gz then ["tar", "xzf", file]
134
+ when :bzip2, :bz2 then ["tar", "xjf", file]
135
+ when :zip then ["unzip", "-q", file]
136
+ else raise ArgumentError, "invalid compression type #{compression.inspect}"
137
+ end
138
+ end
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,20 @@
1
+ require 'capistrano/recipes/deploy/strategy/remote'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module Strategy
6
+
7
+ # Implements the deployment strategy which does an SCM export on each
8
+ # target host.
9
+ class Export < Remote
10
+ protected
11
+
12
+ # Returns the SCM's export command for the revision to deploy.
13
+ def command
14
+ @command ||= source.export(revision, configuration[:release_path])
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ require 'capistrano/recipes/deploy/strategy/base'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module Strategy
6
+
7
+ # An abstract superclass, which forms the base for all deployment
8
+ # strategies which work by grabbing the code from the repository directly
9
+ # from remote host. This includes deploying by checkout (the default),
10
+ # and deploying by export.
11
+ class Remote < Base
12
+ # Executes the SCM command for this strategy and writes the REVISION
13
+ # mark file to each host.
14
+ def deploy!
15
+ scm_run "#{command} && #{mark}"
16
+ end
17
+
18
+ def check!
19
+ super.check do |d|
20
+ d.remote.command(source.command)
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ # Runs the given command, filtering output back through the
27
+ # #handle_data filter of the SCM implementation.
28
+ def scm_run(command)
29
+ run(command) do |ch,stream,text|
30
+ ch[:state] ||= {}
31
+ output = source.handle_data(ch[:state], stream, text)
32
+ ch.send_data(output) if output
33
+ end
34
+ end
35
+
36
+ # An abstract method which must be overridden in subclasses, to
37
+ # return the actual SCM command(s) which must be executed on each
38
+ # target host in order to perform the deployment.
39
+ def command
40
+ raise NotImplementedError, "`command' is not implemented by #{self.class.name}"
41
+ end
42
+
43
+ # Returns the command which will write the identifier of the
44
+ # revision being deployed to the REVISION file on each host.
45
+ def mark
46
+ "(echo #{revision} > #{configuration[:release_path]}/REVISION)"
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ require 'capistrano/recipes/deploy/strategy/remote'
2
+
3
+ module Capistrano
4
+ module Deploy
5
+ module Strategy
6
+
7
+ # Implements the deployment strategy that keeps a cached checkout of
8
+ # the source code on each remote server. Each deploy simply updates the
9
+ # cached checkout, and then does a copy from the cached copy to the
10
+ # final deployment location.
11
+ class RemoteCache < Remote
12
+ # Executes the SCM command for this strategy and writes the REVISION
13
+ # mark file to each host.
14
+ def deploy!
15
+ update_repository_cache
16
+ copy_repository_cache
17
+ end
18
+
19
+ def check!
20
+ super.check do |d|
21
+ d.remote.writable(shared_path)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def repository_cache
28
+ File.join(shared_path, configuration[:repository_cache] || "cached-copy")
29
+ end
30
+
31
+ def update_repository_cache
32
+ logger.trace "updating the cached checkout on all servers"
33
+ command = "if [ -d #{repository_cache} ]; then " +
34
+ "#{source.sync(revision, repository_cache)}; " +
35
+ "else #{source.checkout(revision, repository_cache)}; fi"
36
+ scm_run(command)
37
+ end
38
+
39
+ def copy_repository_cache
40
+ logger.trace "copying the cached version to #{configuration[:release_path]}"
41
+ run "cp -RPp #{repository_cache} #{configuration[:release_path]} && #{mark}"
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,53 @@
1
+
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+
7
+ <head>
8
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
9
+ <title>System down for maintenance</title>
10
+
11
+ <style type="text/css">
12
+ div.outer {
13
+ position: absolute;
14
+ left: 50%;
15
+ top: 50%;
16
+ width: 500px;
17
+ height: 300px;
18
+ margin-left: -260px;
19
+ margin-top: -150px;
20
+ }
21
+
22
+ .DialogBody {
23
+ margin: 0;
24
+ padding: 10px;
25
+ text-align: left;
26
+ border: 1px solid #ccc;
27
+ border-right: 1px solid #999;
28
+ border-bottom: 1px solid #999;
29
+ background-color: #fff;
30
+ }
31
+
32
+ body { background-color: #fff; }
33
+ </style>
34
+ </head>
35
+
36
+ <body>
37
+
38
+ <div class="outer">
39
+ <div class="DialogBody" style="text-align: center;">
40
+ <div style="text-align: center; width: 200px; margin: 0 auto;">
41
+ <p style="color: red; font-size: 16px; line-height: 20px;">
42
+ The system is down for <%= reason ? reason : "maintenance" %>
43
+ as of <%= Time.now.strftime("%H:%M %Z") %>.
44
+ </p>
45
+ <p style="color: #666;">
46
+ It'll be back <%= deadline ? deadline : "shortly" %>.
47
+ </p>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ </body>
53
+ </html>
@@ -1,287 +1,37 @@
1
- # Standard tasks that are useful for most recipes. It makes a few assumptions:
2
- #
3
- # * The :app role has been defined as the set of machines consisting of the
4
- # application servers.
5
- # * The :web role has been defined as the set of machines consisting of the
6
- # web servers.
7
- # * The :db role has been defined as the set of machines consisting of the
8
- # databases, with exactly one set up as the :primary DB server.
9
- # * The Rails spawner and reaper scripts are being used to manage the FCGI
10
- # processes.
11
-
12
- set :rake, "rake"
13
-
14
- set :rails_env, :production
15
-
16
- set :migrate_target, :current
17
- set :migrate_env, ""
18
-
19
- set :use_sudo, true
20
- set(:run_method) { use_sudo ? :sudo : :run }
21
-
22
- set :spinner_user, :app
23
-
24
- desc "Enumerate and describe every available task."
25
- task :show_tasks do
26
- puts "Available tasks"
27
- puts "---------------"
28
- each_task do |info|
29
- wrap_length = 80 - info[:longest] - 1
30
- lines = info[:desc].gsub(/(.{1,#{wrap_length}})(?:\s|\Z)+/, "\\1\n").split(/\n/)
31
- puts "%-#{info[:longest]}s %s" % [info[:task], lines.shift]
32
- puts "%#{info[:longest]}s %s" % ["", lines.shift] until lines.empty?
33
- puts
34
- end
35
- end
36
-
37
- desc "Set up the expected application directory structure on all boxes"
38
- task :setup, :except => { :no_release => true } do
39
- run <<-CMD
40
- umask 02 &&
41
- mkdir -p #{deploy_to} #{releases_path} #{shared_path} #{shared_path}/system &&
42
- mkdir -p #{shared_path}/log &&
43
- mkdir -p #{shared_path}/pids
44
- CMD
45
- end
46
-
47
- desc <<-DESC
48
- Disable the web server by writing a "maintenance.html" file to the web
49
- servers. The servers must be configured to detect the presence of this file,
50
- and if it is present, always display it instead of performing the request.
51
- DESC
52
- task :disable_web, :roles => :web do
53
- on_rollback { delete "#{shared_path}/system/maintenance.html" }
54
-
55
- maintenance = render("maintenance", :deadline => ENV['UNTIL'],
56
- :reason => ENV['REASON'])
57
- put maintenance, "#{shared_path}/system/maintenance.html", :mode => 0644
58
- end
59
-
60
- desc %(Re-enable the web server by deleting any "maintenance.html" file.)
61
- task :enable_web, :roles => :web do
62
- delete "#{shared_path}/system/maintenance.html"
63
- end
64
-
65
- desc <<-DESC
66
- Sets group permissions on checkout. Useful for team environments, bad on
67
- shared hosts. Override this task if you're on a shared host.
68
- DESC
69
- task :set_permissions, :except => { :no_release => true } do
70
- run "chmod -R g+w #{release_path}"
71
- end
72
-
73
- desc <<-DESC
74
- Update all servers with the latest release of the source code. All this does
75
- is do a checkout (as defined by the selected scm module).
76
- DESC
77
- task :update_code, :except => { :no_release => true } do
78
- on_rollback { delete release_path, :recursive => true }
79
-
80
- source.checkout(self)
81
-
82
- set_permissions
83
-
84
- run <<-CMD
85
- rm -rf #{release_path}/log #{release_path}/public/system &&
86
- ln -nfs #{shared_path}/log #{release_path}/log &&
87
- ln -nfs #{shared_path}/system #{release_path}/public/system
88
- CMD
89
-
90
- run <<-CMD
91
- test -d #{shared_path}/pids &&
92
- rm -rf #{release_path}/tmp/pids &&
93
- ln -nfs #{shared_path}/pids #{release_path}/tmp/pids; true
94
- CMD
95
-
96
- # update the asset timestamps so they are in sync across all servers. This
97
- # lets the asset timestamping feature of rails work correctly
98
- stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
99
- asset_paths = %w(images stylesheets javascripts).map { |p| "#{release_path}/public/#{p}" }
100
- run "TZ=UTC find #{asset_paths.join(" ")} -exec touch -t #{stamp} {} \\;; true"
101
-
102
- # uncache the list of releases, so that the next time it is called it will
103
- # include the newly released path.
104
- @releases = nil
105
- end
106
-
107
- desc <<-DESC
108
- Rollback the latest checked-out version to the previous one by fixing the
109
- symlinks and deleting the current release from all servers.
110
- DESC
111
- task :rollback_code, :except => { :no_release => true } do
112
- if releases.length < 2
113
- raise "could not rollback the code because there is no prior release"
114
- else
115
- run <<-CMD
116
- ln -nfs #{previous_release} #{current_path} &&
117
- rm -rf #{current_release}
118
- CMD
119
- end
120
- end
121
-
122
- desc <<-DESC
123
- Update the 'current' symlink to point to the latest version of
124
- the application's code.
125
- DESC
126
- task :symlink, :except => { :no_release => true } do
127
- on_rollback { run "ln -nfs #{previous_release} #{current_path}" }
128
- run "ln -nfs #{current_release} #{current_path}"
129
- end
130
-
131
- desc <<-DESC
132
- Restart the FCGI processes on the app server. This uses the :use_sudo
133
- variable to determine whether to use sudo or not. By default, :use_sudo is
134
- set to true, but you can set it to false if you are in a shared environment.
135
- DESC
136
- task :restart, :roles => :app do
137
- send(run_method, "#{current_path}/script/process/reaper")
138
- end
139
-
140
- desc <<-DESC
141
- Updates the code and fixes the symlink under a transaction
142
- DESC
143
- task :update do
144
- transaction do
145
- update_code
146
- symlink
147
- end
148
- end
149
-
150
1
  desc <<-DESC
151
- Run the migrate rake task. By default, it runs this in the version of the app
152
- indicated by the 'current' symlink. (This means you should not invoke this task
153
- until the symlink has been updated to the most recent version.) However, you
154
- can specify a different release via the migrate_target variable, which must be
155
- one of "current" (for the default behavior), or "latest" (for the latest release
156
- to be deployed with the update_code task). You can also specify additional
157
- environment variables to pass to rake via the migrate_env variable. Finally, you
158
- can specify the full path to the rake executable by setting the rake variable.
159
- DESC
160
- task :migrate, :roles => :db, :only => { :primary => true } do
161
- directory = case migrate_target.to_sym
162
- when :current then current_path
163
- when :latest then current_release
164
- else
165
- raise ArgumentError,
166
- "you must specify one of current or latest for migrate_target"
167
- end
2
+ Invoke a single command on the remote servers. This is useful for performing \
3
+ one-off commands that may not require a full task to be written for them. \
4
+ Simply specify the command to execute via the COMMAND environment variable. \
5
+ To execute the command only on certain roles, specify the ROLES environment \
6
+ variable as a comma-delimited list of role names. Alternatively, you can \
7
+ specify the HOSTS environment variable as a comma-delimited list of hostnames \
8
+ to execute the task on those hosts, explicitly. Lastly, if you want to \
9
+ execute the command via sudo, specify a non-empty value for the SUDO \
10
+ environment variable.
168
11
 
169
- run "cd #{directory} && " +
170
- "#{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
171
- end
172
-
173
- desc <<-DESC
174
- A macro-task that updates the code, fixes the symlink, and restarts the
175
- application servers.
176
- DESC
177
- task :deploy do
178
- update
179
- restart
180
- end
12
+ Sample usage:
181
13
 
182
- desc <<-DESC
183
- Similar to deploy, but it runs the migrate task on the new release before
184
- updating the symlink. (Note that the update in this case it is not atomic,
185
- and transactions are not used, because migrations are not guaranteed to be
186
- reversible.)
187
- DESC
188
- task :deploy_with_migrations do
189
- update_code
190
-
191
- begin
192
- old_migrate_target = migrate_target
193
- set :migrate_target, :latest
194
- migrate
195
- ensure
196
- set :migrate_target, old_migrate_target
197
- end
198
-
199
- symlink
200
-
201
- restart
202
- end
203
-
204
- desc "A macro-task that rolls back the code and restarts the application servers."
205
- task :rollback do
206
- rollback_code
207
- restart
208
- end
209
-
210
- desc <<-DESC
211
- Displays the diff between HEAD and what was last deployed. (Not available
212
- with all SCM's.)
14
+ $ cap COMMAND=uptime HOSTS=foo.capistano.test invoke
15
+ $ cap ROLES=app,web SUDO=1 COMMAND="tail -f /var/log/messages" invoke
213
16
  DESC
214
- task :diff_from_last_deploy do
215
- diff = source.diff(self)
216
- puts
217
- puts diff
218
- puts
219
- end
220
-
221
- desc "Update the currently released version of the software directly via an SCM update operation"
222
- task :update_current do
223
- source.update(self)
224
- end
225
-
226
- desc <<-DESC
227
- Removes unused releases from the releases directory. By default, the last 5
228
- releases are retained, but this can be configured with the 'keep_releases'
229
- variable. This will use sudo to do the delete by default, but you can specify
230
- that run should be used by setting the :use_sudo variable to false.
231
- DESC
232
- task :cleanup, :except => { :no_release => true } do
233
- count = (self[:keep_releases] || 5).to_i
234
- if count >= releases.length
235
- logger.important "no old releases to clean up"
236
- else
237
- logger.info "keeping #{count} of #{releases.length} deployed releases"
238
- directories = (releases - releases.last(count)).map { |release|
239
- File.join(releases_path, release) }.join(" ")
240
-
241
- send(run_method, "rm -rf #{directories}")
242
- end
243
- end
244
-
245
- desc <<-DESC
246
- Start the spinner daemon for the application (requires script/spin). This will
247
- use sudo to start the spinner by default, unless :use_sudo is false. If using
248
- sudo, you can specify the user that the spinner ought to run as by setting the
249
- :spinner_user variable (defaults to :app).
250
- DESC
251
- task :spinner, :roles => :app do
252
- user = (use_sudo && spinner_user) ? "-u #{spinner_user} " : ""
253
- send(run_method, "#{user}#{current_path}/script/spin")
17
+ task :invoke do
18
+ command = ENV["COMMAND"] || ""
19
+ abort "Please specify a command to execute on the remote servers (via the COMMAND environment variable)" if command.empty?
20
+ method = ENV["SUDO"] ? :sudo : :run
21
+ invoke_command(command, :via => method)
254
22
  end
255
23
 
256
24
  desc <<-DESC
257
- Used only for deploying when the spinner isn't running. It invokes 'update',
258
- and when it finishes it then invokes the spinner task (to start the spinner).
259
- DESC
260
- task :cold_deploy do
261
- update
262
- spinner
263
- end
25
+ Begin an interactive Capistrano session. This gives you an interactive \
26
+ terminal from which to execute tasks and commands on all of your servers. \
27
+ (This is still an experimental feature, and is subject to change without \
28
+ notice!)
264
29
 
265
- desc <<-DESC
266
- A simple task for performing one-off commands that may not require a full task
267
- to be written for them. Simply specify the command to execute via the COMMAND
268
- environment variable. To execute the command only on certain roles, specify
269
- the ROLES environment variable as a comma-delimited list of role names. Lastly,
270
- if you want to execute the command via sudo, specify a non-empty value for the
271
- SUDO environment variable.
272
- DESC
273
- task :invoke, :roles => Capistrano.str2roles(ENV["ROLES"] || "") do
274
- method = ENV["SUDO"] ? :sudo : :run
275
- send(method, ENV["COMMAND"])
276
- end
30
+ Sample usage:
277
31
 
278
- desc <<-DESC
279
- Begin an interactive Capistrano session. This gives you an interactive
280
- terminal from which to execute tasks and commands on all of your servers.
281
- (This is still an experimental feature, and is subject to change without
282
- notice!)
32
+ $ cap shell
283
33
  DESC
284
- task(:shell) do
34
+ task :shell do
285
35
  require 'capistrano/shell'
286
- Capistrano::Shell.run!(self)
287
- end
36
+ Capistrano::Shell.run(self)
37
+ end