capistrano 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. data/bin/cap +11 -0
  2. data/examples/sample.rb +113 -0
  3. data/lib/capistrano.rb +1 -0
  4. data/lib/capistrano/actor.rb +438 -0
  5. data/lib/capistrano/cli.rb +295 -0
  6. data/lib/capistrano/command.rb +90 -0
  7. data/lib/capistrano/configuration.rb +243 -0
  8. data/lib/capistrano/extensions.rb +38 -0
  9. data/lib/capistrano/gateway.rb +118 -0
  10. data/lib/capistrano/generators/rails/deployment/deployment_generator.rb +25 -0
  11. data/lib/capistrano/generators/rails/deployment/templates/capistrano.rake +46 -0
  12. data/lib/capistrano/generators/rails/deployment/templates/deploy.rb +122 -0
  13. data/lib/capistrano/generators/rails/loader.rb +20 -0
  14. data/lib/capistrano/logger.rb +59 -0
  15. data/lib/capistrano/recipes/standard.rb +242 -0
  16. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  17. data/lib/capistrano/scm/base.rb +62 -0
  18. data/lib/capistrano/scm/baz.rb +118 -0
  19. data/lib/capistrano/scm/bzr.rb +70 -0
  20. data/lib/capistrano/scm/cvs.rb +124 -0
  21. data/lib/capistrano/scm/darcs.rb +27 -0
  22. data/lib/capistrano/scm/perforce.rb +139 -0
  23. data/lib/capistrano/scm/subversion.rb +122 -0
  24. data/lib/capistrano/ssh.rb +39 -0
  25. data/lib/capistrano/transfer.rb +90 -0
  26. data/lib/capistrano/utils.rb +26 -0
  27. data/lib/capistrano/version.rb +30 -0
  28. data/test/actor_test.rb +294 -0
  29. data/test/command_test.rb +43 -0
  30. data/test/configuration_test.rb +233 -0
  31. data/test/fixtures/config.rb +5 -0
  32. data/test/fixtures/custom.rb +3 -0
  33. data/test/scm/cvs_test.rb +186 -0
  34. data/test/scm/subversion_test.rb +137 -0
  35. data/test/ssh_test.rb +104 -0
  36. data/test/utils.rb +50 -0
  37. metadata +107 -0
