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.
- data/CHANGELOG +140 -4
- data/MIT-LICENSE +1 -1
- data/README +22 -14
- data/bin/cap +1 -8
- data/bin/capify +77 -0
- data/examples/sample.rb +10 -109
- data/lib/capistrano.rb +1 -0
- data/lib/capistrano/callback.rb +41 -0
- data/lib/capistrano/cli.rb +17 -317
- data/lib/capistrano/cli/execute.rb +82 -0
- data/lib/capistrano/cli/help.rb +102 -0
- data/lib/capistrano/cli/help.txt +53 -0
- data/lib/capistrano/cli/options.rb +183 -0
- data/lib/capistrano/cli/ui.rb +28 -0
- data/lib/capistrano/command.rb +62 -29
- data/lib/capistrano/configuration.rb +25 -226
- data/lib/capistrano/configuration/actions/file_transfer.rb +35 -0
- data/lib/capistrano/configuration/actions/inspect.rb +46 -0
- data/lib/capistrano/configuration/actions/invocation.rb +127 -0
- data/lib/capistrano/configuration/callbacks.rb +148 -0
- data/lib/capistrano/configuration/connections.rb +159 -0
- data/lib/capistrano/configuration/execution.rb +126 -0
- data/lib/capistrano/configuration/loading.rb +112 -0
- data/lib/capistrano/configuration/namespaces.rb +190 -0
- data/lib/capistrano/configuration/roles.rb +51 -0
- data/lib/capistrano/configuration/servers.rb +75 -0
- data/lib/capistrano/configuration/variables.rb +127 -0
- data/lib/capistrano/errors.rb +15 -0
- data/lib/capistrano/extensions.rb +27 -8
- data/lib/capistrano/gateway.rb +54 -29
- data/lib/capistrano/logger.rb +11 -11
- data/lib/capistrano/recipes/compat.rb +32 -0
- data/lib/capistrano/recipes/deploy.rb +483 -0
- data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
- data/lib/capistrano/recipes/deploy/local_dependency.rb +46 -0
- data/lib/capistrano/recipes/deploy/remote_dependency.rb +65 -0
- data/lib/capistrano/recipes/deploy/scm.rb +19 -0
- data/lib/capistrano/recipes/deploy/scm/base.rb +180 -0
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +151 -0
- data/lib/capistrano/recipes/deploy/scm/darcs.rb +85 -0
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +129 -0
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +126 -0
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +103 -0
- data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
- data/lib/capistrano/recipes/deploy/strategy/base.rb +64 -0
- data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +143 -0
- data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
- data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +47 -0
- data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +53 -0
- data/lib/capistrano/recipes/standard.rb +26 -276
- data/lib/capistrano/recipes/templates/maintenance.rhtml +1 -1
- data/lib/capistrano/recipes/upgrade.rb +33 -0
- data/lib/capistrano/server_definition.rb +51 -0
- data/lib/capistrano/shell.rb +125 -81
- data/lib/capistrano/ssh.rb +80 -36
- data/lib/capistrano/task_definition.rb +69 -0
- data/lib/capistrano/upload.rb +146 -0
- data/lib/capistrano/version.rb +13 -17
- data/test/cli/execute_test.rb +132 -0
- data/test/cli/help_test.rb +139 -0
- data/test/cli/options_test.rb +226 -0
- data/test/cli/ui_test.rb +28 -0
- data/test/cli_test.rb +17 -0
- data/test/command_test.rb +284 -25
- data/test/configuration/actions/file_transfer_test.rb +40 -0
- data/test/configuration/actions/inspect_test.rb +62 -0
- data/test/configuration/actions/invocation_test.rb +195 -0
- data/test/configuration/callbacks_test.rb +206 -0
- data/test/configuration/connections_test.rb +288 -0
- data/test/configuration/execution_test.rb +159 -0
- data/test/configuration/loading_test.rb +119 -0
- data/test/configuration/namespace_dsl_test.rb +283 -0
- data/test/configuration/roles_test.rb +47 -0
- data/test/configuration/servers_test.rb +90 -0
- data/test/configuration/variables_test.rb +180 -0
- data/test/configuration_test.rb +60 -212
- data/test/deploy/scm/base_test.rb +55 -0
- data/test/deploy/strategy/copy_test.rb +146 -0
- data/test/extensions_test.rb +69 -0
- data/test/fixtures/cli_integration.rb +5 -0
- data/test/fixtures/custom.rb +2 -2
- data/test/gateway_test.rb +167 -0
- data/test/logger_test.rb +123 -0
- data/test/server_definition_test.rb +108 -0
- data/test/shell_test.rb +64 -0
- data/test/ssh_test.rb +67 -154
- data/test/task_definition_test.rb +101 -0
- data/test/upload_test.rb +131 -0
- data/test/utils.rb +31 -39
- data/test/version_test.rb +24 -0
- metadata +145 -98
- data/THANKS +0 -4
- data/lib/capistrano/actor.rb +0 -567
- data/lib/capistrano/generators/rails/deployment/deployment_generator.rb +0 -25
- data/lib/capistrano/generators/rails/deployment/templates/capistrano.rake +0 -49
- data/lib/capistrano/generators/rails/deployment/templates/deploy.rb +0 -122
- data/lib/capistrano/generators/rails/loader.rb +0 -20
- data/lib/capistrano/scm/base.rb +0 -61
- data/lib/capistrano/scm/baz.rb +0 -118
- data/lib/capistrano/scm/bzr.rb +0 -70
- data/lib/capistrano/scm/cvs.rb +0 -129
- data/lib/capistrano/scm/darcs.rb +0 -27
- data/lib/capistrano/scm/mercurial.rb +0 -83
- data/lib/capistrano/scm/perforce.rb +0 -139
- data/lib/capistrano/scm/subversion.rb +0 -128
- data/lib/capistrano/transfer.rb +0 -97
- data/lib/capistrano/utils.rb +0 -26
- data/test/actor_test.rb +0 -402
- data/test/scm/cvs_test.rb +0 -196
- 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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
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
|
-
|
183
|
-
|
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 :
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
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
|
-
|
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
|
34
|
+
task :shell do
|
285
35
|
require 'capistrano/shell'
|
286
|
-
Capistrano::Shell.run
|
287
|
-
end
|
36
|
+
Capistrano::Shell.run(self)
|
37
|
+
end
|