Imported Upstream version 2.9.0
Antonio Terceiro
12 years ago
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 | == 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 | 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 | = 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 | |
1 | 2 | |
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 | |
6 | 8 | end |
7 | 9 | |
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 |
37 | 37 | files = { |
38 | 38 | "Capfile" => unindent(<<-FILE), |
39 | 39 | 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) } | |
41 | 45 | |
42 | 46 | load 'config/deploy' # remove this line to skip loading any of the default tasks |
43 | 47 | FILE |
53 | 57 | role :db, "your primary db-server here", :primary => true # This is where Rails migrations will run |
54 | 58 | role :db, "your slave db-server here" |
55 | 59 | |
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 | |
58 | 61 | # these http://github.com/rails/irs_process_scripts |
59 | 62 | |
63 | # If you are using Passenger mod_rails uncomment this: | |
60 | 64 | # namespace :deploy do |
61 | # task :start {} | |
62 | # task :stop {} | |
65 | # task :start do ; end | |
66 | # task :stop do ; end | |
63 | 67 | # task :restart, :roles => :app, :except => { :no_release => true } do |
64 | 68 | # run "#{try_sudo} touch #{File.join(current_path,\'tmp\',\'restart.txt\')}" |
65 | 69 | # end |
0 | 0 | # -*- encoding: utf-8 -*- |
1 | $:.push File.expand_path("../lib", __FILE__) | |
2 | require "capistrano/version" | |
1 | 3 | |
2 | 4 | 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} | |
9 | 12 | 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) } | |
17 | 16 | 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 | ] | |
22 | 20 | |
23 | 21 | 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"]) | |
28 | 25 | s.add_runtime_dependency(%q<net-ssh>, [">= 2.0.14"]) |
29 | 26 | s.add_runtime_dependency(%q<net-sftp>, [">= 2.0.0"]) |
30 | 27 | 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"]) | |
33 | 30 | else |
34 | 31 | s.add_dependency(%q<net-ssh>, [">= 2.0.14"]) |
35 | 32 | s.add_dependency(%q<net-sftp>, [">= 2.0.0"]) |
36 | 33 | 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"]) | |
38 | 35 | s.add_dependency(%q<highline>, [">= 0"]) |
36 | s.add_dependency(%q<mocha>, [">= 0"]) | |
39 | 37 | end |
40 | 38 | else |
41 | 39 | s.add_dependency(%q<net-ssh>, [">= 2.0.14"]) |
42 | 40 | s.add_dependency(%q<net-sftp>, [">= 2.0.0"]) |
43 | 41 | 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"]) | |
45 | 43 | s.add_dependency(%q<highline>, [">= 0"]) |
44 | s.add_dependency(%q<mocha>, [">= 0"]) | |
46 | 45 | end |
47 | 46 | end |
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 |
23 | 23 | config = instantiate_configuration(options) |
24 | 24 | config.debug = options[:debug] |
25 | 25 | config.dry_run = options[:dry_run] |
26 | config.preserve_roles = options[:preserve_roles] | |
26 | 27 | config.logger.level = options[:verbose] |
27 | 28 | |
28 | 29 | set_pre_vars(config) |
32 | 32 | |
33 | 33 | <%= color '-n, --dry-run', :bold %> |
34 | 34 | 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 | ||
36 | 36 | <%= color '-p, --password', :bold %> |
37 | 37 | 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. |
38 | 38 | |
73 | 73 | <%= color 'HOSTFILTER', :bold %> |
74 | 74 | Execute tasks against this comma-separated list of host, but only if the host has the proper role for the task. |
75 | 75 | |
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 | ||
76 | 79 | <%= color 'ROLES', :bold %> |
77 | 80 | Execute tasks against this comma-separated list of roles. Hosts which do not have the right roles will be skipped. |
53 | 53 | exit |
54 | 54 | end |
55 | 55 | |
56 | opts.on("-l", "--logger [STDERR|STDOUT|file]", | |
56 | opts.on("-l", "--logger [STDERR|STDOUT|file]", | |
57 | 57 | "Choose logger method. STDERR used by default." |
58 | ) do |value| | |
58 | ) do |value| | |
59 | 59 | options[:output] = if value.nil? || value.upcase == 'STDERR' |
60 | 60 | # Using default logger. |
61 | 61 | nil |
78 | 78 | "Make the output as quiet as possible." |
79 | 79 | ) { options[:verbose] = 0 } |
80 | 80 | |
81 | opts.on("-r", "--preserve-roles", | |
82 | "Preserve task roles" | |
83 | ) { options[:preserve_roles] = true } | |
84 | ||
81 | 85 | opts.on("-S", "--set-before NAME=VALUE", |
82 | 86 | "Set a variable before the recipes are loaded." |
83 | 87 | ) do |pair| |
111 | 115 | "Display the Capistrano version, and exit." |
112 | 116 | ) do |
113 | 117 | require 'capistrano/version' |
114 | puts "Capistrano v#{Capistrano::Version::STRING}" | |
118 | puts "Capistrano v#{Capistrano::Version}" | |
115 | 119 | exit |
116 | 120 | end |
117 | 121 | |
196 | 200 | def default_sysconf #:nodoc: |
197 | 201 | File.join(sysconf_directory, "capistrano.conf") |
198 | 202 | end |
199 | ||
203 | ||
200 | 204 | def default_dotfile #:nodoc: |
201 | 205 | File.join(home_directory, ".caprc") |
202 | 206 | end |
206 | 210 | # appropriate location for this file in Windows. |
207 | 211 | ENV["SystemRoot"] || '/etc' |
208 | 212 | end |
209 | ||
213 | ||
210 | 214 | def home_directory #:nodoc: |
211 | 215 | ENV["HOME"] || |
212 | 216 | (ENV["HOMEPATH"] && "#{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}") || |
21 | 21 | def password_prompt(prompt="Password: ") |
22 | 22 | ui.ask(prompt) { |q| q.echo = false } |
23 | 23 | end |
24 | ||
24 | ||
25 | 25 | # Debug mode prompt |
26 | 26 | def debug_prompt(cmd) |
27 | 27 | ui.say("Preparing to execute command: #{cmd}") |
36 | 36 | end |
37 | 37 | end |
38 | 38 | end |
39 | end⏎ | |
39 | end |
16 | 16 | # different set of parameters (such as when embedded cap in a program): |
17 | 17 | # |
18 | 18 | # 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! | |
20 | 20 | # |
21 | 21 | # 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: | |
25 | 24 | # |
26 | 25 | # require 'capistrano' |
27 | 26 | # require 'capistrano/cli' |
28 | 27 | # config = Capistrano::Configuration.new |
29 | # config.logger_level = Capistrano::Logger::TRACE | |
28 | # config.logger.level = Capistrano::Logger::TRACE | |
30 | 29 | # config.set(:password) { Capistrano::CLI.password_prompt } |
31 | 30 | # config.load "config/deploy" |
32 | 31 | # config.update_code |
42 | 41 | # Mix-in the actual behavior |
43 | 42 | include Execute, Options, UI |
44 | 43 | include Help # needs to be included last, because it overrides some methods |
44 | ||
45 | 45 | end |
46 | 46 | end |
0 | require 'benchmark' | |
0 | 1 | require 'capistrano/errors' |
1 | 2 | require 'capistrano/processable' |
2 | 3 | |
158 | 159 | # fails (non-zero return code) on any of the hosts, this will raise a |
159 | 160 | # Capistrano::CommandError. |
160 | 161 | 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 | |
166 | 169 | |
167 | 170 | if (failed = @channels.select { |ch| ch[:status] != 0 }).any? |
168 | 171 | commands = failed.inject({}) { |map, ch| (map[ch[:command]] ||= []) << ch[:server]; map } |
25 | 25 | transfer(:up, from, to, options, &block) |
26 | 26 | if mode |
27 | 27 | mode = mode.is_a?(Numeric) ? mode.to_s(8) : mode.to_s |
28 | run "chmod #{mode} #{to}" | |
28 | run "chmod #{mode} #{to}", options | |
29 | 29 | end |
30 | 30 | end |
31 | 31 | |
36 | 36 | def transfer(direction, from, to, options={}, &block) |
37 | 37 | execute_on_servers(options) do |servers| |
38 | 38 | 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 | |
40 | 44 | end |
41 | 45 | end |
42 | 46 |
110 | 110 | # * :except - specifies a condition limiting which hosts will be selected to |
111 | 111 | # run the command. This is the inverse of :only (hosts that do _not_ match |
112 | 112 | # 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. | |
113 | 116 | # * :once - if true, only the first matching server will be selected. The default |
114 | 117 | # is false (all matching servers will be selected). |
115 | 118 | # * :max_hosts - specifies the maximum number of hosts that should be selected |
116 | 119 | # at a time. If this value is less than the number of hosts that are selected |
117 | 120 | # 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. | |
119 | 124 | # * :shell - says which shell should be used to invoke commands. This |
120 | 125 | # defaults to "sh". Setting this to false causes Capistrano to invoke |
121 | 126 | # the commands directly, without wrapping them in a shell invocation. |
23 | 23 | class GatewayConnectionFactory #:nodoc: |
24 | 24 | def initialize(gateway, options) |
25 | 25 | @options = options |
26 | @options[:logger].debug "Creating gateway using #{[*gateway].join(', ')}" if @options[:logger] | |
27 | 26 | 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| | |
30 | 46 | Net::SSH::Gateway.new(host, user, connect_options) |
31 | 47 | end |
32 | @gateway = (@gateways[1..-1]).inject(tunnel) do |tunnel, destination| | |
48 | (gateways[1..-1]).inject(tunnel) do |tunnel, destination| | |
33 | 49 | @options[:logger].debug "Creating tunnel to #{destination}" if @options[:logger] |
34 | 50 | local_host = ServerDefinition.new("127.0.0.1", :user => destination.user, :port => tunnel.open(destination.host, (destination.port || 22))) |
35 | 51 | SSH.connection_strategy(local_host, @options) do |host, user, connect_options| |
40 | 56 | |
41 | 57 | def connect_to(server) |
42 | 58 | @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)) | |
44 | 60 | session = SSH.connect(local_host, @options) |
45 | 61 | session.xserver = server |
46 | 62 | session |
63 | end | |
64 | ||
65 | def gateway_for(server) | |
66 | @gateways[server.host] || @gateways[:default] | |
47 | 67 | end |
48 | 68 | end |
49 | 69 | |
86 | 106 | def connection_factory |
87 | 107 | @connection_factory ||= begin |
88 | 108 | if exists?(:gateway) |
89 | logger.debug "establishing connection to gateway `#{fetch(:gateway)}'" | |
109 | logger.debug "establishing connection to gateway `#{fetch(:gateway).inspect}'" | |
90 | 110 | GatewayConnectionFactory.new(fetch(:gateway), self) |
91 | 111 | else |
92 | 112 | DefaultConnectionFactory.new(self) |
117 | 137 | # Destroys sessions for each server in the list. |
118 | 138 | def teardown_connections_to(servers) |
119 | 139 | 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 | |
122 | 145 | end |
123 | 146 | end |
124 | 147 | |
132 | 155 | servers = find_servers_for_task(task, options) |
133 | 156 | |
134 | 157 | if servers.empty? |
135 | if ENV['HOSTFILTER'] | |
158 | if ENV['HOSTFILTER'] || task.options.merge(options)[:on_no_matching_servers] == :continue | |
136 | 159 | logger.info "skipping `#{task.fully_qualified_name}' because no servers matched" |
137 | 160 | return |
138 | 161 | else |
146 | 169 | end |
147 | 170 | else |
148 | 171 | 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 | |
150 | 176 | end |
151 | 177 | |
152 | 178 | servers = [servers.first] if options[:once] |
25 | 25 | # Yet additionally, if the HOSTFILTER environment variable is set, it |
26 | 26 | # will limit the result to hosts found in that (comma-separated) list. |
27 | 27 | # |
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 | # | |
28 | 31 | # Usage: |
29 | 32 | # |
30 | 33 | # # return all known servers |
38 | 41 | # # returns the given hosts, translated to ServerDefinition objects |
39 | 42 | # servers = find_servers :hosts => "jamis@example.host.com" |
40 | 43 | 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 | ||
41 | 47 | hosts = server_list_from(ENV['HOSTS'] || options[:hosts]) |
42 | 48 | |
43 | 49 | if hosts.any? |
47 | 53 | filter_server_list(hosts.uniq) |
48 | 54 | end |
49 | 55 | 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 | ||
51 | 59 | only = options[:only] || {} |
52 | 60 | except = options[:except] || {} |
53 | 61 | |
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] || []) } | |
55 | 64 | servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } } |
56 | 65 | servers = servers.reject { |server| except.any? { |key,value| server.options[key] == value } } |
57 | 66 | |
66 | 75 | protected |
67 | 76 | |
68 | 77 | 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 | |
72 | 93 | end |
73 | 94 | |
74 | 95 | def server_list_from(hosts) |
82 | 103 | roles = build_list(roles) |
83 | 104 | roles.map do |role| |
84 | 105 | role = String === role ? role.strip.to_sym : role |
85 | raise ArgumentError, "unknown role `#{role}'" unless self.roles.key?(role) | |
86 | 106 | role |
87 | 107 | end |
88 | 108 | end |
92 | 112 | end |
93 | 113 | end |
94 | 114 | end |
95 | end⏎ | |
115 | end |
111 | 111 | private :protect |
112 | 112 | |
113 | 113 | 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) | |
115 | 115 | end |
116 | 116 | |
117 | 117 | def method_missing_with_variables(sym, *args, &block) #:nodoc: |
123 | 123 | end |
124 | 124 | end |
125 | 125 | end |
126 | end⏎ | |
126 | end |
18 | 18 | # define roles, and set configuration variables. |
19 | 19 | class Configuration |
20 | 20 | # The logger instance defined for this configuration. |
21 | attr_accessor :debug, :logger, :dry_run | |
21 | attr_accessor :debug, :logger, :dry_run, :preserve_roles | |
22 | 22 | |
23 | 23 | def initialize(options={}) #:nodoc: |
24 | 24 | @debug = false |
25 | 25 | @dry_run = false |
26 | @preserve_roles = false | |
26 | 27 | @logger = Logger.new(options) |
27 | 28 | end |
28 | 29 |
0 | 0 | module Capistrano |
1 | class Error < RuntimeError; end | |
1 | ||
2 | Error = Class.new(RuntimeError) | |
2 | 3 | |
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) | |
6 | 7 | |
7 | 8 | class RemoteError < Error |
8 | 9 | attr_accessor :hosts |
9 | 10 | end |
10 | 11 | |
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 | ||
14 | 18 | 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 |
39 | 39 | @message ||= "gem `#{name}' #{version} could not be found" |
40 | 40 | gem_cmd = configuration.fetch(:gem_command, "gem") |
41 | 41 | 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) | |
42 | 48 | self |
43 | 49 | end |
44 | 50 |
160 | 160 | |
161 | 161 | # A helper for accessing variable values, which takes into |
162 | 162 | # consideration the current mode ("normal" vs. "local"). |
163 | def variable(name) | |
163 | def variable(name, default = nil) | |
164 | 164 | 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] | |
166 | 166 | else |
167 | configuration[name] | |
167 | configuration[name].nil? ? default : configuration[name] | |
168 | 168 | end |
169 | 169 | end |
170 | 170 | |
185 | 185 | variable(:repository) |
186 | 186 | end |
187 | 187 | |
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 | |
190 | 194 | end |
191 | 195 | end |
192 | 196 |
58 | 58 | # If the 'revision' argument, on the other hand, is not :head, it is |
59 | 59 | # simply returned. |
60 | 60 | def query_revision(revision) |
61 | revision | |
61 | return revision unless :head == revision | |
62 | ||
63 | command = scm('revno', repository) | |
64 | result = yield(command) | |
62 | 65 | end |
63 | 66 | |
64 | 67 | # Increments the given revision number and returns it. |
68 | 68 | def query_revision(revision) |
69 | 69 | return revision if revision_type(revision) == :date |
70 | 70 | revision = yield(scm(cvs_root, :log, "-r#{revision}")). |
71 | grep(/^date:/). | |
71 | split("\n"). | |
72 | select { |line| line =~ /^date:/ }. | |
72 | 73 | map { |line| line[/^date: (.*?);/, 1] }. |
73 | 74 | sort.last + " UTC" |
74 | 75 | return revision |
17 | 17 | :head |
18 | 18 | end |
19 | 19 | |
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 | ||
20 | 28 | # Returns the command that will check out the given revision to the |
21 | 29 | # given destination. The 'revision' parameter must be the 'hash' value |
22 | 30 | # for the revision in question, as given by 'darcs changes --xml-output'. |
23 | 31 | 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 | |
25 | 36 | end |
26 | 37 | |
27 | 38 | # Tries to update the destination repository in-place, to bring it up |
119 | 119 | # default, Git's reference of HEAD (the latest changeset in the default |
120 | 120 | # branch, usually called "master"). |
121 | 121 | def head |
122 | configuration[:branch] || 'HEAD' | |
122 | variable(:branch) || 'HEAD' | |
123 | 123 | end |
124 | 124 | |
125 | 125 | def origin |
126 | configuration[:remote] || 'origin' | |
126 | variable(:remote) || 'origin' | |
127 | 127 | end |
128 | 128 | |
129 | 129 | # Performs a clone on the remote machine, then checkout on the branch |
134 | 134 | |
135 | 135 | args = [] |
136 | 136 | args << "-o #{remote}" unless remote == 'origin' |
137 | if depth = configuration[:git_shallow_clone] | |
137 | if depth = variable(:git_shallow_clone) | |
138 | 138 | args << "--depth #{depth}" |
139 | 139 | end |
140 | 140 | |
141 | 141 | 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}" | |
147 | 143 | |
148 | 144 | # checkout into a local branch rather than a detached HEAD |
149 | 145 | execute << "cd #{destination} && #{git} checkout #{verbose} -b deploy #{revision}" |
150 | ||
151 | if configuration[:git_enable_submodules] | |
146 | ||
147 | if variable(:git_enable_submodules) | |
152 | 148 | execute << "#{git} submodule #{verbose} init" |
153 | 149 | 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 | ||
160 | 161 | # An expensive export. Performs a checkout as above, then |
161 | 162 | # removes the repo. |
162 | 163 | def export(revision, destination) |
179 | 180 | # changes between calls, but as long as the repositories are all |
180 | 181 | # based from each other it should still work fine. |
181 | 182 | if remote != 'origin' |
182 | execute << "#{git} config remote.#{remote}.url #{configuration[:repository]}" | |
183 | execute << "#{git} config remote.#{remote}.url #{variable(:repository)}" | |
183 | 184 | execute << "#{git} config remote.#{remote}.fetch +refs/heads/*:refs/remotes/#{remote}/*" |
184 | 185 | end |
185 | 186 | |
186 | 187 | # 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) | |
190 | 191 | execute << "#{git} submodule #{verbose} init" |
191 | 192 | 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" |
192 | 193 | 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 | |
194 | 200 | end |
195 | 201 | |
196 | 202 | # Make sure there's nothing else lying around in the repository (for |
202 | 208 | |
203 | 209 | # Returns a string of diffs between two revisions |
204 | 210 | 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}" | |
207 | 213 | end |
208 | 214 | |
209 | 215 | # Returns a log of changes between the two revisions (inclusive). |
227 | 233 | break |
228 | 234 | end |
229 | 235 | 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 | ||
230 | 242 | raise "Unable to resolve revision for '#{revision}' on repository '#{repository}'." unless newrev =~ /^[0-9a-f]{40}$/ |
231 | 243 | return newrev |
232 | 244 | end |
233 | 245 | |
234 | 246 | def command |
235 | 247 | # For backwards compatibility with 1.x version of this module |
236 | configuration[:git] || super | |
248 | variable(:git) || super | |
237 | 249 | end |
238 | 250 | |
239 | 251 | # Determines what the response should be for a particular bit of text |
245 | 257 | case text |
246 | 258 | when /\bpassword.*:/i |
247 | 259 | # git is prompting for a password |
248 | unless pass = configuration[:scm_password] | |
260 | unless pass = variable(:scm_password) | |
249 | 261 | pass = Capistrano::CLI.password_prompt |
250 | 262 | end |
251 | 263 | "#{pass}\n" |
254 | 266 | "yes\n" |
255 | 267 | when /passphrase/i |
256 | 268 | # git is asking for the passphrase for the user's key |
257 | unless pass = configuration[:scm_passphrase] | |
269 | unless pass = variable(:scm_passphrase) | |
258 | 270 | pass = Capistrano::CLI.password_prompt |
259 | 271 | end |
260 | 272 | "#{pass}\n" |
275 | 287 | end |
276 | 288 | end |
277 | 289 | end |
290 |
17 | 17 | # For mercurial HEAD == tip except that it bases this assumption on what |
18 | 18 | # tip is in the current repository (so push before you deploy) |
19 | 19 | def head |
20 | configuration[:branch] || "tip" | |
20 | variable(:branch) || "tip" | |
21 | 21 | end |
22 | 22 | |
23 | 23 | # Clone the repository and update to the specified changeset. |
0 | 0 | require 'capistrano/recipes/deploy/scm/base' |
1 | 1 | |
2 | # Notes: | |
2 | # Notes: | |
3 | 3 | # no global verbose flag for scm_verbose |
4 | 4 | # sync, checkout and export are just sync in p4 |
5 | # | |
5 | # | |
6 | 6 | module Capistrano |
7 | 7 | module Deploy |
8 | 8 | module SCM |
26 | 26 | def checkout(revision, destination) |
27 | 27 | p4_sync(revision, destination, p4sync_flags) |
28 | 28 | end |
29 | ||
29 | ||
30 | 30 | # Returns the command that will sync the given revision to the given |
31 | 31 | # destination directory. The perforce client has a fixed destination so |
32 | 32 | # the files must be copied from there to their intended resting place. |
40 | 40 | def export(revision, destination) |
41 | 41 | p4_sync(revision, destination, p4sync_flags) |
42 | 42 | end |
43 | ||
43 | ||
44 | 44 | # Returns the command that will do an "p4 diff2" for the two revisions. |
45 | 45 | def diff(from, to=head) |
46 | 46 | scm authentication, :diff2, "-u -db", "//#{p4client}/...#{rev_no(from)}", "//#{p4client}/...#{rev_no(to)}" |
71 | 71 | raise Capistrano::Error, "scm_password (or p4passwd) is incorrect or unset" |
72 | 72 | when /Can.t create a new user.*/i |
73 | 73 | raise Capistrano::Error, "scm_username (or p4user) is incorrect or unset" |
74 | when /Perforce client error\:/i | |
74 | when /Perforce client error\:/i | |
75 | 75 | raise Capistrano::Error, "p4port is incorrect or unset" |
76 | 76 | when /Client \'[\w\-\_\.]+\' unknown.*/i |
77 | 77 | raise Capistrano::Error, "p4client is incorrect or unset" |
78 | end | |
78 | end | |
79 | 79 | end |
80 | 80 | |
81 | 81 | private |
89 | 89 | end |
90 | 90 | |
91 | 91 | # 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. | |
95 | 95 | 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}" | |
97 | 97 | end |
98 | 98 | |
99 | 99 | def p4client |
107 | 107 | def p4user |
108 | 108 | variable(:p4user) || variable(:scm_username) |
109 | 109 | end |
110 | ||
110 | ||
111 | 111 | def p4passwd |
112 | 112 | variable(:p4passwd) || variable(:scm_password) |
113 | 113 | end |
119 | 119 | def p4client_root |
120 | 120 | variable(:p4client_root) || "`#{command} #{authentication} client -o | grep ^Root | cut -f2`" |
121 | 121 | 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 | ||
124 | 133 | case revision.to_s |
125 | 134 | when "head" |
126 | 135 | "#head" |
127 | when /^\d+/ | |
136 | when /^\d+/ | |
128 | 137 | "@#{revision}" |
129 | 138 | else |
130 | 139 | revision |
131 | end | |
140 | end | |
132 | 141 | end |
133 | 142 | end |
134 | 143 |
20 | 20 | # Returns the command that will check out the given revision to the |
21 | 21 | # given destination. |
22 | 22 | 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 | |
24 | 24 | end |
25 | 25 | |
26 | 26 | # Returns the command that will do an "svn update" to the given |
27 | 27 | # revision, for the working copy at the given destination. |
28 | 28 | def sync(revision, destination) |
29 | scm :update, arguments, verbose, authentication, "-r#{revision}", destination | |
29 | scm :switch, arguments, verbose, authentication, "-r#{revision}", repository, destination | |
30 | 30 | end |
31 | 31 | |
32 | 32 | # Returns the command that will do an "svn export" of the given revision |
33 | 33 | # to the given destination. |
34 | 34 | 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 | |
36 | 36 | end |
37 | 37 | |
38 | 38 | # Returns the command that will do an "svn diff" for the two revisions. |
39 | 39 | 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}" | |
41 | 41 | end |
42 | 42 | |
43 | 43 | # Returns an "svn log" command for the two revisions. |
44 | 44 | 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}" | |
46 | 46 | end |
47 | 47 | |
48 | 48 | # Attempts to translate the given revision identifier to a "real" |
51 | 51 | # executed (svn info), and will extract the revision from the response. |
52 | 52 | def query_revision(revision) |
53 | 53 | return revision if revision =~ /^\d+$/ |
54 | command = scm(:info, repository, authentication, "-r#{revision}") | |
54 | command = scm(:info, arguments, arguments(:info), repository, authentication, "-r#{revision}") | |
55 | 55 | result = yield(command) |
56 | 56 | yaml = YAML.load(result) |
57 | 57 | raise "tried to run `#{command}' and got unexpected result #{result.inspect}" unless Hash === yaml |
0 | require 'benchmark' | |
0 | 1 | require 'capistrano/recipes/deploy/dependencies' |
1 | 2 | |
2 | 3 | module Capistrano |
48 | 49 | # A wrapper for Kernel#system that logs the command being executed. |
49 | 50 | def system(*args) |
50 | 51 | cmd = args.join(' ') |
52 | result = nil | |
51 | 53 | if RUBY_PLATFORM =~ /win32/ |
52 | 54 | 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:// |
53 | 55 | cmd.gsub!(/^cd /,'cd /D ') # Replace cd with cd /D |
54 | 56 | cmd.gsub!(/&& cd /,'&& cd /D ') # Replace cd with cd /D |
55 | 57 | logger.trace "executing locally: #{cmd}" |
56 | super(cmd) | |
58 | elapsed = Benchmark.realtime do | |
59 | result = super(cmd) | |
60 | end | |
57 | 61 | else |
58 | 62 | logger.trace "executing locally: #{cmd}" |
59 | super | |
63 | elapsed = Benchmark.realtime do | |
64 | result = super | |
65 | end | |
60 | 66 | end |
67 | ||
68 | logger.trace "command finished in #{(elapsed * 1000).round}ms" | |
69 | result | |
61 | 70 | end |
62 | 71 | |
63 | 72 | private |
52 | 52 | system(source.checkout(revision, copy_cache)) |
53 | 53 | end |
54 | 54 | |
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 | ||
55 | 62 | logger.debug "copying cache to deployment staging area #{destination}" |
56 | 63 | Dir.chdir(copy_cache) do |
57 | FileUtils.mkdir_p(destination) | |
58 | 64 | queue = Dir.glob("*", File::FNM_DOTMATCH) |
59 | 65 | while queue.any? |
60 | 66 | item = queue.shift |
64 | 70 | next if copy_exclude.any? { |pattern| File.fnmatch(pattern, item) } |
65 | 71 | |
66 | 72 | 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)) | |
68 | 74 | elsif File.directory?(item) |
69 | 75 | queue += Dir.glob("#{item}/*", File::FNM_DOTMATCH) |
70 | 76 | FileUtils.mkdir(File.join(destination, item)) |
71 | 77 | else |
72 | FileUtils.ln(File.join(copy_cache, item), File.join(destination, item)) | |
78 | FileUtils.ln(item, File.join(destination, item)) | |
73 | 79 | end |
74 | 80 | end |
75 | 81 | end |
79 | 85 | |
80 | 86 | if copy_exclude.any? |
81 | 87 | 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) | |
89 | 94 | end |
90 | 95 | end |
91 | 96 | end |
93 | 98 | File.open(File.join(destination, "REVISION"), "w") { |f| f.puts(revision) } |
94 | 99 | |
95 | 100 | 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! | |
100 | 104 | ensure |
101 | 105 | FileUtils.rm filename rescue nil |
102 | 106 | FileUtils.rm_rf destination rescue nil |
116 | 120 | # is +true+, a default cache location will be returned. |
117 | 121 | def copy_cache |
118 | 122 | @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 | |
121 | 125 | end |
122 | 126 | |
123 | 127 | private |
131 | 135 | # Returns the basename of the release_path, which will be used to |
132 | 136 | # name the local copy and archive file. |
133 | 137 | def destination |
134 | @destination ||= File.join(tmpdir, File.basename(configuration[:release_path])) | |
138 | @destination ||= File.join(copy_dir, File.basename(configuration[:release_path])) | |
135 | 139 | end |
136 | 140 | |
137 | 141 | # Returns the value of the :copy_strategy variable, defaulting to |
154 | 158 | # Returns the name of the file that the source code will be |
155 | 159 | # compressed to. |
156 | 160 | def filename |
157 | @filename ||= File.join(tmpdir, "#{File.basename(destination)}.#{compression.extension}") | |
161 | @filename ||= File.join(copy_dir, "#{File.basename(destination)}.#{compression.extension}") | |
158 | 162 | end |
159 | 163 | |
160 | 164 | # 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) | |
163 | 167 | end |
164 | 168 | |
165 | 169 | # The directory on the remote server to which the archive should be |
178 | 182 | # Commands are arrays, where the first element is the utility to be |
179 | 183 | # used to perform the compression or decompression. |
180 | 184 | Compression = Struct.new(:extension, :compress_command, :decompress_command) |
181 | ||
185 | ||
182 | 186 | # The compression method to use, defaults to :gzip. |
183 | 187 | def compression |
184 | 188 | remote_tar = configuration[:copy_remote_tar] || 'tar' |
185 | 189 | local_tar = configuration[:copy_local_tar] || 'tar' |
186 | ||
190 | ||
187 | 191 | type = configuration[:copy_compression] || :gzip |
188 | 192 | 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']) | |
191 | 195 | when :zip then Compression.new("zip", %w(zip -qr), %w(unzip -q)) |
192 | 196 | else raise ArgumentError, "invalid compression type #{type.inspect}" |
193 | 197 | end |
194 | 198 | end |
195 | ||
199 | ||
196 | 200 | # Returns the command necessary to compress the given directory |
197 | 201 | # into the given file. |
198 | 202 | def compress(directory, file) |
205 | 209 | def decompress(file) |
206 | 210 | compression.decompress_command + [file] |
207 | 211 | 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 | |
208 | 218 | end |
209 | 219 | |
210 | 220 | end |
17 | 17 | |
18 | 18 | def check! |
19 | 19 | super.check do |d| |
20 | d.remote.command("rsync") unless copy_exclude.empty? | |
20 | 21 | d.remote.writable(shared_path) |
21 | 22 | end |
22 | 23 | end |
37 | 38 | |
38 | 39 | def copy_repository_cache |
39 | 40 | logger.trace "copying the cached version to #{configuration[:release_path]}" |
40 | if copy_exclude.empty? | |
41 | if copy_exclude.empty? | |
41 | 42 | run "cp -RPp #{repository_cache} #{configuration[:release_path]} && #{mark}" |
42 | 43 | else |
43 | 44 | 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}" | |
45 | 46 | end |
46 | 47 | end |
47 | ||
48 | ||
48 | 49 | def copy_exclude |
49 | 50 | @copy_exclude ||= Array(configuration.fetch(:copy_exclude, [])) |
50 | 51 | end |
0 | require 'benchmark' | |
0 | 1 | require 'yaml' |
1 | 2 | require 'capistrano/recipes/deploy/scm' |
2 | 3 | require 'capistrano/recipes/deploy/strategy' |
26 | 27 | _cset(:deploy_to) { "/u/apps/#{application}" } |
27 | 28 | _cset(:revision) { source.head } |
28 | 29 | |
30 | _cset :rails_env, "production" | |
31 | _cset :rake, "rake" | |
32 | ||
33 | _cset :maintenance_basename, "maintenance" | |
34 | ||
29 | 35 | # ========================================================================= |
30 | 36 | # These variables should NOT be changed unless you are very confident in |
31 | 37 | # what you are doing. Make sure you understand all the implications of your |
37 | 43 | |
38 | 44 | _cset(:strategy) { Capistrano::Deploy::Strategy.new(deploy_via, self) } |
39 | 45 | |
46 | # If overriding release name, please also select an appropriate setting for :releases below. | |
40 | 47 | _cset(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") } |
41 | 48 | |
42 | 49 | _cset :version_dir, "releases" |
49 | 56 | _cset(:current_path) { File.join(deploy_to, current_dir) } |
50 | 57 | _cset(:release_path) { File.join(releases_path, release_name) } |
51 | 58 | |
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 } | |
54 | 61 | _cset(:previous_release) { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil } |
55 | 62 | |
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 } | |
59 | 66 | |
60 | 67 | _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run } |
61 | 68 | |
93 | 100 | # returns the command output as a string |
94 | 101 | def run_locally(cmd) |
95 | 102 | 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 | ||
107 | 114 | |
108 | 115 | # If a command is given, this will try to execute the given command, as |
109 | 116 | # described below. Otherwise, it will return a string for use in embedding in |
177 | 184 | task :setup, :except => { :no_release => true } do |
178 | 185 | dirs = [deploy_to, releases_path, shared_path] |
179 | 186 | 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) | |
181 | 189 | end |
182 | 190 | |
183 | 191 | desc <<-DESC |
226 | 234 | public/stylesheets, and public/javascripts so that the times are \ |
227 | 235 | consistent (so that asset timestamping works). This touch process \ |
228 | 236 | 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. | |
230 | 239 | DESC |
231 | 240 | task :finalize_update, :except => { :no_release => true } do |
232 | 241 | run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true) |
244 | 253 | |
245 | 254 | if fetch(:normalize_asset_timestamps, true) |
246 | 255 | 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(" ") | |
248 | 257 | run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" } |
249 | 258 | end |
250 | 259 | end |
296 | 305 | end |
297 | 306 | |
298 | 307 | 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. | |
312 | 310 | DESC |
313 | 311 | 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 | |
316 | 313 | end |
317 | 314 | |
318 | 315 | namespace :rollback do |
377 | 374 | set :migrate_target, :latest |
378 | 375 | DESC |
379 | 376 | task :migrate, :roles => :db, :only => { :primary => true } do |
380 | rake = fetch(:rake, "rake") | |
381 | rails_env = fetch(:rails_env, "production") | |
382 | 377 | migrate_env = fetch(:migrate_env, "") |
383 | 378 | migrate_target = fetch(:migrate_target, :latest) |
384 | 379 | |
385 | 380 | directory = case migrate_target.to_sym |
386 | 381 | when :current then current_path |
387 | when :latest then current_release | |
382 | when :latest then latest_release | |
388 | 383 | else raise ArgumentError, "unknown migration target #{migrate_target.inspect}" |
389 | 384 | end |
390 | 385 | |
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" | |
392 | 387 | end |
393 | 388 | |
394 | 389 | desc <<-DESC |
480 | 475 | end |
481 | 476 | |
482 | 477 | 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. | |
493 | 480 | DESC |
494 | 481 | 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. | |
509 | 488 | DESC |
510 | 489 | 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 | |
514 | 491 | end |
515 | 492 | |
516 | 493 | namespace :pending do |
537 | 514 | namespace :web do |
538 | 515 | desc <<-DESC |
539 | 516 | 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 \ | |
541 | 518 | servers must be configured to detect the presence of this file, and if \ |
542 | 519 | it is present, always display it instead of performing the request. |
543 | 520 | |
553 | 530 | DESC |
554 | 531 | task :disable, :roles => :web, :except => { :no_release => true } do |
555 | 532 | require 'erb' |
556 | on_rollback { run "rm #{shared_path}/system/maintenance.html" } | |
533 | on_rollback { run "rm #{shared_path}/system/#{maintenance_basename}.html" } | |
557 | 534 | |
558 | 535 | warn <<-EOHTACCESS |
559 | ||
536 | ||
560 | 537 | # Please add something like this to your site's htaccess to redirect users to the maintenance page. |
561 | 538 | # 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 | |
564 | 541 | RewriteEngine On |
565 | 542 | 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 | |
568 | 545 | RewriteRule ^.*$ - [redirect=503,last] |
569 | 546 | EOHTACCESS |
570 | 547 | |
574 | 551 | template = File.read(File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml")) |
575 | 552 | result = ERB.new(template).result(binding) |
576 | 553 | |
577 | put result, "#{shared_path}/system/maintenance.html", :mode => 0644 | |
554 | put result, "#{shared_path}/system/#{maintenance_basename}.html", :mode => 0644 | |
578 | 555 | end |
579 | 556 | |
580 | 557 | desc <<-DESC |
581 | 558 | 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 \ | |
583 | 560 | web servers are configured correctly) will make your application \ |
584 | 561 | web-accessible again. |
585 | 562 | DESC |
586 | 563 | 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 | # 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 |
68 | 68 | # Once we've loaded the config, we don't need Net::SSH to do it again. |
69 | 69 | ssh_options[:config] = false |
70 | 70 | |
71 | ssh_options[:verbose] = :debug if options[:verbose] && options[:verbose] > 0 | |
72 | ||
71 | 73 | user = server.user || options[:user] || ssh_options[:username] || |
72 | 74 | ssh_options[:user] || ServerDefinition.default_user |
73 | 75 | port = server.port || options[:port] || ssh_options[:port] |
5 | 5 | attr_reader :name, :namespace, :options, :body, :desc, :on_error, :max_hosts |
6 | 6 | |
7 | 7 | 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 | ||
8 | 13 | @name, @namespace, @options = name, namespace, options |
9 | 14 | @desc = @options.delete(:desc) |
10 | 15 | @on_error = options.delete(:on_error) |
12 | 17 | @body = block or raise ArgumentError, "a task requires a block" |
13 | 18 | @servers = nil |
14 | 19 | end |
15 | ||
20 | ||
16 | 21 | # Returns the task's fully-qualified name, including the namespace |
17 | 22 | def fully_qualified_name |
18 | 23 | @fully_qualified_name ||= begin |
66 | 71 | @on_error == :continue |
67 | 72 | end |
68 | 73 | end |
69 | end⏎ | |
74 | end |
0 | require 'net/ssh/version' | |
1 | ||
0 | require 'scanf' | |
2 | 1 | module Capistrano |
3 | 2 | |
4 | # Describes the current version of Capistrano. | |
5 | class Version < Net::SSH::Version | |
3 | class Version | |
4 | ||
6 | 5 | MAJOR = 2 |
7 | MINOR = 5 | |
8 | TINY = 9 | |
6 | MINOR = 9 | |
7 | PATCH = 0 | |
9 | 8 | |
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 | |
12 | 12 | |
13 | # The current version, as a String instance | |
14 | STRING = CURRENT.to_s | |
15 | 13 | end |
16 | 14 | |
17 | 15 | end |
0 | 0 | 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 | # | |
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 |
14 | 14 | def setup |
15 | 15 | @cli = MockCLI.new |
16 | 16 | @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) | |
18 | 18 | @config.stubs(:set) |
19 | 19 | @config.stubs(:load) |
20 | 20 | @config.stubs(:trigger) |
128 | 128 | MockCLI.expects(:parse).with(ARGV).returns(cli) |
129 | 129 | MockCLI.execute |
130 | 130 | end |
131 | end⏎ | |
131 | end |
92 | 92 | assert_equal 0, @cli.options[:verbose] |
93 | 93 | end |
94 | 94 | |
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 | ||
95 | 107 | def test_parse_options_with_S_should_set_pre_vars |
96 | 108 | @cli.args << "-S" << "foo=bar" |
97 | 109 | @cli.parse_options! |
185 | 197 | |
186 | 198 | def test_parse_options_with_V_should_show_version_and_exit |
187 | 199 | @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) } | |
189 | 201 | @cli.expects(:exit).raises(ExitException) |
190 | 202 | assert_raises(ExitException) { @cli.parse_options! } |
191 | 203 | end |
2 | 2 | |
3 | 3 | class CLI_Test < Test::Unit::TestCase |
4 | 4 | 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)) | |
6 | 6 | cli.expects(:puts).at_least_once |
7 | 7 | cli.execute! |
8 | 8 | end |
9 | 9 | |
10 | 10 | def test_options_and_execute_modules_should_integrate_successfully_with_configuration |
11 | 11 | 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)) | |
13 | 13 | config = cli.execute! |
14 | 14 | assert config[:testing_occurred] |
15 | 15 | end |
204 | 204 | new_channel = Proc.new do |times| |
205 | 205 | ch = mock("channel") |
206 | 206 | returns = [false] * (times-1) |
207 | ch.stubs(:to_ary) | |
207 | 208 | ch.stubs(:[]).with(:closed).returns(*(returns + [true])) |
208 | 209 | ch.expects(:[]).with(:status).returns(0) |
209 | 210 | ch |
213 | 214 | mock_session(new_channel[10]), |
214 | 215 | mock_session(new_channel[7])] |
215 | 216 | cmd = Capistrano::Command.new("ls", sessions) |
216 | assert_nothing_raised { cmd.process! } | |
217 | assert_nothing_raised do | |
218 | cmd.process! | |
219 | end | |
217 | 220 | end |
218 | 221 | |
219 | 222 | def test_process_should_instantiate_command_and_process! |
239 | 242 | private |
240 | 243 | |
241 | 244 | 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")) | |
247 | 251 | end |
248 | 252 | |
249 | 253 | class MockChannel < Hash |
3 | 3 | class ConfigurationActionsFileTransferTest < Test::Unit::TestCase |
4 | 4 | class MockConfig |
5 | 5 | include Capistrano::Configuration::Actions::FileTransfer |
6 | attr_accessor :sessions | |
6 | attr_accessor :sessions, :dry_run | |
7 | 7 | end |
8 | 8 | |
9 | 9 | def setup |
36 | 36 | |
37 | 37 | def test_upload_with_mode_should_try_to_chmod |
38 | 38 | @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"}) | |
40 | 40 | @config.upload("testl.txt", "testr.txt", :mode => 0775, :foo => "bar") |
41 | 41 | end |
42 | 42 | |
43 | 43 | def test_upload_with_symbolic_mode_should_try_to_chmod |
44 | 44 | @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"}) | |
46 | 46 | @config.upload("testl.txt", "testr.txt", :mode => "g+w", :foo => "bar") |
47 | 47 | end |
48 | 48 | |
57 | 57 | Capistrano::Transfer.expects(:process).with(:up, "testl.txt", "testr.txt", [1,2,3], {:foo => "bar", :logger => @config.logger}) |
58 | 58 | @config.transfer(:up, "testl.txt", "testr.txt", :foo => "bar") |
59 | 59 | end |
60 | end⏎ | |
60 | end |
0 | 0 | require "utils" |
1 | 1 | require 'capistrano/configuration/actions/invocation' |
2 | require 'capistrano/configuration/actions/file_transfer' | |
2 | 3 | |
3 | 4 | class ConfigurationActionsInvocationTest < Test::Unit::TestCase |
4 | 5 | class MockConfig |
5 | 6 | attr_reader :options |
6 | 7 | attr_accessor :debug |
7 | 8 | attr_accessor :dry_run |
9 | attr_accessor :preserve_roles | |
10 | attr_accessor :servers | |
8 | 11 | |
9 | 12 | def initialize |
10 | 13 | @options = {} |
14 | @servers = [] | |
11 | 15 | end |
12 | 16 | |
13 | 17 | def [](*args) |
22 | 26 | @options.fetch(*args) |
23 | 27 | end |
24 | 28 | |
29 | def execute_on_servers(options = {}) | |
30 | yield @servers | |
31 | end | |
32 | ||
25 | 33 | include Capistrano::Configuration::Actions::Invocation |
34 | include Capistrano::Configuration::Actions::FileTransfer | |
26 | 35 | end |
27 | 36 | |
28 | 37 | def setup |
29 | @config = MockConfig.new | |
38 | @config = make_config | |
30 | 39 | @original_io_proc = MockConfig.default_io_proc |
31 | @config.stubs(:logger).returns(stub_everything) | |
32 | 40 | end |
33 | 41 | |
34 | 42 | def teardown |
44 | 52 | @config.expects(:dry_run).returns(true) |
45 | 53 | @config.expects(:execute_on_servers).never |
46 | 54 | @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 | |
47 | 64 | end |
48 | 65 | |
49 | 66 | def test_add_default_command_options_should_return_bare_options_if_there_is_no_env_or_shell_specified |
201 | 218 | |
202 | 219 | private |
203 | 220 | |
221 | def make_config | |
222 | config = MockConfig.new | |
223 | config.stubs(:logger).returns(stub_everything) | |
224 | config | |
225 | end | |
226 | ||
204 | 227 | def inspectable_proc |
205 | 228 | Proc.new do |ch, stream, data| |
206 | 229 | ch.called |
70 | 70 | Net::SSH::Gateway.expects(:new).with("gateway", "user", :debug => :verbose, :port => 8080, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything) |
71 | 71 | assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory |
72 | 72 | end |
73 | ||
73 | ||
74 | 74 | def test_connection_factory_as_gateway_should_chain_gateways_if_gateway_variable_is_an_array |
75 | 75 | @config.values[:gateway] = ["j@gateway1", "k@gateway2"] |
76 | 76 | gateway1 = mock |
79 | 79 | 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) |
80 | 80 | assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory |
81 | 81 | 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 | ||
83 | 92 | def test_connection_factory_as_gateway_should_share_gateway_between_connections |
84 | 93 | @config.values[:gateway] = "j@gateway" |
85 | 94 | Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything) |
89 | 98 | @config.establish_connections_to(server("another")) |
90 | 99 | end |
91 | 100 | |
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 | ||
92 | 121 | def test_establish_connections_to_should_accept_a_single_nonarray_parameter |
93 | 122 | Capistrano::SSH.expects(:connect).with { |s,| s.host == "capistrano" }.returns(:success) |
94 | 123 | assert @config.sessions.empty? |
95 | 124 | @config.establish_connections_to(server("capistrano")) |
96 | assert ["capistrano"], @config.sessions.keys | |
125 | assert_equal ["capistrano"], @config.sessions.keys.map(&:host) | |
97 | 126 | end |
98 | 127 | |
99 | 128 | def test_establish_connections_to_should_accept_an_array |
100 | 129 | Capistrano::SSH.expects(:connect).times(3).returns(:success) |
101 | 130 | assert @config.sessions.empty? |
102 | 131 | @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) | |
104 | 133 | end |
105 | 134 | |
106 | 135 | def test_establish_connections_to_should_not_attempt_to_reestablish_existing_connections |
107 | 136 | Capistrano::SSH.expects(:connect).times(2).returns(:success) |
108 | 137 | @config.sessions[server("cap1")] = :ok |
109 | 138 | @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 | ||
113 | 142 | def test_establish_connections_to_should_raise_one_connection_error_on_failure |
114 | 143 | Capistrano::SSH.expects(:connect).times(2).raises(Exception) |
115 | 144 | assert_raises(Capistrano::ConnectionError) { |
119 | 148 | |
120 | 149 | def test_connection_error_should_include_accessor_with_host_array |
121 | 150 | Capistrano::SSH.expects(:connect).times(2).raises(Exception) |
122 | ||
123 | 151 | begin |
124 | 152 | @config.establish_connections_to(%w(cap1 cap2).map { |s| server(s) }) |
125 | 153 | flunk "expected an exception to be raised" |
128 | 156 | assert_equal %w(cap1 cap2), e.hosts.map { |h| h.to_s }.sort |
129 | 157 | end |
130 | 158 | end |
131 | ||
159 | ||
132 | 160 | def test_connection_error_should_only_include_failed_hosts |
133 | 161 | Capistrano::SSH.expects(:connect).with(server('cap1'), anything).raises(Exception) |
134 | 162 | Capistrano::SSH.expects(:connect).with(server('cap2'), anything).returns(:success) |
159 | 187 | assert_raises(Capistrano::NoMatchingServersError) { @config.execute_on_servers(:a => :b, :c => :d) { |list| } } |
160 | 188 | end |
161 | 189 | |
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 | ||
162 | 195 | def test_execute_on_servers_should_raise_an_error_if_the_current_task_has_no_matching_servers_by_default |
163 | 196 | @config.current_task = mock_task |
164 | 197 | @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([]) |
168 | 201 | end |
169 | 202 | end |
170 | 203 | 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 | ||
172 | 225 | def test_execute_on_servers_should_determine_server_list_from_active_task |
173 | 226 | assert @config.sessions.empty? |
174 | 227 | @config.current_task = mock_task |
207 | 260 | assert block_called |
208 | 261 | assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host } |
209 | 262 | end |
210 | ||
263 | ||
211 | 264 | def test_execute_servers_should_raise_connection_error_on_failure_by_default |
212 | 265 | @config.current_task = mock_task |
213 | 266 | @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1")]) |
218 | 271 | end |
219 | 272 | end |
220 | 273 | end |
221 | ||
274 | ||
222 | 275 | def test_execute_servers_should_not_raise_connection_error_on_failure_with_on_errors_continue |
223 | 276 | @config.current_task = mock_task(:on_error => :continue) |
224 | 277 | @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2")]) |
230 | 283 | end |
231 | 284 | } |
232 | 285 | end |
233 | ||
286 | ||
234 | 287 | def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_connection_errors_with_on_errors_continue |
235 | 288 | list = [server("cap1"), server("cap2")] |
236 | 289 | @config.current_task = mock_task(:on_error => :continue) |
245 | 298 | assert_equal %w(cap2), servers.map { |s| s.host } |
246 | 299 | end |
247 | 300 | end |
248 | ||
301 | ||
249 | 302 | def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_command_errors_with_on_errors_continue |
250 | 303 | cap1 = server("cap1") |
251 | 304 | cap2 = server("cap2") |
262 | 315 | assert_equal %w(cap2), servers.map { |s| s.host } |
263 | 316 | end |
264 | 317 | end |
265 | ||
318 | ||
266 | 319 | def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_transfer_errors_with_on_errors_continue |
267 | 320 | cap1 = server("cap1") |
268 | 321 | cap2 = server("cap2") |
279 | 332 | assert_equal %w(cap2), servers.map { |s| s.host } |
280 | 333 | end |
281 | 334 | end |
282 | ||
335 | ||
283 | 336 | def test_connect_should_establish_connections_to_all_servers_in_scope |
284 | 337 | assert @config.sessions.empty? |
285 | 338 | @config.current_task = mock_task |
288 | 341 | @config.connect! |
289 | 342 | assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host } |
290 | 343 | end |
291 | ||
344 | ||
292 | 345 | def test_execute_on_servers_should_only_run_on_tasks_max_hosts_hosts_at_once |
293 | 346 | cap1 = server("cap1") |
294 | 347 | cap2 = server("cap2") |
306 | 359 | end |
307 | 360 | assert_equal 2, block_called |
308 | 361 | end |
309 | ||
362 | ||
310 | 363 | def test_execute_on_servers_should_only_run_on_max_hosts_hosts_at_once |
311 | 364 | cap1 = server("cap1") |
312 | 365 | cap2 = server("cap2") |
324 | 377 | end |
325 | 378 | assert_equal 2, block_called |
326 | 379 | 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 | ||
328 | 399 | def test_connect_should_honor_once_option |
329 | 400 | assert @config.sessions.empty? |
330 | 401 | @config.current_task = mock_task |
0 | require "utils" | |
0 | require 'utils' | |
1 | 1 | require 'capistrano/configuration/loading' |
2 | 2 | |
3 | 3 | class ConfigurationLoadingTest < Test::Unit::TestCase |
105 | 105 | |
106 | 106 | def test_require_from_config_should_load_file_in_config_scope |
107 | 107 | assert_nothing_raised do |
108 | @config.require "#{File.dirname(__FILE__)}/../fixtures/custom" | |
108 | @config.require "#{File.expand_path(File.dirname(__FILE__))}/../fixtures/custom" | |
109 | 109 | end |
110 | 110 | assert_equal :custom, @config.ping |
111 | 111 | end |
117 | 117 | end |
118 | 118 | |
119 | 119 | 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") | |
122 | 122 | end |
123 | ||
123 | ||
124 | 124 | def test_require_in_multiple_instances_should_load_recipes_in_each_instance |
125 | 125 | 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" | |
128 | 128 | assert_equal :custom, @config.ping |
129 | 129 | assert_equal :custom, config2.ping |
130 | 130 | end |
4 | 4 | class ConfigurationServersTest < Test::Unit::TestCase |
5 | 5 | class MockConfig |
6 | 6 | attr_reader :roles |
7 | attr_accessor :preserve_roles | |
7 | 8 | |
8 | 9 | def initialize |
9 | 10 | @roles = {} |
11 | @preserve_roles = false | |
10 | 12 | end |
11 | 13 | |
12 | 14 | include Capistrano::Configuration::Servers |
36 | 38 | assert_equal %w(web1 web2).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort |
37 | 39 | end |
38 | 40 | |
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 | |
40 | 44 | 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) | |
44 | 46 | end |
45 | 47 | |
46 | 48 | def test_task_with_hosts_option_should_apply_only_to_those_hosts |
61 | 63 | ENV.delete('ROLES') |
62 | 64 | end |
63 | 65 | |
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 | ||
64 | 93 | def test_task_with_hosts_as_environment_variable_should_apply_only_to_those_hosts |
65 | 94 | ENV['HOSTS'] = "foo,bar" |
66 | 95 | task = new_task(:testing) |
101 | 130 | ENV.delete('HOSTFILTER') |
102 | 131 | end |
103 | 132 | |
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 | ||
104 | 141 | def test_task_with_only_should_apply_only_to_matching_tasks |
105 | 142 | task = new_task(:testing, @config, :roles => :app, :only => { :primary => true }) |
106 | 143 | assert_equal %w(app1), @config.find_servers_for_task(task).map { |s| s.host } |
125 | 162 | assert_equal %w(app1 app2 app3), @config.find_servers(:roles => lambda { :app }).map { |s| s.host }.sort |
126 | 163 | assert_equal %w(app2 file), @config.find_servers(:roles => lambda { [:report, :file] }).map { |s| s.host }.sort |
127 | 164 | 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 |
51 | 51 | assert !@config.respond_to?(:sample) |
52 | 52 | @config[:sample] = :value |
53 | 53 | 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') | |
54 | 60 | end |
55 | 61 | |
56 | 62 | def test_respond_to_with_include_priv_paramter |
36 | 36 | assert_equal "gem `capistrano' 9.9 could not be found (host)", @dependency.message |
37 | 37 | end |
38 | 38 | |
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 | ||
39 | 45 | def test_should_fail_if_directory_not_found |
40 | 46 | setup_for_a_configuration_run("test -d /data", false) |
41 | 47 | assert !@dependency.directory("/data").pass? |
51 | 57 | assert !@dependency.file("/data/foo.txt").pass? |
52 | 58 | end |
53 | 59 | |
54 | def test_should_pas_if_file_found | |
60 | def test_should_pass_if_file_found | |
55 | 61 | setup_for_a_configuration_run("test -f /data/foo.txt", true) |
56 | 62 | assert @dependency.file("/data/foo.txt").pass? |
57 | 63 | end |
86 | 92 | assert @dependency.gem("capistrano", 9.9).pass? |
87 | 93 | end |
88 | 94 | |
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 | ||
89 | 105 | def test_should_use_alternative_message_if_provided |
90 | 106 | setup_for_a_configuration_run("which cat", false) |
91 | 107 | @dependency.command("cat").or("Sorry") |
110 | 126 | find_gem_cmd = "gem specification --version '#{version}' #{name} 2>&1 | awk 'BEGIN { s = 0 } /^name:/ { s = 1; exit }; END { if(s == 0) exit 1 }'" |
111 | 127 | setup_for_a_configuration_run(find_gem_cmd, passing) |
112 | 128 | 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 | |
113 | 134 | 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 |
14 | 14 | |
15 | 15 | def test_head |
16 | 16 | assert_equal "HEAD", @source.head |
17 | ||
17 | ||
18 | 18 | # With :branch |
19 | 19 | @config[:branch] = "master" |
20 | 20 | assert_equal "master", @source.head |
35 | 35 | # With :scm_command |
36 | 36 | git = "/opt/local/bin/git" |
37 | 37 | @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+/, ' ') | |
39 | 39 | |
40 | 40 | # with submodules |
41 | 41 | @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+/, ' ') | |
43 | 52 | end |
44 | 53 | |
45 | 54 | def test_checkout_with_verbose_should_not_use_q_switch |
47 | 56 | @config[:scm_verbose] = true |
48 | 57 | dest = "/var/www" |
49 | 58 | 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) | |
51 | 68 | end |
52 | 69 | |
53 | 70 | def test_diff |
60 | 77 | assert_equal "git log master..branch", @source.log('master', 'branch') |
61 | 78 | end |
62 | 79 | |
63 | def test_query_revision | |
80 | def test_query_revision_from_remote | |
64 | 81 | revision = @source.query_revision('HEAD') do |o| |
65 | 82 | assert_equal "git ls-remote . HEAD", o |
66 | 83 | "d11006102c07c94e5d54dd0ee63dca825c93ed61\tHEAD" |
67 | 84 | end |
68 | 85 | assert_equal "d11006102c07c94e5d54dd0ee63dca825c93ed61", revision |
69 | 86 | 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 | ||
71 | 97 | def test_query_revision_has_whitespace |
72 | 98 | revision = @source.query_revision('HEAD') do |o| |
73 | 99 | assert_equal "git ls-remote . HEAD", o |
91 | 117 | def test_sync |
92 | 118 | dest = "/var/www" |
93 | 119 | 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) | |
95 | 121 | |
96 | 122 | # With :scm_command |
97 | 123 | git = "/opt/local/bin/git" |
98 | 124 | @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) | |
100 | 126 | |
101 | 127 | # with submodules |
102 | 128 | @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) | |
104 | 130 | end |
105 | 131 | |
106 | 132 | def test_sync_with_remote |
112 | 138 | @config[:repository] = repository |
113 | 139 | @config[:remote] = remote |
114 | 140 | |
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) | |
116 | 142 | end |
117 | 143 | |
118 | 144 | def test_shallow_clone |
137 | 163 | @config[:git_enable_submodules] = true |
138 | 164 | dest = "/var/www" |
139 | 165 | 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) | |
141 | 167 | end |
142 | 168 | |
143 | 169 | # Tests from base_test.rb, makin' sure we didn't break anything up there! |
181 | 207 | assert_equal "/foo/bar/git", @source.command |
182 | 208 | end |
183 | 209 | 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⏎ |
28 | 28 | end |
29 | 29 | assert_equal 2095, revision |
30 | 30 | 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 | ||
31 | 39 | end |
13 | 13 | @config.stubs(:source).returns(@source) |
14 | 14 | @strategy = Capistrano::Deploy::Strategy::Copy.new(@config) |
15 | 15 | end |
16 | ||
16 | ||
17 | 17 | def test_deploy_with_defaults_should_use_remote_gtar |
18 | 18 | @config[:copy_remote_tar] = 'gtar' |
19 | ||
19 | ||
20 | 20 | Dir.expects(:tmpdir).returns("/temp/dir") |
21 | 21 | @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout) |
22 | 22 | @strategy.expects(:system).with(:local_checkout) |
23 | 23 | |
24 | 24 | 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") | |
26 | 26 | @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz") |
27 | 27 | @strategy.expects(:run).with("cd /u/apps/test/releases && gtar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz") |
28 | 28 | |
32 | 32 | |
33 | 33 | FileUtils.expects(:rm).with("/temp/dir/1234567890.tar.gz") |
34 | 34 | FileUtils.expects(:rm_rf).with("/temp/dir/1234567890") |
35 | ||
36 | @strategy.deploy! | |
37 | end | |
38 | ||
35 | ||
36 | @strategy.deploy! | |
37 | end | |
38 | ||
39 | 39 | def test_deploy_with_defaults_should_use_local_gtar |
40 | 40 | @config[:copy_local_tar] = 'gtar' |
41 | ||
41 | ||
42 | 42 | Dir.expects(:tmpdir).returns("/temp/dir") |
43 | 43 | @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout) |
44 | 44 | @strategy.expects(:system).with(:local_checkout) |
45 | 45 | |
46 | 46 | 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") | |
48 | 48 | @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz") |
49 | 49 | @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz") |
50 | 50 | |
54 | 54 | |
55 | 55 | FileUtils.expects(:rm).with("/temp/dir/1234567890.tar.gz") |
56 | 56 | FileUtils.expects(:rm_rf).with("/temp/dir/1234567890") |
57 | ||
58 | @strategy.deploy! | |
59 | end | |
57 | ||
58 | @strategy.deploy! | |
59 | end | |
60 | 60 | |
61 | 61 | def test_deploy_with_defaults_should_use_tar_gz_and_checkout |
62 | 62 | Dir.expects(:tmpdir).returns("/temp/dir") |
129 | 129 | @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout) |
130 | 130 | |
131 | 131 | @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") | |
133 | 133 | @strategy.expects(:upload).with("/temp/dir/1234567890.tar.bz2", "/tmp/1234567890.tar.bz2") |
134 | 134 | @strategy.expects(:run).with("cd /u/apps/test/releases && tar xjf /tmp/1234567890.tar.bz2 && rm /tmp/1234567890.tar.bz2") |
135 | 135 | |
142 | 142 | |
143 | 143 | @strategy.deploy! |
144 | 144 | end |
145 | ||
145 | ||
146 | 146 | def test_deploy_with_unknown_compression_type_should_error |
147 | 147 | @config[:copy_compression] = :bogus |
148 | 148 | Dir.expects(:tmpdir).returns("/temp/dir") |
149 | 149 | @source.expects(:checkout).with("154", "/temp/dir/1234567890").returns(:local_checkout) |
150 | 150 | @strategy.stubs(:system) |
151 | 151 | File.stubs(:open) |
152 | ||
152 | ||
153 | 153 | assert_raises(ArgumentError) { @strategy.deploy! } |
154 | 154 | end |
155 | ||
155 | ||
156 | 156 | def test_deploy_with_custom_copy_dir_should_use_that_as_tmpdir |
157 | 157 | Dir.expects(:tmpdir).never |
158 | 158 | Dir.expects(:chdir).with("/other/path").yields |
160 | 160 | @source.expects(:checkout).with("154", "/other/path/1234567890").returns(:local_checkout) |
161 | 161 | |
162 | 162 | @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") | |
164 | 164 | @strategy.expects(:upload).with("/other/path/1234567890.tar.gz", "/tmp/1234567890.tar.gz") |
165 | 165 | @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz") |
166 | 166 | |
181 | 181 | @source.expects(:checkout).returns(:local_checkout) |
182 | 182 | |
183 | 183 | @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") | |
185 | 185 | @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/somewhere/else/1234567890.tar.gz") |
186 | 186 | @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /somewhere/else/1234567890.tar.gz && rm /somewhere/else/1234567890.tar.gz") |
187 | 187 | |
231 | 231 | @strategy.deploy! |
232 | 232 | end |
233 | 233 | |
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 | |
235 | 235 | @config[:copy_cache] = "/u/caches/captest" |
236 | 236 | |
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") | |
237 | 256 | Dir.stubs(:tmpdir).returns("/temp/dir") |
238 | 257 | File.expects(:exists?).with("/u/caches/captest").returns(true) |
239 | 258 | Dir.expects(:chdir).with("/u/caches/captest").yields |
275 | 294 | File.expects(:directory?).with("app").returns(true) |
276 | 295 | FileUtils.expects(:mkdir).with("/temp/dir/1234567890/app") |
277 | 296 | 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") | |
279 | 298 | |
280 | 299 | Dir.expects(:glob).with("app/*", File::FNM_DOTMATCH).returns(["app/.", "app/..", "app/bar.txt"]) |
281 | 300 | |
282 | 301 | unless exclude |
283 | 302 | 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") | |
285 | 304 | end |
286 | 305 | end |
287 | 306 | |
288 | 307 | def prepare_standard_compress_and_copy! |
289 | 308 | 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") | |
291 | 310 | @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz") |
292 | 311 | @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz") |
293 | 312 |
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⏎ |
81 | 81 | assert_equal success, Capistrano::SSH.connect(@server, :ssh_options => ssh_options, :user => "jamis", :port => 1235) |
82 | 82 | end |
83 | 83 | |
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 | ||
84 | 93 | def test_connect_with_ssh_options_should_see_server_options_override_ssh_options |
85 | 94 | ssh_options = { :username => "JamisMan", :port => 8125, :forward_agent => true } |
86 | 95 | server = server("jamis@capistrano:1235") |
0 | 0 | require "utils" |
1 | 1 | require 'capistrano/task_definition' |
2 | ||
3 | # Silences the wanrnings raised in the two deprecation tests | |
4 | $VERBOSE = nil | |
2 | 5 | |
3 | 6 | class TaskDefinitionTest < Test::Unit::TestCase |
4 | 7 | def setup |
19 | 22 | def test_fqn_at_top_level_when_default_should_be_default |
20 | 23 | task = new_task(:default) |
21 | 24 | 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) | |
22 | 37 | end |
23 | 38 | |
24 | 39 | def test_fqn_in_namespace_when_default_should_be_namespace_fqn |