capistrano 1.4.2 → 2.0.0

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