sprinkle 0.1.4

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 (64) hide show
  1. data/CREDITS +14 -0
  2. data/History.txt +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest.txt +63 -0
  5. data/README.rdoc +218 -0
  6. data/Rakefile +4 -0
  7. data/TODO +56 -0
  8. data/bin/sprinkle +79 -0
  9. data/config/hoe.rb +70 -0
  10. data/config/requirements.rb +17 -0
  11. data/examples/merb/deploy.rb +5 -0
  12. data/examples/rails/README +15 -0
  13. data/examples/rails/deploy.rb +2 -0
  14. data/examples/rails/packages/database.rb +9 -0
  15. data/examples/rails/packages/essential.rb +6 -0
  16. data/examples/rails/packages/rails.rb +28 -0
  17. data/examples/rails/packages/search.rb +11 -0
  18. data/examples/rails/packages/server.rb +28 -0
  19. data/examples/rails/rails.rb +71 -0
  20. data/examples/sprinkle/sprinkle.rb +38 -0
  21. data/lib/sprinkle/actors/capistrano.rb +80 -0
  22. data/lib/sprinkle/actors/vlad.rb +30 -0
  23. data/lib/sprinkle/deployment.rb +33 -0
  24. data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
  25. data/lib/sprinkle/extensions/array.rb +7 -0
  26. data/lib/sprinkle/extensions/blank_slate.rb +5 -0
  27. data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
  28. data/lib/sprinkle/extensions/string.rb +10 -0
  29. data/lib/sprinkle/extensions/symbol.rb +7 -0
  30. data/lib/sprinkle/installers/apt.rb +20 -0
  31. data/lib/sprinkle/installers/gem.rb +33 -0
  32. data/lib/sprinkle/installers/installer.rb +85 -0
  33. data/lib/sprinkle/installers/rake.rb +17 -0
  34. data/lib/sprinkle/installers/rpm.rb +20 -0
  35. data/lib/sprinkle/installers/source.rb +120 -0
  36. data/lib/sprinkle/package.rb +94 -0
  37. data/lib/sprinkle/policy.rb +85 -0
  38. data/lib/sprinkle/script.rb +13 -0
  39. data/lib/sprinkle/sequence.rb +21 -0
  40. data/lib/sprinkle/version.rb +9 -0
  41. data/lib/sprinkle.rb +26 -0
  42. data/script/destroy +14 -0
  43. data/script/generate +14 -0
  44. data/spec/spec.opts +1 -0
  45. data/spec/spec_helper.rb +17 -0
  46. data/spec/sprinkle/actors/capistrano_spec.rb +150 -0
  47. data/spec/sprinkle/deployment_spec.rb +80 -0
  48. data/spec/sprinkle/extensions/array_spec.rb +19 -0
  49. data/spec/sprinkle/extensions/string_spec.rb +21 -0
  50. data/spec/sprinkle/installers/apt_spec.rb +53 -0
  51. data/spec/sprinkle/installers/gem_spec.rb +75 -0
  52. data/spec/sprinkle/installers/installer_spec.rb +125 -0
  53. data/spec/sprinkle/installers/rpm_spec.rb +50 -0
  54. data/spec/sprinkle/installers/source_spec.rb +315 -0
  55. data/spec/sprinkle/package_spec.rb +247 -0
  56. data/spec/sprinkle/policy_spec.rb +126 -0
  57. data/spec/sprinkle/script_spec.rb +51 -0
  58. data/spec/sprinkle/sequence_spec.rb +44 -0
  59. data/spec/sprinkle/sprinkle_spec.rb +25 -0
  60. data/sprinkle.gemspec +43 -0
  61. data/tasks/deployment.rake +34 -0
  62. data/tasks/environment.rake +7 -0
  63. data/tasks/rspec.rake +21 -0
  64. metadata +157 -0