@@ -0,0 +1,5 @@
1
+ set :application, "foo"
2
+ set :repository, "1/2/#{application}"
3
+ set :gateway, "#{__FILE__}.example.com"
4
+
5
+ role :web, "www.example.com", :primary => true
@@ -0,0 +1,3 @@
1
+ Capistrano.configuration(:must_exist).load do
2
+ set :gateway, "foo"
3
+ end
@@ -0,0 +1,186 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../../lib"
2
+
3
+ require File.dirname(__FILE__) + "/../utils"
4
+ require 'test/unit'
5
+ require 'capistrano/scm/cvs'
6
+
7
+ class ScmCvsTest < Test::Unit::TestCase
8
+ class CvsTest < Capistrano::SCM::Cvs
9
+ attr_accessor :story
10
+ attr_reader :last_path
11
+
12
+ def cvs_log(path,branch)
13
+ @last_path = path
14
+ story.shift
15
+ end
16
+
17
+ def cvs_branch(path)
18
+ "deploy-me"
19
+ end
20
+
21
+ end
22
+
23
+ class MockChannel
24
+ attr_reader :sent_data
25
+
26
+ def send_data(data)
27
+ @sent_data ||= []
28
+ @sent_data << data
29
+ end
30
+
31
+ def [](name)
32
+ "value"
33
+ end
34
+ end
35
+
36
+ class MockActor
37
+ attr_reader :command
38
+ attr_reader :channels
39
+ attr_accessor :story
40
+
41
+ def initialize(config)
42
+ @config = config
43
+ end
44
+
45
+ def run(command)
46
+ @command = command
47
+ @channels ||= []
48
+ @channels << MockChannel.new
49
+ story.each { |stream, line| yield @channels.last, stream, line }
50
+ end
51
+
52
+ def release_path
53
+ (@config[:now] || Time.now.utc).strftime("%Y%m%d%H%M%S")
54
+ end
55
+
56
+ def method_missing(sym, *args)
57
+ @config.send(sym, *args)
58
+ end
59
+ end
60
+
61
+ def setup
62
+ @config = MockConfiguration.new
63
+ @config[:repository] = ":ext:joetester@rubyforge.org:/hello/world"
64
+ @config[:cvs] = "/path/to/cvs"
65
+ @config[:password] = "chocolatebrownies"
66
+ @config[:now] = Time.utc(2005,8,24,12,0,0)
67
+ @scm = CvsTest.new(@config)
68
+ @actor = MockActor.new(@config)
69
+ @log_msg = <<MSG.strip
70
+ RCS file: /var/cvs/copland/copland/LICENSE,v
71
+ Working file: LICENSE
72
+ head: 1.1
73
+ branch:
74
+ locks: strict
75
+ access list:
76
+ keyword substitution: kv
77
+ total revisions: 1; selected revisions: 1
78
+ description:
79
+ ----------------------------
80
+ revision 1.1
81
+ date: 2004/08/29 04:23:36; author: minam; state: Exp;
82
+ New implementation.
83
+ =============================================================================
84
+
85
+ RCS file: /var/cvs/copland/copland/Rakefile,v
86
+ Working file: Rakefile
87
+ head: 1.7
88
+ branch:
89
+ locks: strict
90
+ access list:
91
+ keyword substitution: kv
92
+ total revisions: 7; selected revisions: 1
93
+ description:
94
+ ----------------------------
95
+ revision 1.7
96
+ date: 2004/09/15 16:35:01; author: minam; state: Exp; lines: +2 -1
97
+ Rakefile now publishes package documentation from doc/packages instead of
98
+ doc/packrat. Updated "latest updates" in manual.
99
+ =============================================================================
100
+
101
+ RCS file: /var/cvs/copland/copland/TODO,v
102
+ Working file: TODO
103
+ head: 1.18
104
+ branch:
105
+ locks: strict
106
+ access list:
107
+ keyword substitution: kv
108
+ total revisions: 18; selected revisions: 1
109
+ description:
110
+ ----------------------------
111
+ revision 1.18
112
+ date: 2004/10/12 02:21:02; author: minam; state: Exp; lines: +4 -1
113
+ Added RubyConf 2004 presentation.
114
+ =============================================================================
115
+
116
+ RCS file: /var/cvs/copland/copland/Attic/build-gemspec.rb,v
117
+ Working file: build-gemspec.rb
118
+ head: 1.5
119
+ branch:
120
+ locks: strict
121
+ access list:
122
+ keyword substitution: kv
123
+ total revisions: 5; selected revisions: 1
124
+ description:
125
+ ----------------------------
126
+ revision 1.5
127
+ date: 2004/08/29 04:10:17; author: minam; state: dead; lines: +0 -0
128
+ Here we go -- point of no return. Deleting existing implementation to make
129
+ way for new implementation.
130
+ =============================================================================
131
+
132
+ RCS file: /var/cvs/copland/copland/copland.gemspec,v
133
+ Working file: copland.gemspec
134
+ head: 1.12
135
+ branch:
136
+ locks: strict
137
+ access list:
138
+ keyword substitution: kv
139
+ total revisions: 13; selected revisions: 1
140
+ description:
141
+ ----------------------------
142
+ revision 1.12
143
+ date: 2004/09/11 21:45:58; author: minam; state: Exp; lines: +4 -4
144
+ Minor change in how version is communicated to gemspec.
145
+ =============================================================================
146
+ MSG
147
+ @scm.story = [ @log_msg ]
148
+ end
149
+
150
+ def test_latest_revision
151
+ @config[:local] = "/hello/world"
152
+ @scm.story = [ @log_msg ]
153
+ assert_equal "2004-10-12 02:21:02", @scm.latest_revision
154
+ assert_equal "/hello/world", @scm.last_path
155
+ end
156
+
157
+ def test_latest_with_default_local
158
+ @config[:local] = nil
159
+ @scm.story = [ @log_msg ]
160
+ assert_equal "2004-10-12 02:21:02", @scm.latest_revision
161
+ assert_equal ".", @scm.last_path
162
+ end
163
+
164
+ def test_checkout
165
+ @actor.story = []
166
+ assert_nothing_raised { @scm.checkout(@actor) }
167
+ assert_nil @actor.channels.last.sent_data
168
+ assert_match %r{/path/to/cvs}, @actor.command
169
+ end
170
+
171
+ def test_checkout_needs_ssh_password
172
+ @actor.story = [[:out, "joetester@rubyforge.org's password: "]]
173
+ assert_nothing_raised { @scm.checkout(@actor) }
174
+ assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
175
+ end
176
+
177
+ def test_current_branch
178
+ assert_equal "deploy-me", @scm.current_branch
179
+ end
180
+
181
+ def test_default_current_branch
182
+ @config[:branch] = "default-branch"
183
+ @scm = CvsTest.new(@config)
184
+ assert_equal "default-branch", @scm.current_branch
185
+ end
186
+ end
@@ -0,0 +1,137 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../../lib"
2
+
3
+ require File.dirname(__FILE__) + "/../utils"
4
+ require 'test/unit'
5
+ require 'capistrano/scm/subversion'
6
+
7
+ class ScmSubversionTest < Test::Unit::TestCase
8
+ class SubversionTest < Capistrano::SCM::Subversion
9
+ attr_accessor :story
10
+ attr_reader :last_path
11
+
12
+ def svn_log(path)
13
+ @last_path = path
14
+ story.shift
15
+ end
16
+ end
17
+
18
+ class MockChannel
19
+ attr_reader :sent_data
20
+
21
+ def send_data(data)
22
+ @sent_data ||= []
23
+ @sent_data << data
24
+ end
25
+
26
+ def [](name)
27
+ "value"
28
+ end
29
+ end
30
+
31
+ class MockActor
32
+ attr_reader :command
33
+ attr_reader :channels
34
+ attr_accessor :story
35
+
36
+ def initialize(config)
37
+ @config = config
38
+ end
39
+
40
+ def run(command)
41
+ @command = command
42
+ @channels ||= []
43
+ @channels << MockChannel.new
44
+ story.each { |stream, line| yield @channels.last, stream, line }
45
+ end
46
+
47
+ def method_missing(sym, *args)
48
+ @config.send(sym, *args)
49
+ end
50
+ end
51
+
52
+ def setup
53
+ @config = MockConfiguration.new
54
+ @config[:current_path] = "/mwa/ha/ha/current"
55
+ @config[:repository] = "/hello/world"
56
+ @config[:svn] = "/path/to/svn"
57
+ @config[:password] = "chocolatebrownies"
58
+ @scm = SubversionTest.new(@config)
59
+ @actor = MockActor.new(@config)
60
+ @log_msg = <<MSG.strip
61
+ ------------------------------------------------------------------------
62
+ r1967 | minam | 2005-08-03 06:59:03 -0600 (Wed, 03 Aug 2005) | 2 lines
63
+
64
+ Initial commit of the new capistrano utility
65
+
66
+ ------------------------------------------------------------------------
67
+ MSG
68
+ @scm.story = [ @log_msg ]
69
+ end
70
+
71
+ def test_latest_revision
72
+ @scm.story = [ @log_msg ]
73
+ assert_equal "1967", @scm.latest_revision
74
+ assert_equal "/hello/world", @scm.last_path
75
+ end
76
+
77
+ def test_latest_revision_searching_upwards
78
+ @scm.story = [ "-----------------------------\n", @log_msg ]
79
+ assert_equal "1967", @scm.latest_revision
80
+ assert_equal "/hello", @scm.last_path
81
+ end
82
+
83
+ def test_checkout
84
+ @actor.story = []
85
+ assert_nothing_raised { @scm.checkout(@actor) }
86
+ assert_nil @actor.channels.last.sent_data
87
+ assert_match %r{/path/to/svn co\s+-q}, @actor.command
88
+ end
89
+
90
+ def test_checkout_via_export
91
+ @actor.story = []
92
+ @config[:checkout] = "export"
93
+ assert_nothing_raised { @scm.checkout(@actor) }
94
+ assert_nil @actor.channels.last.sent_data
95
+ assert_match %r{/path/to/svn export\s+-q}, @actor.command
96
+ end
97
+
98
+ def test_update
99
+ @actor.story = []
100
+ assert_nothing_raised { @scm.update(@actor) }
101
+ assert_nil @actor.channels.last.sent_data
102
+ assert_match %r{/path/to/svn up}, @actor.command
103
+ end
104
+
105
+ def test_checkout_needs_ssh_password
106
+ @actor.story = [[:out, "Password: "]]
107
+ assert_nothing_raised { @scm.checkout(@actor) }
108
+ assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
109
+ end
110
+
111
+ def test_checkout_needs_http_password
112
+ @actor.story = [[:out, "Password for (something): "]]
113
+ assert_nothing_raised { @scm.checkout(@actor) }
114
+ assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
115
+ end
116
+
117
+ def test_checkout_needs_alternative_ssh_password
118
+ @actor.story = [[:out, "someone's password: "]]
119
+ assert_nothing_raised { @scm.checkout(@actor) }
120
+ assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
121
+ end
122
+
123
+ def test_svn_password
124
+ @config[:svn_password] = "butterscotchcandies"
125
+ @actor.story = [[:out, "Password: "]]
126
+ assert_nothing_raised { @scm.checkout(@actor) }
127
+ assert_equal ["butterscotchcandies\n"], @actor.channels.last.sent_data
128
+ end
129
+
130
+ def test_svn_username
131
+ @actor.story = []
132
+ @config[:svn_username] = "turtledove"
133
+ assert_nothing_raised { @scm.checkout(@actor) }
134
+ assert_nil @actor.channels.last.sent_data
135
+ assert_match %r{/path/to/svn co --username turtledove}, @actor.command
136
+ end
137
+ end
@@ -0,0 +1,104 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+
3
+ require File.dirname(__FILE__) + "/utils"
4
+ require 'test/unit'
5
+ require 'capistrano/ssh'
6
+
7
+ class SSHTest < Test::Unit::TestCase
8
+ class MockSSH
9
+ AuthenticationFailed = Net::SSH::AuthenticationFailed
10
+
11
+ class <<self
12
+ attr_accessor :story
13
+ attr_accessor :invocations
14
+ end
15
+
16
+ def self.start(server, opts, &block)
17
+ @invocations << [server, opts, block]
18
+ err = story.shift
19
+ raise err if err
20
+ end
21
+ end
22
+
23
+ def setup
24
+ @config = MockConfiguration.new
25
+ @config[:user] = 'demo'
26
+ @config[:password] = 'c0c0nutfr0st1ng'
27
+ MockSSH.story = []
28
+ MockSSH.invocations = []
29
+ end
30
+
31
+ def test_publickey_auth_succeeds_default_port_no_block
32
+ Net.const_during(:SSH, MockSSH) do
33
+ Capistrano::SSH.connect('demo.server.i', @config)
34
+ end
35
+
36
+ assert_equal 1, MockSSH.invocations.length
37
+ assert_equal 'demo.server.i', MockSSH.invocations.first[0]
38
+ assert_equal 22, MockSSH.invocations.first[1][:port]
39
+ assert_equal 'demo', MockSSH.invocations.first[1][:username]
40
+ assert_nil MockSSH.invocations.first[1][:password]
41
+ assert_equal %w(publickey hostbased),
42
+ MockSSH.invocations.first[1][:auth_methods]
43
+ assert_nil MockSSH.invocations.first[2]
44
+ end
45
+
46
+ def test_publickey_auth_succeeds_explicit_port_no_block
47
+ Net.const_during(:SSH, MockSSH) do
48
+ Capistrano::SSH.connect('demo.server.i', @config, 23)
49
+ end
50
+
51
+ assert_equal 1, MockSSH.invocations.length
52
+ assert_equal 23, MockSSH.invocations.first[1][:port]
53
+ assert_nil MockSSH.invocations.first[2]
54
+ end
55
+
56
+ def test_publickey_auth_succeeds_with_block
57
+ Net.const_during(:SSH, MockSSH) do
58
+ Capistrano::SSH.connect('demo.server.i', @config) do |session|
59
+ end
60
+ end
61
+
62
+ assert_equal 1, MockSSH.invocations.length
63
+ assert_instance_of Proc, MockSSH.invocations.first[2]
64
+ end
65
+
66
+ def test_publickey_auth_fails
67
+ MockSSH.story << Net::SSH::AuthenticationFailed
68
+
69
+ Net.const_during(:SSH, MockSSH) do
70
+ Capistrano::SSH.connect('demo.server.i', @config)
71
+ end
72
+
73
+ assert_equal 2, MockSSH.invocations.length
74
+
75
+ assert_nil MockSSH.invocations.first[1][:password]
76
+ assert_equal %w(publickey hostbased),
77
+ MockSSH.invocations.first[1][:auth_methods]
78
+
79
+ assert_equal 'c0c0nutfr0st1ng', MockSSH.invocations.last[1][:password]
80
+ assert_equal %w(password keyboard-interactive),
81
+ MockSSH.invocations.last[1][:auth_methods]
82
+ end
83
+
84
+ def test_password_auth_fails
85
+ MockSSH.story << Net::SSH::AuthenticationFailed
86
+ MockSSH.story << Net::SSH::AuthenticationFailed
87
+
88
+ Net.const_during(:SSH, MockSSH) do
89
+ assert_raises(Net::SSH::AuthenticationFailed) do
90
+ Capistrano::SSH.connect('demo.server.i', @config)
91
+ end
92
+ end
93
+
94
+ assert_equal 2, MockSSH.invocations.length
95
+
96
+ assert_nil MockSSH.invocations.first[1][:password]
97
+ assert_equal %w(publickey hostbased),
98
+ MockSSH.invocations.first[1][:auth_methods]
99
+
100
+ assert_equal 'c0c0nutfr0st1ng', MockSSH.invocations.last[1][:password]
101
+ assert_equal %w(password keyboard-interactive),
102
+ MockSSH.invocations.last[1][:auth_methods]
103
+ end
104
+ end
@@ -0,0 +1,50 @@
1
+ class Module
2
+ def const_during(constant, value)
3
+ if const_defined?(constant)
4
+ overridden = true
5
+ saved = const_get(constant)
6
+ remove_const(constant)
7
+ end
8
+
9
+ const_set(constant, value)
10
+ yield
11
+ ensure
12
+ if overridden
13
+ remove_const(constant)
14
+ const_set(constant, saved)
15
+ end
16
+ end
17
+ end
18
+
19
+ class MockLogger
20
+ def info(msg,pfx=nil) end
21
+ def debug(msg,pfx=nil) end
22
+ end
23
+
24
+ class MockConfiguration < Hash
25
+ def initialize(*args)
26
+ super
27
+ self[:release_path] = "/path/to/releases/version"
28
+ self[:ssh_options] = {}
29
+ end
30
+
31
+ def logger
32
+ @logger ||= MockLogger.new
33
+ end
34
+
35
+ def set(variable, value=nil, &block)
36
+ self[variable] = value
37
+ end
38
+
39
+ def respond_to?(sym)
40
+ self.has_key?(sym)
41
+ end
42
+
43
+ def method_missing(sym, *args)
44
+ if args.length == 0
45
+ self[sym]
46
+ else
47
+ super
48
+ end
49
+ end
50
+ end