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
data/lib/capistrano/cli.rb
CHANGED
@@ -1,83 +1,25 @@
|
|
1
|
-
require 'optparse'
|
2
1
|
require 'capistrano'
|
2
|
+
require 'capistrano/cli/execute'
|
3
|
+
require 'capistrano/cli/help'
|
4
|
+
require 'capistrano/cli/options'
|
5
|
+
require 'capistrano/cli/ui'
|
3
6
|
|
4
7
|
module Capistrano
|
5
8
|
# The CLI class encapsulates the behavior of capistrano when it is invoked
|
6
|
-
# as a command-line utility. This allows other programs to embed
|
7
|
-
# preserve
|
9
|
+
# as a command-line utility. This allows other programs to embed Capistrano
|
10
|
+
# and preserve its command-line semantics.
|
8
11
|
class CLI
|
9
|
-
# Invoke capistrano using the ARGV array as the option parameters. This
|
10
|
-
# is what the command-line capistrano utility does.
|
11
|
-
def self.execute!
|
12
|
-
new.execute!
|
13
|
-
end
|
14
|
-
|
15
|
-
# The following determines whether or not echo-suppression is available.
|
16
|
-
# This requires the termios library to be installed (which, unfortunately,
|
17
|
-
# is not available for Windows).
|
18
|
-
begin
|
19
|
-
require 'termios'
|
20
|
-
|
21
|
-
# Enable or disable stdin echoing to the terminal.
|
22
|
-
def self.echo(enable)
|
23
|
-
term = Termios::getattr(STDIN)
|
24
|
-
|
25
|
-
if enable
|
26
|
-
term.c_lflag |= (Termios::ECHO | Termios::ICANON)
|
27
|
-
else
|
28
|
-
term.c_lflag &= ~Termios::ECHO
|
29
|
-
end
|
30
|
-
|
31
|
-
Termios::setattr(STDIN, Termios::TCSANOW, term)
|
32
|
-
end
|
33
|
-
rescue LoadError
|
34
|
-
def self.echo(enable)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# execute the associated block with echo-suppression enabled. Note that
|
39
|
-
# if termios is not available, echo suppression will not be available
|
40
|
-
# either.
|
41
|
-
def self.with_echo
|
42
|
-
unless @warned_about_echo
|
43
|
-
puts "WARNING: Password will echo -- install the 'termios' gem to hide your password." if !defined?(Termios) && RUBY_PLATFORM !~ /mswin/
|
44
|
-
@warned_about_echo = true
|
45
|
-
end
|
46
|
-
echo(false)
|
47
|
-
yield
|
48
|
-
ensure
|
49
|
-
echo(true)
|
50
|
-
end
|
51
|
-
|
52
|
-
# Prompt for a password using echo suppression.
|
53
|
-
def self.password_prompt(prompt="Password: ")
|
54
|
-
sync = STDOUT.sync
|
55
|
-
begin
|
56
|
-
with_echo do
|
57
|
-
STDOUT.sync = true
|
58
|
-
print(prompt)
|
59
|
-
STDIN.gets.chomp
|
60
|
-
end
|
61
|
-
ensure
|
62
|
-
STDOUT.sync = sync
|
63
|
-
puts
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
12
|
# The array of (unparsed) command-line options
|
68
13
|
attr_reader :args
|
69
14
|
|
70
|
-
# The hash of (parsed) command-line options
|
71
|
-
attr_reader :options
|
72
|
-
|
73
15
|
# Create a new CLI instance using the given array of command-line parameters
|
74
16
|
# to initialize it. By default, +ARGV+ is used, but you can specify a
|
75
|
-
# different set of parameters (such as when embedded
|
17
|
+
# different set of parameters (such as when embedded cap in a program):
|
76
18
|
#
|
77
19
|
# require 'capistrano/cli'
|
78
|
-
# Capistrano::CLI.
|
20
|
+
# Capistrano::CLI.parse(%w(-vvvv -r config/deploy update_code)).execute!
|
79
21
|
#
|
80
|
-
# Note that you can also embed
|
22
|
+
# Note that you can also embed cap directly by creating a new Configuration
|
81
23
|
# instance and setting it up, but you'll often wind up duplicating logic
|
82
24
|
# defined in the CLI class. The above snippet, redone using the Configuration
|
83
25
|
# class directly, would look like:
|
@@ -87,261 +29,19 @@ module Capistrano
|
|
87
29
|
# config = Capistrano::Configuration.new
|
88
30
|
# config.logger_level = Capistrano::Logger::TRACE
|
89
31
|
# config.set(:password) { Capistrano::CLI.password_prompt }
|
90
|
-
# config.load "
|
91
|
-
# config.
|
32
|
+
# config.load "config/deploy"
|
33
|
+
# config.update_code
|
92
34
|
#
|
93
35
|
# There may be times that you want/need the additional control offered by
|
94
36
|
# manipulating the Configuration directly, but generally interfacing with
|
95
37
|
# the CLI class is recommended.
|
96
|
-
def initialize(args
|
97
|
-
@args = args
|
98
|
-
|
99
|
-
:pre_vars => {}, :sysconf => default_sysconf, :dotfile => default_dotfile }
|
100
|
-
|
101
|
-
OptionParser.new do |opts|
|
102
|
-
opts.banner = "Usage: #{$0} [options] [args]"
|
103
|
-
|
104
|
-
opts.separator ""
|
105
|
-
opts.separator "Recipe Options -----------------------"
|
106
|
-
opts.separator ""
|
107
|
-
|
108
|
-
opts.on("-a", "--action ACTION",
|
109
|
-
"An action to execute. Multiple actions may",
|
110
|
-
"be specified, and are loaded in the given order."
|
111
|
-
) { |value| @options[:actions] << value }
|
112
|
-
|
113
|
-
opts.on("-f", "--file FILE",
|
114
|
-
"A recipe file to load. Multiple recipes may",
|
115
|
-
"be specified, and are loaded in the given order."
|
116
|
-
) { |value| @options[:recipes] << value }
|
117
|
-
|
118
|
-
opts.on("-p", "--password [PASSWORD]",
|
119
|
-
"The password to use when connecting. If the switch",
|
120
|
-
"is given without a password, the password will be",
|
121
|
-
"prompted for immediately. (Default: prompt for password",
|
122
|
-
"the first time it is needed.)"
|
123
|
-
) { |value| @options[:password] = value }
|
124
|
-
|
125
|
-
opts.on("-r", "--recipe RECIPE",
|
126
|
-
"A recipe file to load. Multiple recipes may",
|
127
|
-
"be specified, and are loaded in the given order.",
|
128
|
-
"(This option is deprecated--please use -f instead)"
|
129
|
-
) do |value|
|
130
|
-
warn "Deprecated -r/--recipe flag used. Please use -f instead"
|
131
|
-
@options[:recipes] << value
|
132
|
-
end
|
133
|
-
|
134
|
-
opts.on("-s", "--set NAME=VALUE",
|
135
|
-
"Specify a variable and it's value to set. This",
|
136
|
-
"will be set after loading all recipe files."
|
137
|
-
) do |pair|
|
138
|
-
name, value = pair.split(/=/, 2)
|
139
|
-
@options[:vars][name.to_sym] = value
|
140
|
-
end
|
141
|
-
|
142
|
-
opts.on("-S", "--set-before NAME=VALUE",
|
143
|
-
"Specify a variable and it's value to set. This",
|
144
|
-
"will be set BEFORE loading all recipe files."
|
145
|
-
) do |pair|
|
146
|
-
name, value = pair.split(/=/, 2)
|
147
|
-
@options[:pre_vars][name.to_sym] = value
|
148
|
-
end
|
149
|
-
|
150
|
-
opts.on("-x", "--skip-config",
|
151
|
-
"Disables the loading of the default personal config",
|
152
|
-
"file. Specifying -C after this option will reenable",
|
153
|
-
"it. (Default: config file is loaded)"
|
154
|
-
) { @options[:dotfile] = nil }
|
155
|
-
|
156
|
-
opts.separator ""
|
157
|
-
opts.separator "Framework Integration Options --------"
|
158
|
-
opts.separator ""
|
159
|
-
|
160
|
-
opts.on("-A", "--apply-to DIRECTORY",
|
161
|
-
"Create a minimal set of scripts and recipes to use",
|
162
|
-
"capistrano with the application at the given",
|
163
|
-
"directory. (Currently only works with Rails apps.)"
|
164
|
-
) { |value| @options[:apply_to] = value }
|
165
|
-
|
166
|
-
opts.separator ""
|
167
|
-
opts.separator "Miscellaneous Options ----------------"
|
168
|
-
opts.separator ""
|
169
|
-
|
170
|
-
opts.on("-h", "--help", "Display this help message") do
|
171
|
-
puts opts
|
172
|
-
exit
|
173
|
-
end
|
174
|
-
|
175
|
-
opts.on("-P", "--[no-]pretend",
|
176
|
-
"Run the task(s), but don't actually connect to or",
|
177
|
-
"execute anything on the servers. (For various reasons",
|
178
|
-
"this will not necessarily be an accurate depiction",
|
179
|
-
"of the work that will actually be performed.",
|
180
|
-
"Default: don't pretend.)"
|
181
|
-
) { |value| @options[:pretend] = value }
|
182
|
-
|
183
|
-
opts.on("-q", "--quiet",
|
184
|
-
"Make the output as quiet as possible (the default)"
|
185
|
-
) { @options[:verbose] = 0 }
|
186
|
-
|
187
|
-
opts.on("-v", "--verbose",
|
188
|
-
"Specify the verbosity of the output.",
|
189
|
-
"May be given multiple times. (Default: silent)"
|
190
|
-
) { @options[:verbose] ||= 0; @options[:verbose] += 1 }
|
191
|
-
|
192
|
-
opts.on("-V", "--version",
|
193
|
-
"Display the version info for this utility"
|
194
|
-
) do
|
195
|
-
require 'capistrano/version'
|
196
|
-
puts "Capistrano v#{Capistrano::Version::STRING}"
|
197
|
-
exit
|
198
|
-
end
|
199
|
-
|
200
|
-
opts.separator ""
|
201
|
-
opts.separator <<-DETAIL.split(/\n/)
|
202
|
-
You can use the --apply-to switch to generate a minimal set of capistrano
|
203
|
-
scripts and recipes for an application. Just specify the path to the application
|
204
|
-
as the argument to --apply-to, like this:
|
205
|
-
|
206
|
-
cap --apply-to ~/projects/myapp
|
207
|
-
|
208
|
-
You'll wind up with a sample deployment recipe in config/deploy.rb and some new
|
209
|
-
rake tasks in lib/tasks.
|
210
|
-
|
211
|
-
(Currently, --apply-to only works with Rails applications.)
|
212
|
-
DETAIL
|
213
|
-
#' # vim syntax highlighting fix
|
214
|
-
|
215
|
-
if args.empty?
|
216
|
-
puts opts
|
217
|
-
exit
|
218
|
-
else
|
219
|
-
opts.parse!(args)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
check_options!
|
224
|
-
|
225
|
-
password_proc = Proc.new { self.class.password_prompt }
|
226
|
-
|
227
|
-
if !@options.has_key?(:password)
|
228
|
-
@options[:password] = password_proc
|
229
|
-
elsif !@options[:password]
|
230
|
-
@options[:password] = password_proc.call
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# Beginning running Capistrano based on the configured options.
|
235
|
-
def execute!
|
236
|
-
if @options[:apply_to]
|
237
|
-
execute_apply_to!
|
238
|
-
else
|
239
|
-
execute_recipes!
|
240
|
-
end
|
38
|
+
def initialize(args)
|
39
|
+
@args = args.dup
|
40
|
+
$stdout.sync = true # so that Net::SSH prompts show up
|
241
41
|
end
|
242
42
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
# specified.
|
247
|
-
def execute_recipes!
|
248
|
-
config = Capistrano::Configuration.new
|
249
|
-
config.logger.level = options[:verbose]
|
250
|
-
config.set :password, options[:password]
|
251
|
-
config.set :pretend, options[:pretend]
|
252
|
-
|
253
|
-
options[:pre_vars].each { |name, value| config.set(name, value) }
|
254
|
-
|
255
|
-
# load the standard recipe definition
|
256
|
-
config.load "standard"
|
257
|
-
|
258
|
-
# load systemwide config/recipe definition
|
259
|
-
config.load(@options[:sysconf]) if @options[:sysconf] && File.exist?(@options[:sysconf])
|
260
|
-
|
261
|
-
# load user config/recipe definition
|
262
|
-
config.load(@options[:dotfile]) if @options[:dotfile] && File.exist?(@options[:dotfile])
|
263
|
-
|
264
|
-
options[:recipes].each { |recipe| config.load(recipe) }
|
265
|
-
options[:vars].each { |name, value| config.set(name, value) }
|
266
|
-
|
267
|
-
actor = config.actor
|
268
|
-
options[:actions].each { |action| actor.send action }
|
269
|
-
rescue Exception => error
|
270
|
-
handle_error(error)
|
271
|
-
end
|
272
|
-
|
273
|
-
# Load the Rails generator and apply it to the specified directory.
|
274
|
-
def execute_apply_to!
|
275
|
-
require 'capistrano/generators/rails/loader'
|
276
|
-
Generators::RailsLoader.load! @options
|
277
|
-
end
|
278
|
-
|
279
|
-
APPLY_TO_OPTIONS = [:apply_to]
|
280
|
-
RECIPE_OPTIONS = [:password]
|
281
|
-
DEFAULT_RECIPES = %w(Capfile capfile config/deploy.rb)
|
282
|
-
|
283
|
-
# A sanity check to ensure that a valid operation is specified.
|
284
|
-
def check_options!
|
285
|
-
# if no verbosity has been specified, be verbose
|
286
|
-
@options[:verbose] = 3 if !@options.has_key?(:verbose)
|
287
|
-
|
288
|
-
apply_to_given = !(@options.keys & APPLY_TO_OPTIONS).empty?
|
289
|
-
recipe_given = !(@options.keys & RECIPE_OPTIONS).empty? ||
|
290
|
-
!@options[:recipes].empty? ||
|
291
|
-
!@options[:actions].empty?
|
292
|
-
|
293
|
-
if apply_to_given && recipe_given
|
294
|
-
abort "You cannot specify both recipe options and framework integration options."
|
295
|
-
elsif !apply_to_given
|
296
|
-
look_for_default_recipe_file! if @options[:recipes].empty?
|
297
|
-
look_for_raw_actions!
|
298
|
-
abort "You must specify at least one action" if @options[:actions].empty?
|
299
|
-
else
|
300
|
-
@options[:application] = args.shift
|
301
|
-
@options[:recipe_file] = args.shift
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
def default_sysconf
|
306
|
-
File.join(sysconf_directory, "capistrano.conf")
|
307
|
-
end
|
308
|
-
|
309
|
-
def default_dotfile
|
310
|
-
File.join(home_directory, ".caprc")
|
311
|
-
end
|
312
|
-
|
313
|
-
def sysconf_directory
|
314
|
-
# I'm guessing at where Windows users would keep their conf file.
|
315
|
-
ENV["SystemRoot"] || '/etc'
|
316
|
-
end
|
317
|
-
|
318
|
-
def home_directory
|
319
|
-
ENV["HOME"] ||
|
320
|
-
(ENV["HOMEPATH"] && "#{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}") ||
|
321
|
-
"/"
|
322
|
-
end
|
323
|
-
|
324
|
-
def look_for_default_recipe_file!
|
325
|
-
DEFAULT_RECIPES.each do |file|
|
326
|
-
if File.exist?(file)
|
327
|
-
@options[:recipes] << file
|
328
|
-
break
|
329
|
-
end
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
def look_for_raw_actions!
|
334
|
-
@options[:actions].concat(@args)
|
335
|
-
end
|
336
|
-
|
337
|
-
def handle_error(error)
|
338
|
-
case error
|
339
|
-
when Net::SSH::AuthenticationFailed
|
340
|
-
abort "authentication failed for `#{error.message}'"
|
341
|
-
when Capistrano::Command::Error
|
342
|
-
abort(error.message)
|
343
|
-
else raise error
|
344
|
-
end
|
345
|
-
end
|
43
|
+
# Mix-in the actual behavior
|
44
|
+
include Execute, Options, UI
|
45
|
+
include Help # needs to be included last, because it overrides some methods
|
346
46
|
end
|
347
47
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'capistrano/configuration'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
class CLI
|
5
|
+
module Execute
|
6
|
+
def self.included(base) #:nodoc:
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Invoke capistrano using the ARGV array as the option parameters. This
|
12
|
+
# is what the command-line capistrano utility does.
|
13
|
+
def execute
|
14
|
+
parse(ARGV).execute!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Using the options build when the command-line was parsed, instantiate
|
19
|
+
# a new Capistrano configuration, initialize it, and execute the
|
20
|
+
# requested actions.
|
21
|
+
#
|
22
|
+
# Returns the Configuration instance used, if successful.
|
23
|
+
def execute!
|
24
|
+
config = instantiate_configuration
|
25
|
+
config.logger.level = options[:verbose]
|
26
|
+
|
27
|
+
set_pre_vars(config)
|
28
|
+
load_recipes(config)
|
29
|
+
|
30
|
+
config.trigger(:load)
|
31
|
+
execute_requested_actions(config)
|
32
|
+
config.trigger(:exit)
|
33
|
+
|
34
|
+
config
|
35
|
+
rescue Exception => error
|
36
|
+
handle_error(error)
|
37
|
+
end
|
38
|
+
|
39
|
+
def execute_requested_actions(config)
|
40
|
+
Array(options[:vars]).each { |name, value| config.set(name, value) }
|
41
|
+
|
42
|
+
Array(options[:actions]).each do |action|
|
43
|
+
config.find_and_execute_task(action, :before => :start, :after => :finish)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_pre_vars(config) #:nodoc:
|
48
|
+
config.set :password, options[:password]
|
49
|
+
Array(options[:pre_vars]).each { |name, value| config.set(name, value) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_recipes(config) #:nodoc:
|
53
|
+
# load the standard recipe definition
|
54
|
+
config.load "standard"
|
55
|
+
|
56
|
+
# load systemwide config/recipe definition
|
57
|
+
config.load(options[:sysconf]) if options[:sysconf] && File.file?(options[:sysconf])
|
58
|
+
|
59
|
+
# load user config/recipe definition
|
60
|
+
config.load(options[:dotfile]) if options[:dotfile] && File.file?(options[:dotfile])
|
61
|
+
|
62
|
+
Array(options[:recipes]).each { |recipe| config.load(recipe) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Primarily useful for testing, but subclasses of CLI could conceivably
|
66
|
+
# override this method to return a Configuration subclass or replacement.
|
67
|
+
def instantiate_configuration #:nodoc:
|
68
|
+
Capistrano::Configuration.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_error(error) #:nodoc:
|
72
|
+
case error
|
73
|
+
when Net::SSH::AuthenticationFailed
|
74
|
+
abort "authentication failed for `#{error.message}'"
|
75
|
+
when Capistrano::Error
|
76
|
+
abort(error.message)
|
77
|
+
else raise error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Capistrano
|
2
|
+
class CLI
|
3
|
+
module Help
|
4
|
+
LINE_PADDING = 7
|
5
|
+
MIN_MAX_LEN = 30
|
6
|
+
HEADER_LEN = 60
|
7
|
+
|
8
|
+
def self.included(base) #:nodoc:
|
9
|
+
base.send :alias_method, :execute_requested_actions_without_help, :execute_requested_actions
|
10
|
+
base.send :alias_method, :execute_requested_actions, :execute_requested_actions_with_help
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute_requested_actions_with_help(config)
|
14
|
+
if options[:tasks]
|
15
|
+
task_list(config)
|
16
|
+
elsif options[:explain]
|
17
|
+
explain_task(config, options[:explain])
|
18
|
+
else
|
19
|
+
execute_requested_actions_without_help(config)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def task_list(config) #:nodoc:
|
24
|
+
tasks = config.task_list(:all)
|
25
|
+
|
26
|
+
if tasks.empty?
|
27
|
+
warn "There are no tasks available. Please specify a recipe file to load."
|
28
|
+
else
|
29
|
+
all_tasks_length = tasks.length
|
30
|
+
if options[:verbose].to_i < 1
|
31
|
+
tasks = tasks.reject { |t| t.description.empty? || t.description =~ /^\[internal\]/ }
|
32
|
+
end
|
33
|
+
|
34
|
+
tasks = tasks.sort_by { |task| task.fully_qualified_name }
|
35
|
+
|
36
|
+
longest = tasks.map { |task| task.fully_qualified_name.length }.max
|
37
|
+
max_length = output_columns - longest - LINE_PADDING
|
38
|
+
max_length = MIN_MAX_LEN if max_length < MIN_MAX_LEN
|
39
|
+
|
40
|
+
tasks.each do |task|
|
41
|
+
puts "cap %-#{longest}s # %s" % [task.fully_qualified_name, task.brief_description(max_length)]
|
42
|
+
end
|
43
|
+
|
44
|
+
if all_tasks_length > tasks.length
|
45
|
+
puts
|
46
|
+
puts "Some tasks were not listed, either because they have no description,"
|
47
|
+
puts "or because they are only used internally by other tasks. To see all"
|
48
|
+
puts "tasks, type `#{File.basename($0)} -Tv'."
|
49
|
+
end
|
50
|
+
|
51
|
+
puts
|
52
|
+
puts "Extended help may be available for these tasks."
|
53
|
+
puts "Type `#{File.basename($0)} -e taskname' to view it."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def explain_task(config, name) #:nodoc:
|
58
|
+
task = config.find_task(name)
|
59
|
+
if task.nil?
|
60
|
+
warn "The task `#{name}' does not exist."
|
61
|
+
else
|
62
|
+
puts "-" * HEADER_LEN
|
63
|
+
puts "cap #{name}"
|
64
|
+
puts "-" * HEADER_LEN
|
65
|
+
|
66
|
+
if task.description.empty?
|
67
|
+
puts "There is no description for this task."
|
68
|
+
else
|
69
|
+
puts format_text(task.description)
|
70
|
+
end
|
71
|
+
|
72
|
+
puts
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def long_help #:nodoc:
|
77
|
+
help_text = File.read(File.join(File.dirname(__FILE__), "help.txt"))
|
78
|
+
self.class.ui.page_at = self.class.ui.output_rows - 2
|
79
|
+
self.class.ui.say format_text(help_text)
|
80
|
+
end
|
81
|
+
|
82
|
+
def format_text(text) #:nodoc:
|
83
|
+
formatted = ""
|
84
|
+
text.each_line do |line|
|
85
|
+
indentation = line[/^\s+/] || ""
|
86
|
+
indentation_size = indentation.split(//).inject(0) { |c,s| c + (s[0] == ?\t ? 8 : 1) }
|
87
|
+
lines = line.strip.gsub(/(.{1,#{output_columns - indentation_size}})(?:\s+|\Z)/, "\\1\n").split(/\n/)
|
88
|
+
if lines.empty?
|
89
|
+
formatted << "\n"
|
90
|
+
else
|
91
|
+
formatted << lines.map { |l| "#{indentation}#{l}\n" }.join
|
92
|
+
end
|
93
|
+
end
|
94
|
+
formatted
|
95
|
+
end
|
96
|
+
|
97
|
+
def output_columns #:nodoc:
|
98
|
+
@output_columns ||= self.class.ui.output_cols > 80 ? 80 : self.class.ui.output_cols
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|