Codebase list capistrano / upstream/2.9.0
Imported Upstream version 2.9.0 Antonio Terceiro 12 years ago
72 changed file(s) with 2344 addition(s) and 2764 deletion(s). Raw diff Collapse all Expand all
._CHANGELOG.rdoc less more
Binary diff not shown
._Rakefile less more
Binary diff not shown
0 coverage
1 doc
2 pkg
3 *.swp
4 *.patch
5 *.gem
6 *.sh
7 .DS_Store
8 .bundle/*
9 Gemfile.lock
0 rvm use 1.8.7
0 rvm:
1 - 1.8.7
2 - 1.9.2
3 - ree
0 ## 2.9.0 / September 24 2011
1
2 A vairly heavy release, including some new features which most of you won't
3 need, but power users have contributed, this also marks the beginning of the
4 end of the 2.x series, more information will follow in due course, but with
5 the proliferation of Bundler, and better ways to do deployment, we will be
6 introducing heavier changes to Capistrano to keep the tool current.
7
8 **Please note, following some reported problems with the asset pipeline code
9 being not found, remember Capistrano needs to be in your Gemfile, and as such
10 needs to be run with Bundler, otherwise you risk loading a system-wide version
11 of Capistrano who's behaviour might be different from that specified in your
12 Gemfile. This is also good practice because much of the deploy logic resides
13 in the Gem, and you wouldn't want that to change without your knowledge. Rails
14 applications include Cap in the Gemfile anyway, you should follow this
15 convention.**
16
17 * find_servers() will no longer raise if there are no servers, this behaviour
18 can be modified by way of the `:on_no_matching_servers` option. Thanks to
19 `@ppgengler`.
20
21 * Short Git SHA1 fragments are now supported in commands such as `cap deploy
22 -s revision=d7e99f` thanks to `@ndbroadbent`.
23
24 * One can now specify individual SCM commands by setting
25 `:scm_arguments_<command_name>`. Thanks to `@alextk`.
26
27 * Travis CI build now passes thanks to @andrew, build tested against MRI
28 `1.8.7`. `1.9.2` and `REE`.
29
30 * Support for Perforce labels, I don't know much about this, but I believe
31 it's much like deploying a Git tag, thanks to `@ak47`.
32
33 * Git SCM now correctly adheres to the `:scm_verbose` setting. Thanks
34 `@dubek`.
35
36 * `set()` can now be used to set a `false` value, previously this was a no-op.
37 Thanks to `@nilbus`.
38
39 * Support for Git 1.6x submodules, The Git SCM strategy now queries Git on the
40 server-side to ensure it supports the `--recursive` flag, if it doesn't then
41 it will fall back to using the long-hand. Many thanks to all those involved in
42 the discussion surrounding this topic, and to `@nilbus` for a beautifully
43 clean solution which doesn't hold us back.
44
45 * When using `:cached_copy` with Subversion, use `svn switch` to for more
46 reliable switching of branches/etc. Thanks to `@iGEL` for the patch that we
47 accepted finally, and to `@richmeyers` who also submitted a patch and
48 contributed to the discssion.
49
50 Other cleanups and minor improvements to the code and tests were committed by yours truly
51 (@leehambley), @maxim, @ak47 and @andrew).
52
53 ## 2.8.0 / August 3 2011
54
55 A short release, after the last. Announcing Rails 3.1 asset pipeline support.
56
57 The asset pipeline support requires an additiona `load` in your `Capfile`.
58
59 You can see information pertaining to the pull request, including the inline
60 comments here: https://github.com/capistrano/capistrano/pull/35
61
62 Documentation will be available soon in the wiki.
63
64 * Drop-In Rails 3.1 asset pipeline support. (Chris Griego)
65
66 ## 2.7.0 / August 3 2011
67
68 A fairly substantial release. There are fixes so that current_release works
69 during dry-runs, (although, apparently still not with bundler.)
70
71 The test-suite was also modified to work with Ruby 1.9.2, except in one case
72 where Ruby 1.9.x calls `to_ary` and `to_a` on mocks, which still makes an
73 error. 1.9.x has always been supported, but due to lack of maintenance on my
74 part the tests didn't ever pass.
75
76 The `start`, `stop` and `restart` tasks have been reduced to mere hooks into
77 which extensions can define their own functionality.
78
79 The `readme` was also slightly improved, simply tweaks to express how best to
80 run the test suite.
81
82 * Ensure dry-run works with `:current_release` variable (Carol Nichols)
83 * Added a new variable `:git_submodules_recursive`, setting the value to false
84 will ensure Git doesn't recursively initialize and checkout submodules. (Konstantin Kudryashov)
85 * Added an additional task option, `:on_no_matching_servers`, setting the
86 value to `:continue` will ensure tasks with no matched servers continue
87 without error, instead of raising `Capistrano::NoMatchingServersError` as was
88 the previous behaviour. (Chris Griego)
89
90 A huge thanks to all contributors, as always!
91
92 Remember: @capistranorb on twitter for news.
93
94 ## 2.6.1 / June 25 2011
95
96 A short maintenance release, Some fixes to the verbose flag inside the Git SCM
97 as well as another argument for the (internal) `variable()` command, offering
98 a default. The Git SCM is now verbose by default, but can be disabled by
99 setting `:scm_verbose` to false.
100
101 There has been an additional method added to string, within the context of the
102 test suite, I'm always sketchy about adding additional methods to core
103 classes, but it's a short term fix until I make the time to patch the test
104 suite not to compare strings literally. The method is `String#compact`, and is
105 implemented simply as `self.gsub(/\s+/, ' ')`.
106
107 Here's the run-down of changes, and their committers, as always - a huge thank
108 you to the community that continues to drive Capistrano's development.
109
110 * `deploy:setup` now respects `:group_writable` (Daniel Duvall)
111 * Fixes to `:scm_verbose` for the Git module (defaults to On.) (Matthew Davies)
112 * Will now copy hidden files in the project's root into the release
113 directory (Mark Jaquith)
114 * Now handles closing already-dead connections in a sane way (does not raise
115 an exception) (Will Bryant)
116 * Renamed `Capistrano::VERSION::TINY` to `Capistrano::VERSION::PATCH` (Lee
117 Hambley)
118 * Removed the `VERSION` file (Lee Hambley)
119
120 ## 2.6.0 / May 3 2011
121
122 A rather large release, feature-version bump because of the new
123 multiple-gateways feature as implemented by Ryan Duryea (way to go!)
124
125 Please also note from this release that if you use Git submodules, the
126 Git-version requirement for the new implementation is now >= 1.5.6, from
127 previously un-documented. (1.5.6 is new-enough that I think this is
128 acceptable)
129
130 * Upgrade Net::SSH-gateway dependency to 1.1 (fixes a thread-deadlocking bug on MRI 1.9)
131 * Respect "dry-run" on transfer methods (Florian Frank)
132 * Add support for multiple gateways: (Ryan Duryea)
133 set :gateway, {
134 'gate1.example.com' => 'server1.example.com',
135 [ 'gate2.example.com', 'gate3.example.com' ] => [ 'server5.example.com', 'server6.example.com' ]
136 }
137 * Properly support nested Git submodules, moves Git requirement to >= 1.5.6 [if you rely upon submodules] (Ken Miller)
138 * Fetch tags into the remote cache, allows deploying a tag when using Git, with the remote_cache strategy (Florian Frank)
139 * Various fixes to path handling bugs in the copt strategy. (Philippe Rathé)
140
141 ## 2.5.21 / April 6 2011
142
143 * Fixed to follow best-practice guidelines from Bundler (Ben Langfeld)
144 * No longer force a gemset for Capistrano development. (Ben Langfeld)
145
146 ## 2.5.20 / March 16 2011
147
148 * `deploy:migrations` will now always operate on the latest_release, not
149 current_release (Mike Vincent)
150 * Adds a check for the presence of `rsync` when using the copy strategy with `rsync`. (Chris Griego)
151 * Do not try to look up the `:release_path` on servers which are defined `:no_release` (Chris Griego)
152 * Tiny patch to the `CVS` SCM code to be Ruby 1.9 compatible (Martin Carpenter)
153 * Changed the default `Git` submodule behaviour to use `--recursive`, Lighthouse Issue #176. (Lee Hambley)
154 * `:public_children` can now be `set()`, the default is unchanged, thanks (Chris Griego)
155 * Fixing the load path in the default `Capfile` to search vendored/unpacked Gems. Lighthouse Issue #174 (Mari Carmen/Rafael García)
156 * Adds a `maintenance_basename` variable (default value is `maintenance`) to allow you to set the maintenance page name (Celestino Gomes)
157 * Spelling fixes in inline-documentation (Tom Copeland)
158 * Make `zip` and `tar` handle symlinks the same way (zip follows symlinks by default, tar needs the option `-h`) (Ross Cooperman)
159
160 ## 2.5.19 / June 21, 2010
161
162 * Small bug fixes, no improvements for people who weren't experiencing problems anyway.
163
164 ## 2.5.18 / March 14, 2010
165
166 Small fix for rolling back if a shell scripts exits non-zero; enabled a rollback if git (or other) externals fail during the deploy.
167
168 * #151 check return code status of system command to create local copy and rollback if not 0 (David King)
169
170 ## 2.5.17 / February 27, 2010
171
172 Various small bug fixes.
173
174 ## 2.5.16 / February 14, 2010
175
176 Fixed a small regression in 2.5.15
177
178 ## 2.5.15 / 14 February 2010
179
180 Fixes a feature request not to overwrite roles when using the ROLES environmental variable.
181
182 * #126 - The option to not overwriting the roles which are defined in the task definition.
183 * Removed the `upgrade` file as it has been a couple of years since 1.x was in the wild.
184 * Slight internal re-factor of the way we calculate the `version`
185
186 ## 2.5.14 / 18 January 2010
187
188 Fixes a low-value bug, thanks to Chris G for the well submitted patch:
189
190 * #139 - Improves consistency of variable lookup, scm variables with a local_ prefix will be honoured with priority locally (Chris Griego)
191
192 ## 2.5.13 / 6 January 2010
193
194 * Small maintenance release:
195
196 * #118 - Modified CLI test to not load user or system configuration file (Emily Price)
197 * #88 - Re-fixed a problem here, massive apologies to all concerned. (Hangover from 2.5.12)
198
199 ## 2.5.12 / 5 January 2010
200
201 * Tweak the directory version listing (caused a lot of problems, please upgrade immediately)
202
203 ## 2.5.11 / December 2009
204
205 * Deprecations and other small changes
206
207 ## 2.5.10 / 3 November 2009
208
209 * Fixes Darcs remote repository problem when using the copy strategy [Alex `regularfry` Young]
210 * Documentation improvements for embedding Capistrano [Lee Hambley]
211 * Fixes ticket #95 -formally deprecating the before_something and after_something methods [Lee Hambley]
212
213 ## 2.5.9 / 1 August 2009
214
215 * Adds support for customizing which `tar` command to use. [Jeremy Wells]
216
217 * Fixes a couple of documentation problems, typos and worse. [Lee Hambley]
218
219 * #105 - Add skip_hostfilter option to find_servers() [Eric]
220 * #103 - Using non-master branch fails with Ruby 1.9 [Suraj Kurapati]
221 * #96 - Tweak for 1.9 Compatibility
222 * #79 - Capistrano hangs on shell command for many computers
223 * #77 - Copy command doesn't work on Solaris due to tar/gtar
224 * #76 - Invalid Subversion URL
225 * Improved web:disable task, now suggests a .htaccess block to use suggested by Rafael García
226 * Includes more logger options (can now select stdout, stderr of a file) [Rafael García]
227
228 ## 2.5.8 / July 2009
229
230 * Fixes a problem in 2.5.7 where deploy:finalize_update had been badly merged.
231
232 ## 2.5.6 & 2.5.7 / July 2009
233
234 * 2.5.7 masks a broken 2.5.6 release that was accidentally mirrored via Rubyforge.
235
236 * Clean the cached git repository [Graeme Mathieson]
237
238 * Fixes perforce issues reported at http://bit.ly/wt0es [Scott Johnson]
239
240 * Improved back-tick handling code in relation to the above.
241
242 * Fixes a Git issue when submodules update upstream. (via mailing list) [sneakin]
243
244 * Capify now creates the config directory in directories without one.
245
246 ## 2.5.5 / 24 Feb 2009
247
248 * Make sure role(:foo) actually declares an (empty) role for :foo, even without server arguments [Jamis Buck]
249
250
251 ## 2.5.4 / 4 Feb 2009
252
253 * When using rsync with the remote_cache strategy include -t switch to preserve file times [Kevin McCarthy]
254
255 * Bump Net::SSH dependency to version 2.0.10 [Jamis Buck]
256
257 * Use 'user' from .ssh/config appropriately [Jamis Buck]
258
259 * Allow respond_to?() method to accept optional second parameter (include_priv) [Matthias Marschall]
260
261 * Make sure sudo prompts are retried correctly even if "try again" and the prompt appear in the same text chunk from the server [Jamis Buck]
262
263 * Add supported environment variables to -H output [François Beausoleil]
264
265
266 ## 2.5.3 / December 6, 2008
267
268 * Make previous_release return nil if there is no previous release [Mathias Meyer]
269
270 * Play nice with rubies that don't inspect terminals well (ie. JRuby) by defaulting screen columns to 80 [Bob McWhirter]
271
272 * Rollback of deploy:symlink would explode if there was no previous revision to rollback to [Jamis Buck]
273
274 * Fix bug in transfer.rb that caused get/put/upload/download to ignore blocks passed to them [arika]
275
276 * Fix issue with git SCM that caused "Unable to resolve revision" errors when there was trailing whitespace in git's output [Mark Zuneska, Daniel Berlinger and Evan Closson]
277
278
279 ## 2.5.2 / November 13, 2008
280
281 * Fix issue with git SCM that caused "Unable to resolve revision for 'HEAD'" errors on deploy [Jamis Buck]
282
283
284 ## 2.5.1 / November 7, 2008
285
286 * Add -t (--tools) switch for better task lists for external tools [Jamis Buck]
287
288 * Make the RemoteDependency#try method use invoke_command instead of run, for sudo-ability [Matthias Marschall]
289
290 * Make locally executed commands in Windows more Windows-friendly [esad@esse.at]
291
292 * Added :scm_arguments variable for custom SCM arguments (subversion-only, currently) [David Abdemoulaie]
293
294 * Don't emit -p for sudo when :sudo_prompt is blank [Matthias Marschall]
295
296 * Copy symlinks when using rsync [Paul Paradise]
297
298 * Make sure git query-revision matches on exact branch name [grant@nightriot.com]
299
300 * Use -T <arg> to filter listed tasks by a pattern [Mathias Meyer, Geoffrey Grosenbach]
301
302 * Expose the #scm method on SCM::Base for building custom scm commands [Mathias Meyer]
303
304 * Start logging some locally executed commands [springyweb]
305
306 * Added HOSTFILTER environment variable for constraining tasks so they run only on hosts matching the given list of servers [Walter Smith]
307
308 * Make sure the glob matching for copy excludes does not delete parent directories [Fabio Akita]
309
310 * Ruby 1.9 compatibility [Jamis Buck]
311
312
313 ## 2.5.0 / August 28, 2008
314
315 * Allow :gateway to be set to an array, in which case a chain of tunnels is created [Kerry Buckley]
316
317 * Allow HOSTS spec to override even non-existent roles [Mike Bailey]
318
319 * Sort releases via "ls -xt" instead of "ls -x" to allow for custom release names [Yan Pritzker]
320
321 * Convert arguments to -s and -S into integers, booleans, etc. based on whether the arguments appear to be those types [Jamis Buck]
322
323 * Add descriptions of -n and -d to the verbose help text [Jamis Buck]
324
325 * Make rollbacks work with processes that need the current directory to be valid in order to restart properly (e.g. mongrel_rails) [Jamis Buck]
326
327 * Rename deploy:rollback_code to deploy:rollback:code [Jamis Buck]
328
329 * Added parallel() helper for executing multiple different commands in parallel [Jamis Buck]
330
331 * Make sure a task only uses the last on_rollback block, once, on rollback [Jamis Buck]
332
333 * Add :shared_children variable to customize which subdirectories are created by deploy:setup [Jonathan Share]
334
335 * Allow filename globbing in copy_exclude setting for the copy strategy [Jonathan Share]
336
337 * Allow remote_cache strategy to use copy_exclude settings (requires rsync) [Lewis Mackenzie]
338
339 * Make None SCM module work in Windows [Carlos Kozuszko]
340
341 * Recognize mingw as a Windows platform [Carlos Kozuszko]
342
343 * Fixed failing tests in Windows [Carlos Kozuszko]
344
345 * Made :scm_auth_cache control whether password option is emitted in subversion module [Brendan Schwartz]
346
347 * Fixed timestamp bug in CVS module [Jørgen Fjeld]
348
349 * Added -n/--dry-run switch, to display but not execute remote tasks [Paul Gross]
350
351
352 ## 2.4.3 / June 28, 2008
353
354 * Fix gem dependencies so gem actually understands them [Jamis Buck]
355
356
357 ## 2.4.2 / June 27, 2008
358
359 * Specify gem dependencies in rakefile [Jamis Buck]
360
361
362 ## 2.4.1 / June 27, 2008
363
364 * Use Echoe to manage the Rakefile [Jamis Buck]
365
366 * Let Net::SSH manage the default SSH port selection [Ben Lavender]
367
368 * Changed capture() helper to not raise an exception on error, but to warn instead [Jeff Forcier]
369
370
371 ## 2.4.0 / June 13, 2008
372
373 * Added :normalize_asset_timestamps option to deployment, defaulting to true, which allows asset timestamping to be disabled [John Trupiano]
374
375
376 ## 2.4.0 Preview Release #1 (2.3.101) / June 5, 2008
377
378 * Only make deploy:start, deploy:stop, and deploy:restart try sudo as :runner. The other sudo-enabled tasks (deploy:setup, deploy:cleanup, etc.) will now use the :admin_runner user (which by default is unset). [Jamis Buck]
379
380 * Make sure triggers defined as a block inherit the scope of the task they are attached to, instead of the task they were called from [Jamis Buck]
381
382 * Make deploy:upload use the upload() helper for more efficient directory processing [Jamis Buck]
383
384 * Make deploy:upload accept globs [Mark Imbriaco]
385
386 * Make sure the host is reported with the output from scm_run [Jamis Buck]
387
388 * Make git SCM honor the :scm_verbose option [Jamis Buck]
389
390 * Don't follow symlinks when using :copy_cache [Jamis Buck]
391
392 * If :mode is given to upload() helper, do a chmod after to set the mode [Jamis Buck]
393
394 * Fix load_from_file method for windows users [Neil Wilson]
395
396 * Display a deprecation error if a remote git branch is specified [Tim Harper]
397
398 * Fix deployment recipes to use the updated sudo helper [Jamis Buck]
399
400 * Enhance the sudo helper so it can be used to return the command, instead of executing it [Jamis Buck]
401
402 * Revert "make sudo helper play nicely with complex command chains", since it broke stuff [Jamis Buck]
403
404 * Make set(:default_shell, false) work for not using a shell on a per-command basis [Ryan McGeary]
405
406 * Improved test coverage [Ryan McGeary]
407
408 * Fixed "coverage" take task [Ryan McGeary]
409
410 * Use upload() instead of put() with the copy strategy [Jamis Buck]
411
412 * Revert the "git fetch --tags" change, since it didn't work as expected [Jamis Buck]
413
414 * Fix deploy:pending when using git SCM [Ryan McGeary]
415
416 * Make sure deploy:check works with :none scm (which has no default command) [Jamis Buck]
417
418 * Add debug switch for enabling conditional execution of commands [Mark Imbriaco]
419
420
421 ## 2.3.0 / May 2, 2008
422
423 * Make deploy:setup obey the :use_sudo and :runner directives, and generalize the :use_sudo and :runner options into a try_sudo() helper method [Jamis Buck]
424
425 * Make sudo helper play nicely with complex command chains [Jamis Buck]
426
427 * Expand file-transfer options with new upload() and download() helpers. [Jamis Buck]
428
429 * Allow SCP transfers in addition to SFTP. [Jamis Buck]
430
431 * Use Net::SSH v2 and Net::SSH::Gateway. [Jamis Buck]
432
433 * Added #export method for git SCM [Phillip Goldenburg]
434
435 * For query_revision, git SCM used git-rev-parse on the repo hosting the Capfile, which may NOT be the same tree as the actual source reposistory. Use git-ls-remote instead to resolve the revision for checkout. [Robin H. Johnson]
436
437 * Allow :ssh_options hash to be specified per server [Jesse Newland]
438
439 * Added support for depend :remote, :file to test for existence of a specific file [Andrew Carter]
440
441 * Ensure that the default run options are mixed into the command options when executing a command from the cap shell [Ken Collins]
442
443 * Added :none SCM module for deploying a specific directory's contents [Jamis Buck]
444
445 * Improved "copy" strategy supports local caching and pattern exclusion (via :copy_cache and :copy_exclude variables) [Jamis Buck]
446
447
448 ## 2.2.0 / February 27, 2008
449
450 * Fix git submodule support to init on sync [halorgium]
451
452 * Add alternative server-centric role definition method [James Duncan Davidson]
453
454 * Add support for password prompts from the Mercurial SCM [ches]
455
456 * Add support for :max_hosts option in task definition or run() [Rob Holland <rob@inversepath.com>]
457
458 * Distributed git support for better operability with remote_cache strategy [voidlock]
459
460 * Use a default line length in help text if line length is otherwise too small [Jamis Buck]
461
462 * Fix incorrect reference to the 'setup' task in task documentation [rajeshduggal]
463
464 * Don't try to kill the spawner process on deploy:stop if no spawner process exists [Jamis Buck]
465
466 * Dynamic roles (e.g. role(:app) { "host.name" }) [dmasover]
467
468 * Implement Bzr#next_revision so that pending changes can be reported correctly [casret]
469
470 * Use a proper export command for bzr SCM [drudru]
471
472 * Use checkout instead of merge for git SCM [nuttycom]
473
474 * Fix typo in Subversion SCM module, encountered when an update fails [kemiller]
475
476 * Fix documentation typo in upload.rb [evolving_jerk]
477
478 * Added test case to show that the :scm_command is honored by the git SCM module [grempe]
479
480 * Fail gracefully when double-colons are used to delimit namespaces [richie]
481
482 * Add support for :git_enable_submodules variable, to enable submodules with the git SCM [halorgium]
483
484 * If subversion asks for a password, prompt as a last resort [Jamis Buck]
485
486 * Use checkout --lightweight for bzr checkout, instead of branch [michiels]
487
488 * Make sure bzr SCM works when revision is head (or unspecified) [michiels]
489
490 * Support p4sync_flags and p4client_root variables for Perforce [gseidman]
491
492 * Prepare for Net::SSH v2 by making sure Capistrano only tries to load Net::SSH versions less than 1.99.0 [Jamis Buck]
493
494
495 ## 2.1.0 / October 14, 2007
496
497 * Default to 0664 instead of 0660 on upload [Jamis Buck]
498
499 * Fix deploy:pending to query SCM for the subsequent revision so that it does not include the last deployed change [Jamis Buck]
500
501 * Prefer 'Last Changed Rev' over 'Revision' when querying latest revision via Subversion [Jamis Buck]
502
503 * Explicitly require 'stringio' in copy_test [mislav]
504
505 * When Subversion#query_revision fails, give a more sane error [Jamis Buck]
506
507 * Don't run the upgrade:revisions task on non-release servers [Jamis Buck]
508
509 * Fix cap shell to properly recognize sudo prompt [Mark Imbriaco, barnaby, Jamis Buck]
510
511 * Git SCM module [Garry Dolley, Geoffrey Grosenbach, Scott Chacon]
512
513 * Use the --password switch for subversion by default, but add :scm_prefer_prompt variable (defaults to false) [Jamis Buck]
514
515
516 ## 2.0.100 (2.1 Preview 1) / September 1, 2007
517
518 * capify-generated Capfile will autoload all recipes from vendor/plugins/*/recipes/*.rb [Graeme Mathieson]
519
520 * Use sudo -p switch to set sudo password prompt to something predictable [Mike Bailey]
521
522 * Allow independent configurations to require the same recipe file [Jamis Buck]
523
524 * Set :shell to false to run a command without wrapping it in "sh -c" [Jamis Buck]
525
526 * Don't request a pty by default [Jamis Buck]
527
528 * Add a "match" remote dependency method [Adam Greene]
529
530 * Allow auth-caching of subversion credentials to be enabled via :scm_auth_cache [tsmith]
531
532 * Don't let a task trigger itself when used as the source for an "on" hook [Jamis Buck]
533
534 * Avoid using the --password switch with subversion for security purposes [sentinel]
535
536 * Add version_dir, current_dir, and shared_dir variables for naming the directories used in deployment [drinkingbird]
537
538 * Use Windows-safe binary reads for reading file contents [Ladislav Martincik]
539
540 * Add Accurev SCM support [Doug Barth]
541
542 * Use the :runner variable to determine who to sudo as for deploy:restart [Graham Ashton]
543
544 * Add Namespaces#top to always return a reference to the topmost namespace [Jamis Buck]
545
546 * Change the "-h" output so that it does not say that "-q" is the default [Jamis Buck]
547
548
549 ## 2.0.0 / July 21, 2007
550
551 * Make the "no matching servers" error more sane [halorgium]
552
553 * Make sure the invoke task gives a sane error when the COMMAND value is omitted [halorgium]
554
555 * Make sure variables are conditionally set in the deploy recipes, so as not to clobber values set elsewhere [Jamis Buck]
556
557 * Fix "input stream is empty" errors from HighLine on prompt [Jamis Buck]
558
559 * Added "synchronous_connect" setting to try and work around SFTP hangs for certain users [Jamis Buck]
560
561 * Auto-require the SSH shell service, to avoid race conditions [Jamis Buck]
562
563 * Add a millisecond sleep in upload to reduce CPU impact [Jamis Buck]
564
565 * Allow the logger to be set via Configuration#logger= [Jamis Buck]
566
567 * Allow $CAPISTRANO:HOST$ to be used in filenames to the put command [Jamis Buck]
568
569 * Allow execute_on_servers to be called without a current task again [Jamis Buck]
570
571 * Put $stdout in sync mode, so that Net::SSH prompts are displayed [Jamis Buck]
572
573 * Make sure deploy:check aborts if it fails [Jamis Buck]
574
575 * Spelling corrections in docs [Tim Carey-Smith, Giles Bowkett]
576
577
578 ## 1.99.3 (2.0 Preview 4) / June 28, 2007
579
580 * Don't break task descriptions on a period that appears in the middle of a sentence [Jamis Buck]
581
582 * Added support for :on_error => :continue in task definitions, allowing tasks to effectively ignore connection and execution errors that occur as they run [Rob Holland]
583
584 * Use correct parameters for Logger constructor in the SCM and Strategy base initializers [Jamis Buck]
585
586 * Set LC_ALL=C before querying the revision, to make sure the output is in a predictable locale and can be parsed predictably [via Leandro Nunes dos Santos]
587
588 * Add :copy_remote_dir variable for the :copy strategy, to indicate where the archive should be copied to on the remote servers [Jamis Buck]
589
590 * Make the awk use in the dependencies code work with POSIX awk [mcornick]
591
592 * Make variable accesses thread safe [via Adrian Danieli]
593
594 * Make user input for yes/no prompts work correctly in the Mercurial module [Matthew Elder]
595
596 * Use single quotes to escape semicolon in find command, instead of a backslash [via michael.italia@gmail.com]
597
598 * Better quoting of reserved characters in commands [Jamis Buck]
599
600 * Make sure Net::SSH versions prior to 1.1.0 still work [Jamis Buck]
601
602 * Allow the :hosts and :roles keys to accept lambdas, which will be evaluated lazily to allow runtime selection of hosts and roles in tasks [Jamis Buck]
603
604 * Use `which' to test whether a command exists in the remote path, instead of `test -p' [Jamis Buck]
605
606 * Make sure the connection factory is established synchronously, to avoid multiple gateway instances being spawned [Jamis Buck]
607
608 * Make sure symlink and finalize_update tasks reference the most recent release when called by themselves [Jamis Buck]
609
610
611 ## 1.99.2 (2.0 Preview 3) / June 15, 2007
612
613 * CVS SCM module [Brian Phillips]
614
615 * Fix typo in Perforce SCM module [Chris Bailey]
616
617 * ssh_options < server options when connecting [Jamis Buck]
618
619 * Logger defaults to $stderr instead of STDERR [lhartley]
620
621 * Use cp -RPp instead of -a in the remote cache strategy
622
623 * Make the UploadError exception include an array of the hosts that failed [rob@inversepath.com]
624
625 * Allow "empty" roles to be declared [Jamis Buck]
626
627 * Mercurial SCM module [Tobias Luetke, Matthew Elder]
628
629 * Invoke all commands via sh (customizable via :default_shell) [Jamis Buck]
630
631 * Make sure all directories exist on each deploy which are necessary for subsequent commands to succeed, since some SCM's won't save empty directories [Matthew Elder]
632
633 * Add :default_environment variable, which is applied to every command
634
635
636 ## 1.99.1 (2.0 Preview 2) / May 10, 2007
637
638 * Fix some documentation typos [eventualbuddha]
639
640 * Don't retry failed connections if an explicit auth_methods list is given [Chris Farms]
641
642 * Added support for load and exit callbacks, which get invoked when all recipes have been loaded and when all requested tasks have been executed [Jamis Buck]
643
644 * Added support for start and finish callbacks, which get invoked when any task is called via the command-line [Jamis Buck]
645
646 * Make `capify' understand simple command-line switches [Jamis Buck]
647
648 * Make the server definition itself available to SSH channels, rather than just the host name [Jamis Buck]
649
650 * Identify servers by their complete credentials in logs, rather than simply by hostname [Jamis Buck]
651
652 * Uniquely identify servers based on hostname, port, and username, instead of merely on hostname [Jamis Buck]
653
654 * Allow (e.g.) scm_command and local_scm_command to be set in the event of different paths to the scm command on local vs. remote hosts. [Jamis Buck]
655
656 * Kill the "deploy:app" namespace and move those tasks into deploy, directly. [Jamis Buck]
657
658 * Make sure 'desc' applies to the next defined task, in any namespace. [Jamis Buck]
659
660 * Fix shell so that servers for a task are correctly discovered. [Jamis Buck]
661
662 * Added before(), after(), and on() callback creation methods. [Jamis Buck]
663
664 * Fix broken check! method for some deployment strategies. [Jamis Buck]
665
666 * deploy:cold should also run migrations before starting the app [Jamis Buck]
667
668 * Make the copy strategy check out to a temporary directory [Jamis Buck]
669
670
671 ## 1.99.0 (2.0 Preview 1) / April 24, 2007
672
673 * Add `capify' script to make it easier to prepare a project for deployment using cap [Jamis Buck]
674
675 * Make sure the sudo helper understands the SuSE dialect of the sudo password prompt [Steven Wisener]
676
677 * Fix synchronization issue with Gateway initialization [Doug Barth]
678
679 * Added opt-in "compat" and "upgrade" recipes for tasks to aid in the upgrade process to Capistrano 2 [Jamis Buck]
680
681 * The deployment recipes are now opt-in. Just do 'load "deploy"' in your recipe script. [Jamis Buck]
682
683 * Added $CAPISTRANO:HOST$ placeholder in commands, which will be replaced with the name of the host on which the command is executing [Jamis Buck]
684
685 * Added -e switch to explain specific task. Added -X to extend -x. Made -h much briefer. Added -T to list known tasks. [Jamis Buck]
686
687 * Added namespaces for tasks [Jamis Buck]
688
689 * Merged the Configuration and Actor classes, performed various other massive refactorings of the code [Jamis Buck]
690
691
692 ## 1.4.1 / February 24, 2007
693
694 * Use the no-auth-cache option with subversion so that username/password tokens do not get cached by capistrano usage [jonathan]
695
696 * Deprecated upper-cased variables [Jamis Buck]
697
698 * Make sure Actor#get does not close the SFTP channel (so subsequent SFTP operations work) [Dov Murik]
699
700 * Add :env option to 'run' (and friends) so that you can specify environment variables to be injected into the new process' environment [Mathieu Lajugie]
701
702
703 ## 1.4.0 / February 3, 2007
704
705 * Use the auth info for subversion more consistently [Jamis Buck]
706
707 * Add a "capture" helper, for capturing the stdout of a remote command and returning it as a string [Jamis Buck]
708
709 * Add a "get" helper, to pull a file from a remote server to the localhost [bmihelac]
710
711 * Fix gateway to actually increment local_port if a port is in use, so that multiple capistrano instances can run at the same time [Mark Imbriaco]
712
713 * Refactor the permissions tweaking in update_code to a separate task so that people on shared hosts can override it as necessary [jaw6]
714
715 * Set umask during the setup task, so that intermediate directories are created with the proper permissions [NeilW]
716
717 * Removed -c/--caprc switch, since the new load order renders it meaningless (just use -f now) [Mike Bailey]
718
719 * Make sure the standard recipe loads first, so that .caprc and friends can override what it defines. [Mike Bailey]
720
721 * Add support for a system-wide capistrano config file [Mike Bailey]
722
723 * Make cold_deploy call update instead of deploy (to avoid invoking the restart task).
724
725 * Make the touch command run by update_code set the TZ to UTC, for consistent setting of asset timestamps. [NeilW]
726
727 * Fix off-by-one bug in show_tasks width-computation [NeilW]
728
729
730 ## 1.3.1 / January 5, 2007
731
732 * Fix connection problems when using gateways [Ezra Zygmuntowicz]
733
734
735 ## 1.3.0 / December 23, 2006
736
737 * Deprecate rake integration in favor of invoking `cap' directly [Jamis Buck]
738
739 * Make sure the CVS module references the repository explicitly in cvs_log [weyus@att.net]
740
741 * Remove trace messages when loading a file [Jamis Buck]
742
743 * Cleaner error messages for authentication failures and command errors [Jamis Buck]
744
745 * Added support for ~/.caprc, also -x and -c switches. [Jamis Buck]
746
747 * Updated migrate action to use db:migrate task in Rails instead of the deprecated migrate task [DHH]
748
749 * Allow SSH user and port to be encoded in the hostname strings [Ezra Zygmuntowicz]
750
751 * Fixed that new checkouts were not group-writable [DHH, Jamis Buck]
752
753 * Fixed that cap setup would use 755 on the deploy_to and shared directory roots instead of 775 [DHH]
754
755 * Don't run the cleanup task on servers marked no_release [Jamis Buck]
756
757 * Fix typo in default_io_proc so it correctly checks the stream parameter to see if it is the error stream [Stephen Haberman]
758
759 * Make sure assets in images, javascripts, and stylesheets are touched after updating the code, to ensure the asset timestamping feature of rails works correctly [Jamis Buck]
760
761 * Added warning if password is prompted for and termios is not installed [John Labovitz]
762
763 * Added :as option to sudo, so you can specify who the command is executed as [Mark Imbriaco]
764
765
766 ## 1.2.0 / September 14, 2006
767
768 * Add experimental 'shell' task [Jamis Buck]
769
770 * Display file for external configurations, rather than inspected proc. [Jamis Buck]
771
772 * Connect to multiple servers in parallel, rather than serially. [Jamis Buck]
773
774 * Add SCM module for Mercurial (closes #4150) [Matthew Elder]
775
776 * Remove unused line in SCM::Base (closes #5619) [chris@seagul.co.uk]
777
778 * More efficient "svn log" usage (closes #5620) [Anatol Pomozov]
779
780 * Better support for key passphrases in the SVN module (closes #5920) [llasram@gmail.com]
781
782 * Fix missing default for :local in cvs.rb (closes #3645) [jeremy@hinegardner.org]
783
784 * Fix awkward spacing in gemspec file (closes #3888) [grant@antiflux.org]
785
786 * Add support for :sudo variable to specify path to sudo (closes #4578) [timothee.peignier@tryphon.org]
787
788 * Make previous_release return nil if there are no previous releases (closes #4959) [bdabney@cavoksolutions.com]
789
790 * Uncache releases list after update_code is called so that newly released dir is included (closes #3766) [Jamis Buck]
791
792 * Allow the subversion scm to accept HTTPS certificates (closes #4792) [Jamis Buck]
793
794 * Make sure rollbacks occur within the scope of the task that triggered them [Jamis Buck]
795
796 * Fixed the default recipe to work with setups that haven't yet gone pids [DHH]
797
798 * Symlink and setup for shared/pids to tmp/pids [DHH]
799
800 * Fix some incorrect usage text (closes #4507) [gerry_shaw@yahoo.com]
801
802 * Added Actor#stream method that makes it easy to create cross-server streams [DHH]. Example:
803
804 desc "Run a tail on multiple log files at the same time"
805 task :tail_fcgi, :roles => :app do
806 stream "tail -f #{shared_path}/log/fastcgi.crash.log"
807 end
808
809 * Make update_code and symlink a macro task under the name "update" for easy of deploy to servers that does not run fcgis [DHH]
810
811 * Changed setup, update_code, rollback_code, and symlink to work on all servers instead of only those in the :app, :web, and :db roles. A server can opt out of being part of the release deployment by setting :no_release => true [DHH]
812
813 * Added support for :except on task declarations as the opposite of :only [DHH]. Example:
814
815 role :app, "192.168.0.2"
816 role :file, "192.168.0.3", :no_release => true
817
818 task :symlink, :except => { :no_release => true } do
819 on_rollback { run "ln -nfs #{previous_release} #{current_path}" }
820 run "ln -nfs #{current_release} #{current_path}"
821 end
822
823 cap symlink # will not run on 192.168.0.3
824
825 * Deprecate the -r/--recipe switch in favor of -f/--file (for more make/rake-like semantics) [Jamis Buck]
826
827 * Fix gemspec to include a dependency on rake 0.7 [Jamis Buck]
828
829 * Added respect for ENV["HOSTS"] that'll be used instead of the roles specified in the task definition [DHH]. Example:
830
831 HOSTS=192.168.0.1 cap setup # one-off setup for that server, doesn't need to be prespecified in the recipes file
832
833 * Added respect for ENV["ROLES"] that'll be used instead of the roles specified in the task definition [DHH]. Example:
834
835 task :setup, :roles => [ :app, :web, :db ]
836 # normally this would run every where
837 end
838
839 ROLES=app cap setup # this will only run for the app role, overwritting the default declaration
840
841 * Added :hosts option to task definition that allows you to specify cross-cutting tasks [DHH]. Example:
842
843 task :setup, :hosts => [ "06.example.com", "01.example.com" ] do
844 # this task will happen on 06 and 01 regardless of which roles they belong to
845 end
846
847 * Fix operator precedence problem in script for touching the revisions.log #3223 [jason.garber@emu.edu]
848
849
850 ## 1.1.0 / March 6th, 2006
851
852 * Simplify the generated capistrano.rake file, and make it easier to customize
853
854 * Use rake-like command-line semantics ("cap deploy", in addition to "cap -a deploy")
855
856 * Rename to capistrano
857
858 * Make the generated capistrano.rake file use rake namespaces, and include all default tasks
859
860 * Look for config/deploy.rb, capfile, and Capfile by default
861
862
863 ## 1.0.1 / February 20th, 2006
864
865 * Fix broken switchtower_invoke function in switchtower.rake (missing require statement)
866
867
868 ## 1.0.0 / Feburary 18th, 2006
869
870 * Make CVS module's :local value default to "."
871
872 * Add "invoke" task for executing one-off commands
873
874 * Make port selection smarter for gateway connections
875
876 * Add extension mechanism for custom ST operations and third-party task libraries
877
878 * Make ST rails rake tasks more configurable
879
880 * Add Actor#current_task and simplify Task#servers
881
882 * Add Actor#connect! method for working around lazy connection establishing
883
884 * Make sure IO::TRUNC is specified for Net::SFTP uploads (#3510)
885
886 * Add branch support to CVS [jeremy@hinegardner.org] (#3596)
887
888 * Add bazaar-ng SCM module [Damien Merenne]
889
890 * Add optional :svn_username and :svn_password variables
891
892 * Allow Proc-valued variables to be set more conveniently (set(:foo) { "bar" })
893
894 * Add perforce SCM module [Richard McMahon]
895
896 * Add bazaar (v1) SCM module [Edd Dumbill] (#3533)
897
898 * Fix stftime format string used in CVS module to be Windows-compatible (fixes #3383)
899
900 * Add an better error when a task is run and no servers match the required conditions
901
902 * Add default spinner and cold_deploy tasks, and spinner_user variable
903
904 * Changed restart_via variable to (boolean) use_sudo
905
906 * Only chmod when the revisions.log file is first created
907
908 * Make UPPERCASE variables work
909
910 * Added rails_env variable (defaults to production) for use by tasks that employ the RAILS_ENV environment variable
911
912 * Added Actor.default_io_proc
913
914 * Set :actor key on SSH channel instances
915
916
917 ## 0.10.0 / January 2nd, 2006
918
919 * Handle ssh password prompts like "someone's password:"
920
921 * Make CLI#echo available as a class method.
922
923 * Add CLI#with_echo.
924
925 * Make the default password prompt available as a class method.
926
927 # Add documentation for the CLI class.
928
929 * Add a sanity check to make sure the correct versions of Net::SSH and Net::SFTP are installed.
930
931 * Added a cleanup task to remove unused releases from the deployment directory
932
933 * Allow password to be reentered on sudo if it was entered incorrectly
934
935 * Use && as the command separator for the checkouts, so that errors are caught early.
936
937 * Ping each SSH connection every 1s during command processing so that long-running commands don't cause the connection to timeout.
938
939 * Add a 0.01s sleep during the command loop so that the CPU doesn't go ballistic while ST is doing its thing.
940
941 * Add :restart_via variable for specifying whether restart ought to use :sudo (default, use sudo)
942
943 * Use SFTP for file transfers (if available).
944
945 * Add an "update_current" task that will do an svn up on the current release
946
947 * Use the :checkout variable to determine what operation to use for svn checkouts (instead of co, like "export").
948
949 * The Rails rake tasks now load ST directly, instead of invoking it via system
950
951 * Added ssh_options variable to configure the SSH connection parameters #2734 [jerrett@bravenet.com]
952
953 * Require Net::SSH 1.0.5
954
955
956 ## 0.9.0 / October 18th, 2005
957
958 * Use process reaper instead of custom reap script for restarting
959
960 * Use -S switch to set variables before reading recipe files #2242
961
962 * Have setup.rb create a switchtower.cmd file on Win32 platforms #2402
963
964 * Add diff_from_last_deploy to the rails switchtower rakefile template
965
966 * Add diff_from_last_deploy task (currently only works with subversion)
967
968 * Add deploy_with_migrations task.
969
970 * Make the migrate task more customizable.
971
972 * If no password is given with the -p switch, prompt for password immediately.
973
974 * Do not install a switchtower stub in the script directory. Assume the switchtower executable is in the path.
975
976 * Remove trailing newlines from commands to prevent trailing backslash #2141
977
978 * Default parameters work correctly with the generator #2218 [Scott Barron]
979
980 * Attempt to require 'rubygems' explicitly when running the switchtower utility #2134
981
982 * Make default tasks work only on app/db/web roles, so that additional roles may be created for boxes with specific needs without needing to (for instance) deploy the app to those boxes
983
984 * Default the application name to "Application" when using --apply-to
985
986 * Show the help screen instead of an error when no arguments are given
987
988 * Make SwitchTower easier to invoke programmatically via SwitchTower::CLI
989
990 * Specify the revision to release via the :revision variable (defaults to latest revision)
991
992 * Allow variables to be set via the cli using the -s switch
993
994 * Log checkouts to a "revisions.log" file
995
996 * Changed behavior of checkout to use the timestamp as the release name, instead of the revision number
997
998 * Added CVS module (very very experimental!)
999
1000 * Works with public keys now, for passwordless deployment
1001
1002 * Subversion module recognizes the password prompt for HTTP authentication
1003
1004 * Preserve +x on scripts when using darcs #1929 [Scott Barron]
1005
1006 * When executing multiline commands, use a backslash to escape the newline
+0
-791
CHANGELOG.rdoc less more
0 == 2.5.9 / 12 September 2009
1
2 * #105 - Adds skip_hostfilter option to find_servers() [Eric]
3 * #103 - Fixes Using non-master branch fails with Ruby 1.9 [Suraj Kurapati]
4 * #96 - Tweak for 1.9 Compatibility
5 * #79 - Fixes Capistrano hangs on shell command for many computers
6 * #77 - Fixes Copy command doesn't work on Solaris due to tar/gtar
7 * #76 - Fixes Invalid Subversion URL
8 * Improved web:disable task, now suggests a .htaccess block to use suggested by Rafael García
9 * Includes more logger options (can now select stdout, stderr of a file) [Rafael García]
10 * Fixes a couple of documentation problems, typos and worse. [Lee Hambley]
11
12 == 2.5.8 / July 2009
13
14 * Fixes a problem in 2.5.7 where deploy:finalize_update had been badly merged.
15
16 == 2.5.6 & 2.5.7 / July 2009
17
18 * 2.5.7 masks a broken 2.5.6 release that was accidentally mirrored via Rubyforge.
19
20 * Clean the cached git repository [Graeme Mathieson]
21
22 * Fixes perforce issues reported at http://bit.ly/wt0es [Scott Johnson]
23
24 * Improved back-tick handling code in relation to the above.
25
26 * Fixes a Git issue when submodules update upstream. (via mailing list) [sneakin]
27
28 * Capify now creates the config directory in directories without one.
29
30 == 2.5.5 / 24 Feb 2009
31
32 * Make sure role(:foo) actually declares an (empty) role for :foo, even without server arguments [Jamis Buck]
33
34
35 == 2.5.4 / 4 Feb 2009
36
37 * When using rsync with the remote_cache strategy include -t switch to preserve file times [Kevin McCarthy]
38
39 * Bump Net::SSH dependency to version 2.0.10 [Jamis Buck]
40
41 * Use 'user' from .ssh/config appropriately [Jamis Buck]
42
43 * Allow respond_to?() method to accept optional second parameter (include_priv) [Matthias Marschall]
44
45 * Make sure sudo prompts are retried correctly even if "try again" and the prompt appear in the same text chunk from the server [Jamis Buck]
46
47 * Add supported environment variables to -H output [François Beausoleil]
48
49
50 == 2.5.3 / December 6, 2008
51
52 * Make previous_release return nil if there is no previous release [Mathias Meyer]
53
54 * Play nice with rubies that don't inspect terminals well (ie. JRuby) by defaulting screen columns to 80 [Bob McWhirter]
55
56 * Rollback of deploy:symlink would explode if there was no previous revision to rollback to [Jamis Buck]
57
58 * Fix bug in transfer.rb that caused get/put/upload/download to ignore blocks passed to them [arika]
59
60 * Fix issue with git SCM that caused "Unable to resolve revision" errors when there was trailing whitespace in git's output [Mark Zuneska, Daniel Berlinger and Evan Closson]
61
62
63 == 2.5.2 / November 13, 2008
64
65 * Fix issue with git SCM that caused "Unable to resolve revision for 'HEAD'" errors on deploy [Jamis Buck]
66
67
68 == 2.5.1 / November 7, 2008
69
70 * Add -t (--tools) switch for better task lists for external tools [Jamis Buck]
71
72 * Make the RemoteDependency#try method use invoke_command instead of run, for sudo-ability [Matthias Marschall]
73
74 * Make locally executed commands in Windows more Windows-friendly [esad@esse.at]
75
76 * Added :scm_arguments variable for custom SCM arguments (subversion-only, currently) [David Abdemoulaie]
77
78 * Don't emit -p for sudo when :sudo_prompt is blank [Matthias Marschall]
79
80 * Copy symlinks when using rsync [Paul Paradise]
81
82 * Make sure git query-revision matches on exact branch name [grant@nightriot.com]
83
84 * Use -T <arg> to filter listed tasks by a pattern [Mathias Meyer, Geoffrey Grosenbach]
85
86 * Expose the #scm method on SCM::Base for building custom scm commands [Mathias Meyer]
87
88 * Start logging some locally executed commands [springyweb]
89
90 * Added HOSTFILTER environment variable for constraining tasks so they run only on hosts matching the given list of servers [Walter Smith]
91
92 * Make sure the glob matching for copy excludes does not delete parent directories [Fabio Akita]
93
94 * Ruby 1.9 compatibility [Jamis Buck]
95
96
97 == 2.5.0 / August 28, 2008
98
99 * Allow :gateway to be set to an array, in which case a chain of tunnels is created [Kerry Buckley]
100
101 * Allow HOSTS spec to override even non-existent roles [Mike Bailey]
102
103 * Sort releases via "ls -xt" instead of "ls -x" to allow for custom release names [Yan Pritzker]
104
105 * Convert arguments to -s and -S into integers, booleans, etc. based on whether the arguments appear to be those types [Jamis Buck]
106
107 * Add descriptions of -n and -d to the verbose help text [Jamis Buck]
108
109 * Make rollbacks work with processes that need the current directory to be valid in order to restart properly (e.g. mongrel_rails) [Jamis Buck]
110
111 * Rename deploy:rollback_code to deploy:rollback:code [Jamis Buck]
112
113 * Added parallel() helper for executing multiple different commands in parallel [Jamis Buck]
114
115 * Make sure a task only uses the last on_rollback block, once, on rollback [Jamis Buck]
116
117 * Add :shared_children variable to customize which subdirectories are created by deploy:setup [Jonathan Share]
118
119 * Allow filename globbing in copy_exclude setting for the copy strategy [Jonathan Share]
120
121 * Allow remote_cache strategy to use copy_exclude settings (requires rsync) [Lewis Mackenzie]
122
123 * Make None SCM module work in Windows [Carlos Kozuszko]
124
125 * Recognize mingw as a Windows platform [Carlos Kozuszko]
126
127 * Fixed failing tests in Windows [Carlos Kozuszko]
128
129 * Made :scm_auth_cache control whether password option is emitted in subversion module [Brendan Schwartz]
130
131 * Fixed timestamp bug in CVS module [Jørgen Fjeld]
132
133 * Added -n/--dry-run switch, to display but not execute remote tasks [Paul Gross]
134
135
136 == 2.4.3 / June 28, 2008
137
138 * Fix gem dependencies so gem actually understands them [Jamis Buck]
139
140
141 == 2.4.2 / June 27, 2008
142
143 * Specify gem dependencies in rakefile [Jamis Buck]
144
145
146 == 2.4.1 / June 27, 2008
147
148 * Use Echoe to manage the Rakefile [Jamis Buck]
149
150 * Let Net::SSH manage the default SSH port selection [Ben Lavender]
151
152 * Changed capture() helper to not raise an exception on error, but to warn instead [Jeff Forcier]
153
154
155 == 2.4.0 / June 13, 2008
156
157 * Added :normalize_asset_timestamps option to deployment, defaulting to true, which allows asset timestamping to be disabled [John Trupiano]
158
159
160 == 2.4.0 Preview Release #1 (2.3.101) / June 5, 2008
161
162 * Only make deploy:start, deploy:stop, and deploy:restart try sudo as :runner. The other sudo-enabled tasks (deploy:setup, deploy:cleanup, etc.) will now use the :admin_runner user (which by default is unset). [Jamis Buck]
163
164 * Make sure triggers defined as a block inherit the scope of the task they are attached to, instead of the task they were called from [Jamis Buck]
165
166 * Make deploy:upload use the upload() helper for more efficient directory processing [Jamis Buck]
167
168 * Make deploy:upload accept globs [Mark Imbriaco]
169
170 * Make sure the host is reported with the output from scm_run [Jamis Buck]
171
172 * Make git SCM honor the :scm_verbose option [Jamis Buck]
173
174 * Don't follow symlinks when using :copy_cache [Jamis Buck]
175
176 * If :mode is given to upload() helper, do a chmod after to set the mode [Jamis Buck]
177
178 * Fix load_from_file method for windows users [Neil Wilson]
179
180 * Display a deprecation error if a remote git branch is specified [Tim Harper]
181
182 * Fix deployment recipes to use the updated sudo helper [Jamis Buck]
183
184 * Enhance the sudo helper so it can be used to return the command, instead of executing it [Jamis Buck]
185
186 * Revert "make sudo helper play nicely with complex command chains", since it broke stuff [Jamis Buck]
187
188 * Make set(:default_shell, false) work for not using a shell on a per-command basis [Ryan McGeary]
189
190 * Improved test coverage [Ryan McGeary]
191
192 * Fixed "coverage" take task [Ryan McGeary]
193
194 * Use upload() instead of put() with the copy strategy [Jamis Buck]
195
196 * Revert the "git fetch --tags" change, since it didn't work as expected [Jamis Buck]
197
198 * Fix deploy:pending when using git SCM [Ryan McGeary]
199
200 * Make sure deploy:check works with :none scm (which has no default command) [Jamis Buck]
201
202 * Add debug switch for enabling conditional execution of commands [Mark Imbriaco]
203
204
205 == 2.3.0 / May 2, 2008
206
207 * Make deploy:setup obey the :use_sudo and :runner directives, and generalize the :use_sudo and :runner options into a try_sudo() helper method [Jamis Buck]
208
209 * Make sudo helper play nicely with complex command chains [Jamis Buck]
210
211 * Expand file-transfer options with new upload() and download() helpers. [Jamis Buck]
212
213 * Allow SCP transfers in addition to SFTP. [Jamis Buck]
214
215 * Use Net::SSH v2 and Net::SSH::Gateway. [Jamis Buck]
216
217 * Added #export method for git SCM [Phillip Goldenburg]
218
219 * For query_revision, git SCM used git-rev-parse on the repo hosting the Capfile, which may NOT be the same tree as the actual source reposistory. Use git-ls-remote instead to resolve the revision for checkout. [Robin H. Johnson]
220
221 * Allow :ssh_options hash to be specified per server [Jesse Newland]
222
223 * Added support for depend :remote, :file to test for existence of a specific file [Andrew Carter]
224
225 * Ensure that the default run options are mixed into the command options when executing a command from the cap shell [Ken Collins]
226
227 * Added :none SCM module for deploying a specific directory's contents [Jamis Buck]
228
229 * Improved "copy" strategy supports local caching and pattern exclusion (via :copy_cache and :copy_exclude variables) [Jamis Buck]
230
231
232 == 2.2.0 / February 27, 2008
233
234 * Fix git submodule support to init on sync [halorgium]
235
236 * Add alternative server-centric role definition method [James Duncan Davidson]
237
238 * Add support for password prompts from the Mercurial SCM [ches]
239
240 * Add support for :max_hosts option in task definition or run() [Rob Holland <rob@inversepath.com>]
241
242 * Distributed git support for better operability with remote_cache strategy [voidlock]
243
244 * Use a default line length in help text if line length is otherwise too small [Jamis Buck]
245
246 * Fix incorrect reference to the 'setup' task in task documentation [rajeshduggal]
247
248 * Don't try to kill the spawner process on deploy:stop if no spawner process exists [Jamis Buck]
249
250 * Dynamic roles (e.g. role(:app) { "host.name" }) [dmasover]
251
252 * Implement Bzr#next_revision so that pending changes can be reported correctly [casret]
253
254 * Use a proper export command for bzr SCM [drudru]
255
256 * Use checkout instead of merge for git SCM [nuttycom]
257
258 * Fix typo in Subversion SCM module, encountered when an update fails [kemiller]
259
260 * Fix documentation typo in upload.rb [evolving_jerk]
261
262 * Added test case to show that the :scm_command is honored by the git SCM module [grempe]
263
264 * Fail gracefully when double-colons are used to delimit namespaces [richie]
265
266 * Add support for :git_enable_submodules variable, to enable submodules with the git SCM [halorgium]
267
268 * If subversion asks for a password, prompt as a last resort [Jamis Buck]
269
270 * Use checkout --lightweight for bzr checkout, instead of branch [michiels]
271
272 * Make sure bzr SCM works when revision is head (or unspecified) [michiels]
273
274 * Support p4sync_flags and p4client_root variables for Perforce [gseidman]
275
276 * Prepare for Net::SSH v2 by making sure Capistrano only tries to load Net::SSH versions less than 1.99.0 [Jamis Buck]
277
278
279 == 2.1.0 / October 14, 2007
280
281 * Default to 0664 instead of 0660 on upload [Jamis Buck]
282
283 * Fix deploy:pending to query SCM for the subsequent revision so that it does not include the last deployed change [Jamis Buck]
284
285 * Prefer 'Last Changed Rev' over 'Revision' when querying latest revision via Subversion [Jamis Buck]
286
287 * Explicitly require 'stringio' in copy_test [mislav]
288
289 * When Subversion#query_revision fails, give a more sane error [Jamis Buck]
290
291 * Don't run the upgrade:revisions task on non-release servers [Jamis Buck]
292
293 * Fix cap shell to properly recognize sudo prompt [Mark Imbriaco, barnaby, Jamis Buck]
294
295 * Git SCM module [Garry Dolley, Geoffrey Grosenbach, Scott Chacon]
296
297 * Use the --password switch for subversion by default, but add :scm_prefer_prompt variable (defaults to false) [Jamis Buck]
298
299
300 == 2.0.100 (2.1 Preview 1) / September 1, 2007
301
302 * capify-generated Capfile will autoload all recipes from vendor/plugins/*/recipes/*.rb [Graeme Mathieson]
303
304 * Use sudo -p switch to set sudo password prompt to something predictable [Mike Bailey]
305
306 * Allow independent configurations to require the same recipe file [Jamis Buck]
307
308 * Set :shell to false to run a command without wrapping it in "sh -c" [Jamis Buck]
309
310 * Don't request a pty by default [Jamis Buck]
311
312 * Add a "match" remote dependency method [Adam Greene]
313
314 * Allow auth-caching of subversion credentials to be enabled via :scm_auth_cache [tsmith]
315
316 * Don't let a task trigger itself when used as the source for an "on" hook [Jamis Buck]
317
318 * Avoid using the --password switch with subversion for security purposes [sentinel]
319
320 * Add version_dir, current_dir, and shared_dir variables for naming the directories used in deployment [drinkingbird]
321
322 * Use Windows-safe binary reads for reading file contents [Ladislav Martincik]
323
324 * Add Accurev SCM support [Doug Barth]
325
326 * Use the :runner variable to determine who to sudo as for deploy:restart [Graham Ashton]
327
328 * Add Namespaces#top to always return a reference to the topmost namespace [Jamis Buck]
329
330 * Change the "-h" output so that it does not say that "-q" is the default [Jamis Buck]
331
332
333 == 2.0.0 / July 21, 2007
334
335 * Make the "no matching servers" error more sane [halorgium]
336
337 * Make sure the invoke task gives a sane error when the COMMAND value is omitted [halorgium]
338
339 * Make sure variables are conditionally set in the deploy recipes, so as not to clobber values set elsewhere [Jamis Buck]
340
341 * Fix "input stream is empty" errors from HighLine on prompt [Jamis Buck]
342
343 * Added "synchronous_connect" setting to try and work around SFTP hangs for certain users [Jamis Buck]
344
345 * Auto-require the SSH shell service, to avoid race conditions [Jamis Buck]
346
347 * Add a millisecond sleep in upload to reduce CPU impact [Jamis Buck]
348
349 * Allow the logger to be set via Configuration#logger= [Jamis Buck]
350
351 * Allow $CAPISTRANO:HOST$ to be used in filenames to the put command [Jamis Buck]
352
353 * Allow execute_on_servers to be called without a current task again [Jamis Buck]
354
355 * Put $stdout in sync mode, so that Net::SSH prompts are displayed [Jamis Buck]
356
357 * Make sure deploy:check aborts if it fails [Jamis Buck]
358
359 * Spelling corrections in docs [Tim Carey-Smith, Giles Bowkett]
360
361
362 == 1.99.3 (2.0 Preview 4) / June 28, 2007
363
364 * Don't break task descriptions on a period that appears in the middle of a sentence [Jamis Buck]
365
366 * Added support for :on_error => :continue in task definitions, allowing tasks to effectively ignore connection and execution errors that occur as they run [Rob Holland]
367
368 * Use correct parameters for Logger constructor in the SCM and Strategy base initializers [Jamis Buck]
369
370 * Set LC_ALL=C before querying the revision, to make sure the output is in a predictable locale and can be parsed predictably [via Leandro Nunes dos Santos]
371
372 * Add :copy_remote_dir variable for the :copy strategy, to indicate where the archive should be copied to on the remote servers [Jamis Buck]
373
374 * Make the awk use in the dependencies code work with POSIX awk [mcornick]
375
376 * Make variable accesses thread safe [via Adrian Danieli]
377
378 * Make user input for yes/no prompts work correctly in the Mercurial module [Matthew Elder]
379
380 * Use single quotes to escape semicolon in find command, instead of a backslash [via michael.italia@gmail.com]
381
382 * Better quoting of reserved characters in commands [Jamis Buck]
383
384 * Make sure Net::SSH versions prior to 1.1.0 still work [Jamis Buck]
385
386 * Allow the :hosts and :roles keys to accept lambdas, which will be evaluated lazily to allow runtime selection of hosts and roles in tasks [Jamis Buck]
387
388 * Use `which' to test whether a command exists in the remote path, instead of `test -p' [Jamis Buck]
389
390 * Make sure the connection factory is established synchronously, to avoid multiple gateway instances being spawned [Jamis Buck]
391
392 * Make sure symlink and finalize_update tasks reference the most recent release when called by themselves [Jamis Buck]
393
394
395 == 1.99.2 (2.0 Preview 3) / June 15, 2007
396
397 * CVS SCM module [Brian Phillips]
398
399 * Fix typo in Perforce SCM module [Chris Bailey]
400
401 * ssh_options < server options when connecting [Jamis Buck]
402
403 * Logger defaults to $stderr instead of STDERR [lhartley]
404
405 * Use cp -RPp instead of -a in the remote cache strategy
406
407 * Make the UploadError exception include an array of the hosts that failed [rob@inversepath.com]
408
409 * Allow "empty" roles to be declared [Jamis Buck]
410
411 * Mercurial SCM module [Tobias Luetke, Matthew Elder]
412
413 * Invoke all commands via sh (customizable via :default_shell) [Jamis Buck]
414
415 * Make sure all directories exist on each deploy which are necessary for subsequent commands to succeed, since some SCM's won't save empty directories [Matthew Elder]
416
417 * Add :default_environment variable, which is applied to every command
418
419
420 == 1.99.1 (2.0 Preview 2) / May 10, 2007
421
422 * Fix some documentation typos [eventualbuddha]
423
424 * Don't retry failed connections if an explicit auth_methods list is given [Chris Farms]
425
426 * Added support for load and exit callbacks, which get invoked when all recipes have been loaded and when all requested tasks have been executed [Jamis Buck]
427
428 * Added support for start and finish callbacks, which get invoked when any task is called via the command-line [Jamis Buck]
429
430 * Make `capify' understand simple command-line switches [Jamis Buck]
431
432 * Make the server definition itself available to SSH channels, rather than just the host name [Jamis Buck]
433
434 * Identify servers by their complete credentials in logs, rather than simply by hostname [Jamis Buck]
435
436 * Uniquely identify servers based on hostname, port, and username, instead of merely on hostname [Jamis Buck]
437
438 * Allow (e.g.) scm_command and local_scm_command to be set in the event of different paths to the scm command on local vs. remote hosts. [Jamis Buck]
439
440 * Kill the "deploy:app" namespace and move those tasks into deploy, directly. [Jamis Buck]
441
442 * Make sure 'desc' applies to the next defined task, in any namespace. [Jamis Buck]
443
444 * Fix shell so that servers for a task are correctly discovered. [Jamis Buck]
445
446 * Added before(), after(), and on() callback creation methods. [Jamis Buck]
447
448 * Fix broken check! method for some deployment strategies. [Jamis Buck]
449
450 * deploy:cold should also run migrations before starting the app [Jamis Buck]
451
452 * Make the copy strategy check out to a temporary directory [Jamis Buck]
453
454
455 == 1.99.0 (2.0 Preview 1) / April 24, 2007
456
457 * Add `capify' script to make it easier to prepare a project for deployment using cap [Jamis Buck]
458
459 * Make sure the sudo helper understands the SuSE dialect of the sudo password prompt [Steven Wisener]
460
461 * Fix synchronization issue with Gateway initialization [Doug Barth]
462
463 * Added opt-in "compat" and "upgrade" recipes for tasks to aid in the upgrade process to Capistrano 2 [Jamis Buck]
464
465 * The deployment recipes are now opt-in. Just do 'load "deploy"' in your recipe script. [Jamis Buck]
466
467 * Added $CAPISTRANO:HOST$ placeholder in commands, which will be replaced with the name of the host on which the command is executing [Jamis Buck]
468
469 * Added -e switch to explain specific task. Added -X to extend -x. Made -h much briefer. Added -T to list known tasks. [Jamis Buck]
470
471 * Added namespaces for tasks [Jamis Buck]
472
473 * Merged the Configuration and Actor classes, performed various other massive refactorings of the code [Jamis Buck]
474
475
476 == 1.4.1 / February 24, 2007
477
478 * Use the no-auth-cache option with subversion so that username/password tokens do not get cached by capistrano usage [jonathan]
479
480 * Deprecated upper-cased variables [Jamis Buck]
481
482 * Make sure Actor#get does not close the SFTP channel (so subsequent SFTP operations work) [Dov Murik]
483
484 * Add :env option to 'run' (and friends) so that you can specify environment variables to be injected into the new process' environment [Mathieu Lajugie]
485
486
487 == 1.4.0 / February 3, 2007
488
489 * Use the auth info for subversion more consistently [Jamis Buck]
490
491 * Add a "capture" helper, for capturing the stdout of a remote command and returning it as a string [Jamis Buck]
492
493 * Add a "get" helper, to pull a file from a remote server to the localhost [bmihelac]
494
495 * Fix gateway to actually increment local_port if a port is in use, so that multiple capistrano instances can run at the same time [Mark Imbriaco]
496
497 * Refactor the permissions tweaking in update_code to a separate task so that people on shared hosts can override it as necessary [jaw6]
498
499 * Set umask during the setup task, so that intermediate directories are created with the proper permissions [NeilW]
500
501 * Removed -c/--caprc switch, since the new load order renders it meaningless (just use -f now) [Mike Bailey]
502
503 * Make sure the standard recipe loads first, so that .caprc and friends can override what it defines. [Mike Bailey]
504
505 * Add support for a system-wide capistrano config file [Mike Bailey]
506
507 * Make cold_deploy call update instead of deploy (to avoid invoking the restart task).
508
509 * Make the touch command run by update_code set the TZ to UTC, for consistent setting of asset timestamps. [NeilW]
510
511 * Fix off-by-one bug in show_tasks width-computation [NeilW]
512
513
514 == 1.3.1 / January 5, 2007
515
516 * Fix connection problems when using gateways [Ezra Zygmuntowicz]
517
518
519 == 1.3.0 / December 23, 2006
520
521 * Deprecate rake integration in favor of invoking `cap' directly [Jamis Buck]
522
523 * Make sure the CVS module references the repository explicitly in cvs_log [weyus@att.net]
524
525 * Remove trace messages when loading a file [Jamis Buck]
526
527 * Cleaner error messages for authentication failures and command errors [Jamis Buck]
528
529 * Added support for ~/.caprc, also -x and -c switches. [Jamis Buck]
530
531 * Updated migrate action to use db:migrate task in Rails instead of the deprecated migrate task [DHH]
532
533 * Allow SSH user and port to be encoded in the hostname strings [Ezra Zygmuntowicz]
534
535 * Fixed that new checkouts were not group-writable [DHH, Jamis Buck]
536
537 * Fixed that cap setup would use 755 on the deploy_to and shared directory roots instead of 775 [DHH]
538
539 * Don't run the cleanup task on servers marked no_release [Jamis Buck]
540
541 * Fix typo in default_io_proc so it correctly checks the stream parameter to see if it is the error stream [Stephen Haberman]
542
543 * Make sure assets in images, javascripts, and stylesheets are touched after updating the code, to ensure the asset timestamping feature of rails works correctly [Jamis Buck]
544
545 * Added warning if password is prompted for and termios is not installed [John Labovitz]
546
547 * Added :as option to sudo, so you can specify who the command is executed as [Mark Imbriaco]
548
549
550 == 1.2.0 / September 14, 2006
551
552 * Add experimental 'shell' task [Jamis Buck]
553
554 * Display file for external configurations, rather than inspected proc. [Jamis Buck]
555
556 * Connect to multiple servers in parallel, rather than serially. [Jamis Buck]
557
558 * Add SCM module for Mercurial (closes #4150) [Matthew Elder]
559
560 * Remove unused line in SCM::Base (closes #5619) [chris@seagul.co.uk]
561
562 * More efficient "svn log" usage (closes #5620) [Anatol Pomozov]
563
564 * Better support for key passphrases in the SVN module (closes #5920) [llasram@gmail.com]
565
566 * Fix missing default for :local in cvs.rb (closes #3645) [jeremy@hinegardner.org]
567
568 * Fix awkward spacing in gemspec file (closes #3888) [grant@antiflux.org]
569
570 * Add support for :sudo variable to specify path to sudo (closes #4578) [timothee.peignier@tryphon.org]
571
572 * Make previous_release return nil if there are no previous releases (closes #4959) [bdabney@cavoksolutions.com]
573
574 * Uncache releases list after update_code is called so that newly released dir is included (closes #3766) [Jamis Buck]
575
576 * Allow the subversion scm to accept HTTPS certificates (closes #4792) [Jamis Buck]
577
578 * Make sure rollbacks occur within the scope of the task that triggered them [Jamis Buck]
579
580 * Fixed the default recipe to work with setups that haven't yet gone pids [DHH]
581
582 * Symlink and setup for shared/pids to tmp/pids [DHH]
583
584 * Fix some incorrect usage text (closes #4507) [gerry_shaw@yahoo.com]
585
586 * Added Actor#stream method that makes it easy to create cross-server streams [DHH]. Example:
587
588 desc "Run a tail on multiple log files at the same time"
589 task :tail_fcgi, :roles => :app do
590 stream "tail -f #{shared_path}/log/fastcgi.crash.log"
591 end
592
593 * Make update_code and symlink a macro task under the name "update" for easy of deploy to servers that does not run fcgis [DHH]
594
595 * Changed setup, update_code, rollback_code, and symlink to work on all servers instead of only those in the :app, :web, and :db roles. A server can opt out of being part of the release deployment by setting :no_release => true [DHH]
596
597 * Added support for :except on task declarations as the opposite of :only [DHH]. Example:
598
599 role :app, "192.168.0.2"
600 role :file, "192.168.0.3", :no_release => true
601
602 task :symlink, :except => { :no_release => true } do
603 on_rollback { run "ln -nfs #{previous_release} #{current_path}" }
604 run "ln -nfs #{current_release} #{current_path}"
605 end
606
607 cap symlink # will not run on 192.168.0.3
608
609 * Deprecate the -r/--recipe switch in favor of -f/--file (for more make/rake-like semantics) [Jamis Buck]
610
611 * Fix gemspec to include a dependency on rake 0.7 [Jamis Buck]
612
613 * Added respect for ENV["HOSTS"] that'll be used instead of the roles specified in the task definition [DHH]. Example:
614
615 HOSTS=192.168.0.1 cap setup # one-off setup for that server, doesn't need to be prespecified in the recipes file
616
617 * Added respect for ENV["ROLES"] that'll be used instead of the roles specified in the task definition [DHH]. Example:
618
619 task :setup, :roles => [ :app, :web, :db ]
620 # normally this would run every where
621 end
622
623 ROLES=app cap setup # this will only run for the app role, overwritting the default declaration
624
625 * Added :hosts option to task definition that allows you to specify cross-cutting tasks [DHH]. Example:
626
627 task :setup, :hosts => [ "06.example.com", "01.example.com" ] do
628 # this task will happen on 06 and 01 regardless of which roles they belong to
629 end
630
631 * Fix operator precedence problem in script for touching the revisions.log #3223 [jason.garber@emu.edu]
632
633
634 == 1.1.0 / March 6th, 2006
635
636 * Simplify the generated capistrano.rake file, and make it easier to customize
637
638 * Use rake-like command-line semantics ("cap deploy", in addition to "cap -a deploy")
639
640 * Rename to capistrano
641
642 * Make the generated capistrano.rake file use rake namespaces, and include all default tasks
643
644 * Look for config/deploy.rb, capfile, and Capfile by default
645
646
647 == 1.0.1 / February 20th, 2006
648
649 * Fix broken switchtower_invoke function in switchtower.rake (missing require statement)
650
651
652 == 1.0.0 / Feburary 18th, 2006
653
654 * Make CVS module's :local value default to "."
655
656 * Add "invoke" task for executing one-off commands
657
658 * Make port selection smarter for gateway connections
659
660 * Add extension mechanism for custom ST operations and third-party task libraries
661
662 * Make ST rails rake tasks more configurable
663
664 * Add Actor#current_task and simplify Task#servers
665
666 * Add Actor#connect! method for working around lazy connection establishing
667
668 * Make sure IO::TRUNC is specified for Net::SFTP uploads (#3510)
669
670 * Add branch support to CVS [jeremy@hinegardner.org] (#3596)
671
672 * Add bazaar-ng SCM module [Damien Merenne]
673
674 * Add optional :svn_username and :svn_password variables
675
676 * Allow Proc-valued variables to be set more conveniently (set(:foo) { "bar" })
677
678 * Add perforce SCM module [Richard McMahon]
679
680 * Add bazaar (v1) SCM module [Edd Dumbill] (#3533)
681
682 * Fix stftime format string used in CVS module to be Windows-compatible (fixes #3383)
683
684 * Add an better error when a task is run and no servers match the required conditions
685
686 * Add default spinner and cold_deploy tasks, and spinner_user variable
687
688 * Changed restart_via variable to (boolean) use_sudo
689
690 * Only chmod when the revisions.log file is first created
691
692 * Make UPPERCASE variables work
693
694 * Added rails_env variable (defaults to production) for use by tasks that employ the RAILS_ENV environment variable
695
696 * Added Actor.default_io_proc
697
698 * Set :actor key on SSH channel instances
699
700
701 == 0.10.0 / January 2nd, 2006
702
703 * Handle ssh password prompts like "someone's password:"
704
705 * Make CLI#echo available as a class method.
706
707 * Add CLI#with_echo.
708
709 * Make the default password prompt available as a class method.
710
711 # Add documentation for the CLI class.
712
713 * Add a sanity check to make sure the correct versions of Net::SSH and Net::SFTP are installed.
714
715 * Added a cleanup task to remove unused releases from the deployment directory
716
717 * Allow password to be reentered on sudo if it was entered incorrectly
718
719 * Use && as the command separator for the checkouts, so that errors are caught early.
720
721 * Ping each SSH connection every 1s during command processing so that long-running commands don't cause the connection to timeout.
722
723 * Add a 0.01s sleep during the command loop so that the CPU doesn't go ballistic while ST is doing its thing.
724
725 * Add :restart_via variable for specifying whether restart ought to use :sudo (default, use sudo)
726
727 * Use SFTP for file transfers (if available).
728
729 * Add an "update_current" task that will do an svn up on the current release
730
731 * Use the :checkout variable to determine what operation to use for svn checkouts (instead of co, like "export").
732
733 * The Rails rake tasks now load ST directly, instead of invoking it via system
734
735 * Added ssh_options variable to configure the SSH connection parameters #2734 [jerrett@bravenet.com]
736
737 * Require Net::SSH 1.0.5
738
739
740 == 0.9.0 / October 18th, 2005
741
742 * Use process reaper instead of custom reap script for restarting
743
744 * Use -S switch to set variables before reading recipe files #2242
745
746 * Have setup.rb create a switchtower.cmd file on Win32 platforms #2402
747
748 * Add diff_from_last_deploy to the rails switchtower rakefile template
749
750 * Add diff_from_last_deploy task (currently only works with subversion)
751
752 * Add deploy_with_migrations task.
753
754 * Make the migrate task more customizable.
755
756 * If no password is given with the -p switch, prompt for password immediately.
757
758 * Do not install a switchtower stub in the script directory. Assume the switchtower executable is in the path.
759
760 * Remove trailing newlines from commands to prevent trailing backslash #2141
761
762 * Default parameters work correctly with the generator #2218 [Scott Barron]
763
764 * Attempt to require 'rubygems' explicitly when running the switchtower utility #2134
765
766 * Make default tasks work only on app/db/web roles, so that additional roles may be created for boxes with specific needs without needing to (for instance) deploy the app to those boxes
767
768 * Default the application name to "Application" when using --apply-to
769
770 * Show the help screen instead of an error when no arguments are given
771
772 * Make SwitchTower easier to invoke programmatically via SwitchTower::CLI
773
774 * Specify the revision to release via the :revision variable (defaults to latest revision)
775
776 * Allow variables to be set via the cli using the -s switch
777
778 * Log checkouts to a "revisions.log" file
779
780 * Changed behavior of checkout to use the timestamp as the release name, instead of the revision number
781
782 * Added CVS module (very very experimental!)
783
784 * Works with public keys now, for passwordless deployment
785
786 * Subversion module recognizes the password prompt for HTTP authentication
787
788 * Preserve +x on scripts when using darcs #1929 [Scott Barron]
789
790 * When executing multiline commands, use a backslash to escape the newline
0 source "http://rubygems.org"
1
2 # Specify your gem's dependencies in capistrano.gemspec
3 gemspec
4
5 #
6 # Development Dependencies from the Gemfile
7 # are merged here.
8 #
9 group :development do
10 gem 'rake', '0.8.7'
11 gem 'ruby-debug', :platform => :mri_18
12 gem 'ruby-debug19', :platform => :mri_19
13 end
+0
-104
Manifest less more
0 bin/cap
1 bin/capify
2 CHANGELOG.rdoc
3 examples/sample.rb
4 lib/capistrano/callback.rb
5 lib/capistrano/cli/execute.rb
6 lib/capistrano/cli/help.rb
7 lib/capistrano/cli/help.txt
8 lib/capistrano/cli/options.rb
9 lib/capistrano/cli/ui.rb
10 lib/capistrano/cli.rb
11 lib/capistrano/command.rb
12 lib/capistrano/configuration/actions/file_transfer.rb
13 lib/capistrano/configuration/actions/inspect.rb
14 lib/capistrano/configuration/actions/invocation.rb
15 lib/capistrano/configuration/callbacks.rb
16 lib/capistrano/configuration/connections.rb
17 lib/capistrano/configuration/execution.rb
18 lib/capistrano/configuration/loading.rb
19 lib/capistrano/configuration/namespaces.rb
20 lib/capistrano/configuration/roles.rb
21 lib/capistrano/configuration/servers.rb
22 lib/capistrano/configuration/variables.rb
23 lib/capistrano/configuration.rb
24 lib/capistrano/errors.rb
25 lib/capistrano/extensions.rb
26 lib/capistrano/logger.rb
27 lib/capistrano/processable.rb
28 lib/capistrano/recipes/compat.rb
29 lib/capistrano/recipes/deploy/dependencies.rb
30 lib/capistrano/recipes/deploy/local_dependency.rb
31 lib/capistrano/recipes/deploy/remote_dependency.rb
32 lib/capistrano/recipes/deploy/scm/accurev.rb
33 lib/capistrano/recipes/deploy/scm/base.rb
34 lib/capistrano/recipes/deploy/scm/bzr.rb
35 lib/capistrano/recipes/deploy/scm/cvs.rb
36 lib/capistrano/recipes/deploy/scm/darcs.rb
37 lib/capistrano/recipes/deploy/scm/git.rb
38 lib/capistrano/recipes/deploy/scm/mercurial.rb
39 lib/capistrano/recipes/deploy/scm/none.rb
40 lib/capistrano/recipes/deploy/scm/perforce.rb
41 lib/capistrano/recipes/deploy/scm/subversion.rb
42 lib/capistrano/recipes/deploy/scm.rb
43 lib/capistrano/recipes/deploy/strategy/base.rb
44 lib/capistrano/recipes/deploy/strategy/checkout.rb
45 lib/capistrano/recipes/deploy/strategy/copy.rb
46 lib/capistrano/recipes/deploy/strategy/export.rb
47 lib/capistrano/recipes/deploy/strategy/remote.rb
48 lib/capistrano/recipes/deploy/strategy/remote_cache.rb
49 lib/capistrano/recipes/deploy/strategy.rb
50 lib/capistrano/recipes/deploy/templates/maintenance.rhtml
51 lib/capistrano/recipes/deploy.rb
52 lib/capistrano/recipes/standard.rb
53 lib/capistrano/recipes/templates/maintenance.rhtml
54 lib/capistrano/recipes/upgrade.rb
55 lib/capistrano/role.rb
56 lib/capistrano/server_definition.rb
57 lib/capistrano/shell.rb
58 lib/capistrano/ssh.rb
59 lib/capistrano/task_definition.rb
60 lib/capistrano/transfer.rb
61 lib/capistrano/version.rb
62 lib/capistrano.rb
63 Rakefile
64 README.rdoc
65 setup.rb
66 test/cli/execute_test.rb
67 test/cli/help_test.rb
68 test/cli/options_test.rb
69 test/cli/ui_test.rb
70 test/cli_test.rb
71 test/command_test.rb
72 test/configuration/actions/file_transfer_test.rb
73 test/configuration/actions/inspect_test.rb
74 test/configuration/actions/invocation_test.rb
75 test/configuration/callbacks_test.rb
76 test/configuration/connections_test.rb
77 test/configuration/execution_test.rb
78 test/configuration/loading_test.rb
79 test/configuration/namespace_dsl_test.rb
80 test/configuration/roles_test.rb
81 test/configuration/servers_test.rb
82 test/configuration/variables_test.rb
83 test/configuration_test.rb
84 test/deploy/local_dependency_test.rb
85 test/deploy/remote_dependency_test.rb
86 test/deploy/scm/accurev_test.rb
87 test/deploy/scm/base_test.rb
88 test/deploy/scm/git_test.rb
89 test/deploy/scm/mercurial_test.rb
90 test/deploy/strategy/copy_test.rb
91 test/extensions_test.rb
92 test/fixtures/cli_integration.rb
93 test/fixtures/config.rb
94 test/fixtures/custom.rb
95 test/logger_test.rb
96 test/role_test.rb
97 test/server_definition_test.rb
98 test/shell_test.rb
99 test/ssh_test.rb
100 test/task_definition_test.rb
101 test/transfer_test.rb
102 test/utils.rb
103 Manifest
0 ## Capistrano
1
2 [![Build
3 Status](https://secure.travis-ci.org/capistrano/capistrano.png)](http://travis-ci.org/capistrano/capistrano)
4
5
6 Capistrano is a utility and framework for executing commands in parallel on
7 multiple remote machines, via SSH. It uses a simple DSL (borrowed in part from
8 [Rake](http://rake.rubyforge.org/)) that allows you to define _tasks_, which may
9 be applied to machines in certain roles. It also supports tunneling connections
10 via some gateway machine to allow operations to be performed behind VPN's and
11 firewalls.
12
13 Capistrano was originally designed to simplify and automate deployment of web
14 applications to distributed environments, and originally came bundled with a set
15 of tasks designed for deploying Rails applications.
16
17 ## Documentation
18
19 * [http://github.com/capistrano/capistrano/wiki/Documentation-v2.x](http://github.com/capistrano/capistrano/wiki/Documentation-v2.x)
20
21 ## DEPENDENCIES
22
23 * [Net::SSH](http://net-ssh.rubyforge.org)
24 * [Net::SFTP](http://net-ssh.rubyforge.org)
25 * [Net::SCP](http://net-ssh.rubyforge.org)
26 * [Net::SSH::Gateway](http://net-ssh.rubyforge.org)
27 * [HighLine](http://highline.rubyforge.org)
28
29 If you want to run the tests, you'll also need to install the dependencies with
30 Bundler, see the `Gemfile` within .
31
32 ## ASSUMPTIONS
33
34 Capistrano is "opinionated software", which means it has very firm ideas about
35 how things ought to be done, and tries to force those ideas on you. Some of the
36 assumptions behind these opinions are:
37
38 * You are using SSH to access the remote servers.
39 * You either have the same password to all target machines, or you have public
40 keys in place to allow passwordless access to them.
41
42 Do not expect these assumptions to change.
43
44 ## USAGE
45
46 In general, you'll use Capistrano as follows:
47
48 * Create a recipe file ("capfile" or "Capfile").
49 * Use the `cap` script to execute your recipe.
50
51 Use the `cap` script as follows:
52
53 cap sometask
54
55 By default, the script will look for a file called one of `capfile` or
56 `Capfile`. The `someaction` text indicates which task to execute. You can do
57 "cap -h" to see all the available options and "cap -T" to see all the available
58 tasks.
59
60 ## CONTRIBUTING:
61
62 * Fork Capistrano
63 * Create a topic branch - `git checkout -b my_branch`
64 * Rebase your branch so that all your changes are reflected in one
65 commit
66 * Push to your branch - `git push origin my_branch`
67 * Create a Pull Request from your branch, include as much documentation
68 as you can in the commit message/pull request, following these
69 [guidelies on writing a good commit message](http://spheredev.org/wiki/Git_for_the_lazy#Writing_good_commit_messages)
70 * That's it!
71
72
73 ## LICENSE:
74
75 Permission is hereby granted, free of charge, to any person obtaining
76 a copy of this software and associated documentation files (the
77 'Software'), to deal in the Software without restriction, including
78 without limitation the rights to use, copy, modify, merge, publish,
79 distribute, sublicense, and/or sell copies of the Software, and to
80 permit persons to whom the Software is furnished to do so, subject to
81 the following conditions:
82
83 The above copyright notice and this permission notice shall be
84 included in all copies or substantial portions of the Software.
85
86 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
87 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
88 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
89 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
90 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
91 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
92 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+0
-102
README.rdoc less more
0 = Capistrano
1
2 Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH. It uses a simple DSL (borrowed in part from Rake, http://rake.rubyforge.org/) that allows you to define _tasks_, which may be applied to machines in certain roles. It also supports tunneling connections via some gateway machine to allow operations to be performed behind VPN's and firewalls.
3
4 Capistrano was originally designed to simplify and automate deployment of web applications to distributed environments, and originally came bundled with a set of tasks designed for deploying Rails applications. The deployment tasks are now (as of Capistrano 2.0) opt-in and require clients to explicitly put
5 "load 'deploy'" in their recipes.
6
7 == Documentation
8
9 We know that documentation is something that really lets us down, that's why there is a repository for a handbook below, please open an issue on it if you would like something documented:
10
11 * http://github.com/leehambley/capistrano-handbook
12
13 If you prefer the wiki style of documentation, then please see our wiki
14
15 * http://wiki.capify.org
16
17 Due to a failure of MySQL with PHP, searches shorter than three characters are all but ignored, we're going to rectify this, but in the meantime, please do what you can, tickets opened on the handbook for the wiki will be answered too, so please let us know if you don't find something you needed.
18
19 We take bug reports via lighthouse app, you can find that page here:
20
21 * http://capistrano.lighthouseapp.com
22
23 More documentation is on the way, if in doubt try opening the recipes that ship with capistrano.
24
25 == DEPENDENCIES
26
27 * Net::SSH v2 (http://net-ssh.rubyforge.org)
28 * Net::SFTP v2 (http://net-ssh.rubyforge.org)
29 * Net::SCP v1 (http://net-ssh.rubyforge.org)
30 * Net::SSH::Gateway v1 (http://net-ssh.rubyforge.org)
31 * HighLine (http://highline.rubyforge.org)
32
33 If you want to run the tests, you'll also need to have the following dependencies installed:
34
35 * Echoe (for the Rakefile)
36 * Mocha (http://mocha.rubyforge.org)
37
38 == ASSUMPTIONS
39
40 Capistrano is "opinionated software", which means it has very firm ideas about how things ought to be done, and tries to force those ideas on you. Some of the assumptions behind these opinions are:
41
42 * You are using SSH to access the remote servers.
43 * You either have the same password to all target machines, or you have public keys in place to allow passwordless access to them.
44
45 Do not expect these assumptions to change.
46
47 == USAGE
48
49 In general, you'll use Capistrano as follows:
50
51 * Create a recipe file ("capfile" or "Capfile").
52 * Use the +cap+ script to execute your recipe.
53
54 Use the +cap+ script as follows:
55
56 cap sometask
57
58 By default, the script will look for a file called one of +capfile+ or +Capfile+. The +someaction+ text indicates which task to execute. You can do "cap -h" to see all the available options and "cap -T" to see all the available tasks.
59
60 == Capistrano Edge
61
62 If you want to try Capistrano code that hasn't been formerly released yet, this repository now includes a gemspec that should build what you need, here's how to get a copy:
63
64 git clone git://github.com/capistrano/capistrano.git capistrano-capistrano
65 cd capistrano-capsitrano
66 rake package
67 sudo gem install pkg/capistrano-*.gem
68
69 This will install the most recent version of capistrano and make it available for both cap, and capify.
70
71 We recommend that you capify a new test application, as the resulting files are different to previous versions.
72
73 If you have multiple versions of capistrano (or indeed any gem with a binary) installed, you can call `cap` like so to specify which version to use:
74
75 cap _2.5.5_ deploy
76 cap _2.5.6_ deploy:setup
77
78 == LICENSE:
79
80 (The MIT License)
81
82 Copyright (c) 2005-2008 Jamis Buck <jamis@37signals.com>
83
84 Permission is hereby granted, free of charge, to any person obtaining
85 a copy of this software and associated documentation files (the
86 'Software'), to deal in the Software without restriction, including
87 without limitation the rights to use, copy, modify, merge, publish,
88 distribute, sublicense, and/or sell copies of the Software, and to
89 permit persons to whom the Software is furnished to do so, subject to
90 the following conditions:
91
92 The above copyright notice and this permission notice shall be
93 included in all copies or substantial portions of the Software.
94
95 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
96 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
97 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
98 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
99 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
100 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
101 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0 require "./lib/capistrano/version"
0 require 'bundler'
1 Bundler::GemHelper.install_tasks
12
2 begin
3 require 'echoe'
4 rescue LoadError
5 abort "You'll need to have `echoe' installed to use Capistrano's Rakefile"
3 require 'rake/testtask'
4 Rake::TestTask.new(:test) do |test|
5 test.libs << 'lib' << 'test'
6 test.pattern = 'test/**/*_test.rb'
7 test.verbose = true
68 end
79
8 version = Capistrano::Version::STRING.dup
9 if ENV['SNAPSHOT'].to_i == 1
10 version << "." << Time.now.utc.strftime("%Y%m%d%H%M%S")
11 end
12
13 Echoe.new('capistrano', version) do |p|
14 p.include_gemspec = true
15 p.changelog = "CHANGELOG.rdoc"
16
17 p.author = ["Jamis Buck", "Lee Hambley"]
18 p.email = ["jamis@jamisbuck.org", "lee.hambley@gmail.com"]
19
20 p.summary = <<-DESC.strip.gsub(/\n\s+/, " ")
21 Capistrano is a utility and framework for executing commands in parallel
22 on multiple remote machines, via SSH.
23 DESC
24
25 p.url = "http://www.capify.org"
26 p.need_zip = true
27 p.rdoc_pattern = /^(lib|README.rdoc|CHANGELOG.rdoc)/
28
29 p.dependencies = ["net-ssh >=2.0.14",
30 "net-sftp >=2.0.0",
31 "net-scp >=1.0.0",
32 "net-ssh-gateway >=1.0.0",
33 "highline"]
34 end
10 task :default => :test
3737 files = {
3838 "Capfile" => unindent(<<-FILE),
3939 load 'deploy' if respond_to?(:namespace) # cap2 differentiator
40 Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
40
41 # Uncomment if you are using Rails' asset pipeline
42 # load 'deploy/assets'
43
44 Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
4145
4246 load 'config/deploy' # remove this line to skip loading any of the default tasks
4347 FILE
5357 role :db, "your primary db-server here", :primary => true # This is where Rails migrations will run
5458 role :db, "your slave db-server here"
5559
56 # If you are using Passenger mod_rails uncomment this:
57 # if you\'re still using the script/reapear helper you will need
60 # if you\'re still using the script/reaper helper you will need
5861 # these http://github.com/rails/irs_process_scripts
5962
63 # If you are using Passenger mod_rails uncomment this:
6064 # namespace :deploy do
61 # task :start {}
62 # task :stop {}
65 # task :start do ; end
66 # task :stop do ; end
6367 # task :restart, :roles => :app, :except => { :no_release => true } do
6468 # run "#{try_sudo} touch #{File.join(current_path,\'tmp\',\'restart.txt\')}"
6569 # end
00 # -*- encoding: utf-8 -*-
1 $:.push File.expand_path("../lib", __FILE__)
2 require "capistrano/version"
13
24 Gem::Specification.new do |s|
3 s.name = %q{capistrano}
4 s.version = "2.5.9"
5
6 s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
7 s.authors = ["Jamis Buck, Lee Hambley"]
8 s.date = %q{2009-09-12}
5 s.name = "capistrano"
6 s.version = Capistrano::Version.to_s
7 s.platform = Gem::Platform::RUBY
8 s.authors = ["Jamis Buck", "Lee Hambley"]
9 s.email = ["jamis@jamisbuck.org", "lee.hambley@gmail.com"]
10 s.homepage = "http://github.com/capistrano/capistrano"
11 s.summary = %q{Capistrano - Welcome to easy deployment with Ruby over SSH}
912 s.description = %q{Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.}
10 s.email = ["jamis@jamisbuck.org", "lee.hambley@gmail.com"]
11 s.executables = ["cap", "capify"]
12 s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/capistrano/callback.rb", "lib/capistrano/cli/execute.rb", "lib/capistrano/cli/help.rb", "lib/capistrano/cli/help.txt", "lib/capistrano/cli/options.rb", "lib/capistrano/cli/ui.rb", "lib/capistrano/cli.rb", "lib/capistrano/command.rb", "lib/capistrano/configuration/actions/file_transfer.rb", "lib/capistrano/configuration/actions/inspect.rb", "lib/capistrano/configuration/actions/invocation.rb", "lib/capistrano/configuration/callbacks.rb", "lib/capistrano/configuration/connections.rb", "lib/capistrano/configuration/execution.rb", "lib/capistrano/configuration/loading.rb", "lib/capistrano/configuration/namespaces.rb", "lib/capistrano/configuration/roles.rb", "lib/capistrano/configuration/servers.rb", "lib/capistrano/configuration/variables.rb", "lib/capistrano/configuration.rb", "lib/capistrano/errors.rb", "lib/capistrano/extensions.rb", "lib/capistrano/logger.rb", "lib/capistrano/processable.rb", "lib/capistrano/recipes/compat.rb", "lib/capistrano/recipes/deploy/dependencies.rb", "lib/capistrano/recipes/deploy/local_dependency.rb", "lib/capistrano/recipes/deploy/remote_dependency.rb", "lib/capistrano/recipes/deploy/scm/accurev.rb", "lib/capistrano/recipes/deploy/scm/base.rb", "lib/capistrano/recipes/deploy/scm/bzr.rb", "lib/capistrano/recipes/deploy/scm/cvs.rb", "lib/capistrano/recipes/deploy/scm/darcs.rb", "lib/capistrano/recipes/deploy/scm/git.rb", "lib/capistrano/recipes/deploy/scm/mercurial.rb", "lib/capistrano/recipes/deploy/scm/none.rb", "lib/capistrano/recipes/deploy/scm/perforce.rb", "lib/capistrano/recipes/deploy/scm/subversion.rb", "lib/capistrano/recipes/deploy/scm.rb", "lib/capistrano/recipes/deploy/strategy/base.rb", "lib/capistrano/recipes/deploy/strategy/checkout.rb", "lib/capistrano/recipes/deploy/strategy/copy.rb", "lib/capistrano/recipes/deploy/strategy/export.rb", "lib/capistrano/recipes/deploy/strategy/remote.rb", "lib/capistrano/recipes/deploy/strategy/remote_cache.rb", "lib/capistrano/recipes/deploy/strategy.rb", "lib/capistrano/recipes/deploy/templates/maintenance.rhtml", "lib/capistrano/recipes/deploy.rb", "lib/capistrano/recipes/standard.rb", "lib/capistrano/recipes/templates/maintenance.rhtml", "lib/capistrano/recipes/upgrade.rb", "lib/capistrano/role.rb", "lib/capistrano/server_definition.rb", "lib/capistrano/shell.rb", "lib/capistrano/ssh.rb", "lib/capistrano/task_definition.rb", "lib/capistrano/transfer.rb", "lib/capistrano/version.rb", "lib/capistrano.rb", "README.rdoc"]
13 s.files = ["bin/cap", "bin/capify", "CHANGELOG.rdoc", "examples/sample.rb", "lib/capistrano/callback.rb", "lib/capistrano/cli/execute.rb", "lib/capistrano/cli/help.rb", "lib/capistrano/cli/help.txt", "lib/capistrano/cli/options.rb", "lib/capistrano/cli/ui.rb", "lib/capistrano/cli.rb", "lib/capistrano/command.rb", "lib/capistrano/configuration/actions/file_transfer.rb", "lib/capistrano/configuration/actions/inspect.rb", "lib/capistrano/configuration/actions/invocation.rb", "lib/capistrano/configuration/callbacks.rb", "lib/capistrano/configuration/connections.rb", "lib/capistrano/configuration/execution.rb", "lib/capistrano/configuration/loading.rb", "lib/capistrano/configuration/namespaces.rb", "lib/capistrano/configuration/roles.rb", "lib/capistrano/configuration/servers.rb", "lib/capistrano/configuration/variables.rb", "lib/capistrano/configuration.rb", "lib/capistrano/errors.rb", "lib/capistrano/extensions.rb", "lib/capistrano/logger.rb", "lib/capistrano/processable.rb", "lib/capistrano/recipes/compat.rb", "lib/capistrano/recipes/deploy/dependencies.rb", "lib/capistrano/recipes/deploy/local_dependency.rb", "lib/capistrano/recipes/deploy/remote_dependency.rb", "lib/capistrano/recipes/deploy/scm/accurev.rb", "lib/capistrano/recipes/deploy/scm/base.rb", "lib/capistrano/recipes/deploy/scm/bzr.rb", "lib/capistrano/recipes/deploy/scm/cvs.rb", "lib/capistrano/recipes/deploy/scm/darcs.rb", "lib/capistrano/recipes/deploy/scm/git.rb", "lib/capistrano/recipes/deploy/scm/mercurial.rb", "lib/capistrano/recipes/deploy/scm/none.rb", "lib/capistrano/recipes/deploy/scm/perforce.rb", "lib/capistrano/recipes/deploy/scm/subversion.rb", "lib/capistrano/recipes/deploy/scm.rb", "lib/capistrano/recipes/deploy/strategy/base.rb", "lib/capistrano/recipes/deploy/strategy/checkout.rb", "lib/capistrano/recipes/deploy/strategy/copy.rb", "lib/capistrano/recipes/deploy/strategy/export.rb", "lib/capistrano/recipes/deploy/strategy/remote.rb", "lib/capistrano/recipes/deploy/strategy/remote_cache.rb", "lib/capistrano/recipes/deploy/strategy.rb", "lib/capistrano/recipes/deploy/templates/maintenance.rhtml", "lib/capistrano/recipes/deploy.rb", "lib/capistrano/recipes/standard.rb", "lib/capistrano/recipes/templates/maintenance.rhtml", "lib/capistrano/recipes/upgrade.rb", "lib/capistrano/role.rb", "lib/capistrano/server_definition.rb", "lib/capistrano/shell.rb", "lib/capistrano/ssh.rb", "lib/capistrano/task_definition.rb", "lib/capistrano/transfer.rb", "lib/capistrano/version.rb", "lib/capistrano.rb", "Rakefile", "README.rdoc", "setup.rb", "test/cli/execute_test.rb", "test/cli/help_test.rb", "test/cli/options_test.rb", "test/cli/ui_test.rb", "test/cli_test.rb", "test/command_test.rb", "test/configuration/actions/file_transfer_test.rb", "test/configuration/actions/inspect_test.rb", "test/configuration/actions/invocation_test.rb", "test/configuration/callbacks_test.rb", "test/configuration/connections_test.rb", "test/configuration/execution_test.rb", "test/configuration/loading_test.rb", "test/configuration/namespace_dsl_test.rb", "test/configuration/roles_test.rb", "test/configuration/servers_test.rb", "test/configuration/variables_test.rb", "test/configuration_test.rb", "test/deploy/local_dependency_test.rb", "test/deploy/remote_dependency_test.rb", "test/deploy/scm/accurev_test.rb", "test/deploy/scm/base_test.rb", "test/deploy/scm/git_test.rb", "test/deploy/scm/mercurial_test.rb", "test/deploy/strategy/copy_test.rb", "test/extensions_test.rb", "test/fixtures/cli_integration.rb", "test/fixtures/config.rb", "test/fixtures/custom.rb", "test/logger_test.rb", "test/role_test.rb", "test/server_definition_test.rb", "test/shell_test.rb", "test/ssh_test.rb", "test/task_definition_test.rb", "test/transfer_test.rb", "test/utils.rb", "Manifest", "capistrano.gemspec", "test/deploy/scm/none_test.rb", "test/deploy/scm/subversion_test.rb"]
14 s.has_rdoc = true
15 s.homepage = %q{http://www.capify.org}
16 s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Capistrano", "--main", "README.rdoc"]
13 s.files = `git ls-files`.split("\n")
14 s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15 s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
1716 s.require_paths = ["lib"]
18 s.rubyforge_project = %q{capistrano}
19 s.rubygems_version = %q{1.3.1}
20 s.summary = %q{Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.}
21 s.test_files = ["test/cli/execute_test.rb", "test/cli/help_test.rb", "test/cli/options_test.rb", "test/cli/ui_test.rb", "test/cli_test.rb", "test/command_test.rb", "test/configuration/actions/file_transfer_test.rb", "test/configuration/actions/inspect_test.rb", "test/configuration/actions/invocation_test.rb", "test/configuration/callbacks_test.rb", "test/configuration/connections_test.rb", "test/configuration/execution_test.rb", "test/configuration/loading_test.rb", "test/configuration/namespace_dsl_test.rb", "test/configuration/roles_test.rb", "test/configuration/servers_test.rb", "test/configuration/variables_test.rb", "test/configuration_test.rb", "test/deploy/local_dependency_test.rb", "test/deploy/remote_dependency_test.rb", "test/deploy/scm/accurev_test.rb", "test/deploy/scm/base_test.rb", "test/deploy/scm/git_test.rb", "test/deploy/scm/mercurial_test.rb", "test/deploy/scm/none_test.rb", "test/deploy/scm/subversion_test.rb", "test/deploy/strategy/copy_test.rb", "test/extensions_test.rb", "test/logger_test.rb", "test/role_test.rb", "test/server_definition_test.rb", "test/shell_test.rb", "test/ssh_test.rb", "test/task_definition_test.rb", "test/transfer_test.rb"]
17 s.extra_rdoc_files = [
18 "README.mdown"
19 ]
2220
2321 if s.respond_to? :specification_version then
24 current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25 s.specification_version = 2
26
27 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
22 s.specification_version = 3
23 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
24 s.add_runtime_dependency(%q<highline>, [">= 0"])
2825 s.add_runtime_dependency(%q<net-ssh>, [">= 2.0.14"])
2926 s.add_runtime_dependency(%q<net-sftp>, [">= 2.0.0"])
3027 s.add_runtime_dependency(%q<net-scp>, [">= 1.0.0"])
31 s.add_runtime_dependency(%q<net-ssh-gateway>, [">= 1.0.0"])
32 s.add_runtime_dependency(%q<highline>, [">= 0"])
28 s.add_runtime_dependency(%q<net-ssh-gateway>, [">= 1.1.0"])
29 s.add_development_dependency(%q<mocha>, [">= 0"])
3330 else
3431 s.add_dependency(%q<net-ssh>, [">= 2.0.14"])
3532 s.add_dependency(%q<net-sftp>, [">= 2.0.0"])
3633 s.add_dependency(%q<net-scp>, [">= 1.0.0"])
37 s.add_dependency(%q<net-ssh-gateway>, [">= 1.0.0"])
34 s.add_dependency(%q<net-ssh-gateway>, [">= 1.1.0"])
3835 s.add_dependency(%q<highline>, [">= 0"])
36 s.add_dependency(%q<mocha>, [">= 0"])
3937 end
4038 else
4139 s.add_dependency(%q<net-ssh>, [">= 2.0.14"])
4240 s.add_dependency(%q<net-sftp>, [">= 2.0.0"])
4341 s.add_dependency(%q<net-scp>, [">= 1.0.0"])
44 s.add_dependency(%q<net-ssh-gateway>, [">= 1.0.0"])
42 s.add_dependency(%q<net-ssh-gateway>, [">= 1.1.0"])
4543 s.add_dependency(%q<highline>, [">= 0"])
44 s.add_dependency(%q<mocha>, [">= 0"])
4645 end
4746 end
+0
-14
examples/sample.rb less more
0 # set :user, "flippy"
1 # set :password, "hello-flippy"
2 # set :gateway, "gateway.example.com"
3
4 role :web, "web1.example.com"
5 role :app, "app1.example.com", "app2.example.com"
6
7 desc <<-DESC
8 This is a sample task. It is only intended to be used as a demonstration of \
9 how you can define your own tasks.
10 DESC
11 task :sample_task, :roles => :app do
12 run "ls -l"
13 end
2323 config = instantiate_configuration(options)
2424 config.debug = options[:debug]
2525 config.dry_run = options[:dry_run]
26 config.preserve_roles = options[:preserve_roles]
2627 config.logger.level = options[:verbose]
2728
2829 set_pre_vars(config)
3232
3333 <%= color '-n, --dry-run', :bold %>
3434 Causes Capistrano to simply display each remote command, without executing it. In this sense it is similar to --debug, but without the prompt. Note that commands executed locally are still run--only remote commands are skipped.
35
35
3636 <%= color '-p, --password', :bold %>
3737 Normally, cap will prompt for the password on-demand, the first time it is needed. This can make it hard to walk away from Capistrano, since you might not know if it will prompt for a password down the road. In such cases, you can use the -p option to force cap to prompt for the password immediately.
3838
7373 <%= color 'HOSTFILTER', :bold %>
7474 Execute tasks against this comma-separated list of host, but only if the host has the proper role for the task.
7575
76 <%= color 'HOSTROLEFILTER', :bold %>
77 Execute tasks against the hosts in this comma-separated list of roles, but only if the host has the proper role for the task.
78
7679 <%= color 'ROLES', :bold %>
7780 Execute tasks against this comma-separated list of roles. Hosts which do not have the right roles will be skipped.
5353 exit
5454 end
5555
56 opts.on("-l", "--logger [STDERR|STDOUT|file]",
56 opts.on("-l", "--logger [STDERR|STDOUT|file]",
5757 "Choose logger method. STDERR used by default."
58 ) do |value|
58 ) do |value|
5959 options[:output] = if value.nil? || value.upcase == 'STDERR'
6060 # Using default logger.
6161 nil
7878 "Make the output as quiet as possible."
7979 ) { options[:verbose] = 0 }
8080
81 opts.on("-r", "--preserve-roles",
82 "Preserve task roles"
83 ) { options[:preserve_roles] = true }
84
8185 opts.on("-S", "--set-before NAME=VALUE",
8286 "Set a variable before the recipes are loaded."
8387 ) do |pair|
111115 "Display the Capistrano version, and exit."
112116 ) do
113117 require 'capistrano/version'
114 puts "Capistrano v#{Capistrano::Version::STRING}"
118 puts "Capistrano v#{Capistrano::Version}"
115119 exit
116120 end
117121
196200 def default_sysconf #:nodoc:
197201 File.join(sysconf_directory, "capistrano.conf")
198202 end
199
203
200204 def default_dotfile #:nodoc:
201205 File.join(home_directory, ".caprc")
202206 end
206210 # appropriate location for this file in Windows.
207211 ENV["SystemRoot"] || '/etc'
208212 end
209
213
210214 def home_directory #:nodoc:
211215 ENV["HOME"] ||
212216 (ENV["HOMEPATH"] && "#{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}") ||
2121 def password_prompt(prompt="Password: ")
2222 ui.ask(prompt) { |q| q.echo = false }
2323 end
24
24
2525 # Debug mode prompt
2626 def debug_prompt(cmd)
2727 ui.say("Preparing to execute command: #{cmd}")
3636 end
3737 end
3838 end
39 end
39 end
1616 # different set of parameters (such as when embedded cap in a program):
1717 #
1818 # require 'capistrano/cli'
19 # Capistrano::CLI.parse(%w(-vvvv -r config/deploy update_code)).execute!
19 # Capistrano::CLI.parse(%W(-vvvv -f config/deploy update_code)).execute!
2020 #
2121 # Note that you can also embed cap directly by creating a new Configuration
22 # instance and setting it up, but you'll often wind up duplicating logic
23 # defined in the CLI class. The above snippet, redone using the Configuration
24 # class directly, would look like:
22 # instance and setting it up, The above snippet, redone using the
23 # Configuration class directly, would look like:
2524 #
2625 # require 'capistrano'
2726 # require 'capistrano/cli'
2827 # config = Capistrano::Configuration.new
29 # config.logger_level = Capistrano::Logger::TRACE
28 # config.logger.level = Capistrano::Logger::TRACE
3029 # config.set(:password) { Capistrano::CLI.password_prompt }
3130 # config.load "config/deploy"
3231 # config.update_code
4241 # Mix-in the actual behavior
4342 include Execute, Options, UI
4443 include Help # needs to be included last, because it overrides some methods
44
4545 end
4646 end
0 require 'benchmark'
01 require 'capistrano/errors'
12 require 'capistrano/processable'
23
158159 # fails (non-zero return code) on any of the hosts, this will raise a
159160 # Capistrano::CommandError.
160161 def process!
161 loop do
162 break unless process_iteration { @channels.any? { |ch| !ch[:closed] } }
163 end
164
165 logger.trace "command finished" if logger
162 elapsed = Benchmark.realtime do
163 loop do
164 break unless process_iteration { @channels.any? { |ch| !ch[:closed] } }
165 end
166 end
167
168 logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger
166169
167170 if (failed = @channels.select { |ch| ch[:status] != 0 }).any?
168171 commands = failed.inject({}) { |map, ch| (map[ch[:command]] ||= []) << ch[:server]; map }
2525 transfer(:up, from, to, options, &block)
2626 if mode
2727 mode = mode.is_a?(Numeric) ? mode.to_s(8) : mode.to_s
28 run "chmod #{mode} #{to}"
28 run "chmod #{mode} #{to}", options
2929 end
3030 end
3131
3636 def transfer(direction, from, to, options={}, &block)
3737 execute_on_servers(options) do |servers|
3838 targets = servers.map { |s| sessions[s] }
39 Transfer.process(direction, from, to, targets, options.merge(:logger => logger), &block)
39 if dry_run
40 logger.debug "transfering: #{[direction, from, to, targets, options.merge(:logger => logger).inspect ] * ', '}"
41 else
42 Transfer.process(direction, from, to, targets, options.merge(:logger => logger), &block)
43 end
4044 end
4145 end
4246
110110 # * :except - specifies a condition limiting which hosts will be selected to
111111 # run the command. This is the inverse of :only (hosts that do _not_ match
112112 # the condition will be selected).
113 # * :on_no_matching_servers - if :continue, will continue to execute tasks if
114 # no matching servers are found for the host criteria. The default is to raise
115 # a NoMatchingServersError exception.
113116 # * :once - if true, only the first matching server will be selected. The default
114117 # is false (all matching servers will be selected).
115118 # * :max_hosts - specifies the maximum number of hosts that should be selected
116119 # at a time. If this value is less than the number of hosts that are selected
117120 # to run, then the hosts will be run in groups of max_hosts. The default is nil,
118 # which indicates that there is no maximum host limit.
121 # which indicates that there is no maximum host limit. Please note this does not
122 # limit the number of SSH channels that can be open, only the number of hosts upon
123 # which this will be called.
119124 # * :shell - says which shell should be used to invoke commands. This
120125 # defaults to "sh". Setting this to false causes Capistrano to invoke
121126 # the commands directly, without wrapping them in a shell invocation.
2323 class GatewayConnectionFactory #:nodoc:
2424 def initialize(gateway, options)
2525 @options = options
26 @options[:logger].debug "Creating gateway using #{[*gateway].join(', ')}" if @options[:logger]
2726 Thread.abort_on_exception = true
28 @gateways = [*gateway].collect { |g| ServerDefinition.new(g) }
29 tunnel = SSH.connection_strategy(@gateways[0], @options) do |host, user, connect_options|
27 @gateways = {}
28 if gateway.is_a?(Hash)
29 @options[:logger].debug "Creating multiple gateways using #{gateway.inspect}" if @options[:logger]
30 gateway.each do |gw, hosts|
31 gateway_connection = add_gateway(gw)
32 [*hosts].each do |host|
33 @gateways[:default] ||= gateway_connection
34 @gateways[host] = gateway_connection
35 end
36 end
37 else
38 @options[:logger].debug "Creating gateway using #{[*gateway].join(', ')}" if @options[:logger]
39 @gateways[:default] = add_gateway(gateway)
40 end
41 end
42
43 def add_gateway(gateway)
44 gateways = [*gateway].collect { |g| ServerDefinition.new(g) }
45 tunnel = SSH.connection_strategy(gateways[0], @options) do |host, user, connect_options|
3046 Net::SSH::Gateway.new(host, user, connect_options)
3147 end
32 @gateway = (@gateways[1..-1]).inject(tunnel) do |tunnel, destination|
48 (gateways[1..-1]).inject(tunnel) do |tunnel, destination|
3349 @options[:logger].debug "Creating tunnel to #{destination}" if @options[:logger]
3450 local_host = ServerDefinition.new("127.0.0.1", :user => destination.user, :port => tunnel.open(destination.host, (destination.port || 22)))
3551 SSH.connection_strategy(local_host, @options) do |host, user, connect_options|
4056
4157 def connect_to(server)
4258 @options[:logger].debug "establishing connection to `#{server}' via gateway" if @options[:logger]
43 local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => @gateway.open(server.host, server.port || 22))
59 local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => gateway_for(server).open(server.host, server.port || 22))
4460 session = SSH.connect(local_host, @options)
4561 session.xserver = server
4662 session
63 end
64
65 def gateway_for(server)
66 @gateways[server.host] || @gateways[:default]
4767 end
4868 end
4969
86106 def connection_factory
87107 @connection_factory ||= begin
88108 if exists?(:gateway)
89 logger.debug "establishing connection to gateway `#{fetch(:gateway)}'"
109 logger.debug "establishing connection to gateway `#{fetch(:gateway).inspect}'"
90110 GatewayConnectionFactory.new(fetch(:gateway), self)
91111 else
92112 DefaultConnectionFactory.new(self)
117137 # Destroys sessions for each server in the list.
118138 def teardown_connections_to(servers)
119139 servers.each do |server|
120 sessions[server].close
121 sessions.delete(server)
140 begin
141 sessions.delete(server).close
142 rescue IOError
143 # the TCP connection is already dead
144 end
122145 end
123146 end
124147
132155 servers = find_servers_for_task(task, options)
133156
134157 if servers.empty?
135 if ENV['HOSTFILTER']
158 if ENV['HOSTFILTER'] || task.options.merge(options)[:on_no_matching_servers] == :continue
136159 logger.info "skipping `#{task.fully_qualified_name}' because no servers matched"
137160 return
138161 else
146169 end
147170 else
148171 servers = find_servers(options)
149 raise Capistrano::NoMatchingServersError, "no servers found to match #{options.inspect}" if servers.empty?
172 if servers.empty?
173 raise Capistrano::NoMatchingServersError, "no servers found to match #{options.inspect}" if options[:on_no_matching_servers] != :continue
174 return
175 end
150176 end
151177
152178 servers = [servers.first] if options[:once]
2525 # Yet additionally, if the HOSTFILTER environment variable is set, it
2626 # will limit the result to hosts found in that (comma-separated) list.
2727 #
28 # If the HOSTROLEFILTER environment variable is set, it will limit the
29 # result to hosts found in that (comma-separated) list of roles
30 #
2831 # Usage:
2932 #
3033 # # return all known servers
3841 # # returns the given hosts, translated to ServerDefinition objects
3942 # servers = find_servers :hosts => "jamis@example.host.com"
4043 def find_servers(options={})
44 return [] if options.key?(:hosts) && (options[:hosts].nil? || [] == options[:hosts])
45 return [] if options.key?(:roles) && (options[:roles].nil? || [] == options[:roles])
46
4147 hosts = server_list_from(ENV['HOSTS'] || options[:hosts])
4248
4349 if hosts.any?
4753 filter_server_list(hosts.uniq)
4854 end
4955 else
50 roles = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
56 roles = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
57 roles = roles & Array(options[:roles]) if preserve_roles && !options[:roles].nil?
58
5159 only = options[:only] || {}
5260 except = options[:except] || {}
5361
54 servers = roles.inject([]) { |list, role| list.concat(self.roles[role]) }
62 # If we don't have a def for a role it means its bogus, skip it so higher level can handle
63 servers = roles.inject([]) { |list, role| list.concat(self.roles[role] || []) }
5564 servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } }
5665 servers = servers.reject { |server| except.any? { |key,value| server.options[key] == value } }
5766
6675 protected
6776
6877 def filter_server_list(servers)
69 return servers unless ENV['HOSTFILTER']
70 filters = ENV['HOSTFILTER'].split(/,/)
71 servers.select { |server| filters.include?(server.host) }
78 return servers unless ENV['HOSTFILTER'] or ENV['HOSTROLEFILTER']
79 if ENV['HOSTFILTER']
80 filters = ENV['HOSTFILTER'].split(/,/)
81 servers.select { |server| filters.include?(server.host) }
82 elsif ENV['HOSTROLEFILTER']
83 filters = ENV['HOSTROLEFILTER'].split(/,/).map do |role|
84 local_roles = roles[role.to_sym]
85 if local_roles.is_a? Array
86 roles[role.to_sym]
87 else
88 roles[role.to_sym].servers
89 end
90 end.flatten
91 servers.select { |server| filters.include?(server) }
92 end
7293 end
7394
7495 def server_list_from(hosts)
82103 roles = build_list(roles)
83104 roles.map do |role|
84105 role = String === role ? role.strip.to_sym : role
85 raise ArgumentError, "unknown role `#{role}'" unless self.roles.key?(role)
86106 role
87107 end
88108 end
92112 end
93113 end
94114 end
95 end
115 end
111111 private :protect
112112
113113 def respond_to_with_variables?(sym, include_priv=false) #:nodoc:
114 @variables.has_key?(sym) || respond_to_without_variables?(sym, include_priv)
114 @variables.has_key?(sym.to_sym) || respond_to_without_variables?(sym, include_priv)
115115 end
116116
117117 def method_missing_with_variables(sym, *args, &block) #:nodoc:
123123 end
124124 end
125125 end
126 end
126 end
1818 # define roles, and set configuration variables.
1919 class Configuration
2020 # The logger instance defined for this configuration.
21 attr_accessor :debug, :logger, :dry_run
21 attr_accessor :debug, :logger, :dry_run, :preserve_roles
2222
2323 def initialize(options={}) #:nodoc:
2424 @debug = false
2525 @dry_run = false
26 @preserve_roles = false
2627 @logger = Logger.new(options)
2728 end
2829
00 module Capistrano
1 class Error < RuntimeError; end
1
2 Error = Class.new(RuntimeError)
23
3 class CaptureError < Error; end
4 class NoSuchTaskError < Error; end
5 class NoMatchingServersError < Error; end
4 CaptureError = Class.new(Capistrano::Error)
5 NoSuchTaskError = Class.new(Capistrano::Error)
6 NoMatchingServersError = Class.new(Capistrano::Error)
67
78 class RemoteError < Error
89 attr_accessor :hosts
910 end
1011
11 class ConnectionError < RemoteError; end
12 class TransferError < RemoteError; end
13 class CommandError < RemoteError; end
12 ConnectionError = Class.new(Capistrano::RemoteError)
13 TransferError = Class.new(Capistrano::RemoteError)
14 CommandError = Class.new(Capistrano::RemoteError)
15
16 LocalArgumentError = Class.new(Capistrano::Error)
17
1418 end
0 class String
1 def compact
2 self.gsub(/\s+/, ' ')
3 end
4 end
0 load 'deploy' unless defined?(_cset)
1
2 _cset :asset_env, "RAILS_GROUPS=assets"
3 _cset :assets_prefix, "assets"
4
5 before 'deploy:finalize_update', 'deploy:assets:symlink'
6 after 'deploy:update_code', 'deploy:assets:precompile'
7
8 namespace :deploy do
9 namespace :assets do
10 desc <<-DESC
11 [internal] This task will set up a symlink to the shared directory \
12 for the assets directory. Assets are shared across deploys to avoid \
13 mid-deploy mismatches between old application html asking for assets \
14 and getting a 404 file not found error. The assets cache is shared \
15 for efficiency. If you cutomize the assets path prefix, override the \
16 :assets_prefix variable to match.
17 DESC
18 task :symlink, :roles => :web, :except => { :no_release => true } do
19 run <<-CMD
20 rm -rf #{latest_release}/public/#{assets_prefix} &&
21 mkdir -p #{latest_release}/public &&
22 mkdir -p #{shared_path}/assets &&
23 ln -s #{shared_path}/assets #{latest_release}/public/#{assets_prefix}
24 CMD
25 end
26
27 desc <<-DESC
28 Run the asset precompilation rake task. You can specify the full path \
29 to the rake executable by setting the rake variable. You can also \
30 specify additional environment variables to pass to rake via the \
31 asset_env variable. The defaults are:
32
33 set :rake, "rake"
34 set :rails_env, "production"
35 set :asset_env, "RAILS_GROUPS=assets"
36 DESC
37 task :precompile, :roles => :web, :except => { :no_release => true } do
38 run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile"
39 end
40
41 desc <<-DESC
42 Run the asset clean rake task. Use with caution, this will delete \
43 all of your compiled assets. You can specify the full path \
44 to the rake executable by setting the rake variable. You can also \
45 specify additional environment variables to pass to rake via the \
46 asset_env variable. The defaults are:
47
48 set :rake, "rake"
49 set :rails_env, "production"
50 set :asset_env, "RAILS_GROUPS=assets"
51 DESC
52 task :clean, :roles => :web, :except => { :no_release => true } do
53 run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:clean"
54 end
55 end
56 end
3939 @message ||= "gem `#{name}' #{version} could not be found"
4040 gem_cmd = configuration.fetch(:gem_command, "gem")
4141 try("#{gem_cmd} specification --version '#{version}' #{name} 2>&1 | awk 'BEGIN { s = 0 } /^name:/ { s = 1; exit }; END { if(s == 0) exit 1 }'", options)
42 self
43 end
44
45 def deb(name, version, options={})
46 @message ||= "package `#{name}' #{version} could not be found"
47 try("dpkg -s #{name} | grep '^Version: #{version}'", options)
4248 self
4349 end
4450
160160
161161 # A helper for accessing variable values, which takes into
162162 # consideration the current mode ("normal" vs. "local").
163 def variable(name)
163 def variable(name, default = nil)
164164 if local? && configuration.exists?("local_#{name}".to_sym)
165 return configuration["local_#{name}".to_sym]
165 return configuration["local_#{name}".to_sym].nil? ? default : configuration["local_#{name}".to_sym]
166166 else
167 configuration[name]
167 configuration[name].nil? ? default : configuration[name]
168168 end
169169 end
170170
185185 variable(:repository)
186186 end
187187
188 def arguments
189 variable(:scm_arguments)
188 def arguments(command = :all)
189 value = variable(:scm_arguments)
190 if value.is_a?(Hash)
191 value = value[command]
192 end
193 value
190194 end
191195 end
192196
5858 # If the 'revision' argument, on the other hand, is not :head, it is
5959 # simply returned.
6060 def query_revision(revision)
61 revision
61 return revision unless :head == revision
62
63 command = scm('revno', repository)
64 result = yield(command)
6265 end
6366
6467 # Increments the given revision number and returns it.
6868 def query_revision(revision)
6969 return revision if revision_type(revision) == :date
7070 revision = yield(scm(cvs_root, :log, "-r#{revision}")).
71 grep(/^date:/).
71 split("\n").
72 select { |line| line =~ /^date:/ }.
7273 map { |line| line[/^date: (.*?);/, 1] }.
7374 sort.last + " UTC"
7475 return revision
1717 :head
1818 end
1919
20 def to_match(revision)
21 if revision.nil? || revision == self.head
22 nil
23 else
24 "--to-match='hash #{revision}'"
25 end
26 end
27
2028 # Returns the command that will check out the given revision to the
2129 # given destination. The 'revision' parameter must be the 'hash' value
2230 # for the revision in question, as given by 'darcs changes --xml-output'.
2331 def checkout(revision, destination)
24 scm :get, verbose, "--repo-name=#{destination}", "--to-match='hash #{revision}'", repository
32 scm :get, *[verbose,
33 "--repo-name=#{destination}",
34 to_match(revision),
35 repository].compact
2536 end
2637
2738 # Tries to update the destination repository in-place, to bring it up
119119 # default, Git's reference of HEAD (the latest changeset in the default
120120 # branch, usually called "master").
121121 def head
122 configuration[:branch] || 'HEAD'
122 variable(:branch) || 'HEAD'
123123 end
124124
125125 def origin
126 configuration[:remote] || 'origin'
126 variable(:remote) || 'origin'
127127 end
128128
129129 # Performs a clone on the remote machine, then checkout on the branch
134134
135135 args = []
136136 args << "-o #{remote}" unless remote == 'origin'
137 if depth = configuration[:git_shallow_clone]
137 if depth = variable(:git_shallow_clone)
138138 args << "--depth #{depth}"
139139 end
140140
141141 execute = []
142 if args.empty?
143 execute << "#{git} clone #{verbose} #{configuration[:repository]} #{destination}"
144 else
145 execute << "#{git} clone #{verbose} #{args.join(' ')} #{configuration[:repository]} #{destination}"
146 end
142 execute << "#{git} clone #{verbose} #{args.join(' ')} #{variable(:repository)} #{destination}"
147143
148144 # checkout into a local branch rather than a detached HEAD
149145 execute << "cd #{destination} && #{git} checkout #{verbose} -b deploy #{revision}"
150
151 if configuration[:git_enable_submodules]
146
147 if variable(:git_enable_submodules)
152148 execute << "#{git} submodule #{verbose} init"
153149 execute << "#{git} submodule #{verbose} sync"
154 execute << "#{git} submodule #{verbose} update"
155 end
156
157 execute.join(" && ")
158 end
159
150 if false == variable(:git_submodules_recursive)
151 execute << "#{git} submodule #{verbose} update --init"
152 else
153 execute << %Q(export GIT_RECURSIVE=$([ ! "`#{git} --version`" \\< "git version 1.6.5" ] && echo --recursive))
154 execute << "#{git} submodule #{verbose} update --init $GIT_RECURSIVE"
155 end
156 end
157
158 execute.compact.join(" && ").gsub(/\s+/, ' ')
159 end
160
160161 # An expensive export. Performs a checkout as above, then
161162 # removes the repo.
162163 def export(revision, destination)
179180 # changes between calls, but as long as the repositories are all
180181 # based from each other it should still work fine.
181182 if remote != 'origin'
182 execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}"
183 execute << "#{git} config remote.#{remote}.url #{variable(:repository)}"
183184 execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*"
184185 end
185186
186187 # since we're in a local branch already, just reset to specified revision rather than merge
187 execute << "#{git} fetch #{verbose} #{remote} && #{git} reset #{verbose} --hard #{revision}"
188
189 if configuration[:git_enable_submodules]
188 execute << "#{git} fetch #{verbose} #{remote} && #{git} fetch --tags #{verbose} #{remote} && #{git} reset #{verbose} --hard #{revision}"
189
190 if variable(:git_enable_submodules)
190191 execute << "#{git} submodule #{verbose} init"
191192 execute << "for mod in `#{git} submodule status | awk '{ print $2 }'`; do #{git} config -f .git/config submodule.${mod}.url `#{git} config -f .gitmodules --get submodule.${mod}.url` && echo Synced $mod; done"
192193 execute << "#{git} submodule #{verbose} sync"
193 execute << "#{git} submodule #{verbose} update"
194 if false == variable(:git_submodules_recursive)
195 execute << "#{git} submodule #{verbose} update --init"
196 else
197 execute << %Q(export GIT_RECURSIVE=$([ ! "`#{git} --version`" \\< "git version 1.6.5" ] && echo --recursive))
198 execute << "#{git} submodule #{verbose} update --init $GIT_RECURSIVE"
199 end
194200 end
195201
196202 # Make sure there's nothing else lying around in the repository (for
202208
203209 # Returns a string of diffs between two revisions
204210 def diff(from, to=nil)
205 from << "..#{to}" if to
206 scm :diff, from
211 return scm :diff, from unless to
212 scm :diff, "#{from}..#{to}"
207213 end
208214
209215 # Returns a log of changes between the two revisions (inclusive).
227233 break
228234 end
229235 end
236 return newrev if newrev =~ /^[0-9a-f]{40}$/
237
238 # If sha is not found on remote, try expanding from local repository
239 command = scm('rev-parse --revs-only', revision)
240 newrev = yield(command).to_s.strip
241
230242 raise "Unable to resolve revision for '#{revision}' on repository '#{repository}'." unless newrev =~ /^[0-9a-f]{40}$/
231243 return newrev
232244 end
233245
234246 def command
235247 # For backwards compatibility with 1.x version of this module
236 configuration[:git] || super
248 variable(:git) || super
237249 end
238250
239251 # Determines what the response should be for a particular bit of text
245257 case text
246258 when /\bpassword.*:/i
247259 # git is prompting for a password
248 unless pass = configuration[:scm_password]
260 unless pass = variable(:scm_password)
249261 pass = Capistrano::CLI.password_prompt
250262 end
251263 "#{pass}\n"
254266 "yes\n"
255267 when /passphrase/i
256268 # git is asking for the passphrase for the user's key
257 unless pass = configuration[:scm_passphrase]
269 unless pass = variable(:scm_passphrase)
258270 pass = Capistrano::CLI.password_prompt
259271 end
260272 "#{pass}\n"
275287 end
276288 end
277289 end
290
1717 # For mercurial HEAD == tip except that it bases this assumption on what
1818 # tip is in the current repository (so push before you deploy)
1919 def head
20 configuration[:branch] || "tip"
20 variable(:branch) || "tip"
2121 end
2222
2323 # Clone the repository and update to the specified changeset.
00 require 'capistrano/recipes/deploy/scm/base'
11
2 # Notes:
2 # Notes:
33 # no global verbose flag for scm_verbose
44 # sync, checkout and export are just sync in p4
5 #
5 #
66 module Capistrano
77 module Deploy
88 module SCM
2626 def checkout(revision, destination)
2727 p4_sync(revision, destination, p4sync_flags)
2828 end
29
29
3030 # Returns the command that will sync the given revision to the given
3131 # destination directory. The perforce client has a fixed destination so
3232 # the files must be copied from there to their intended resting place.
4040 def export(revision, destination)
4141 p4_sync(revision, destination, p4sync_flags)
4242 end
43
43
4444 # Returns the command that will do an "p4 diff2" for the two revisions.
4545 def diff(from, to=head)
4646 scm authentication, :diff2, "-u -db", "//#{p4client}/...#{rev_no(from)}", "//#{p4client}/...#{rev_no(to)}"
7171 raise Capistrano::Error, "scm_password (or p4passwd) is incorrect or unset"
7272 when /Can.t create a new user.*/i
7373 raise Capistrano::Error, "scm_username (or p4user) is incorrect or unset"
74 when /Perforce client error\:/i
74 when /Perforce client error\:/i
7575 raise Capistrano::Error, "p4port is incorrect or unset"
7676 when /Client \'[\w\-\_\.]+\' unknown.*/i
7777 raise Capistrano::Error, "p4client is incorrect or unset"
78 end
78 end
7979 end
8080
8181 private
8989 end
9090
9191 # Returns the command that will sync the given revision to the given
92 # destination directory with specific options. The perforce client has
93 # a fixed destination so the files must be copied from there to their
94 # intended resting place.
92 # destination directory with specific options. The perforce client has
93 # a fixed destination so the files must be copied from there to their
94 # intended resting place.
9595 def p4_sync(revision, destination, options="")
96 scm authentication, :sync, options, "#{rev_no(revision)}", "&& cp -rf #{p4client_root} #{destination}"
96 scm authentication, :sync, options, "#{rev_no(revision)}", "&& cp -rf #{p4client_root} #{destination}"
9797 end
9898
9999 def p4client
107107 def p4user
108108 variable(:p4user) || variable(:scm_username)
109109 end
110
110
111111 def p4passwd
112112 variable(:p4passwd) || variable(:scm_password)
113113 end
119119 def p4client_root
120120 variable(:p4client_root) || "`#{command} #{authentication} client -o | grep ^Root | cut -f2`"
121121 end
122
123 def rev_no(revision)
122
123 def rev_no(revision)
124 if variable(:p4_label)
125 p4_label = if variable(:p4_label) =~ /\A@/
126 variable(:p4_label)
127 else
128 "@#{variable(:p4_label)}"
129 end
130 return p4_label
131 end
132
124133 case revision.to_s
125134 when "head"
126135 "#head"
127 when /^\d+/
136 when /^\d+/
128137 "@#{revision}"
129138 else
130139 revision
131 end
140 end
132141 end
133142 end
134143
2020 # Returns the command that will check out the given revision to the
2121 # given destination.
2222 def checkout(revision, destination)
23 scm :checkout, arguments, verbose, authentication, "-r#{revision}", repository, destination
23 scm :checkout, arguments, arguments(:checkout), verbose, authentication, "-r#{revision}", repository, destination
2424 end
2525
2626 # Returns the command that will do an "svn update" to the given
2727 # revision, for the working copy at the given destination.
2828 def sync(revision, destination)
29 scm :update, arguments, verbose, authentication, "-r#{revision}", destination
29 scm :switch, arguments, verbose, authentication, "-r#{revision}", repository, destination
3030 end
3131
3232 # Returns the command that will do an "svn export" of the given revision
3333 # to the given destination.
3434 def export(revision, destination)
35 scm :export, arguments, verbose, authentication, "-r#{revision}", repository, destination
35 scm :export, arguments, arguments(:export), verbose, authentication, "-r#{revision}", repository, destination
3636 end
3737
3838 # Returns the command that will do an "svn diff" for the two revisions.
3939 def diff(from, to=nil)
40 scm :diff, repository, authentication, "-r#{from}:#{to || head}"
40 scm :diff, repository, arguments(:diff), authentication, "-r#{from}:#{to || head}"
4141 end
4242
4343 # Returns an "svn log" command for the two revisions.
4444 def log(from, to=nil)
45 scm :log, repository, authentication, "-r#{from}:#{to || head}"
45 scm :log, repository, arguments(:log), authentication, "-r#{from}:#{to || head}"
4646 end
4747
4848 # Attempts to translate the given revision identifier to a "real"
5151 # executed (svn info), and will extract the revision from the response.
5252 def query_revision(revision)
5353 return revision if revision =~ /^\d+$/
54 command = scm(:info, repository, authentication, "-r#{revision}")
54 command = scm(:info, arguments, arguments(:info), repository, authentication, "-r#{revision}")
5555 result = yield(command)
5656 yaml = YAML.load(result)
5757 raise "tried to run `#{command}' and got unexpected result #{result.inspect}" unless Hash === yaml
0 require 'benchmark'
01 require 'capistrano/recipes/deploy/dependencies'
12
23 module Capistrano
4849 # A wrapper for Kernel#system that logs the command being executed.
4950 def system(*args)
5051 cmd = args.join(' ')
52 result = nil
5153 if RUBY_PLATFORM =~ /win32/
5254 cmd = cmd.split(/\s+/).collect {|w| w.match(/^[\w+]+:\/\//) ? w : w.gsub('/', '\\') }.join(' ') # Split command by spaces, change / by \\ unless element is a some+thing://
5355 cmd.gsub!(/^cd /,'cd /D ') # Replace cd with cd /D
5456 cmd.gsub!(/&& cd /,'&& cd /D ') # Replace cd with cd /D
5557 logger.trace "executing locally: #{cmd}"
56 super(cmd)
58 elapsed = Benchmark.realtime do
59 result = super(cmd)
60 end
5761 else
5862 logger.trace "executing locally: #{cmd}"
59 super
63 elapsed = Benchmark.realtime do
64 result = super
65 end
6066 end
67
68 logger.trace "command finished in #{(elapsed * 1000).round}ms"
69 result
6170 end
6271
6372 private
5252 system(source.checkout(revision, copy_cache))
5353 end
5454
55 # Check the return code of last system command and rollback if not 0
56 unless $? == 0
57 raise Capistrano::Error, "shell command failed with return code #{$?}"
58 end
59
60 FileUtils.mkdir_p(destination)
61
5562 logger.debug "copying cache to deployment staging area #{destination}"
5663 Dir.chdir(copy_cache) do
57 FileUtils.mkdir_p(destination)
5864 queue = Dir.glob("*", File::FNM_DOTMATCH)
5965 while queue.any?
6066 item = queue.shift
6470 next if copy_exclude.any? { |pattern| File.fnmatch(pattern, item) }
6571
6672 if File.symlink?(item)
67 FileUtils.ln_s(File.readlink(File.join(copy_cache, item)), File.join(destination, item))
73 FileUtils.ln_s(File.readlink(item), File.join(destination, item))
6874 elsif File.directory?(item)
6975 queue += Dir.glob("#{item}/*", File::FNM_DOTMATCH)
7076 FileUtils.mkdir(File.join(destination, item))
7177 else
72 FileUtils.ln(File.join(copy_cache, item), File.join(destination, item))
78 FileUtils.ln(item, File.join(destination, item))
7379 end
7480 end
7581 end
7985
8086 if copy_exclude.any?
8187 logger.debug "processing exclusions..."
82 if copy_exclude.any?
83 copy_exclude.each do |pattern|
84 delete_list = Dir.glob(File.join(destination, pattern), File::FNM_DOTMATCH)
85 # avoid the /.. trap that deletes the parent directories
86 delete_list.delete_if { |dir| dir =~ /\/\.\.$/ }
87 FileUtils.rm_rf(delete_list.compact)
88 end
88
89 copy_exclude.each do |pattern|
90 delete_list = Dir.glob(File.join(destination, pattern), File::FNM_DOTMATCH)
91 # avoid the /.. trap that deletes the parent directories
92 delete_list.delete_if { |dir| dir =~ /\/\.\.$/ }
93 FileUtils.rm_rf(delete_list.compact)
8994 end
9095 end
9196 end
9398 File.open(File.join(destination, "REVISION"), "w") { |f| f.puts(revision) }
9499
95100 logger.trace "compressing #{destination} to #{filename}"
96 Dir.chdir(tmpdir) { system(compress(File.basename(destination), File.basename(filename)).join(" ")) }
97
98 upload(filename, remote_filename)
99 run "cd #{configuration[:releases_path]} && #{decompress(remote_filename).join(" ")} && rm #{remote_filename}"
101 Dir.chdir(copy_dir) { system(compress(File.basename(destination), File.basename(filename)).join(" ")) }
102
103 distribute!
100104 ensure
101105 FileUtils.rm filename rescue nil
102106 FileUtils.rm_rf destination rescue nil
116120 # is +true+, a default cache location will be returned.
117121 def copy_cache
118122 @copy_cache ||= configuration[:copy_cache] == true ?
119 File.join(Dir.tmpdir, configuration[:application]) :
120 configuration[:copy_cache]
123 File.expand_path(configuration[:application], Dir.tmpdir) :
124 File.expand_path(configuration[:copy_cache], Dir.pwd) rescue nil
121125 end
122126
123127 private
131135 # Returns the basename of the release_path, which will be used to
132136 # name the local copy and archive file.
133137 def destination
134 @destination ||= File.join(tmpdir, File.basename(configuration[:release_path]))
138 @destination ||= File.join(copy_dir, File.basename(configuration[:release_path]))
135139 end
136140
137141 # Returns the value of the :copy_strategy variable, defaulting to
154158 # Returns the name of the file that the source code will be
155159 # compressed to.
156160 def filename
157 @filename ||= File.join(tmpdir, "#{File.basename(destination)}.#{compression.extension}")
161 @filename ||= File.join(copy_dir, "#{File.basename(destination)}.#{compression.extension}")
158162 end
159163
160164 # The directory to which the copy should be checked out
161 def tmpdir
162 @tmpdir ||= configuration[:copy_dir] || Dir.tmpdir
165 def copy_dir
166 @copy_dir ||= File.expand_path(configuration[:copy_dir] || Dir.tmpdir, Dir.pwd)
163167 end
164168
165169 # The directory on the remote server to which the archive should be
178182 # Commands are arrays, where the first element is the utility to be
179183 # used to perform the compression or decompression.
180184 Compression = Struct.new(:extension, :compress_command, :decompress_command)
181
185
182186 # The compression method to use, defaults to :gzip.
183187 def compression
184188 remote_tar = configuration[:copy_remote_tar] || 'tar'
185189 local_tar = configuration[:copy_local_tar] || 'tar'
186
190
187191 type = configuration[:copy_compression] || :gzip
188192 case type
189 when :gzip, :gz then Compression.new("tar.gz", [local_tar, 'czf'], [remote_tar, 'xzf'])
190 when :bzip2, :bz2 then Compression.new("tar.bz2", [local_tar, 'cjf'], [remote_tar, 'xjf'])
193 when :gzip, :gz then Compression.new("tar.gz", [local_tar, 'chzf'], [remote_tar, 'xzf'])
194 when :bzip2, :bz2 then Compression.new("tar.bz2", [local_tar, 'chjf'], [remote_tar, 'xjf'])
191195 when :zip then Compression.new("zip", %w(zip -qr), %w(unzip -q))
192196 else raise ArgumentError, "invalid compression type #{type.inspect}"
193197 end
194198 end
195
199
196200 # Returns the command necessary to compress the given directory
197201 # into the given file.
198202 def compress(directory, file)
205209 def decompress(file)
206210 compression.decompress_command + [file]
207211 end
212
213 # Distributes the file to the remote servers
214 def distribute!
215 upload(filename, remote_filename)
216 run "cd #{configuration[:releases_path]} && #{decompress(remote_filename).join(" ")} && rm #{remote_filename}"
217 end
208218 end
209219
210220 end
1717
1818 def check!
1919 super.check do |d|
20 d.remote.command("rsync") unless copy_exclude.empty?
2021 d.remote.writable(shared_path)
2122 end
2223 end
3738
3839 def copy_repository_cache
3940 logger.trace "copying the cached version to #{configuration[:release_path]}"
40 if copy_exclude.empty?
41 if copy_exclude.empty?
4142 run "cp -RPp #{repository_cache} #{configuration[:release_path]} && #{mark}"
4243 else
4344 exclusions = copy_exclude.map { |e| "--exclude=\"#{e}\"" }.join(' ')
44 run "rsync -lrpt #{exclusions} #{repository_cache}/* #{configuration[:release_path]} && #{mark}"
45 run "rsync -lrpt #{exclusions} #{repository_cache}/ #{configuration[:release_path]} && #{mark}"
4546 end
4647 end
47
48
4849 def copy_exclude
4950 @copy_exclude ||= Array(configuration.fetch(:copy_exclude, []))
5051 end
0 require 'benchmark'
01 require 'yaml'
12 require 'capistrano/recipes/deploy/scm'
23 require 'capistrano/recipes/deploy/strategy'
2627 _cset(:deploy_to) { "/u/apps/#{application}" }
2728 _cset(:revision) { source.head }
2829
30 _cset :rails_env, "production"
31 _cset :rake, "rake"
32
33 _cset :maintenance_basename, "maintenance"
34
2935 # =========================================================================
3036 # These variables should NOT be changed unless you are very confident in
3137 # what you are doing. Make sure you understand all the implications of your
3743
3844 _cset(:strategy) { Capistrano::Deploy::Strategy.new(deploy_via, self) }
3945
46 # If overriding release name, please also select an appropriate setting for :releases below.
4047 _cset(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") }
4148
4249 _cset :version_dir, "releases"
4956 _cset(:current_path) { File.join(deploy_to, current_dir) }
5057 _cset(:release_path) { File.join(releases_path, release_name) }
5158
52 _cset(:releases) { capture("ls -xt #{releases_path}").split.reverse }
53 _cset(:current_release) { File.join(releases_path, releases.last) }
59 _cset(:releases) { capture("ls -x #{releases_path}", :except => { :no_release => true }).split.sort }
60 _cset(:current_release) { releases.length > 0 ? File.join(releases_path, releases.last) : nil }
5461 _cset(:previous_release) { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil }
5562
56 _cset(:current_revision) { capture("cat #{current_path}/REVISION").chomp }
57 _cset(:latest_revision) { capture("cat #{current_release}/REVISION").chomp }
58 _cset(:previous_revision) { capture("cat #{previous_release}/REVISION").chomp }
63 _cset(:current_revision) { capture("cat #{current_path}/REVISION", :except => { :no_release => true }).chomp }
64 _cset(:latest_revision) { capture("cat #{current_release}/REVISION", :except => { :no_release => true }).chomp }
65 _cset(:previous_revision) { capture("cat #{previous_release}/REVISION", :except => { :no_release => true }).chomp if previous_release }
5966
6067 _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
6168
93100 # returns the command output as a string
94101 def run_locally(cmd)
95102 logger.trace "executing locally: #{cmd.inspect}" if logger
96 command_present?(cmd)
97 `#{cmd}`
98 end
99
100 # tests if the given command is present on the local system
101 def command_present?(cmd)
102 executable = cmd.to_s.split(" ").first
103 unless system("which #{executable}")
104 logger.important "executable '#{executable}' not present or not in $PATH on the local system!"
105 end
106 end
103 output_on_stdout = nil
104 elapsed = Benchmark.realtime do
105 output_on_stdout = `#{cmd}`
106 end
107 if $?.to_i > 0 # $? is command exit code (posix style)
108 raise Capistrano::LocalArgumentError, "Command #{cmd} returned status code #{$?}"
109 end
110 logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger
111 output_on_stdout
112 end
113
107114
108115 # If a command is given, this will try to execute the given command, as
109116 # described below. Otherwise, it will return a string for use in embedding in
177184 task :setup, :except => { :no_release => true } do
178185 dirs = [deploy_to, releases_path, shared_path]
179186 dirs += shared_children.map { |d| File.join(shared_path, d) }
180 run "#{try_sudo} mkdir -p #{dirs.join(' ')} && #{try_sudo} chmod g+w #{dirs.join(' ')}"
187 run "#{try_sudo} mkdir -p #{dirs.join(' ')}"
188 run "#{try_sudo} chmod g+w #{dirs.join(' ')}" if fetch(:group_writable, true)
181189 end
182190
183191 desc <<-DESC
226234 public/stylesheets, and public/javascripts so that the times are \
227235 consistent (so that asset timestamping works). This touch process \
228236 is only carried out if the :normalize_asset_timestamps variable is \
229 set to true, which is the default.
237 set to true, which is the default The asset directories can be overridden \
238 using the :public_children variable.
230239 DESC
231240 task :finalize_update, :except => { :no_release => true } do
232241 run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true)
244253
245254 if fetch(:normalize_asset_timestamps, true)
246255 stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
247 asset_paths = %w(images stylesheets javascripts).map { |p| "#{latest_release}/public/#{p}" }.join(" ")
256 asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).map { |p| "#{latest_release}/public/#{p}" }.join(" ")
248257 run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
249258 end
250259 end
296305 end
297306
298307 desc <<-DESC
299 Restarts your application. This works by calling the script/process/reaper \
300 script under the current path.
301
302 If you are deploying a Rails 2.3.x application, you will need to install
303 these http://github.com/rails/irs_process_scripts (more info about why
304 on that page.)
305
306 By default, this will be invoked via sudo as the `app' user. If \
307 you wish to run it as a different user, set the :runner variable to \
308 that user. If you are in an environment where you can't use sudo, set \
309 the :use_sudo variable to false:
310
311 set :use_sudo, false
308 Blank task exists as a hook into which to install your own environment \
309 specific behaviour.
312310 DESC
313311 task :restart, :roles => :app, :except => { :no_release => true } do
314 warn "[DEPRECATED] `deploy:restart` is going to be changed to Passenger mod_rails' method after 2.5.9 - see http://is.gd/2BPeA"
315 try_runner "#{current_path}/script/process/reaper"
312 # Empty Task to overload with your platform specifics
316313 end
317314
318315 namespace :rollback do
377374 set :migrate_target, :latest
378375 DESC
379376 task :migrate, :roles => :db, :only => { :primary => true } do
380 rake = fetch(:rake, "rake")
381 rails_env = fetch(:rails_env, "production")
382377 migrate_env = fetch(:migrate_env, "")
383378 migrate_target = fetch(:migrate_target, :latest)
384379
385380 directory = case migrate_target.to_sym
386381 when :current then current_path
387 when :latest then current_release
382 when :latest then latest_release
388383 else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
389384 end
390385
391 run "cd #{directory}; #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
386 run "cd #{directory} && #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
392387 end
393388
394389 desc <<-DESC
480475 end
481476
482477 desc <<-DESC
483 Start the application servers. This will attempt to invoke a script \
484 in your application called `script/spin', which must know how to start \
485 your application listeners. For Rails applications, you might just have \
486 that script invoke `script/process/spawner' with the appropriate \
487 arguments.
488
489 By default, the script will be executed via sudo as the `app' user. If \
490 you wish to run it as a different user, set the :runner variable to \
491 that user. If you are in an environment where you can't use sudo, set \
492 the :use_sudo variable to false.
478 Blank task exists as a hook into which to install your own environment \
479 specific behaviour.
493480 DESC
494481 task :start, :roles => :app do
495 warn "[DEPRECATED] `deploy:start` is going to be removed after 2.5.9 - see http://is.gd/2BPeA"
496 run "cd #{current_path} && #{try_runner} nohup script/spin"
497 end
498
499 desc <<-DESC
500 Stop the application servers. This will call script/process/reaper for \
501 both the spawner process, and all of the application processes it has \
502 spawned. As such, it is fairly Rails specific and may need to be \
503 overridden for other systems.
504
505 By default, the script will be executed via sudo as the `app' user. If \
506 you wish to run it as a different user, set the :runner variable to \
507 that user. If you are in an environment where you can't use sudo, set \
508 the :use_sudo variable to false.
482 # Empty Task to overload with your platform specifics
483 end
484
485 desc <<-DESC
486 Blank task exists as a hook into which to install your own environment \
487 specific behaviour.
509488 DESC
510489 task :stop, :roles => :app do
511 warn "[DEPRECATED] `deploy:start` is going to be removed after 2.5.9 - see http://is.gd/2BPeA"
512 run "if [ -f #{current_path}/tmp/pids/dispatch.spawner.pid ]; then #{try_runner} #{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid; fi"
513 try_runner "#{current_path}/script/process/reaper -a kill"
490 # Empty Task to overload with your platform specifics
514491 end
515492
516493 namespace :pending do
537514 namespace :web do
538515 desc <<-DESC
539516 Present a maintenance page to visitors. Disables your application's web \
540 interface by writing a "maintenance.html" file to each web server. The \
517 interface by writing a "#{maintenance_basename}.html" file to each web server. The \
541518 servers must be configured to detect the presence of this file, and if \
542519 it is present, always display it instead of performing the request.
543520
553530 DESC
554531 task :disable, :roles => :web, :except => { :no_release => true } do
555532 require 'erb'
556 on_rollback { run "rm #{shared_path}/system/maintenance.html" }
533 on_rollback { run "rm #{shared_path}/system/#{maintenance_basename}.html" }
557534
558535 warn <<-EOHTACCESS
559
536
560537 # Please add something like this to your site's htaccess to redirect users to the maintenance page.
561538 # More Info: http://www.shiftcommathree.com/articles/make-your-rails-maintenance-page-respond-with-a-503
562
563 ErrorDocument 503 /system/maintenance.html
539
540 ErrorDocument 503 /system/#{maintenance_basename}.html
564541 RewriteEngine On
565542 RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
566 RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
567 RewriteCond %{SCRIPT_FILENAME} !maintenance.html
543 RewriteCond %{DOCUMENT_ROOT}/system/#{maintenance_basename}.html -f
544 RewriteCond %{SCRIPT_FILENAME} !#{maintenance_basename}.html
568545 RewriteRule ^.*$ - [redirect=503,last]
569546 EOHTACCESS
570547
574551 template = File.read(File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml"))
575552 result = ERB.new(template).result(binding)
576553
577 put result, "#{shared_path}/system/maintenance.html", :mode => 0644
554 put result, "#{shared_path}/system/#{maintenance_basename}.html", :mode => 0644
578555 end
579556
580557 desc <<-DESC
581558 Makes the application web-accessible again. Removes the \
582 "maintenance.html" page generated by deploy:web:disable, which (if your \
559 "#{maintenance_basename}.html" page generated by deploy:web:disable, which (if your \
583560 web servers are configured correctly) will make your application \
584561 web-accessible again.
585562 DESC
586563 task :enable, :roles => :web, :except => { :no_release => true } do
587 run "rm #{shared_path}/system/maintenance.html"
588 end
589 end
590 end
564 run "rm #{shared_path}/system/#{maintenance_basename}.html"
565 end
566 end
567 end
+0
-33
lib/capistrano/recipes/upgrade.rb less more
0 # Tasks to aid the migration of an established Capistrano 1.x installation to
1 # Capistrano 2.x.
2
3 namespace :upgrade do
4 desc <<-DESC
5 Migrate from the revisions log to REVISION. Capistrano 1.x recorded each \
6 deployment to a revisions.log file. Capistrano 2.x is cleaner, and just \
7 puts a REVISION file in the root of the deployed revision. This task \
8 migrates from the revisions.log used in Capistrano 1.x, to the REVISION \
9 tag file used in Capistrano 2.x. It is non-destructive and may be safely \
10 run any number of times.
11 DESC
12 task :revisions, :except => { :no_release => true } do
13 revisions = capture("cat #{deploy_to}/revisions.log")
14
15 mapping = {}
16 revisions.each do |line|
17 revision, directory = line.chomp.split[-2,2]
18 mapping[directory] = revision
19 end
20
21 commands = mapping.keys.map do |directory|
22 "echo '.'; test -d #{directory} && echo '#{mapping[directory]}' > #{directory}/REVISION"
23 end
24
25 command = commands.join(";")
26
27 run "cd #{releases_path}; #{command}; true" do |ch, stream, out|
28 STDOUT.print(".")
29 STDOUT.flush
30 end
31 end
32 end
6868 # Once we've loaded the config, we don't need Net::SSH to do it again.
6969 ssh_options[:config] = false
7070
71 ssh_options[:verbose] = :debug if options[:verbose] && options[:verbose] > 0
72
7173 user = server.user || options[:user] || ssh_options[:username] ||
7274 ssh_options[:user] || ServerDefinition.default_user
7375 port = server.port || options[:port] || ssh_options[:port]
55 attr_reader :name, :namespace, :options, :body, :desc, :on_error, :max_hosts
66
77 def initialize(name, namespace, options={}, &block)
8
9 if name.to_s =~ /^(?:before_|after_)/
10 Kernel.warn("[Deprecation Warning] Naming tasks with before_ and after_ is deprecated, please see the new before() and after() methods. (Offending task name was #{name})")
11 end
12
813 @name, @namespace, @options = name, namespace, options
914 @desc = @options.delete(:desc)
1015 @on_error = options.delete(:on_error)
1217 @body = block or raise ArgumentError, "a task requires a block"
1318 @servers = nil
1419 end
15
20
1621 # Returns the task's fully-qualified name, including the namespace
1722 def fully_qualified_name
1823 @fully_qualified_name ||= begin
6671 @on_error == :continue
6772 end
6873 end
69 end
74 end
0 require 'net/ssh/version'
1
0 require 'scanf'
21 module Capistrano
32
4 # Describes the current version of Capistrano.
5 class Version < Net::SSH::Version
3 class Version
4
65 MAJOR = 2
7 MINOR = 5
8 TINY = 9
6 MINOR = 9
7 PATCH = 0
98
10 # The current version, as a Version instance
11 CURRENT = new(MAJOR, MINOR, TINY)
9 def self.to_s
10 "#{MAJOR}.#{MINOR}.#{PATCH}"
11 end
1212
13 # The current version, as a String instance
14 STRING = CURRENT.to_s
1513 end
1614
1715 end
00 require 'capistrano/configuration'
1 require 'capistrano/extensions'
1 require 'capistrano/extensions'
2 require 'capistrano/ext/string'
0 --- !ruby/object:Gem::Specification
1 name: capistrano
2 version: !ruby/object:Gem::Version
3 version: 2.9.0
4 prerelease:
5 platform: ruby
6 authors:
7 - Jamis Buck
8 - Lee Hambley
9 autorequire:
10 bindir: bin
11 cert_chain: []
12 date: 2011-09-24 00:00:00.000000000Z
13 dependencies:
14 - !ruby/object:Gem::Dependency
15 name: highline
16 requirement: &70363620892360 !ruby/object:Gem::Requirement
17 none: false
18 requirements:
19 - - ! '>='
20 - !ruby/object:Gem::Version
21 version: '0'
22 type: :runtime
23 prerelease: false
24 version_requirements: *70363620892360
25 - !ruby/object:Gem::Dependency
26 name: net-ssh
27 requirement: &70363620908220 !ruby/object:Gem::Requirement
28 none: false
29 requirements:
30 - - ! '>='
31 - !ruby/object:Gem::Version
32 version: 2.0.14
33 type: :runtime
34 prerelease: false
35 version_requirements: *70363620908220
36 - !ruby/object:Gem::Dependency
37 name: net-sftp
38 requirement: &70363620907740 !ruby/object:Gem::Requirement
39 none: false
40 requirements:
41 - - ! '>='
42 - !ruby/object:Gem::Version
43 version: 2.0.0
44 type: :runtime
45 prerelease: false
46 version_requirements: *70363620907740
47 - !ruby/object:Gem::Dependency
48 name: net-scp
49 requirement: &70363620907260 !ruby/object:Gem::Requirement
50 none: false
51 requirements:
52 - - ! '>='
53 - !ruby/object:Gem::Version
54 version: 1.0.0
55 type: :runtime
56 prerelease: false
57 version_requirements: *70363620907260
58 - !ruby/object:Gem::Dependency
59 name: net-ssh-gateway
60 requirement: &70363620906780 !ruby/object:Gem::Requirement
61 none: false
62 requirements:
63 - - ! '>='
64 - !ruby/object:Gem::Version
65 version: 1.1.0
66 type: :runtime
67 prerelease: false
68 version_requirements: *70363620906780
69 - !ruby/object:Gem::Dependency
70 name: mocha
71 requirement: &70363620906300 !ruby/object:Gem::Requirement
72 none: false
73 requirements:
74 - - ! '>='
75 - !ruby/object:Gem::Version
76 version: '0'
77 type: :development
78 prerelease: false
79 version_requirements: *70363620906300
80 description: Capistrano is a utility and framework for executing commands in parallel
81 on multiple remote machines, via SSH.
82 email:
83 - jamis@jamisbuck.org
84 - lee.hambley@gmail.com
85 executables:
86 - cap
87 - capify
88 extensions: []
89 extra_rdoc_files:
90 - README.mdown
91 files:
92 - .gitignore
93 - .rvmrc
94 - .travis.yml
95 - CHANGELOG
96 - Gemfile
97 - README.mdown
98 - Rakefile
99 - bin/cap
100 - bin/capify
101 - capistrano.gemspec
102 - lib/capistrano.rb
103 - lib/capistrano/callback.rb
104 - lib/capistrano/cli.rb
105 - lib/capistrano/cli/execute.rb
106 - lib/capistrano/cli/help.rb
107 - lib/capistrano/cli/help.txt
108 - lib/capistrano/cli/options.rb
109 - lib/capistrano/cli/ui.rb
110 - lib/capistrano/command.rb
111 - lib/capistrano/configuration.rb
112 - lib/capistrano/configuration/actions/file_transfer.rb
113 - lib/capistrano/configuration/actions/inspect.rb
114 - lib/capistrano/configuration/actions/invocation.rb
115 - lib/capistrano/configuration/callbacks.rb
116 - lib/capistrano/configuration/connections.rb
117 - lib/capistrano/configuration/execution.rb
118 - lib/capistrano/configuration/loading.rb
119 - lib/capistrano/configuration/namespaces.rb
120 - lib/capistrano/configuration/roles.rb
121 - lib/capistrano/configuration/servers.rb
122 - lib/capistrano/configuration/variables.rb
123 - lib/capistrano/errors.rb
124 - lib/capistrano/ext/string.rb
125 - lib/capistrano/extensions.rb
126 - lib/capistrano/logger.rb
127 - lib/capistrano/processable.rb
128 - lib/capistrano/recipes/compat.rb
129 - lib/capistrano/recipes/deploy.rb
130 - lib/capistrano/recipes/deploy/assets.rb
131 - lib/capistrano/recipes/deploy/dependencies.rb
132 - lib/capistrano/recipes/deploy/local_dependency.rb
133 - lib/capistrano/recipes/deploy/remote_dependency.rb
134 - lib/capistrano/recipes/deploy/scm.rb
135 - lib/capistrano/recipes/deploy/scm/accurev.rb
136 - lib/capistrano/recipes/deploy/scm/base.rb
137 - lib/capistrano/recipes/deploy/scm/bzr.rb
138 - lib/capistrano/recipes/deploy/scm/cvs.rb
139 - lib/capistrano/recipes/deploy/scm/darcs.rb
140 - lib/capistrano/recipes/deploy/scm/git.rb
141 - lib/capistrano/recipes/deploy/scm/mercurial.rb
142 - lib/capistrano/recipes/deploy/scm/none.rb
143 - lib/capistrano/recipes/deploy/scm/perforce.rb
144 - lib/capistrano/recipes/deploy/scm/subversion.rb
145 - lib/capistrano/recipes/deploy/strategy.rb
146 - lib/capistrano/recipes/deploy/strategy/base.rb
147 - lib/capistrano/recipes/deploy/strategy/checkout.rb
148 - lib/capistrano/recipes/deploy/strategy/copy.rb
149 - lib/capistrano/recipes/deploy/strategy/export.rb
150 - lib/capistrano/recipes/deploy/strategy/remote.rb
151 - lib/capistrano/recipes/deploy/strategy/remote_cache.rb
152 - lib/capistrano/recipes/deploy/templates/maintenance.rhtml
153 - lib/capistrano/recipes/standard.rb
154 - lib/capistrano/recipes/templates/maintenance.rhtml
155 - lib/capistrano/role.rb
156 - lib/capistrano/server_definition.rb
157 - lib/capistrano/shell.rb
158 - lib/capistrano/ssh.rb
159 - lib/capistrano/task_definition.rb
160 - lib/capistrano/transfer.rb
161 - lib/capistrano/version.rb
162 - rvmrc.sample
163 - test/cli/execute_test.rb
164 - test/cli/help_test.rb
165 - test/cli/options_test.rb
166 - test/cli/ui_test.rb
167 - test/cli_test.rb
168 - test/command_test.rb
169 - test/configuration/actions/file_transfer_test.rb
170 - test/configuration/actions/inspect_test.rb
171 - test/configuration/actions/invocation_test.rb
172 - test/configuration/callbacks_test.rb
173 - test/configuration/connections_test.rb
174 - test/configuration/execution_test.rb
175 - test/configuration/loading_test.rb
176 - test/configuration/namespace_dsl_test.rb
177 - test/configuration/roles_test.rb
178 - test/configuration/servers_test.rb
179 - test/configuration/variables_test.rb
180 - test/configuration_test.rb
181 - test/deploy/local_dependency_test.rb
182 - test/deploy/remote_dependency_test.rb
183 - test/deploy/scm/accurev_test.rb
184 - test/deploy/scm/base_test.rb
185 - test/deploy/scm/bzr_test.rb
186 - test/deploy/scm/darcs_test.rb
187 - test/deploy/scm/git_test.rb
188 - test/deploy/scm/mercurial_test.rb
189 - test/deploy/scm/none_test.rb
190 - test/deploy/scm/perforce_test.rb
191 - test/deploy/scm/subversion_test.rb
192 - test/deploy/strategy/copy_test.rb
193 - test/extensions_test.rb
194 - test/fixtures/cli_integration.rb
195 - test/fixtures/config.rb
196 - test/fixtures/custom.rb
197 - test/logger_test.rb
198 - test/recipes_test.rb
199 - test/role_test.rb
200 - test/server_definition_test.rb
201 - test/shell_test.rb
202 - test/ssh_test.rb
203 - test/task_definition_test.rb
204 - test/transfer_test.rb
205 - test/utils.rb
206 homepage: http://github.com/capistrano/capistrano
207 licenses: []
208 post_install_message:
209 rdoc_options: []
210 require_paths:
211 - lib
212 required_ruby_version: !ruby/object:Gem::Requirement
213 none: false
214 requirements:
215 - - ! '>='
216 - !ruby/object:Gem::Version
217 version: '0'
218 required_rubygems_version: !ruby/object:Gem::Requirement
219 none: false
220 requirements:
221 - - ! '>='
222 - !ruby/object:Gem::Version
223 version: '0'
224 requirements: []
225 rubyforge_project:
226 rubygems_version: 1.8.7
227 signing_key:
228 specification_version: 3
229 summary: Capistrano - Welcome to easy deployment with Ruby over SSH
230 test_files:
231 - test/cli/execute_test.rb
232 - test/cli/help_test.rb
233 - test/cli/options_test.rb
234 - test/cli/ui_test.rb
235 - test/cli_test.rb
236 - test/command_test.rb
237 - test/configuration/actions/file_transfer_test.rb
238 - test/configuration/actions/inspect_test.rb
239 - test/configuration/actions/invocation_test.rb
240 - test/configuration/callbacks_test.rb
241 - test/configuration/connections_test.rb
242 - test/configuration/execution_test.rb
243 - test/configuration/loading_test.rb
244 - test/configuration/namespace_dsl_test.rb
245 - test/configuration/roles_test.rb
246 - test/configuration/servers_test.rb
247 - test/configuration/variables_test.rb
248 - test/configuration_test.rb
249 - test/deploy/local_dependency_test.rb
250 - test/deploy/remote_dependency_test.rb
251 - test/deploy/scm/accurev_test.rb
252 - test/deploy/scm/base_test.rb
253 - test/deploy/scm/bzr_test.rb
254 - test/deploy/scm/darcs_test.rb
255 - test/deploy/scm/git_test.rb
256 - test/deploy/scm/mercurial_test.rb
257 - test/deploy/scm/none_test.rb
258 - test/deploy/scm/perforce_test.rb
259 - test/deploy/scm/subversion_test.rb
260 - test/deploy/strategy/copy_test.rb
261 - test/extensions_test.rb
262 - test/fixtures/cli_integration.rb
263 - test/fixtures/config.rb
264 - test/fixtures/custom.rb
265 - test/logger_test.rb
266 - test/recipes_test.rb
267 - test/role_test.rb
268 - test/server_definition_test.rb
269 - test/shell_test.rb
270 - test/ssh_test.rb
271 - test/task_definition_test.rb
272 - test/transfer_test.rb
273 - test/utils.rb
0 rvm use 1.8.7@capistrano-devel --create
+0
-1346
setup.rb less more
0 #
1 # setup.rb
2 #
3 # Copyright (c) 2000-2004 Minero Aoki
4 #
5 # This program is free software.
6 # You can distribute/modify this program under the terms of
7 # the GNU Lesser General Public License version 2.1.
8 #
9
10 #
11 # For backward compatibility
12 #
13
14 unless Enumerable.method_defined?(:map)
15 module Enumerable
16 alias map collect
17 end
18 end
19
20 unless Enumerable.method_defined?(:detect)
21 module Enumerable
22 alias detect find
23 end
24 end
25
26 unless Enumerable.method_defined?(:select)
27 module Enumerable
28 alias select find_all
29 end
30 end
31
32 unless Enumerable.method_defined?(:reject)
33 module Enumerable
34 def reject
35 result = []
36 each do |i|
37 result.push i unless yield(i)
38 end
39 result
40 end
41 end
42 end
43
44 unless Enumerable.method_defined?(:inject)
45 module Enumerable
46 def inject(result)
47 each do |i|
48 result = yield(result, i)
49 end
50 result
51 end
52 end
53 end
54
55 unless Enumerable.method_defined?(:any?)
56 module Enumerable
57 def any?
58 each do |i|
59 return true if yield(i)
60 end
61 false
62 end
63 end
64 end
65
66 unless File.respond_to?(:read)
67 def File.read(fname)
68 open(fname) {|f|
69 return f.read
70 }
71 end
72 end
73
74 #
75 # Application independent utilities
76 #
77
78 def File.binread(fname)
79 open(fname, 'rb') {|f|
80 return f.read
81 }
82 end
83
84 # for corrupted windows stat(2)
85 def File.dir?(path)
86 File.directory?((path[-1,1] == '/') ? path : path + '/')
87 end
88
89 #
90 # Config
91 #
92
93 if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg }
94 ARGV.delete(arg)
95 require arg.split(/=/, 2)[1]
96 $".push 'rbconfig.rb'
97 else
98 require 'rbconfig'
99 end
100
101 def multipackage_install?
102 FileTest.directory?(File.dirname($0) + '/packages')
103 end
104
105
106 class ConfigTable
107
108 c = ::Config::CONFIG
109
110 rubypath = c['bindir'] + '/' + c['ruby_install_name']
111
112 major = c['MAJOR'].to_i
113 minor = c['MINOR'].to_i
114 teeny = c['TEENY'].to_i
115 version = "#{major}.#{minor}"
116
117 # ruby ver. >= 1.4.4?
118 newpath_p = ((major >= 2) or
119 ((major == 1) and
120 ((minor >= 5) or
121 ((minor == 4) and (teeny >= 4)))))
122
123 subprefix = lambda {|path|
124 path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')
125 }
126
127 if c['rubylibdir']
128 # V < 1.6.3
129 stdruby = subprefix.call(c['rubylibdir'])
130 siteruby = subprefix.call(c['sitedir'])
131 versite = subprefix.call(c['sitelibdir'])
132 sodir = subprefix.call(c['sitearchdir'])
133 elsif newpath_p
134 # 1.4.4 <= V <= 1.6.3
135 stdruby = "$prefix/lib/ruby/#{version}"
136 siteruby = subprefix.call(c['sitedir'])
137 versite = siteruby + '/' + version
138 sodir = "$site-ruby/#{c['arch']}"
139 else
140 # V < 1.4.4
141 stdruby = "$prefix/lib/ruby/#{version}"
142 siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
143 versite = siteruby
144 sodir = "$site-ruby/#{c['arch']}"
145 end
146
147 if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
148 makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
149 else
150 makeprog = 'make'
151 end
152
153 common_descripters = [
154 [ 'prefix', [ c['prefix'],
155 'path',
156 'path prefix of target environment' ] ],
157 [ 'std-ruby', [ stdruby,
158 'path',
159 'the directory for standard ruby libraries' ] ],
160 [ 'site-ruby-common', [ siteruby,
161 'path',
162 'the directory for version-independent non-standard ruby libraries' ] ],
163 [ 'site-ruby', [ versite,
164 'path',
165 'the directory for non-standard ruby libraries' ] ],
166 [ 'bin-dir', [ '$prefix/bin',
167 'path',
168 'the directory for commands' ] ],
169 [ 'rb-dir', [ '$site-ruby',
170 'path',
171 'the directory for ruby scripts' ] ],
172 [ 'so-dir', [ sodir,
173 'path',
174 'the directory for ruby extentions' ] ],
175 [ 'data-dir', [ '$prefix/share',
176 'path',
177 'the directory for shared data' ] ],
178 [ 'ruby-path', [ rubypath,
179 'path',
180 'path to set to #! line' ] ],
181 [ 'ruby-prog', [ rubypath,
182 'name',
183 'the ruby program using for installation' ] ],
184 [ 'make-prog', [ makeprog,
185 'name',
186 'the make program to compile ruby extentions' ] ],
187 [ 'without-ext', [ 'no',
188 'yes/no',
189 'does not compile/install ruby extentions' ] ]
190 ]
191 multipackage_descripters = [
192 [ 'with', [ '',
193 'name,name...',
194 'package names that you want to install',
195 'ALL' ] ],
196 [ 'without', [ '',
197 'name,name...',
198 'package names that you do not want to install',
199 'NONE' ] ]
200 ]
201 if multipackage_install?
202 DESCRIPTER = common_descripters + multipackage_descripters
203 else
204 DESCRIPTER = common_descripters
205 end
206
207 SAVE_FILE = 'config.save'
208
209 def ConfigTable.each_name(&block)
210 keys().each(&block)
211 end
212
213 def ConfigTable.keys
214 DESCRIPTER.map {|name, *dummy| name }
215 end
216
217 def ConfigTable.each_definition(&block)
218 DESCRIPTER.each(&block)
219 end
220
221 def ConfigTable.get_entry(name)
222 name, ent = DESCRIPTER.assoc(name)
223 ent
224 end
225
226 def ConfigTable.get_entry!(name)
227 get_entry(name) or raise ArgumentError, "no such config: #{name}"
228 end
229
230 def ConfigTable.add_entry(name, vals)
231 ConfigTable::DESCRIPTER.push [name,vals]
232 end
233
234 def ConfigTable.remove_entry(name)
235 get_entry(name) or raise ArgumentError, "no such config: #{name}"
236 DESCRIPTER.delete_if {|n, arr| n == name }
237 end
238
239 def ConfigTable.config_key?(name)
240 get_entry(name) ? true : false
241 end
242
243 def ConfigTable.bool_config?(name)
244 ent = get_entry(name) or return false
245 ent[1] == 'yes/no'
246 end
247
248 def ConfigTable.value_config?(name)
249 ent = get_entry(name) or return false
250 ent[1] != 'yes/no'
251 end
252
253 def ConfigTable.path_config?(name)
254 ent = get_entry(name) or return false
255 ent[1] == 'path'
256 end
257
258
259 class << self
260 alias newobj new
261 end
262
263 def ConfigTable.new
264 c = newobj()
265 c.initialize_from_table
266 c
267 end
268
269 def ConfigTable.load
270 c = newobj()
271 c.initialize_from_file
272 c
273 end
274
275 def initialize_from_table
276 @table = {}
277 DESCRIPTER.each do |k, (default, vname, desc, default2)|
278 @table[k] = default
279 end
280 end
281
282 def initialize_from_file
283 raise InstallError, "#{File.basename $0} config first"\
284 unless File.file?(SAVE_FILE)
285 @table = {}
286 File.foreach(SAVE_FILE) do |line|
287 k, v = line.split(/=/, 2)
288 @table[k] = v.strip
289 end
290 end
291
292 def save
293 File.open(SAVE_FILE, 'w') {|f|
294 @table.each do |k, v|
295 f.printf "%s=%s\n", k, v if v
296 end
297 }
298 end
299
300 def []=(k, v)
301 raise InstallError, "unknown config option #{k}"\
302 unless ConfigTable.config_key?(k)
303 @table[k] = v
304 end
305
306 def [](key)
307 return nil unless @table[key]
308 @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
309 end
310
311 def set_raw(key, val)
312 @table[key] = val
313 end
314
315 def get_raw(key)
316 @table[key]
317 end
318
319 end
320
321
322 module MetaConfigAPI
323
324 def eval_file_ifexist(fname)
325 instance_eval File.read(fname), fname, 1 if File.file?(fname)
326 end
327
328 def config_names
329 ConfigTable.keys
330 end
331
332 def config?(name)
333 ConfigTable.config_key?(name)
334 end
335
336 def bool_config?(name)
337 ConfigTable.bool_config?(name)
338 end
339
340 def value_config?(name)
341 ConfigTable.value_config?(name)
342 end
343
344 def path_config?(name)
345 ConfigTable.path_config?(name)
346 end
347
348 def add_config(name, argname, default, desc)
349 ConfigTable.add_entry name,[default,argname,desc]
350 end
351
352 def add_path_config(name, default, desc)
353 add_config name, 'path', default, desc
354 end
355
356 def add_bool_config(name, default, desc)
357 add_config name, 'yes/no', default ? 'yes' : 'no', desc
358 end
359
360 def set_config_default(name, default)
361 if bool_config?(name)
362 ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
363 else
364 ConfigTable.get_entry!(name)[0] = default
365 end
366 end
367
368 def remove_config(name)
369 ent = ConfigTable.get_entry(name)
370 ConfigTable.remove_entry name
371 ent
372 end
373
374 end
375
376 #
377 # File Operations
378 #
379
380 module FileOperations
381
382 def mkdir_p(dirname, prefix = nil)
383 dirname = prefix + dirname if prefix
384 $stderr.puts "mkdir -p #{dirname}" if verbose?
385 return if no_harm?
386
387 # does not check '/'... it's too abnormal case
388 dirs = dirname.split(%r<(?=/)>)
389 if /\A[a-z]:\z/i =~ dirs[0]
390 disk = dirs.shift
391 dirs[0] = disk + dirs[0]
392 end
393 dirs.each_index do |idx|
394 path = dirs[0..idx].join('')
395 Dir.mkdir path unless File.dir?(path)
396 end
397 end
398
399 def rm_f(fname)
400 $stderr.puts "rm -f #{fname}" if verbose?
401 return if no_harm?
402
403 if File.exist?(fname) or File.symlink?(fname)
404 File.chmod 0777, fname
405 File.unlink fname
406 end
407 end
408
409 def rm_rf(dn)
410 $stderr.puts "rm -rf #{dn}" if verbose?
411 return if no_harm?
412
413 Dir.chdir dn
414 Dir.foreach('.') do |fn|
415 next if fn == '.'
416 next if fn == '..'
417 if File.dir?(fn)
418 verbose_off {
419 rm_rf fn
420 }
421 else
422 verbose_off {
423 rm_f fn
424 }
425 end
426 end
427 Dir.chdir '..'
428 Dir.rmdir dn
429 end
430
431 def move_file(src, dest)
432 File.unlink dest if File.exist?(dest)
433 begin
434 File.rename src, dest
435 rescue
436 File.open(dest, 'wb') {|f| f.write File.binread(src) }
437 File.chmod File.stat(src).mode, dest
438 File.unlink src
439 end
440 end
441
442 def install(from, dest, mode, prefix = nil)
443 $stderr.puts "install #{from} #{dest}" if verbose?
444 return if no_harm?
445
446 realdest = prefix + dest if prefix
447 realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
448 str = File.binread(from)
449 if diff?(str, realdest)
450 verbose_off {
451 rm_f realdest if File.exist?(realdest)
452 }
453 File.open(realdest, 'wb') {|f|
454 f.write str
455 }
456 File.chmod mode, realdest
457
458 File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
459 if prefix
460 f.puts realdest.sub(prefix, '')
461 else
462 f.puts realdest
463 end
464 }
465 end
466 end
467
468 def diff?(new_content, path)
469 return true unless File.exist?(path)
470 new_content != File.binread(path)
471 end
472
473 def command(str)
474 $stderr.puts str if verbose?
475 system str or raise RuntimeError, "'system #{str}' failed"
476 end
477
478 def ruby(str)
479 command config('ruby-prog') + ' ' + str
480 end
481
482 def make(task = '')
483 command config('make-prog') + ' ' + task
484 end
485
486 def extdir?(dir)
487 File.exist?(dir + '/MANIFEST')
488 end
489
490 def all_files_in(dirname)
491 Dir.open(dirname) {|d|
492 return d.select {|ent| File.file?("#{dirname}/#{ent}") }
493 }
494 end
495
496 REJECT_DIRS = %w(
497 CVS SCCS RCS CVS.adm .svn
498 )
499
500 def all_dirs_in(dirname)
501 Dir.open(dirname) {|d|
502 return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
503 }
504 end
505
506 end
507
508 #
509 # Main Installer
510 #
511
512 class InstallError < StandardError; end
513
514
515 module HookUtils
516
517 def run_hook(name)
518 try_run_hook "#{curr_srcdir()}/#{name}" or
519 try_run_hook "#{curr_srcdir()}/#{name}.rb"
520 end
521
522 def try_run_hook(fname)
523 return false unless File.file?(fname)
524 begin
525 instance_eval File.read(fname), fname, 1
526 rescue
527 raise InstallError, "hook #{fname} failed:\n" + $!.message
528 end
529 true
530 end
531
532 end
533
534
535 module HookScriptAPI
536
537 def get_config(key)
538 @config[key]
539 end
540
541 alias config get_config
542
543 def set_config(key, val)
544 @config[key] = val
545 end
546
547 #
548 # srcdir/objdir (works only in the package directory)
549 #
550
551 #abstract srcdir_root
552 #abstract objdir_root
553 #abstract relpath
554
555 def curr_srcdir
556 "#{srcdir_root()}/#{relpath()}"
557 end
558
559 def curr_objdir
560 "#{objdir_root()}/#{relpath()}"
561 end
562
563 def srcfile(path)
564 "#{curr_srcdir()}/#{path}"
565 end
566
567 def srcexist?(path)
568 File.exist?(srcfile(path))
569 end
570
571 def srcdirectory?(path)
572 File.dir?(srcfile(path))
573 end
574
575 def srcfile?(path)
576 File.file? srcfile(path)
577 end
578
579 def srcentries(path = '.')
580 Dir.open("#{curr_srcdir()}/#{path}") {|d|
581 return d.to_a - %w(. ..)
582 }
583 end
584
585 def srcfiles(path = '.')
586 srcentries(path).select {|fname|
587 File.file?(File.join(curr_srcdir(), path, fname))
588 }
589 end
590
591 def srcdirectories(path = '.')
592 srcentries(path).select {|fname|
593 File.dir?(File.join(curr_srcdir(), path, fname))
594 }
595 end
596
597 end
598
599
600 class ToplevelInstaller
601
602 Version = '3.2.4'
603 Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
604
605 TASKS = [
606 [ 'config', 'saves your configurations' ],
607 [ 'show', 'shows current configuration' ],
608 [ 'setup', 'compiles ruby extentions and others' ],
609 [ 'install', 'installs files' ],
610 [ 'clean', "does `make clean' for each extention" ],
611 [ 'distclean',"does `make distclean' for each extention" ]
612 ]
613
614 def ToplevelInstaller.invoke
615 instance().invoke
616 end
617
618 @singleton = nil
619
620 def ToplevelInstaller.instance
621 @singleton ||= new(File.dirname($0))
622 @singleton
623 end
624
625 include MetaConfigAPI
626
627 def initialize(ardir_root)
628 @config = nil
629 @options = { 'verbose' => true }
630 @ardir = File.expand_path(ardir_root)
631 end
632
633 def inspect
634 "#<#{self.class} #{__id__()}>"
635 end
636
637 def invoke
638 run_metaconfigs
639 task = parsearg_global()
640 @config = load_config(task)
641 __send__ "parsearg_#{task}"
642 init_installers
643 __send__ "exec_#{task}"
644 end
645
646 def run_metaconfigs
647 eval_file_ifexist "#{@ardir}/metaconfig"
648 end
649
650 def load_config(task)
651 case task
652 when 'config'
653 ConfigTable.new
654 when 'clean', 'distclean'
655 if File.exist?('config.save')
656 then ConfigTable.load
657 else ConfigTable.new
658 end
659 else
660 ConfigTable.load
661 end
662 end
663
664 def init_installers
665 @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
666 end
667
668 #
669 # Hook Script API bases
670 #
671
672 def srcdir_root
673 @ardir
674 end
675
676 def objdir_root
677 '.'
678 end
679
680 def relpath
681 '.'
682 end
683
684 #
685 # Option Parsing
686 #
687
688 def parsearg_global
689 valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
690
691 while arg = ARGV.shift
692 case arg
693 when /\A\w+\z/
694 raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg
695 return arg
696
697 when '-q', '--quiet'
698 @options['verbose'] = false
699
700 when '--verbose'
701 @options['verbose'] = true
702
703 when '-h', '--help'
704 print_usage $stdout
705 exit 0
706
707 when '-v', '--version'
708 puts "#{File.basename($0)} version #{Version}"
709 exit 0
710
711 when '--copyright'
712 puts Copyright
713 exit 0
714
715 else
716 raise InstallError, "unknown global option '#{arg}'"
717 end
718 end
719
720 raise InstallError, <<EOS
721 No task or global option given.
722 Typical installation procedure is:
723 $ ruby #{File.basename($0)} config
724 $ ruby #{File.basename($0)} setup
725 # ruby #{File.basename($0)} install (may require root privilege)
726 EOS
727 end
728
729
730 def parsearg_no_options
731 raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\
732 unless ARGV.empty?
733 end
734
735 alias parsearg_show parsearg_no_options
736 alias parsearg_setup parsearg_no_options
737 alias parsearg_clean parsearg_no_options
738 alias parsearg_distclean parsearg_no_options
739
740 def parsearg_config
741 re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
742 @options['config-opt'] = []
743
744 while i = ARGV.shift
745 if /\A--?\z/ =~ i
746 @options['config-opt'] = ARGV.dup
747 break
748 end
749 m = re.match(i) or raise InstallError, "config: unknown option #{i}"
750 name, value = m.to_a[1,2]
751 if value
752 if ConfigTable.bool_config?(name)
753 raise InstallError, "config: --#{name} allows only yes/no for argument"\
754 unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value
755 value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
756 end
757 else
758 raise InstallError, "config: --#{name} requires argument"\
759 unless ConfigTable.bool_config?(name)
760 value = 'yes'
761 end
762 @config[name] = value
763 end
764 end
765
766 def parsearg_install
767 @options['no-harm'] = false
768 @options['install-prefix'] = ''
769 while a = ARGV.shift
770 case a
771 when /\A--no-harm\z/
772 @options['no-harm'] = true
773 when /\A--prefix=(.*)\z/
774 path = $1
775 path = File.expand_path(path) unless path[0,1] == '/'
776 @options['install-prefix'] = path
777 else
778 raise InstallError, "install: unknown option #{a}"
779 end
780 end
781 end
782
783 def print_usage(out)
784 out.puts 'Typical Installation Procedure:'
785 out.puts " $ ruby #{File.basename $0} config"
786 out.puts " $ ruby #{File.basename $0} setup"
787 out.puts " # ruby #{File.basename $0} install (may require root privilege)"
788 out.puts
789 out.puts 'Detailed Usage:'
790 out.puts " ruby #{File.basename $0} <global option>"
791 out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
792
793 fmt = " %-20s %s\n"
794 out.puts
795 out.puts 'Global options:'
796 out.printf fmt, '-q,--quiet', 'suppress message outputs'
797 out.printf fmt, ' --verbose', 'output messages verbosely'
798 out.printf fmt, '-h,--help', 'print this message'
799 out.printf fmt, '-v,--version', 'print version and quit'
800 out.printf fmt, ' --copyright', 'print copyright and quit'
801
802 out.puts
803 out.puts 'Tasks:'
804 TASKS.each do |name, desc|
805 out.printf " %-10s %s\n", name, desc
806 end
807
808 out.puts
809 out.puts 'Options for config:'
810 ConfigTable.each_definition do |name, (default, arg, desc, default2)|
811 out.printf " %-20s %s [%s]\n",
812 '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),
813 desc,
814 default2 || default
815 end
816 out.printf " %-20s %s [%s]\n",
817 '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
818
819 out.puts
820 out.puts 'Options for install:'
821 out.printf " %-20s %s [%s]\n",
822 '--no-harm', 'only display what to do if given', 'off'
823 out.printf " %-20s %s [%s]\n",
824 '--prefix', 'install path prefix', '$prefix'
825
826 out.puts
827 end
828
829 #
830 # Task Handlers
831 #
832
833 def exec_config
834 @installer.exec_config
835 @config.save # must be final
836 end
837
838 def exec_setup
839 @installer.exec_setup
840 end
841
842 def exec_install
843 @installer.exec_install
844 end
845
846 def exec_show
847 ConfigTable.each_name do |k|
848 v = @config.get_raw(k)
849 if not v or v.empty?
850 v = '(not specified)'
851 end
852 printf "%-10s %s\n", k, v
853 end
854 end
855
856 def exec_clean
857 @installer.exec_clean
858 end
859
860 def exec_distclean
861 @installer.exec_distclean
862 end
863
864 end
865
866
867 class ToplevelInstallerMulti < ToplevelInstaller
868
869 include HookUtils
870 include HookScriptAPI
871 include FileOperations
872
873 def initialize(ardir)
874 super
875 @packages = all_dirs_in("#{@ardir}/packages")
876 raise 'no package exists' if @packages.empty?
877 end
878
879 def run_metaconfigs
880 eval_file_ifexist "#{@ardir}/metaconfig"
881 @packages.each do |name|
882 eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
883 end
884 end
885
886 def init_installers
887 @installers = {}
888 @packages.each do |pack|
889 @installers[pack] = Installer.new(@config, @options,
890 "#{@ardir}/packages/#{pack}",
891 "packages/#{pack}")
892 end
893
894 with = extract_selection(config('with'))
895 without = extract_selection(config('without'))
896 @selected = @installers.keys.select {|name|
897 (with.empty? or with.include?(name)) \
898 and not without.include?(name)
899 }
900 end
901
902 def extract_selection(list)
903 a = list.split(/,/)
904 a.each do |name|
905 raise InstallError, "no such package: #{name}" \
906 unless @installers.key?(name)
907 end
908 a
909 end
910
911 def print_usage(f)
912 super
913 f.puts 'Inluded packages:'
914 f.puts ' ' + @packages.sort.join(' ')
915 f.puts
916 end
917
918 #
919 # multi-package metaconfig API
920 #
921
922 attr_reader :packages
923
924 def declare_packages(list)
925 raise 'package list is empty' if list.empty?
926 list.each do |name|
927 raise "directory packages/#{name} does not exist"\
928 unless File.dir?("#{@ardir}/packages/#{name}")
929 end
930 @packages = list
931 end
932
933 #
934 # Task Handlers
935 #
936
937 def exec_config
938 run_hook 'pre-config'
939 each_selected_installers {|inst| inst.exec_config }
940 run_hook 'post-config'
941 @config.save # must be final
942 end
943
944 def exec_setup
945 run_hook 'pre-setup'
946 each_selected_installers {|inst| inst.exec_setup }
947 run_hook 'post-setup'
948 end
949
950 def exec_install
951 run_hook 'pre-install'
952 each_selected_installers {|inst| inst.exec_install }
953 run_hook 'post-install'
954 end
955
956 def exec_clean
957 rm_f 'config.save'
958 run_hook 'pre-clean'
959 each_selected_installers {|inst| inst.exec_clean }
960 run_hook 'post-clean'
961 end
962
963 def exec_distclean
964 rm_f 'config.save'
965 run_hook 'pre-distclean'
966 each_selected_installers {|inst| inst.exec_distclean }
967 run_hook 'post-distclean'
968 end
969
970 #
971 # lib
972 #
973
974 def each_selected_installers
975 Dir.mkdir 'packages' unless File.dir?('packages')
976 @selected.each do |pack|
977 $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
978 Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
979 Dir.chdir "packages/#{pack}"
980 yield @installers[pack]
981 Dir.chdir '../..'
982 end
983 end
984
985 def verbose?
986 @options['verbose']
987 end
988
989 def no_harm?
990 @options['no-harm']
991 end
992
993 end
994
995
996 class Installer
997
998 FILETYPES = %w( bin lib ext data )
999
1000 include HookScriptAPI
1001 include HookUtils
1002 include FileOperations
1003
1004 def initialize(config, opt, srcroot, objroot)
1005 @config = config
1006 @options = opt
1007 @srcdir = File.expand_path(srcroot)
1008 @objdir = File.expand_path(objroot)
1009 @currdir = '.'
1010 end
1011
1012 def inspect
1013 "#<#{self.class} #{File.basename(@srcdir)}>"
1014 end
1015
1016 #
1017 # Hook Script API bases
1018 #
1019
1020 def srcdir_root
1021 @srcdir
1022 end
1023
1024 def objdir_root
1025 @objdir
1026 end
1027
1028 def relpath
1029 @currdir
1030 end
1031
1032 #
1033 # configs/options
1034 #
1035
1036 def no_harm?
1037 @options['no-harm']
1038 end
1039
1040 def verbose?
1041 @options['verbose']
1042 end
1043
1044 def verbose_off
1045 begin
1046 save, @options['verbose'] = @options['verbose'], false
1047 yield
1048 ensure
1049 @options['verbose'] = save
1050 end
1051 end
1052
1053 #
1054 # TASK config
1055 #
1056
1057 def exec_config
1058 exec_task_traverse 'config'
1059 end
1060
1061 def config_dir_bin(rel)
1062 end
1063
1064 def config_dir_lib(rel)
1065 end
1066
1067 def config_dir_ext(rel)
1068 extconf if extdir?(curr_srcdir())
1069 end
1070
1071 def extconf
1072 opt = @options['config-opt'].join(' ')
1073 command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
1074 end
1075
1076 def config_dir_data(rel)
1077 end
1078
1079 #
1080 # TASK setup
1081 #
1082
1083 def exec_setup
1084 exec_task_traverse 'setup'
1085 end
1086
1087 def setup_dir_bin(rel)
1088 all_files_in(curr_srcdir()).each do |fname|
1089 adjust_shebang "#{curr_srcdir()}/#{fname}"
1090 end
1091 end
1092
1093 # modify: #!/usr/bin/ruby
1094 # modify: #! /usr/bin/ruby
1095 # modify: #!ruby
1096 # not modify: #!/usr/bin/env ruby
1097 SHEBANG_RE = /\A\#!\s*\S*ruby\S*/
1098
1099 def adjust_shebang(path)
1100 return if no_harm?
1101
1102 tmpfile = File.basename(path) + '.tmp'
1103 begin
1104 File.open(path, 'rb') {|r|
1105 File.open(tmpfile, 'wb') {|w|
1106 first = r.gets
1107 return unless SHEBANG_RE =~ first
1108
1109 $stderr.puts "adjusting shebang: #{File.basename path}" if verbose?
1110 w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))
1111 w.write r.read
1112 }
1113 }
1114 move_file tmpfile, File.basename(path)
1115 ensure
1116 File.unlink tmpfile if File.exist?(tmpfile)
1117 end
1118 end
1119
1120 def setup_dir_lib(rel)
1121 end
1122
1123 def setup_dir_ext(rel)
1124 make if extdir?(curr_srcdir())
1125 end
1126
1127 def setup_dir_data(rel)
1128 end
1129
1130 #
1131 # TASK install
1132 #
1133
1134 def exec_install
1135 exec_task_traverse 'install'
1136 end
1137
1138 def install_dir_bin(rel)
1139 list = collect_filenames_auto
1140 install_files list, "#{config('bin-dir')}/#{rel}", 0755
1141 install_cmd_files list, "#{config('bin-dir')}/#{rel}", 0755
1142 end
1143
1144 def install_dir_lib(rel)
1145 install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644
1146 end
1147
1148 def install_dir_ext(rel)
1149 return unless extdir?(curr_srcdir())
1150 install_files ruby_extentions('.'),
1151 "#{config('so-dir')}/#{File.dirname(rel)}",
1152 0555
1153 end
1154
1155 def install_dir_data(rel)
1156 install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644
1157 end
1158
1159 def install_cmd_files(list, dest, mode)
1160 if Config::CONFIG["arch"] =~ /dos|win32/i
1161 mkdir_p dest, @options['install-prefix']
1162 list.each do |fname|
1163 next if no_harm?
1164 File.open(File.join(dest, "#{fname}.cmd"), "w") do |file|
1165 file.puts "@ruby \"#{File.join(dest, fname)}\" %*"
1166 end
1167 File.chmod mode, File.join(dest, "#{fname}.cmd")
1168 end
1169 end
1170 end
1171
1172 def install_files(list, dest, mode)
1173 mkdir_p dest, @options['install-prefix']
1174 list.each do |fname|
1175 install fname, dest, mode, @options['install-prefix']
1176 end
1177 end
1178
1179 def ruby_scripts
1180 collect_filenames_auto().select {|n| /\.(r(b|html)|txt)\z/ =~ n}
1181 end
1182
1183 # picked up many entries from cvs-1.11.1/src/ignore.c
1184 reject_patterns = %w(
1185 core RCSLOG tags TAGS .make.state
1186 .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1187 *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1188
1189 *.org *.in .*
1190 )
1191 mapping = {
1192 '.' => '\.',
1193 '$' => '\$',
1194 '#' => '\#',
1195 '*' => '.*'
1196 }
1197 REJECT_PATTERNS = Regexp.new('\A(?:' +
1198 reject_patterns.map {|pat|
1199 pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1200 }.join('|') +
1201 ')\z')
1202
1203 def collect_filenames_auto
1204 mapdir((existfiles() - hookfiles()).reject {|fname|
1205 REJECT_PATTERNS =~ fname
1206 })
1207 end
1208
1209 def existfiles
1210 all_files_in(curr_srcdir()) | all_files_in('.')
1211 end
1212
1213 def hookfiles
1214 %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1215 %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1216 }.flatten
1217 end
1218
1219 def mapdir(filelist)
1220 filelist.map {|fname|
1221 if File.exist?(fname) # objdir
1222 fname
1223 else # srcdir
1224 File.join(curr_srcdir(), fname)
1225 end
1226 }
1227 end
1228
1229 def ruby_extentions(dir)
1230 _ruby_extentions(dir) or
1231 raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
1232 end
1233
1234 DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/
1235
1236 def _ruby_extentions(dir)
1237 Dir.open(dir) {|d|
1238 return d.select {|fname| DLEXT =~ fname }
1239 }
1240 end
1241
1242 #
1243 # TASK clean
1244 #
1245
1246 def exec_clean
1247 exec_task_traverse 'clean'
1248 rm_f 'config.save'
1249 rm_f 'InstalledFiles'
1250 end
1251
1252 def clean_dir_bin(rel)
1253 end
1254
1255 def clean_dir_lib(rel)
1256 end
1257
1258 def clean_dir_ext(rel)
1259 return unless extdir?(curr_srcdir())
1260 make 'clean' if File.file?('Makefile')
1261 end
1262
1263 def clean_dir_data(rel)
1264 end
1265
1266 #
1267 # TASK distclean
1268 #
1269
1270 def exec_distclean
1271 exec_task_traverse 'distclean'
1272 rm_f 'config.save'
1273 rm_f 'InstalledFiles'
1274 end
1275
1276 def distclean_dir_bin(rel)
1277 end
1278
1279 def distclean_dir_lib(rel)
1280 end
1281
1282 def distclean_dir_ext(rel)
1283 return unless extdir?(curr_srcdir())
1284 make 'distclean' if File.file?('Makefile')
1285 end
1286
1287 #
1288 # lib
1289 #
1290
1291 def exec_task_traverse(task)
1292 run_hook "pre-#{task}"
1293 FILETYPES.each do |type|
1294 if config('without-ext') == 'yes' and type == 'ext'
1295 $stderr.puts 'skipping ext/* by user option' if verbose?
1296 next
1297 end
1298 traverse task, type, "#{task}_dir_#{type}"
1299 end
1300 run_hook "post-#{task}"
1301 end
1302
1303 def traverse(task, rel, mid)
1304 dive_into(rel) {
1305 run_hook "pre-#{task}"
1306 __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1307 all_dirs_in(curr_srcdir()).each do |d|
1308 traverse task, "#{rel}/#{d}", mid
1309 end
1310 run_hook "post-#{task}"
1311 }
1312 end
1313
1314 def dive_into(rel)
1315 return unless File.dir?("#{@srcdir}/#{rel}")
1316
1317 dir = File.basename(rel)
1318 Dir.mkdir dir unless File.dir?(dir)
1319 prevdir = Dir.pwd
1320 Dir.chdir dir
1321 $stderr.puts '---> ' + rel if verbose?
1322 @currdir = rel
1323 yield
1324 Dir.chdir prevdir
1325 $stderr.puts '<--- ' + rel if verbose?
1326 @currdir = File.dirname(rel)
1327 end
1328
1329 end
1330
1331
1332 if $0 == __FILE__
1333 begin
1334 if multipackage_install?
1335 ToplevelInstallerMulti.invoke
1336 else
1337 ToplevelInstaller.invoke
1338 end
1339 rescue
1340 raise if $DEBUG
1341 $stderr.puts $!.message
1342 $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1343 exit 1
1344 end
1345 end
1414 def setup
1515 @cli = MockCLI.new
1616 @logger = stub_everything
17 @config = stub(:logger => @logger, :debug= => nil, :dry_run= => nil)
17 @config = stub(:logger => @logger, :debug= => nil, :dry_run= => nil, :preserve_roles= => nil)
1818 @config.stubs(:set)
1919 @config.stubs(:load)
2020 @config.stubs(:trigger)
128128 MockCLI.expects(:parse).with(ARGV).returns(cli)
129129 MockCLI.execute
130130 end
131 end
131 end
9292 assert_equal 0, @cli.options[:verbose]
9393 end
9494
95 def test_parse_options_with_r_should_set_preserve_roles_option
96 @cli.args << "-r"
97 @cli.parse_options!
98 assert @cli.options[:preserve_roles]
99 end
100
101 def test_parse_options_with_preserve_roles_should_set_preserve_roles_option
102 @cli.args << "--preserve-roles"
103 @cli.parse_options!
104 assert @cli.options[:preserve_roles]
105 end
106
95107 def test_parse_options_with_S_should_set_pre_vars
96108 @cli.args << "-S" << "foo=bar"
97109 @cli.parse_options!
185197
186198 def test_parse_options_with_V_should_show_version_and_exit
187199 @cli.args << "-V"
188 @cli.expects(:puts).with { |s| s.include?(Capistrano::Version::STRING) }
200 @cli.expects(:puts).with { |s| s.include?(Capistrano::Version.to_s) }
189201 @cli.expects(:exit).raises(ExitException)
190202 assert_raises(ExitException) { @cli.parse_options! }
191203 end
22
33 class CLI_Test < Test::Unit::TestCase
44 def test_options_ui_and_help_modules_should_integrate_successfully_with_configuration
5 cli = Capistrano::CLI.parse(%w(-T))
5 cli = Capistrano::CLI.parse(%w(-T -x -X))
66 cli.expects(:puts).at_least_once
77 cli.execute!
88 end
99
1010 def test_options_and_execute_modules_should_integrate_successfully_with_configuration
1111 path = "#{File.dirname(__FILE__)}/fixtures/cli_integration.rb"
12 cli = Capistrano::CLI.parse(%W(-q -f #{path} testing))
12 cli = Capistrano::CLI.parse(%W(-x -X -q -f #{path} testing))
1313 config = cli.execute!
1414 assert config[:testing_occurred]
1515 end
204204 new_channel = Proc.new do |times|
205205 ch = mock("channel")
206206 returns = [false] * (times-1)
207 ch.stubs(:to_ary)
207208 ch.stubs(:[]).with(:closed).returns(*(returns + [true]))
208209 ch.expects(:[]).with(:status).returns(0)
209210 ch
213214 mock_session(new_channel[10]),
214215 mock_session(new_channel[7])]
215216 cmd = Capistrano::Command.new("ls", sessions)
216 assert_nothing_raised { cmd.process! }
217 assert_nothing_raised do
218 cmd.process!
219 end
217220 end
218221
219222 def test_process_should_instantiate_command_and_process!
239242 private
240243
241244 def mock_session(channel=nil)
242 stub('session', :open_channel => channel,
243 :preprocess => true,
244 :postprocess => true,
245 :listeners => {},
246 :xserver => server("capistrano"))
245 stub('session',
246 :open_channel => channel,
247 :preprocess => true,
248 :postprocess => true,
249 :listeners => {},
250 :xserver => server("capistrano"))
247251 end
248252
249253 class MockChannel < Hash
33 class ConfigurationActionsFileTransferTest < Test::Unit::TestCase
44 class MockConfig
55 include Capistrano::Configuration::Actions::FileTransfer
6 attr_accessor :sessions
6 attr_accessor :sessions, :dry_run
77 end
88
99 def setup
3636
3737 def test_upload_with_mode_should_try_to_chmod
3838 @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", :foo => "bar")
39 @config.expects(:run).with("chmod 775 testr.txt")
39 @config.expects(:run).with("chmod 775 testr.txt", {:foo => "bar"})
4040 @config.upload("testl.txt", "testr.txt", :mode => 0775, :foo => "bar")
4141 end
4242
4343 def test_upload_with_symbolic_mode_should_try_to_chmod
4444 @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", :foo => "bar")
45 @config.expects(:run).with("chmod g+w testr.txt")
45 @config.expects(:run).with("chmod g+w testr.txt", {:foo => "bar"})
4646 @config.upload("testl.txt", "testr.txt", :mode => "g+w", :foo => "bar")
4747 end
4848
5757 Capistrano::Transfer.expects(:process).with(:up, "testl.txt", "testr.txt", [1,2,3], {:foo => "bar", :logger => @config.logger})
5858 @config.transfer(:up, "testl.txt", "testr.txt", :foo => "bar")
5959 end
60 end
60 end
00 require "utils"
11 require 'capistrano/configuration/actions/invocation'
2 require 'capistrano/configuration/actions/file_transfer'
23
34 class ConfigurationActionsInvocationTest < Test::Unit::TestCase
45 class MockConfig
56 attr_reader :options
67 attr_accessor :debug
78 attr_accessor :dry_run
9 attr_accessor :preserve_roles
10 attr_accessor :servers
811
912 def initialize
1013 @options = {}
14 @servers = []
1115 end
1216
1317 def [](*args)
2226 @options.fetch(*args)
2327 end
2428
29 def execute_on_servers(options = {})
30 yield @servers
31 end
32
2533 include Capistrano::Configuration::Actions::Invocation
34 include Capistrano::Configuration::Actions::FileTransfer
2635 end
2736
2837 def setup
29 @config = MockConfig.new
38 @config = make_config
3039 @original_io_proc = MockConfig.default_io_proc
31 @config.stubs(:logger).returns(stub_everything)
3240 end
3341
3442 def teardown
4452 @config.expects(:dry_run).returns(true)
4553 @config.expects(:execute_on_servers).never
4654 @config.run "ls", :foo => "bar"
55 end
56
57 def test_put_wont_transfer_if_dry_run
58 config = make_config
59 config.dry_run = true
60 config.servers = %w[ foo ]
61 config.expects(:sessions).returns({ 'foo-server' => 'bar' })
62 ::Capistrano::Transfer.expects(:process).never
63 config.put "foo", "bar", :mode => 0644
4764 end
4865
4966 def test_add_default_command_options_should_return_bare_options_if_there_is_no_env_or_shell_specified
201218
202219 private
203220
221 def make_config
222 config = MockConfig.new
223 config.stubs(:logger).returns(stub_everything)
224 config
225 end
226
204227 def inspectable_proc
205228 Proc.new do |ch, stream, data|
206229 ch.called
7070 Net::SSH::Gateway.expects(:new).with("gateway", "user", :debug => :verbose, :port => 8080, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
7171 assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
7272 end
73
73
7474 def test_connection_factory_as_gateway_should_chain_gateways_if_gateway_variable_is_an_array
7575 @config.values[:gateway] = ["j@gateway1", "k@gateway2"]
7676 gateway1 = mock
7979 Net::SSH::Gateway.expects(:new).with("127.0.0.1", "k", :port => 65535, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
8080 assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
8181 end
82
82
83 def test_connection_factory_as_gateway_should_chain_gateways_if_gateway_variable_is_a_hash
84 @config.values[:gateway] = { ["j@gateway1", "k@gateway2"] => :default }
85 gateway1 = mock
86 Net::SSH::Gateway.expects(:new).with("gateway1", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(gateway1)
87 gateway1.expects(:open).returns(65535)
88 Net::SSH::Gateway.expects(:new).with("127.0.0.1", "k", :port => 65535, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
89 assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
90 end
91
8392 def test_connection_factory_as_gateway_should_share_gateway_between_connections
8493 @config.values[:gateway] = "j@gateway"
8594 Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
8998 @config.establish_connections_to(server("another"))
9099 end
91100
101 def test_connection_factory_as_gateway_should_share_gateway_between_like_connections_if_gateway_variable_is_a_hash
102 @config.values[:gateway] = { "j@gateway" => [ "capistrano", "another"] }
103 Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
104 Capistrano::SSH.stubs(:connect).returns(stub_everything)
105 assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
106 @config.establish_connections_to(server("capistrano"))
107 @config.establish_connections_to(server("another"))
108 end
109
110 def test_connection_factory_as_gateways_should_not_share_gateway_between_unlike_connections_if_gateway_variable_is_a_hash
111 @config.values[:gateway] = { "j@gateway" => [ "capistrano", "another"], "k@gateway2" => "yafhost" }
112 Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
113 Net::SSH::Gateway.expects(:new).once.with("gateway2", "k", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
114 Capistrano::SSH.stubs(:connect).returns(stub_everything)
115 assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
116 @config.establish_connections_to(server("capistrano"))
117 @config.establish_connections_to(server("another"))
118 @config.establish_connections_to(server("yafhost"))
119 end
120
92121 def test_establish_connections_to_should_accept_a_single_nonarray_parameter
93122 Capistrano::SSH.expects(:connect).with { |s,| s.host == "capistrano" }.returns(:success)
94123 assert @config.sessions.empty?
95124 @config.establish_connections_to(server("capistrano"))
96 assert ["capistrano"], @config.sessions.keys
125 assert_equal ["capistrano"], @config.sessions.keys.map(&:host)
97126 end
98127
99128 def test_establish_connections_to_should_accept_an_array
100129 Capistrano::SSH.expects(:connect).times(3).returns(:success)
101130 assert @config.sessions.empty?
102131 @config.establish_connections_to(%w(cap1 cap2 cap3).map { |s| server(s) })
103 assert %w(cap1 cap2 cap3), @config.sessions.keys.sort
132 assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map(&:host)
104133 end
105134
106135 def test_establish_connections_to_should_not_attempt_to_reestablish_existing_connections
107136 Capistrano::SSH.expects(:connect).times(2).returns(:success)
108137 @config.sessions[server("cap1")] = :ok
109138 @config.establish_connections_to(%w(cap1 cap2 cap3).map { |s| server(s) })
110 assert %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
111 end
112
139 assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map(&:host)
140 end
141
113142 def test_establish_connections_to_should_raise_one_connection_error_on_failure
114143 Capistrano::SSH.expects(:connect).times(2).raises(Exception)
115144 assert_raises(Capistrano::ConnectionError) {
119148
120149 def test_connection_error_should_include_accessor_with_host_array
121150 Capistrano::SSH.expects(:connect).times(2).raises(Exception)
122
123151 begin
124152 @config.establish_connections_to(%w(cap1 cap2).map { |s| server(s) })
125153 flunk "expected an exception to be raised"
128156 assert_equal %w(cap1 cap2), e.hosts.map { |h| h.to_s }.sort
129157 end
130158 end
131
159
132160 def test_connection_error_should_only_include_failed_hosts
133161 Capistrano::SSH.expects(:connect).with(server('cap1'), anything).raises(Exception)
134162 Capistrano::SSH.expects(:connect).with(server('cap2'), anything).returns(:success)
159187 assert_raises(Capistrano::NoMatchingServersError) { @config.execute_on_servers(:a => :b, :c => :d) { |list| } }
160188 end
161189
190 def test_execute_on_servers_without_current_task_should_not_raise_error_if_no_matching_servers_and_continue_on_no_matching_servers
191 @config.expects(:find_servers).with(:a => :b, :c => :d, :on_no_matching_servers => :continue).returns([])
192 assert_nothing_raised { @config.execute_on_servers(:a => :b, :c => :d, :on_no_matching_servers => :continue) { |list| } }
193 end
194
162195 def test_execute_on_servers_should_raise_an_error_if_the_current_task_has_no_matching_servers_by_default
163196 @config.current_task = mock_task
164197 @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([])
168201 end
169202 end
170203 end
171
204
205 def test_execute_on_servers_should_not_raise_an_error_if_the_current_task_has_no_matching_servers_by_default_and_continue_on_no_matching_servers
206 @config.current_task = mock_task(:on_no_matching_servers => :continue)
207 @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([])
208 assert_nothing_raised do
209 @config.execute_on_servers do
210 flunk "should not get here"
211 end
212 end
213 end
214
215 def test_execute_on_servers_should_not_raise_an_error_if_the_current_task_has_no_matching_servers_by_default_and_command_continues_on_no_matching_servers
216 @config.current_task = mock_task
217 @config.expects(:find_servers_for_task).with(@config.current_task, :on_no_matching_servers => :continue).returns([])
218 assert_nothing_raised do
219 @config.execute_on_servers(:on_no_matching_servers => :continue) do
220 flunk "should not get here"
221 end
222 end
223 end
224
172225 def test_execute_on_servers_should_determine_server_list_from_active_task
173226 assert @config.sessions.empty?
174227 @config.current_task = mock_task
207260 assert block_called
208261 assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
209262 end
210
263
211264 def test_execute_servers_should_raise_connection_error_on_failure_by_default
212265 @config.current_task = mock_task
213266 @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1")])
218271 end
219272 end
220273 end
221
274
222275 def test_execute_servers_should_not_raise_connection_error_on_failure_with_on_errors_continue
223276 @config.current_task = mock_task(:on_error => :continue)
224277 @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2")])
230283 end
231284 }
232285 end
233
286
234287 def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_connection_errors_with_on_errors_continue
235288 list = [server("cap1"), server("cap2")]
236289 @config.current_task = mock_task(:on_error => :continue)
245298 assert_equal %w(cap2), servers.map { |s| s.host }
246299 end
247300 end
248
301
249302 def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_command_errors_with_on_errors_continue
250303 cap1 = server("cap1")
251304 cap2 = server("cap2")
262315 assert_equal %w(cap2), servers.map { |s| s.host }
263316 end
264317 end
265
318
266319 def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_transfer_errors_with_on_errors_continue
267320 cap1 = server("cap1")
268321 cap2 = server("cap2")
279332 assert_equal %w(cap2), servers.map { |s| s.host }
280333 end
281334 end
282
335
283336 def test_connect_should_establish_connections_to_all_servers_in_scope
284337 assert @config.sessions.empty?
285338 @config.current_task = mock_task
288341 @config.connect!
289342 assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
290343 end
291
344
292345 def test_execute_on_servers_should_only_run_on_tasks_max_hosts_hosts_at_once
293346 cap1 = server("cap1")
294347 cap2 = server("cap2")
306359 end
307360 assert_equal 2, block_called
308361 end
309
362
310363 def test_execute_on_servers_should_only_run_on_max_hosts_hosts_at_once
311364 cap1 = server("cap1")
312365 cap2 = server("cap2")
324377 end
325378 assert_equal 2, block_called
326379 end
327
380
381 def test_execute_on_servers_should_cope_with_already_dropped_connections_when_attempting_to_close_them
382 cap1 = server("cap1")
383 cap2 = server("cap2")
384 connection1 = mock()
385 connection2 = mock()
386 connection3 = mock()
387 connection4 = mock()
388 connection1.expects(:close).raises(IOError)
389 connection2.expects(:close)
390 connection3.expects(:close)
391 connection4.expects(:close)
392 @config.current_task = mock_task(:max_hosts => 1)
393 @config.expects(:find_servers_for_task).times(2).with(@config.current_task, {}).returns([cap1, cap2])
394 Capistrano::SSH.expects(:connect).times(4).returns(connection1).then.returns(connection2).then.returns(connection3).then.returns(connection4)
395 @config.execute_on_servers {}
396 @config.execute_on_servers {}
397 end
398
328399 def test_connect_should_honor_once_option
329400 assert @config.sessions.empty?
330401 @config.current_task = mock_task
0 require "utils"
0 require 'utils'
11 require 'capistrano/configuration/loading'
22
33 class ConfigurationLoadingTest < Test::Unit::TestCase
105105
106106 def test_require_from_config_should_load_file_in_config_scope
107107 assert_nothing_raised do
108 @config.require "#{File.dirname(__FILE__)}/../fixtures/custom"
108 @config.require "#{File.expand_path(File.dirname(__FILE__))}/../fixtures/custom"
109109 end
110110 assert_equal :custom, @config.ping
111111 end
117117 end
118118
119119 def test_require_from_config_should_return_false_when_called_a_second_time_with_same_args
120 assert @config.require("#{File.dirname(__FILE__)}/../fixtures/custom")
121 assert_equal false, @config.require("#{File.dirname(__FILE__)}/../fixtures/custom")
120 assert @config.require("#{File.expand_path(File.dirname(__FILE__))}/../fixtures/custom")
121 assert_equal false, @config.require("#{File.expand_path(File.dirname(__FILE__))}/../fixtures/custom")
122122 end
123
123
124124 def test_require_in_multiple_instances_should_load_recipes_in_each_instance
125125 config2 = MockConfig.new
126 @config.require "#{File.dirname(__FILE__)}/../fixtures/custom"
127 config2.require "#{File.dirname(__FILE__)}/../fixtures/custom"
126 @config.require "#{File.expand_path(File.dirname(__FILE__))}/../fixtures/custom"
127 config2.require "#{File.expand_path(File.dirname(__FILE__))}/../fixtures/custom"
128128 assert_equal :custom, @config.ping
129129 assert_equal :custom, config2.ping
130130 end
44 class ConfigurationServersTest < Test::Unit::TestCase
55 class MockConfig
66 attr_reader :roles
7 attr_accessor :preserve_roles
78
89 def initialize
910 @roles = {}
11 @preserve_roles = false
1012 end
1113
1214 include Capistrano::Configuration::Servers
3638 assert_equal %w(web1 web2).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort
3739 end
3840
39 def test_task_with_unknown_role_should_raise_exception
41 # NOTE Rather than throw an error, as it used to, we return an
42 # empty array so that if a task is okay with a missing role it can continue on
43 def test_task_with_unknown_role_should_return_empty_array
4044 task = new_task(:testing, @config, :roles => :bogus)
41 assert_raises(ArgumentError) do
42 @config.find_servers_for_task(task)
43 end
45 assert_equal [], @config.find_servers_for_task(task)
4446 end
4547
4648 def test_task_with_hosts_option_should_apply_only_to_those_hosts
6163 ENV.delete('ROLES')
6264 end
6365
66 def test_task_with_roles_as_environment_variable_and_preserve_roles_should_apply_only_to_existant_task_role
67 ENV['ROLES'] = "app,file"
68 @config.preserve_roles = true
69 task = new_task(:testing,@config, :roles => :app)
70 assert_equal %w(app1 app2 app3).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort
71 ensure
72 ENV.delete('ROLES')
73 end
74
75 def test_task_with_roles_as_environment_variable_and_preserve_roles_should_apply_only_to_existant_task_roles
76 ENV['ROLES'] = "app,file,web"
77 @config.preserve_roles = true
78 task = new_task(:testing,@config, :roles => [ :app,:file ])
79 assert_equal %w(app1 app2 app3 file).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort
80 ensure
81 ENV.delete('ROLES')
82 end
83
84 def test_task_with_roles_as_environment_variable_and_preserve_roles_should_not_apply_if_not_exists_those_task_roles
85 ENV['ROLES'] = "file,web"
86 @config.preserve_roles = true
87 task = new_task(:testing,@config, :roles => [ :app ])
88 assert_equal [], @config.find_servers_for_task(task).map { |s| s.host }.sort
89 ensure
90 ENV.delete('ROLES')
91 end
92
6493 def test_task_with_hosts_as_environment_variable_should_apply_only_to_those_hosts
6594 ENV['HOSTS'] = "foo,bar"
6695 task = new_task(:testing)
101130 ENV.delete('HOSTFILTER')
102131 end
103132
133 def test_task_with_hostrolefilter_environment_variable_should_apply_only_to_those_hosts
134 ENV['HOSTROLEFILTER'] = "web"
135 task = new_task(:testing)
136 assert_equal %w(web1 web2).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort
137 ensure
138 ENV.delete('HOSTROLEFILTER')
139 end
140
104141 def test_task_with_only_should_apply_only_to_matching_tasks
105142 task = new_task(:testing, @config, :roles => :app, :only => { :primary => true })
106143 assert_equal %w(app1), @config.find_servers_for_task(task).map { |s| s.host }
125162 assert_equal %w(app1 app2 app3), @config.find_servers(:roles => lambda { :app }).map { |s| s.host }.sort
126163 assert_equal %w(app2 file), @config.find_servers(:roles => lambda { [:report, :file] }).map { |s| s.host }.sort
127164 end
128 end
165
166 def test_find_servers_with_hosts_nil_or_empty
167 assert_equal [], @config.find_servers(:hosts => nil)
168 assert_equal [], @config.find_servers(:hosts => [])
169 result = @config.find_servers(:hosts => @config.find_servers(:roles => :report)[0])
170 assert_equal 1, result.size
171 result = @config.find_servers(:hosts => "app1")
172 assert_equal 1, result.size
173 end
174
175 def test_find_servers_with_rolees_nil_or_empty
176 assert_equal [], @config.find_servers(:roles => nil)
177 assert_equal [], @config.find_servers(:roles => [])
178 result = @config.find_servers(:roles => :report)
179 assert_equal 1, result.size
180 end
181
182 end
5151 assert !@config.respond_to?(:sample)
5252 @config[:sample] = :value
5353 assert @config.respond_to?(:sample)
54 end
55
56 def test_respond_to_should_be_true_when_passed_a_string
57 assert !@config.respond_to?('sample')
58 @config[:sample] = :value
59 assert @config.respond_to?('sample')
5460 end
5561
5662 def test_respond_to_with_include_priv_paramter
3636 assert_equal "gem `capistrano' 9.9 could not be found (host)", @dependency.message
3737 end
3838
39 def test_should_use_standard_error_message_for_deb
40 setup_for_a_configuration_deb_run("dpkg", "1.15", false)
41 @dependency.deb("dpkg", "1.15")
42 assert_equal "package `dpkg' 1.15 could not be found (host)", @dependency.message
43 end
44
3945 def test_should_fail_if_directory_not_found
4046 setup_for_a_configuration_run("test -d /data", false)
4147 assert !@dependency.directory("/data").pass?
5157 assert !@dependency.file("/data/foo.txt").pass?
5258 end
5359
54 def test_should_pas_if_file_found
60 def test_should_pass_if_file_found
5561 setup_for_a_configuration_run("test -f /data/foo.txt", true)
5662 assert @dependency.file("/data/foo.txt").pass?
5763 end
8692 assert @dependency.gem("capistrano", 9.9).pass?
8793 end
8894
95 def test_should_pass_if_deb_found
96 setup_for_a_configuration_deb_run("dpkg", "1.15", true)
97 assert @dependency.deb("dpkg", "1.15").pass?
98 end
99
100 def test_should_fail_if_deb_not_found
101 setup_for_a_configuration_deb_run("dpkg", "1.15", false)
102 assert !@dependency.deb("dpkg", "1.15").pass?
103 end
104
89105 def test_should_use_alternative_message_if_provided
90106 setup_for_a_configuration_run("which cat", false)
91107 @dependency.command("cat").or("Sorry")
110126 find_gem_cmd = "gem specification --version '#{version}' #{name} 2>&1 | awk 'BEGIN { s = 0 } /^name:/ { s = 1; exit }; END { if(s == 0) exit 1 }'"
111127 setup_for_a_configuration_run(find_gem_cmd, passing)
112128 end
129
130 def setup_for_a_configuration_deb_run(name, version, passing)
131 find_deb_cmd = "dpkg -s #{name} | grep '^Version: #{version}'"
132 setup_for_a_configuration_run(find_deb_cmd, passing)
133 end
113134 end
0 require "utils"
1 require 'capistrano/recipes/deploy/scm/bzr'
2
3 class DeploySCMBzrTest < Test::Unit::TestCase
4 class TestSCM < Capistrano::Deploy::SCM::Bzr
5 default_command "bzr"
6 end
7
8 def setup
9 @config = { :repository => "." }
10
11 def @config.exists?(name); key?(name); end # is this actually needed?
12
13 @source = TestSCM.new(@config)
14 end
15
16 # The bzr scm does not support pseudo-ids. The bzr adapter uses symbol :head
17 # to refer to the recently committed revision.
18 def test_head_revision
19 assert_equal(:head,
20 @source.head,
21 "Since bzr doesn't know a real head revision, symbol :head is used instead.")
22 end
23
24 # The bzr scm does support many different ways to specify a revision. Only
25 # symbol :head triggers the bzr command 'revno'.
26 def test_query_revision
27 assert_equal("bzr revno #{@config[:repository]}",
28 @source.query_revision(:head) { |o| o },
29 "Query for :head revision should call bzr command 'revno' in repository directory.")
30
31 # Many valid revision specifications, some invalid on the last line
32 revision_samples = [ 5, -7, '2', '-4',
33 'revid:revid:aaaa@bbbb-123456789',
34 'submit:',
35 'ancestor:/path/to/branch',
36 'date:yesterday',
37 'branch:/path/to/branch',
38 'tag:trunk',
39 'revno:3:/path/to/branch',
40 'before:revid:aaaa@bbbb-1234567890',
41 'last:3',
42 nil, {}, [], true, false, 1.34, ]
43
44 revision_samples.each do |revivsion_spec|
45 assert_equal(revivsion_spec,
46 @source.query_revision(revivsion_spec),
47 "Any revision specification other than symbol :head should simply by returned.")
48 end
49 end
50 end
0 require "utils"
1 require 'capistrano/recipes/deploy/scm/darcs'
2
3 class DeploySCMDarcsTest < Test::Unit::TestCase
4 class TestSCM < Capistrano::Deploy::SCM::Darcs
5 default_command "darcs"
6 end
7 def setup
8 @config = { :repository => "." }
9 # def @config.exists?(name); key?(name); end
10
11 @source = TestSCM.new(@config)
12 end
13
14 # We should be able to pick a specific hash.
15 def test_checkout_hash
16 hsh = "*version_hash*"
17 assert_match(%r{--to-match=.hash #{Regexp.quote(hsh)}},
18 @source.checkout(hsh, "*foo_location*"),
19 "Specifying a revision hash got the --to-match option wrong.")
20 end
21
22 # Picking the head revision should leave out the hash, because head is the
23 # default and we don't have a HEAD pseudotag
24 def test_checkout_head
25 hsh = @source.head
26 assert_no_match(%r{--to-match}, @source.checkout(hsh, "*foo_location*"),
27 "Selecting the head revision incorrectly produced a --to-match option.")
28 end
29
30 # Leaving the revision as nil shouldn't break anything.
31 def test_checkout_nil
32 assert_no_match(%r{--to-match}, @source.checkout(nil, "*foo_location*"),
33 "Leaving the revision as nil incorrectly produced a --to-match option.")
34 end
35 end
36
1414
1515 def test_head
1616 assert_equal "HEAD", @source.head
17
17
1818 # With :branch
1919 @config[:branch] = "master"
2020 assert_equal "master", @source.head
3535 # With :scm_command
3636 git = "/opt/local/bin/git"
3737 @config[:scm_command] = git
38 assert_equal "#{git} clone -q git@somehost.com:project.git /var/www && cd /var/www && #{git} checkout -q -b deploy #{rev}", @source.checkout(rev, dest)
38 assert_equal "#{git} clone -q git@somehost.com:project.git /var/www && cd /var/www && #{git} checkout -q -b deploy #{rev}", @source.checkout(rev, dest).gsub(/\s+/, ' ')
3939
4040 # with submodules
4141 @config[:git_enable_submodules] = true
42 assert_equal "#{git} clone -q git@somehost.com:project.git /var/www && cd /var/www && #{git} checkout -q -b deploy #{rev} && #{git} submodule -q init && #{git} submodule -q sync && #{git} submodule -q update", @source.checkout(rev, dest)
42 assert_equal "#{git} clone -q git@somehost.com:project.git /var/www && cd /var/www && #{git} checkout -q -b deploy #{rev} && #{git} submodule -q init && #{git} submodule -q sync && export GIT_RECURSIVE=$([ ! \"`#{git} --version`\" \\< \"git version 1.6.5\" ] && echo --recursive) && #{git} submodule -q update --init $GIT_RECURSIVE", @source.checkout(rev, dest).gsub(/\s+/, ' ')
43 end
44
45 def test_checkout_submodules_without_recursive
46 @config[:repository] = "git@somehost.com:project.git"
47 dest = "/var/www"
48 rev = 'c2d9e79'
49 @config[:git_enable_submodules] = true
50 @config[:git_submodules_recursive] = false
51 assert_equal "git clone -q git@somehost.com:project.git /var/www && cd /var/www && git checkout -q -b deploy #{rev} && git submodule -q init && git submodule -q sync && git submodule -q update --init", @source.checkout(rev, dest).gsub(/\s+/, ' ')
4352 end
4453
4554 def test_checkout_with_verbose_should_not_use_q_switch
4756 @config[:scm_verbose] = true
4857 dest = "/var/www"
4958 rev = 'c2d9e79'
50 assert_equal "git clone git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy #{rev}", @source.checkout(rev, dest)
59 assert_equal "git clone git@somehost.com:project.git /var/www && cd /var/www && git checkout -b deploy #{rev}", @source.checkout(rev, dest)
60 end
61
62 def test_checkout_with_verbose_off_should_use_q_switch
63 @config[:repository] = "git@somehost.com:project.git"
64 @config[:scm_verbose] = false
65 dest = "/var/www"
66 rev = 'c2d9e79'
67 assert_equal "git clone -q git@somehost.com:project.git /var/www && cd /var/www && git checkout -q -b deploy #{rev}", @source.checkout(rev, dest)
5168 end
5269
5370 def test_diff
6077 assert_equal "git log master..branch", @source.log('master', 'branch')
6178 end
6279
63 def test_query_revision
80 def test_query_revision_from_remote
6481 revision = @source.query_revision('HEAD') do |o|
6582 assert_equal "git ls-remote . HEAD", o
6683 "d11006102c07c94e5d54dd0ee63dca825c93ed61\tHEAD"
6784 end
6885 assert_equal "d11006102c07c94e5d54dd0ee63dca825c93ed61", revision
6986 end
70
87
88 def test_query_revision_falls_back_to_local
89 revision = @source.query_revision('d11006') do |o|
90 return nil if o == "git ls-remote . d11006"
91 assert_equal "git rev-parse --revs-only d11006", o
92 "d11006102c07c94e5d54dd0ee63dca825c93ed61"
93 end
94 assert_equal "d11006102c07c94e5d54dd0ee63dca825c93ed61", revision
95 end
96
7197 def test_query_revision_has_whitespace
7298 revision = @source.query_revision('HEAD') do |o|
7399 assert_equal "git ls-remote . HEAD", o
91117 def test_sync
92118 dest = "/var/www"
93119 rev = 'c2d9e79'
94 assert_equal "cd #{dest} && git fetch -q origin && git reset -q --hard #{rev} && git clean -q -d -x -f", @source.sync(rev, dest)
120 assert_equal "cd #{dest} && git fetch -q origin && git fetch --tags -q origin && git reset -q --hard #{rev} && git clean -q -d -x -f", @source.sync(rev, dest)
95121
96122 # With :scm_command
97123 git = "/opt/local/bin/git"
98124 @config[:scm_command] = git
99 assert_equal "cd #{dest} && #{git} fetch -q origin && #{git} reset -q --hard #{rev} && #{git} clean -q -d -x -f", @source.sync(rev, dest)
125 assert_equal "cd #{dest} && #{git} fetch -q origin && #{git} fetch --tags -q origin && #{git} reset -q --hard #{rev} && #{git} clean -q -d -x -f", @source.sync(rev, dest)
100126
101127 # with submodules
102128 @config[:git_enable_submodules] = true
103 assert_equal "cd #{dest} && #{git} fetch -q origin && #{git} reset -q --hard #{rev} && #{git} submodule -q init && for mod in `#{git} submodule status | awk '{ print $2 }'`; do #{git} config -f .git/config submodule.${mod}.url `#{git} config -f .gitmodules --get submodule.${mod}.url` && echo Synced $mod; done && #{git} submodule -q sync && #{git} submodule -q update && #{git} clean -q -d -x -f", @source.sync(rev, dest)
129 assert_equal "cd #{dest} && #{git} fetch -q origin && #{git} fetch --tags -q origin && #{git} reset -q --hard #{rev} && #{git} submodule -q init && for mod in `#{git} submodule status | awk '{ print $2 }'`; do #{git} config -f .git/config submodule.${mod}.url `#{git} config -f .gitmodules --get submodule.${mod}.url` && echo Synced $mod; done && #{git} submodule -q sync && export GIT_RECURSIVE=$([ ! \"`#{git} --version`\" \\< \"git version 1.6.5\" ] && echo --recursive) && #{git} submodule -q update --init $GIT_RECURSIVE && #{git} clean -q -d -x -f", @source.sync(rev, dest)
104130 end
105131
106132 def test_sync_with_remote
112138 @config[:repository] = repository
113139 @config[:remote] = remote
114140
115 assert_equal "cd #{dest} && git config remote.#{remote}.url #{repository} && git config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/* && git fetch -q #{remote} && git reset -q --hard #{rev} && git clean -q -d -x -f", @source.sync(rev, dest)
141 assert_equal "cd #{dest} && git config remote.#{remote}.url #{repository} && git config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/* && git fetch -q #{remote} && git fetch --tags -q username && git reset -q --hard #{rev} && git clean -q -d -x -f", @source.sync(rev, dest)
116142 end
117143
118144 def test_shallow_clone
137163 @config[:git_enable_submodules] = true
138164 dest = "/var/www"
139165 rev = 'c2d9e79'
140 assert_equal "git clone -q -o username git@somehost.com:project.git /var/www && cd /var/www && git checkout -q -b deploy #{rev} && git submodule -q init && git submodule -q sync && git submodule -q update", @source.checkout(rev, dest)
166 assert_equal "git clone -q -o username git@somehost.com:project.git /var/www && cd /var/www && git checkout -q -b deploy #{rev} && git submodule -q init && git submodule -q sync && export GIT_RECURSIVE=$([ ! \"`git --version`\" \\< \"git version 1.6.5\" ] && echo --recursive) && git submodule -q update --init $GIT_RECURSIVE", @source.checkout(rev, dest)
141167 end
142168
143169 # Tests from base_test.rb, makin' sure we didn't break anything up there!
181207 assert_equal "/foo/bar/git", @source.command
182208 end
183209 end
210
0 require "utils"
1 require 'capistrano/recipes/deploy/scm/perforce'
2
3 class DeploySCMPerforceTest < Test::Unit::TestCase
4 class TestSCM < Capistrano::Deploy::SCM::Perforce
5 default_command "perforce"
6 end
7 def setup
8 @config = { :repository => "." }
9 @source = TestSCM.new(@config)
10 end
11
12 def test_p4_label
13 @config[:p4_label] = "some_p4_label"
14 assert_equal "@some_p4_label", @source.send(:rev_no, 'foo')
15 end
16
17 def test_p4_label_with_symbol
18 @config[:p4_label] = "@some_p4_label"
19 assert_equal "@some_p4_label", @source.send(:rev_no, 'foo')
20 end
21
22 end
2828 end
2929 assert_equal 2095, revision
3030 end
31
32 def test_sync
33 @config[:repository] = "http://svn.github.com/capistrano/capistrano.git"
34 rev = '602'
35 dest = "/var/www"
36 assert_equal "svn switch -q -r602 http://svn.github.com/capistrano/capistrano.git /var/www", @source.sync(rev, dest)
37 end
38
3139 end
1313 @config.stubs(:source).returns(@source)
1414 @strategy = Capistrano::Deploy::Strategy::Copy.new(@config)
1515 end
16
16
1717 def test_deploy_with_defaults_should_use_remote_gtar
1818 @config[:copy_remote_tar] = 'gtar'
19
19
2020 Dir.expects(:tmpdir).returns("/temp/dir")
2121 @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout)
2222 @strategy.expects(:system).with(:local_checkout)
2323
2424 Dir.expects(:chdir).with("/temp/dir").yields
25 @strategy.expects(:system).with("tar czf 1234567890.tar.gz 1234567890")
25 @strategy.expects(:system).with("tar chzf 1234567890.tar.gz 1234567890")
2626 @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz")
2727 @strategy.expects(:run).with("cd /u/apps/test/releases && gtar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz")
2828
3232
3333 FileUtils.expects(:rm).with("/temp/dir/1234567890.tar.gz")
3434 FileUtils.expects(:rm_rf).with("/temp/dir/1234567890")
35
36 @strategy.deploy!
37 end
38
35
36 @strategy.deploy!
37 end
38
3939 def test_deploy_with_defaults_should_use_local_gtar
4040 @config[:copy_local_tar] = 'gtar'
41
41
4242 Dir.expects(:tmpdir).returns("/temp/dir")
4343 @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout)
4444 @strategy.expects(:system).with(:local_checkout)
4545
4646 Dir.expects(:chdir).with("/temp/dir").yields
47 @strategy.expects(:system).with("gtar czf 1234567890.tar.gz 1234567890")
47 @strategy.expects(:system).with("gtar chzf 1234567890.tar.gz 1234567890")
4848 @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz")
4949 @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz")
5050
5454
5555 FileUtils.expects(:rm).with("/temp/dir/1234567890.tar.gz")
5656 FileUtils.expects(:rm_rf).with("/temp/dir/1234567890")
57
58 @strategy.deploy!
59 end
57
58 @strategy.deploy!
59 end
6060
6161 def test_deploy_with_defaults_should_use_tar_gz_and_checkout
6262 Dir.expects(:tmpdir).returns("/temp/dir")
129129 @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout)
130130
131131 @strategy.expects(:system).with(:local_checkout)
132 @strategy.expects(:system).with("tar cjf 1234567890.tar.bz2 1234567890")
132 @strategy.expects(:system).with("tar chjf 1234567890.tar.bz2 1234567890")
133133 @strategy.expects(:upload).with("/temp/dir/1234567890.tar.bz2", "/tmp/1234567890.tar.bz2")
134134 @strategy.expects(:run).with("cd /u/apps/test/releases && tar xjf /tmp/1234567890.tar.bz2 && rm /tmp/1234567890.tar.bz2")
135135
142142
143143 @strategy.deploy!
144144 end
145
145
146146 def test_deploy_with_unknown_compression_type_should_error
147147 @config[:copy_compression] = :bogus
148148 Dir.expects(:tmpdir).returns("/temp/dir")
149149 @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout)
150150 @strategy.stubs(:system)
151151 File.stubs(:open)
152
152
153153 assert_raises(ArgumentError) { @strategy.deploy! }
154154 end
155
155
156156 def test_deploy_with_custom_copy_dir_should_use_that_as_tmpdir
157157 Dir.expects(:tmpdir).never
158158 Dir.expects(:chdir).with("/other/path").yields
160160 @source.expects(:checkout).with("154", "/other/path/1234567890").returns(:local_checkout)
161161
162162 @strategy.expects(:system).with(:local_checkout)
163 @strategy.expects(:system).with("tar czf 1234567890.tar.gz 1234567890")
163 @strategy.expects(:system).with("tar chzf 1234567890.tar.gz 1234567890")
164164 @strategy.expects(:upload).with("/other/path/1234567890.tar.gz", "/tmp/1234567890.tar.gz")
165165 @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz")
166166
181181 @source.expects(:checkout).returns(:local_checkout)
182182
183183 @strategy.expects(:system).with(:local_checkout)
184 @strategy.expects(:system).with("tar czf 1234567890.tar.gz 1234567890")
184 @strategy.expects(:system).with("tar chzf 1234567890.tar.gz 1234567890")
185185 @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/somewhere/else/1234567890.tar.gz")
186186 @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /somewhere/else/1234567890.tar.gz && rm /somewhere/else/1234567890.tar.gz")
187187
231231 @strategy.deploy!
232232 end
233233
234 def test_with_copy_cache_with_custom_cache_dir_should_use_specified_cache_dir
234 def test_with_copy_cache_with_custom_absolute_cache_dir_path_should_use_specified_cache_dir
235235 @config[:copy_cache] = "/u/caches/captest"
236236
237 Dir.stubs(:tmpdir).returns("/temp/dir")
238 File.expects(:exists?).with("/u/caches/captest").returns(true)
239 Dir.expects(:chdir).with("/u/caches/captest").yields
240
241 @source.expects(:sync).with("154", "/u/caches/captest").returns(:local_sync)
242 @strategy.expects(:system).with(:local_sync)
243
244 FileUtils.expects(:mkdir_p).with("/temp/dir/1234567890")
245
246 prepare_directory_tree!("/u/caches/captest")
247
248 prepare_standard_compress_and_copy!
249 @strategy.deploy!
250 end
251
252 def test_with_copy_cache_with_custom_relative_cache_dir_path_should_use_specified_cache_dir
253 @config[:copy_cache] = "caches/captest"
254
255 Dir.stubs(:pwd).returns("/u")
237256 Dir.stubs(:tmpdir).returns("/temp/dir")
238257 File.expects(:exists?).with("/u/caches/captest").returns(true)
239258 Dir.expects(:chdir).with("/u/caches/captest").yields
275294 File.expects(:directory?).with("app").returns(true)
276295 FileUtils.expects(:mkdir).with("/temp/dir/1234567890/app")
277296 File.expects(:directory?).with("foo.txt").returns(false)
278 FileUtils.expects(:ln).with("#{cache}/foo.txt", "/temp/dir/1234567890/foo.txt")
297 FileUtils.expects(:ln).with("foo.txt", "/temp/dir/1234567890/foo.txt")
279298
280299 Dir.expects(:glob).with("app/*", File::FNM_DOTMATCH).returns(["app/.", "app/..", "app/bar.txt"])
281300
282301 unless exclude
283302 File.expects(:directory?).with("app/bar.txt").returns(false)
284 FileUtils.expects(:ln).with("#{cache}/app/bar.txt", "/temp/dir/1234567890/app/bar.txt")
303 FileUtils.expects(:ln).with("app/bar.txt", "/temp/dir/1234567890/app/bar.txt")
285304 end
286305 end
287306
288307 def prepare_standard_compress_and_copy!
289308 Dir.expects(:chdir).with("/temp/dir").yields
290 @strategy.expects(:system).with("tar czf 1234567890.tar.gz 1234567890")
309 @strategy.expects(:system).with("tar chzf 1234567890.tar.gz 1234567890")
291310 @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz")
292311 @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz")
293312
0 require 'utils'
1 require 'capistrano/configuration'
2
3 class RecipesTest < Test::Unit::TestCase
4
5 def setup
6 @config = Capistrano::Configuration.new
7 @config.stubs(:logger).returns(stub_everything)
8 end
9
10 def test_current_releases_does_not_cause_error_on_dry_run
11 @config.dry_run = true
12 @config.load 'deploy'
13 @config.load do
14 set :application, "foo"
15 task :dry_run_test do
16 fetch :current_release
17 end
18 end
19
20 assert_nothing_raised do
21 @config.dry_run_test
22 end
23 end
24 end
8181 assert_equal success, Capistrano::SSH.connect(@server, :ssh_options => ssh_options, :user => "jamis", :port => 1235)
8282 end
8383
84 def test_connect_with_verbose_option_should_set_verbose_option_on_ssh
85 Net::SSH.expects(:start).with(@server.host, "default-user", @options).returns(success = Object.new)
86 assert_equal success, Capistrano::SSH.connect(@server, :verbose => 0)
87 Net::SSH.expects(:start).with(@server.host, "default-user", @options.merge(:verbose => :debug)).returns(success = Object.new)
88 assert_equal success, Capistrano::SSH.connect(@server, :verbose => 1)
89 Net::SSH.expects(:start).with(@server.host, "default-user", @options.merge(:verbose => :debug)).returns(success = Object.new)
90 assert_equal success, Capistrano::SSH.connect(@server, :verbose => 2)
91 end
92
8493 def test_connect_with_ssh_options_should_see_server_options_override_ssh_options
8594 ssh_options = { :username => "JamisMan", :port => 8125, :forward_agent => true }
8695 server = server("jamis@capistrano:1235")
00 require "utils"
11 require 'capistrano/task_definition'
2
3 # Silences the wanrnings raised in the two deprecation tests
4 $VERBOSE = nil
25
36 class TaskDefinitionTest < Test::Unit::TestCase
47 def setup
1922 def test_fqn_at_top_level_when_default_should_be_default
2023 task = new_task(:default)
2124 assert_equal "default", task.fully_qualified_name
25 end
26
27 def test_deprecation_warning_on_method_name_beginning_with_before_underscore
28 name = "before_test"
29 Kernel.expects(:warn).with("[Deprecation Warning] Naming tasks with before_ and after_ is deprecated, please see the new before() and after() methods. (Offending task name was #{name})")
30 new_task(name)
31 end
32
33 def test_deprecation_warning_on_method_name_beginning_with_after_underscore
34 name = "after_test"
35 Kernel.expects(:warn).with("[Deprecation Warning] Naming tasks with before_ and after_ is deprecated, please see the new before() and after() methods. (Offending task name was #{name})")
36 new_task(name)
2237 end
2338
2439 def test_fqn_in_namespace_when_default_should_be_namespace_fqn
0 begin
1 require 'rubygems'
2 gem 'mocha'
3 rescue LoadError
4 end
0 require 'rubygems'
1 require 'bundler/setup'
52
3 require 'ruby-debug'
64 require 'test/unit'
75 require 'mocha'
6
87 require 'capistrano/server_definition'
98
109 module TestExtensions