capistrano 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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