@@ -0,0 +1,75 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Sprinkle::Installers::Gem do
4
+
5
+ before do
6
+ @gem = 'rails'
7
+ @version = '2.0.2'
8
+ @options = { :source => 'http://gems.github.com/', :repository => '/tmp/gems' }
9
+ end
10
+
11
+ def create_gem(gem, version = nil, options = {}, &block)
12
+ @package = mock(Sprinkle::Package, :name => gem, :version => version, :source => nil, :repository => nil)
13
+ Sprinkle::Installers::Gem.new(@package, gem, options, &block)
14
+ end
15
+
16
+ describe 'when created' do
17
+
18
+ before do
19
+ @installer = create_gem @gem, @version, @options
20
+ end
21
+
22
+ it 'should accept a single package to install' do
23
+ @installer.gem.should == @gem
24
+ end
25
+
26
+ it 'should optionally store a version of the gem to install' do
27
+ @installer.version.should == '2.0.2'
28
+ end
29
+
30
+ it 'should optionally store a source location of the gem to install' do
31
+ @installer.source.should == 'http://gems.github.com/'
32
+ end
33
+
34
+ it 'should optionally store the repository location where gems are to be installed' do
35
+ @installer.repository.should == @options[:repository]
36
+ end
37
+
38
+ end
39
+
40
+ describe 'during installation' do
41
+
42
+ describe 'without a version' do
43
+
44
+ before do
45
+ @installer = create_gem @gem do
46
+ pre :install, 'op1'
47
+ post :install, 'op2'
48
+ end
49
+ end
50
+
51
+ it 'should invoke the gem installer for the specified package' do
52
+ @installer.send(:install_commands).should == "gem install #{@gem}"
53
+ end
54
+
55
+ it 'should automatically insert pre/post commands for the specified package' do
56
+ @installer.send(:install_sequence).should == [ 'op1', "gem install #{@gem}", 'op2' ]
57
+ end
58
+
59
+ end
60
+
61
+ describe 'with a specific version' do
62
+
63
+ before do
64
+ @installer = create_gem @gem, @version
65
+ end
66
+
67
+ it 'should install a specific version if defined' do
68
+ @installer.send(:install_commands).should == "gem install #{@gem} --version '#{@version}'"
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,125 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Sprinkle::Installers::Installer do
4
+ include Sprinkle::Deployment
5
+
6
+ before do
7
+ @package = mock(Sprinkle::Package, :name => 'package')
8
+ @empty = Proc.new { }
9
+ @sequence = ['op1', 'op2']
10
+ @delivery = mock(Sprinkle::Deployment, :process => true)
11
+ @installer = create_installer
12
+ @installer.delivery = @delivery
13
+ @roles = []
14
+ @deployment = deployment do
15
+ delivery :capistrano
16
+ installer do; prefix '/usr/bin'; end
17
+ end
18
+ end
19
+
20
+ def create_installer(&block)
21
+ installer = Sprinkle::Installers::Installer.new @package, &block
22
+ installer.stub!(:puts).and_return
23
+
24
+ # this is actually an abstract class so we'll insert a few fake install sequences
25
+ class << installer
26
+ def install_sequence
27
+ ['op1', 'op2']
28
+ end
29
+ end
30
+
31
+ installer
32
+ end
33
+
34
+ describe 'when created' do
35
+
36
+ it 'should belong to a package' do
37
+ @installer.package.should == @package
38
+ end
39
+
40
+ describe 'with a block to customize installer defaults' do
41
+
42
+ it 'should accept an optional block to customize installers defaults' do
43
+ @installer = create_installer do; prefix '/usr/local'; end
44
+ @installer.prefix.should == '/usr/local'
45
+ end
46
+
47
+ it 'should override any deployment level defaults' do
48
+ @installer = create_installer do; prefix '/usr/local'; end
49
+ @installer.defaults(@deployment)
50
+ @installer.prefix.should == '/usr/local'
51
+ end
52
+ end
53
+ end
54
+
55
+ describe 'during configuration' do
56
+
57
+ before do
58
+ @default = Proc.new { }
59
+ @defaults = { :installer => @default }
60
+ @deployment.stub!(:defaults).and_return(@defaults)
61
+ end
62
+
63
+ it 'should be configurable via external defaults' do
64
+ @installer.should respond_to(:defaults)
65
+ end
66
+
67
+ it 'should select the defaults for the particular concrete installer class' do
68
+ @deployment.should_receive(:defaults).and_return(@defaults)
69
+ @defaults.should_receive(:[]).with(:installer).and_return(@default)
70
+ end
71
+
72
+ it 'should configure the installer delivery mechansim' do
73
+ @installer.should_receive(:instance_eval)
74
+ end
75
+
76
+ it 'should maintain an options hash set arbitrarily via method missing' do
77
+ @installer.instance_eval do
78
+ hsv 'gts'
79
+ end
80
+ @installer.hsv.should == 'gts'
81
+ end
82
+
83
+ after do
84
+ @installer.defaults(@deployment)
85
+ end
86
+
87
+ end
88
+
89
+ describe 'during installation' do
90
+
91
+ it 'should request the install sequence from the concrete class' do
92
+ @installer.should_receive(:install_sequence).and_return(@sequence)
93
+ end
94
+
95
+ describe 'when testing' do
96
+
97
+ before do
98
+ Sprinkle::OPTIONS[:testing] = true
99
+ @logger = mock(ActiveSupport::BufferedLogger, :debug => true, :debug? => true)
100
+ end
101
+
102
+ it 'should not invoke the delivery mechanism with the install sequence' do
103
+ @delivery.should_not_receive(:process)
104
+ end
105
+
106
+ it 'should print the install sequence to the console' do
107
+ @installer.should_receive(:logger).twice.and_return(@logger)
108
+ end
109
+
110
+ end
111
+
112
+ describe 'when in production' do
113
+ it 'should invoke the delivery mechanism to process the install sequence' do
114
+ @delivery.should_receive(:process).with(@package.name, @sequence, @roles)
115
+ end
116
+ end
117
+
118
+ after do
119
+ @installer.process(@roles)
120
+ Sprinkle::OPTIONS[:testing] = false
121
+ end
122
+
123
+ end
124
+
125
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Sprinkle::Installers::Rpm do
4
+
5
+ before do
6
+ @package = mock(Sprinkle::Package, :name => 'package')
7
+ end
8
+
9
+ def create_rpm(debs, &block)
10
+ Sprinkle::Installers::Rpm.new(@package, debs, &block)
11
+ end
12
+
13
+ describe 'when created' do
14
+
15
+ it 'should accept a single package to install' do
16
+ @installer = create_rpm 'ruby'
17
+ @installer.packages.should == [ 'ruby' ]
18
+ end
19
+
20
+ it 'should accept an array of packages to install' do
21
+ @installer = create_rpm %w( gcc gdb g++ )
22
+ @installer.packages.should == ['gcc', 'gdb', 'g++']
23
+ end
24
+
25
+ end
26
+
27
+ describe 'during installation' do
28
+
29
+ before do
30
+ @installer = create_rpm 'ruby' do
31
+ pre :install, 'op1'
32
+ post :install, 'op2'
33
+ end
34
+ @install_commands = @installer.send :install_commands
35
+ end
36
+
37
+ it 'should invoke the rpm installer for all specified packages' do
38
+ @install_commands.should =~ /rpm -Uvh ruby/
39
+ end
40
+
41
+ it 'should automatically insert pre/post commands for the specified package' do
42
+ @installer.send(:install_sequence).should == [ 'op1', 'rpm -Uvh ruby', 'op2' ]
43
+ end
44
+
45
+ it 'should specify a non interactive mode to the apt installer'
46
+ it 'should install a specific version if defined'
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,315 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Sprinkle::Installers::Source do
4
+ include Sprinkle::Deployment
5
+
6
+ before do
7
+ @source = 'ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz'
8
+
9
+ @deployment = deployment do
10
+ delivery :capistrano
11
+ source do
12
+ prefix '/usr'
13
+ archives '/usr/archives'
14
+ builds '/usr/builds'
15
+ end
16
+ end
17
+
18
+ @installer = create_source @source do
19
+ prefix '/usr/local'
20
+ archives '/usr/local/archives'
21
+ builds '/usr/local/builds'
22
+
23
+ enable %w( headers ssl deflate so )
24
+ disable %w( cache proxy rewrite )
25
+
26
+ with %w( debug extras )
27
+ without %w( fancyisms )
28
+ end
29
+
30
+ @installer.defaults(@deployment)
31
+ end
32
+
33
+ def create_source(source, version = nil, &block)
34
+ @package = mock(Sprinkle::Package, :name => 'package', :version => version)
35
+ Sprinkle::Installers::Source.new(@package, source, &block)
36
+ end
37
+
38
+ describe 'when created' do
39
+
40
+ it 'should accept a source archive name to install' do
41
+ @installer.source.should == @source
42
+ end
43
+
44
+ end
45
+
46
+ describe 'before installation' do
47
+
48
+ before do
49
+ @settings = { :prefix => '/usr/local', :archives => '/usr/local/tmp', :builds => '/usr/local/stage' }
50
+ end
51
+
52
+ it 'should fail if no installation area has been specified' do
53
+ @settings.delete(:prefix)
54
+ end
55
+
56
+ it 'should fail if no build area has been specified' do
57
+ @settings.delete(:builds)
58
+ end
59
+
60
+ it 'should fail if no source download area has been specified' do
61
+ @settings.delete(:archives)
62
+ end
63
+
64
+ after do
65
+ @settings.each { |k, v| @installer.send k, v }
66
+ lambda { @installer.install_sequence }.should raise_error
67
+ end
68
+
69
+ end
70
+
71
+ describe 'customized configuration' do
72
+
73
+ it 'should support specification of "enable" options' do
74
+ @installer.enable.should == %w( headers ssl deflate so )
75
+ end
76
+
77
+ it 'should support specification of "disable" options' do
78
+ @installer.disable.should == %w( cache proxy rewrite )
79
+ end
80
+
81
+ it 'should support specification of "with" options' do
82
+ @installer.with.should == %w( debug extras )
83
+ end
84
+
85
+ it 'should support specification of "without" options' do
86
+ @installer.without.should == %w( fancyisms )
87
+ end
88
+
89
+ it 'should support customized build area' do
90
+ @installer.prefix.should == '/usr/local'
91
+ end
92
+
93
+ it 'should support customized source area' do
94
+ @installer.archives.should == '/usr/local/archives'
95
+ end
96
+
97
+ it 'should support customized install area' do
98
+ @installer.builds.should == '/usr/local/builds'
99
+ end
100
+
101
+ end
102
+
103
+ describe 'during gnu source archive style installation' do
104
+
105
+ it 'should prepare the build, installation and source archives area' do
106
+ @installer.should_receive(:prepare).and_return(
107
+ [
108
+ 'mkdir -p /usr/local',
109
+ 'mkdir -p /usr/local/builds',
110
+ 'mkdir -p /usr/local/archives'
111
+ ]
112
+ )
113
+
114
+ end
115
+
116
+ it 'should download the source archive' do
117
+ @installer.should_receive(:download).and_return(
118
+ [
119
+ "wget -cq --directory-prefix='/usr/local/archives' #{@source}"
120
+ ]
121
+ )
122
+ end
123
+
124
+ it 'should extract the source archive' do
125
+ @installer.should_receive(:extract).and_return(
126
+ [
127
+ "bash -c 'cd /usr/local/builds && tar xzf /usr/local/archives/ruby-1.8.6-p111.tar.gz"
128
+ ]
129
+ )
130
+ end
131
+
132
+ it 'should configure the source' do
133
+ enable = %w( headers ssl deflate so ).inject([]) { |m, value| m << "--enable-#{value}"; m }
134
+ disable = %w( cache proxy rewrite ).inject([]) { |m, value| m << "--disable-#{value}"; m }
135
+
136
+ with = %w( debug extras ).inject([]) { |m, value| m << "--with-#{value}"; m }
137
+ without = %w( fancyisms ).inject([]) { |m, value| m << "--without-#{value}"; m }
138
+
139
+ options = "#{enable.join(' ')} #{disable.join(' ')} #{with.join(' ')} #{without.join(' ')}"
140
+
141
+ @installer.should_receive(:build).and_return(
142
+ [
143
+ "bash -c 'cd /usr/local/builds && ./configure --prefix=/usr/local #{options} > #{@package.name}-configure.log 2>&1'"
144
+ ]
145
+ )
146
+ end
147
+
148
+ it 'should build the source' do
149
+ @installer.should_receive(:build).and_return(
150
+ [
151
+ "bash -c 'cd /usr/local/builds && make > #{@package.name}-build.log 2>&1'"
152
+ ]
153
+ )
154
+ end
155
+
156
+ it 'should install the source' do
157
+ @installer.should_receive(:install).and_return(
158
+ [
159
+ "bash -c 'cd /usr/local/builds && make install > #{@package.name}-install.log 2>&1'"
160
+ ]
161
+ )
162
+ end
163
+
164
+ describe 'during a customized install' do
165
+
166
+ before do
167
+ @installer = create_source @source do
168
+ custom_install 'ruby setup.rb'
169
+ end
170
+
171
+ @installer.defaults(@deployment)
172
+ end
173
+
174
+ it 'should store the custom insatll commands' do
175
+ @installer.options[:custom_install].should == 'ruby setup.rb'
176
+ end
177
+
178
+ it 'should identify as having a custom install command' do
179
+ @installer.should be_custom_install
180
+ end
181
+
182
+ it 'should not configure the source automatically' do
183
+ @installer.should_receive(:configure).and_return([])
184
+ end
185
+
186
+ it 'should not build the source automatically' do
187
+ @installer.should_receive(:build).and_return([])
188
+ end
189
+
190
+ it 'should install the source using a custom installation command' do
191
+ @installer.send(:custom_install_commands).should == [ "bash -c 'cd /usr/builds/ruby-1.8.6-p111 && ruby setup.rb >> package-install.log 2>&1'" ]
192
+ end
193
+
194
+ it 'should be run relative to the source build area' do
195
+ @installer.send(:custom_install_commands).first.should =~ %r{cd /usr/builds/ruby-1.8.6-p111}
196
+ end
197
+
198
+ end
199
+
200
+ after do
201
+ @installer.send :install_sequence
202
+ end
203
+
204
+ end
205
+
206
+ describe 'pre stage commands' do
207
+
208
+ before do
209
+ @commands = {
210
+ :prepare => %w( prepare1 prepare2 ),
211
+ :download => %w( down1 down2 ),
212
+ :extract => %w( ex1 ex2 ),
213
+ :configure => %w( conf1 conf2 ),
214
+ :build => %w( build1 build2 ),
215
+ :install => %w( install1 install2 )
216
+ }
217
+
218
+ @installer = create_source @source
219
+ @commands.each { |k, v| @installer.pre k, *v }
220
+ @installer.defaults(@deployment)
221
+ end
222
+
223
+ it 'should run all pre-prepare commands' do
224
+ @commands.each { |k, v| @installer.should_receive(:pre_commands).with(k).and_return(v) }
225
+ end
226
+
227
+ it 'should be run relative to the source build area' do
228
+ @commands.each { |stage, command| @installer.send(:pre_commands, stage).first.should =~ %r{cd /usr/builds/ruby-1.8.6-p111} }
229
+ end
230
+
231
+ after do
232
+ @installer.send :install_sequence
233
+ end
234
+
235
+ end
236
+
237
+ describe 'post stage commands' do
238
+
239
+ before do
240
+ @commands = {
241
+ :prepare => %w( prepare1 prepare2 ),
242
+ :download => %w( down1 down2 ),
243
+ :extract => %w( ex1 ex2 ),
244
+ :configure => %w( conf1 conf2 ),
245
+ :build => %w( build1 build2 ),
246
+ :install => %w( install1 install2 )
247
+ }
248
+
249
+ @installer = create_source @source
250
+ @commands.each { |k, v| @installer.post k, *v }
251
+ @installer.defaults(@deployment)
252
+ end
253
+
254
+ it 'should run all post-prepare commands' do
255
+ @commands.each { |k, v| @installer.should_receive(:post_commands).with(k).and_return(v) }
256
+ end
257
+
258
+ it 'should be run relative to the source build area' do
259
+ @commands.each { |stage, command| @installer.send(:post_commands, stage).first.should =~ %r{cd /usr/builds/ruby-1.8.6-p111} }
260
+ end
261
+
262
+ after do
263
+ @installer.send :install_sequence
264
+ end
265
+
266
+ end
267
+
268
+ describe 'install sequence' do
269
+
270
+ it 'should prepare, then download, then extract, then configure, then build, then install' do
271
+ %w( prepare download extract configure build install ).each do |stage|
272
+ @installer.should_receive(stage).ordered.and_return([])
273
+ end
274
+ end
275
+
276
+ after do
277
+ @installer.send :install_sequence
278
+ end
279
+
280
+ end
281
+
282
+ describe 'source extraction' do
283
+
284
+ it 'should support tgz archives' do
285
+ @installer.source = 'blah.tgz'
286
+ @extraction = 'tar xzf'
287
+ end
288
+
289
+ it 'should support tar.gz archives' do
290
+ @installer.source = 'blah.tgz'
291
+ @extraction = 'tar xzf'
292
+ end
293
+
294
+ it 'should support tar.bz2 archives' do
295
+ @installer.source = 'blah.tar.bz2'
296
+ @extraction = 'tar xjf'
297
+ end
298
+
299
+ it 'should support tb2 archives' do
300
+ @installer.source = 'blah.tb2'
301
+ @extraction = 'tar xjf'
302
+ end
303
+
304
+ it 'should support zip archives' do
305
+ @installer.source = 'blah.zip'
306
+ @extraction = 'unzip'
307
+ end
308
+
309
+ after do
310
+ @installer.send(:extract_command).should == @extraction
311
+ end
312
+
313
+ end
314
+
315
+ end