statistrano 1.2.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 (72) hide show
  1. checksums.yaml +7 -0
  2. data/changelog.md +161 -0
  3. data/doc/config/file-permissions.md +33 -0
  4. data/doc/config/log-files.md +32 -0
  5. data/doc/config/task-definitions.md +88 -0
  6. data/doc/getting-started.md +96 -0
  7. data/doc/strategies/base.md +38 -0
  8. data/doc/strategies/branches.md +82 -0
  9. data/doc/strategies/releases.md +110 -0
  10. data/doc/strategies.md +17 -0
  11. data/lib/statistrano/config/configurable.rb +53 -0
  12. data/lib/statistrano/config/rake_task_with_context_creation.rb +43 -0
  13. data/lib/statistrano/config.rb +52 -0
  14. data/lib/statistrano/deployment/log_file.rb +44 -0
  15. data/lib/statistrano/deployment/manifest.rb +88 -0
  16. data/lib/statistrano/deployment/rake_tasks.rb +74 -0
  17. data/lib/statistrano/deployment/registerable.rb +11 -0
  18. data/lib/statistrano/deployment/releaser/revisions.rb +163 -0
  19. data/lib/statistrano/deployment/releaser/single.rb +48 -0
  20. data/lib/statistrano/deployment/releaser.rb +2 -0
  21. data/lib/statistrano/deployment/strategy/base.rb +132 -0
  22. data/lib/statistrano/deployment/strategy/branches/index/template.html.erb +78 -0
  23. data/lib/statistrano/deployment/strategy/branches/index.rb +40 -0
  24. data/lib/statistrano/deployment/strategy/branches/release.rb +73 -0
  25. data/lib/statistrano/deployment/strategy/branches.rb +198 -0
  26. data/lib/statistrano/deployment/strategy/check_git.rb +43 -0
  27. data/lib/statistrano/deployment/strategy/invoke_tasks.rb +58 -0
  28. data/lib/statistrano/deployment/strategy/releases.rb +76 -0
  29. data/lib/statistrano/deployment/strategy.rb +37 -0
  30. data/lib/statistrano/deployment.rb +10 -0
  31. data/lib/statistrano/log/default_logger.rb +105 -0
  32. data/lib/statistrano/log.rb +33 -0
  33. data/lib/statistrano/remote/file.rb +79 -0
  34. data/lib/statistrano/remote.rb +111 -0
  35. data/lib/statistrano/shell.rb +17 -0
  36. data/lib/statistrano/util/file_permissions.rb +34 -0
  37. data/lib/statistrano/util.rb +27 -0
  38. data/lib/statistrano/version.rb +3 -0
  39. data/lib/statistrano.rb +55 -0
  40. data/readme.md +247 -0
  41. data/spec/integration_tests/base_integration_spec.rb +103 -0
  42. data/spec/integration_tests/branches_integration_spec.rb +189 -0
  43. data/spec/integration_tests/releases/deploy_integration_spec.rb +116 -0
  44. data/spec/integration_tests/releases/list_releases_integration_spec.rb +38 -0
  45. data/spec/integration_tests/releases/prune_releases_integration_spec.rb +86 -0
  46. data/spec/integration_tests/releases/rollback_release_integration_spec.rb +46 -0
  47. data/spec/lib/statistrano/config/configurable_spec.rb +88 -0
  48. data/spec/lib/statistrano/config/rake_task_with_context_creation_spec.rb +73 -0
  49. data/spec/lib/statistrano/config_spec.rb +34 -0
  50. data/spec/lib/statistrano/deployment/log_file_spec.rb +75 -0
  51. data/spec/lib/statistrano/deployment/manifest_spec.rb +171 -0
  52. data/spec/lib/statistrano/deployment/rake_tasks_spec.rb +107 -0
  53. data/spec/lib/statistrano/deployment/registerable_spec.rb +19 -0
  54. data/spec/lib/statistrano/deployment/releaser/revisions_spec.rb +486 -0
  55. data/spec/lib/statistrano/deployment/releaser/single_spec.rb +59 -0
  56. data/spec/lib/statistrano/deployment/strategy/base_spec.rb +158 -0
  57. data/spec/lib/statistrano/deployment/strategy/branches_spec.rb +19 -0
  58. data/spec/lib/statistrano/deployment/strategy/check_git_spec.rb +39 -0
  59. data/spec/lib/statistrano/deployment/strategy/invoke_tasks_spec.rb +66 -0
  60. data/spec/lib/statistrano/deployment/strategy/releases_spec.rb +257 -0
  61. data/spec/lib/statistrano/deployment/strategy_spec.rb +76 -0
  62. data/spec/lib/statistrano/deployment_spec.rb +4 -0
  63. data/spec/lib/statistrano/log/default_logger_spec.rb +172 -0
  64. data/spec/lib/statistrano/log_spec.rb +36 -0
  65. data/spec/lib/statistrano/remote/file_spec.rb +166 -0
  66. data/spec/lib/statistrano/remote_spec.rb +226 -0
  67. data/spec/lib/statistrano/util/file_permissions_spec.rb +25 -0
  68. data/spec/lib/statistrano/util_spec.rb +23 -0
  69. data/spec/lib/statistrano_spec.rb +52 -0
  70. data/spec/spec_helper.rb +86 -0
  71. data/spec/support/given.rb +39 -0
  72. metadata +223 -0
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Statistrano::Deployment::Registerable do
4
+
5
+ describe "::register_strategy" do
6
+ it "calls register strategy on Deployment" do
7
+ class Foo; end
8
+ expect( Statistrano::Deployment::Strategy ).to receive(:register)
9
+ .with( Foo, :foo )
10
+
11
+ class Foo
12
+ extend Statistrano::Deployment::Registerable
13
+
14
+ register_strategy :foo
15
+ end
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,486 @@
1
+ require 'spec_helper'
2
+
3
+ describe Statistrano::Deployment::Releaser::Revisions do
4
+
5
+ let(:default_remote_config_responses) do
6
+ {
7
+ remote_dir: '/var/www/proj',
8
+ local_dir: 'build',
9
+ release_count: 5,
10
+ release_dir: 'releases',
11
+ public_dir: 'current',
12
+ log_file_path: nil
13
+ }
14
+ end
15
+
16
+ describe "#initialize" do
17
+ it "creates a release_name based on current time" do
18
+ allow( Time ).to receive(:now).and_return(12345)
19
+ subject = described_class.new
20
+ expect( subject.release_name ).to eq "12345"
21
+ end
22
+ end
23
+
24
+ describe "#setup_release_path" do
25
+ context "with an existing release" do
26
+ it "copies existing 'current' release to release_path" do
27
+ config = double("Statistrano::Config", default_remote_config_responses )
28
+ remote = instance_double("Statistrano::Remote", config: config )
29
+ subject = described_class.new
30
+ release_path = File.join( '/var/www/proj/releases', subject.release_name )
31
+ allow( remote ).to receive(:run)
32
+ .and_return( HereOrThere::Response.new("","",true) )
33
+
34
+ expect( remote ).to receive(:create_remote_dir)
35
+ .with( '/var/www/proj/releases' )
36
+ expect( remote ).not_to receive(:create_remote_dir)
37
+ .with( release_path )
38
+
39
+ allow( remote ).to receive(:run).with("readlink /var/www/proj/current")
40
+ .and_return( HereOrThere::Response.new("/var/www/proj/releases/1234","",true) )
41
+ expect( remote ).to receive(:run)
42
+ .with("cp -a /var/www/proj/releases/1234 #{release_path}")
43
+ subject.setup_release_path remote
44
+ end
45
+ end
46
+ context "with no existing releases" do
47
+ it "creates the release_path on the remote" do
48
+ config = double("Statistrano::Config", default_remote_config_responses )
49
+ remote = instance_double("Statistrano::Remote", config: config )
50
+ subject = described_class.new
51
+ allow( remote ).to receive(:run)
52
+ .and_return( HereOrThere::Response.new("","",true) )
53
+
54
+ expect( remote ).to receive(:create_remote_dir)
55
+ .with( '/var/www/proj/releases' )
56
+ expect( remote ).to receive(:create_remote_dir)
57
+ .with( File.join( '/var/www/proj/releases', subject.release_name ) )
58
+ subject.setup_release_path remote
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "#rsync_to_remote" do
64
+ it "calls rsync_to_remote on the remote with the local_dir & release_path" do
65
+ config = double("Statistrano::Config", default_remote_config_responses )
66
+ remote = instance_double("Statistrano::Remote", config: config )
67
+ subject = described_class.new
68
+
69
+ allow( Dir ).to receive(:pwd).and_return('/local')
70
+ expect( remote ).to receive(:rsync_to_remote)
71
+ .with( '/local/build', File.join( '/var/www/proj/releases', subject.release_name ) )
72
+ .and_return( HereOrThere::Response.new("","",true) )
73
+ subject.rsync_to_remote remote
74
+ end
75
+ end
76
+
77
+ describe "#symlink_release" do
78
+ it "runs symlink command on remote" do
79
+ config = double("Statistrano::Config", default_remote_config_responses )
80
+ remote = instance_double("Statistrano::Remote", config: config )
81
+ subject = described_class.new
82
+ release_path = File.join( '/var/www/proj/releases', subject.release_name )
83
+
84
+ expect( remote ).to receive(:run)
85
+ .with( "ln -nfs #{release_path} /var/www/proj/current" )
86
+ subject.symlink_release remote
87
+ end
88
+ end
89
+
90
+ describe "#prune_releases" do
91
+ it "removes releases not tracked in manifest" do
92
+ config = double("Statistrano::Config", default_remote_config_responses )
93
+ remote = instance_double("Statistrano::Remote", config: config )
94
+ manifest = instance_double("Statistrano::Deployment::Manifest")
95
+ subject = described_class.new
96
+ releases = [ Time.now.to_i.to_s,
97
+ (Time.now.to_i + 1 ).to_s,
98
+ (Time.now.to_i + 2 ).to_s ]
99
+ extra_release = (Time.now.to_i + 3).to_s
100
+
101
+ allow(remote).to receive(:run)
102
+ .with("ls -m /var/www/proj/releases")
103
+ .and_return( HereOrThere::Response.new( (releases + [extra_release]).join(','), '', true ) )
104
+ allow(remote).to receive(:run)
105
+ .with("readlink /var/www/proj/current")
106
+ .and_return( HereOrThere::Response.new("",'',true) )
107
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
108
+ .and_return(manifest)
109
+ allow(manifest).to receive(:remove_if)
110
+ allow(manifest).to receive(:data)
111
+ .and_return(releases.map { |r| {release: r} })
112
+
113
+
114
+ expect(remote).to receive(:run)
115
+ .with("rm -rf /var/www/proj/releases/#{extra_release}")
116
+ expect(manifest).to receive(:save!)
117
+ subject.prune_releases remote
118
+ end
119
+
120
+ it "removes older releases beyond release count from remote & manifest" do
121
+ config = double("Statistrano::Config", default_remote_config_responses.merge(release_count: 2) )
122
+ remote = instance_double("Statistrano::Remote", config: config )
123
+ subject = described_class.new
124
+ manifest = Statistrano::Deployment::Manifest.new '/var/www/proj', remote
125
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
126
+ .with( '/var/www/proj', remote )
127
+ .and_return(manifest)
128
+ releases = [ Time.now.to_i.to_s,
129
+ (Time.now.to_i + 1 ).to_s,
130
+ (Time.now.to_i + 2 ).to_s ]
131
+
132
+ allow(remote).to receive(:run)
133
+ .with("ls -m /var/www/proj/releases")
134
+ .and_return( HereOrThere::Response.new( releases.join(','), '', true ) )
135
+ allow(remote).to receive(:run)
136
+ .with("readlink /var/www/proj/current")
137
+ .and_return( HereOrThere::Response.new("",'',true) )
138
+
139
+ # this is gnarly but we need to test against
140
+ # the manifest release data because of the block in
141
+ # Manifest#remove_if
142
+ allow(remote).to receive(:run)
143
+ .with("cat /var/www/proj/manifest.json")
144
+ .and_return( HereOrThere::Response.new("[#{ releases.map{ |r| "{\"release\": \"#{r}\"}" }.join(',') }]",'',true) )
145
+
146
+ expect(remote).to receive(:run)
147
+ .with("rm -rf /var/www/proj/releases/#{releases.first}")
148
+ expect(manifest).to receive(:save!)
149
+ subject.prune_releases remote
150
+
151
+ # our expectation is for manifest data to be missing
152
+ # the release that is to be removed
153
+ expect(manifest.data).to eq releases[1..-1].map {|r| {release: r}}
154
+ end
155
+
156
+ it "skips removing a release if it is currently symlinked" do
157
+ config = double("Statistrano::Config", default_remote_config_responses.merge(release_count: 2) )
158
+ remote = instance_double("Statistrano::Remote", config: config )
159
+ subject = described_class.new
160
+ manifest = Statistrano::Deployment::Manifest.new '/var/www/proj', remote
161
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
162
+ .with( '/var/www/proj', remote )
163
+ .and_return(manifest)
164
+ releases = [ Time.now.to_i.to_s,
165
+ (Time.now.to_i + 1 ).to_s,
166
+ (Time.now.to_i + 2 ).to_s ]
167
+
168
+ allow(remote).to receive(:run)
169
+ .with("ls -m /var/www/proj/releases")
170
+ .and_return( HereOrThere::Response.new( releases.join(','), '', true ) )
171
+
172
+ # this is gnarly but we need to test against
173
+ # the manifest release data because of the block in
174
+ # Manifest#remove_if
175
+ allow(remote).to receive(:run)
176
+ .with("cat /var/www/proj/manifest.json")
177
+ .and_return( HereOrThere::Response.new("[#{ releases.map{ |r| "{\"release\": \"#{r}\"}" }.join(',') }]",'',true) )
178
+
179
+ allow(remote).to receive(:run)
180
+ .with("readlink /var/www/proj/current")
181
+ .and_return( HereOrThere::Response.new("/var/www/proj/releases/#{releases.first}\n",'',true) )
182
+ expect(remote).not_to receive(:run)
183
+ .with("rm -rf /var/www/proj/releases/#{releases.first}")
184
+ expect(manifest).to receive(:save!)
185
+
186
+ subject.prune_releases remote
187
+ end
188
+ end
189
+
190
+ describe "#add_release_to_manifest" do
191
+ it "adds release to manifest & saves" do
192
+ config = double("Statistrano::Config", default_remote_config_responses )
193
+ remote = instance_double("Statistrano::Remote", config: config )
194
+ manifest = instance_double("Statistrano::Deployment::Manifest")
195
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
196
+ .and_return(manifest)
197
+ subject = described_class.new
198
+
199
+ expect(manifest).to receive(:push)
200
+ .with( release: subject.release_name )
201
+ expect(manifest).to receive(:save!)
202
+ subject.add_release_to_manifest remote
203
+ end
204
+
205
+ it "merges build_data to release in manifest" do
206
+ config = double("Statistrano::Config", default_remote_config_responses )
207
+ remote = instance_double("Statistrano::Remote", config: config )
208
+ manifest = instance_double("Statistrano::Deployment::Manifest")
209
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
210
+ .and_return(manifest)
211
+ subject = described_class.new
212
+
213
+ expect(manifest).to receive(:push)
214
+ .with( release: subject.release_name, arbitrary: 'data' )
215
+ expect(manifest).to receive(:save!)
216
+
217
+ subject.add_release_to_manifest remote, arbitrary: 'data'
218
+ end
219
+ end
220
+
221
+ describe "#create_release" do
222
+ it "runs through the pipeline" do
223
+ # stupid spec for now
224
+ remote = instance_double("Statistrano::Remote")
225
+ subject = described_class.new
226
+
227
+ expect(subject).to receive(:setup_release_path).with(remote)
228
+ expect(subject).to receive(:rsync_to_remote).with(remote)
229
+ expect(subject).to receive(:invoke_pre_symlink_task)
230
+ expect(subject).to receive(:symlink_release).with(remote)
231
+ expect(subject).to receive(:add_release_to_manifest).with(remote, arbitrary: 'data')
232
+ expect(subject).to receive(:prune_releases).with(remote)
233
+
234
+ subject.create_release remote, arbitrary: 'data'
235
+ end
236
+
237
+ it "aborts deploy if pre_symlink_task returns false or raises" do
238
+ config = double("Statistrano::Config", default_remote_config_responses.merge(
239
+ pre_symlink_task: lambda { false }
240
+ ))
241
+ remote = instance_double("Statistrano::Remote", config: config )
242
+ subject = described_class.new
243
+
244
+ expect(subject).to receive(:setup_release_path).with(remote)
245
+ expect(subject).to receive(:rsync_to_remote).with(remote)
246
+ expect(subject).to receive(:invoke_pre_symlink_task).and_call_original
247
+
248
+ expect(subject).not_to receive(:symlink_release)
249
+ expect(subject).not_to receive(:add_release_to_manifest)
250
+ expect(subject).not_to receive(:prune_releases)
251
+
252
+ expect {
253
+ subject.create_release remote
254
+ }.to raise_error SystemExit
255
+ end
256
+ end
257
+
258
+ describe "#list_releases" do
259
+ it "returns manifest data of releases" do
260
+ config = double("Statistrano::Config", default_remote_config_responses )
261
+ remote = instance_double("Statistrano::Remote", config: config )
262
+ subject = described_class.new
263
+ manifest = instance_double("Statistrano::Deployment::Manifest")
264
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
265
+ .and_return(manifest)
266
+
267
+ release_data = [{release:"one"},{release:"two"}]
268
+ allow(manifest).to receive(:data)
269
+ .and_return( release_data + [{not_release:"foo"}])
270
+
271
+ expect( subject.list_releases(remote) ).to match_array release_data
272
+ end
273
+ it "sorts releases by release data (newest first)" do
274
+ config = double("Statistrano::Config", default_remote_config_responses )
275
+ remote = instance_double("Statistrano::Remote", config: config )
276
+ subject = described_class.new
277
+ manifest = instance_double("Statistrano::Deployment::Manifest")
278
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
279
+ .and_return(manifest)
280
+
281
+ release_one = ( Time.now.to_i + 0 ).to_s
282
+ release_two = ( Time.now.to_i + 1 ).to_s
283
+ release_three = ( Time.now.to_i + 2 ).to_s
284
+
285
+ release_data = [{release:release_one},{release:release_three},{release:release_two}]
286
+ allow(manifest).to receive(:data)
287
+ .and_return( release_data + [{not_release:"foo"}])
288
+
289
+ expect( subject.list_releases(remote) ).to eq [{release:release_three},{release:release_two},{release:release_one}]
290
+ end
291
+ end
292
+
293
+ describe "#current_release_data" do
294
+ it "returns data from current release" do
295
+ config = double("Statistrano::Config", default_remote_config_responses )
296
+ remote = instance_double("Statistrano::Remote", config: config )
297
+ subject = described_class.new
298
+ manifest = instance_double("Statistrano::Deployment::Manifest")
299
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
300
+ .and_return(manifest)
301
+
302
+ manifest_data = [{release: 'first', foo: 'bar'},{release: 'current', random: 'data'}]
303
+ allow(manifest).to receive(:data)
304
+ .and_return(manifest_data)
305
+
306
+ expect( subject.current_release_data(remote) ).to eq release:'current', random:'data'
307
+ end
308
+
309
+ it "merges data from log if log_file_path given" do
310
+ config = double("Statistrano::Config", default_remote_config_responses.merge(log_file_path: '/var/log') )
311
+ remote = instance_double("Statistrano::Remote", config: config )
312
+ subject = described_class.new
313
+ manifest = instance_double("Statistrano::Deployment::Manifest")
314
+ log_file = instance_double("Statistrano::Deployment::LogFile")
315
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
316
+ .and_return(manifest)
317
+ allow( Statistrano::Deployment::LogFile ).to receive(:new)
318
+ .with('/var/log', remote)
319
+ .and_return(log_file)
320
+
321
+ manifest_data = [{release: 'first', foo: 'bar'},{release: 'current', random: 'data'}]
322
+ allow(manifest).to receive(:data)
323
+ .and_return(manifest_data)
324
+ allow(log_file).to receive(:last_entry)
325
+ .and_return name: 'current',
326
+ log: 'data-current',
327
+ nested: {
328
+ data: 'nested'
329
+ }
330
+
331
+ expect( subject.current_release_data(remote) ).to eq release:'current',
332
+ random: 'data',
333
+ name: 'current',
334
+ log: 'data-current',
335
+ nested: {
336
+ data: 'nested'
337
+ }
338
+ end
339
+
340
+ it "handles an empty log file if log_file_path is given" do
341
+ config = double("Statistrano::Config", default_remote_config_responses.merge(log_file_path: '/var/log') )
342
+ remote = instance_double("Statistrano::Remote", config: config )
343
+ subject = described_class.new
344
+ manifest = instance_double("Statistrano::Deployment::Manifest")
345
+ log_file = instance_double("Statistrano::Remote::File")
346
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
347
+ .and_return(manifest)
348
+ allow( Statistrano::Remote::File ).to receive(:new)
349
+ .with('/var/log', remote)
350
+ .and_return(log_file)
351
+
352
+ manifest_data = [{release: 'first', foo: 'bar'},{release: 'current', random: 'data'}]
353
+ allow(manifest).to receive(:data)
354
+ .and_return(manifest_data)
355
+ allow(log_file).to receive(:content)
356
+ .and_return ""
357
+
358
+ expect( subject.current_release_data(remote) ).to eq release:'current',
359
+ random: 'data'
360
+ end
361
+ end
362
+
363
+ describe "#rollback_release" do
364
+ it "symlinks the previous release" do
365
+ config = double("Statistrano::Config", default_remote_config_responses )
366
+ remote = instance_double("Statistrano::Remote", config: config )
367
+ subject = described_class.new
368
+ manifest = instance_double("Statistrano::Deployment::Manifest")
369
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
370
+ .with( '/var/www/proj', remote )
371
+ .and_return(manifest)
372
+
373
+
374
+ release_one = ( Time.now.to_i + 0 ).to_s
375
+ release_two = ( Time.now.to_i + 1 ).to_s
376
+ release_three = ( Time.now.to_i + 2 ).to_s
377
+
378
+ allow( manifest ).to receive(:data)
379
+ .and_return([
380
+ {release: release_one},
381
+ {release: release_two},
382
+ {release: release_three}
383
+ ])
384
+ allow( remote ).to receive(:run)
385
+ allow(remote).to receive(:run)
386
+ .with("readlink /var/www/proj/current")
387
+ .and_return( HereOrThere::Response.new("",'',true) )
388
+ allow( manifest ).to receive(:remove_if)
389
+ allow( manifest ).to receive(:save!)
390
+
391
+ expect( subject ).to receive(:symlink_release)
392
+ .with( remote, release_two )
393
+
394
+ subject.rollback_release remote
395
+ end
396
+
397
+ it "removes the newest release from disk on remote" do
398
+ config = double("Statistrano::Config", default_remote_config_responses )
399
+ remote = instance_double("Statistrano::Remote", config: config )
400
+ subject = described_class.new
401
+ manifest = instance_double("Statistrano::Deployment::Manifest")
402
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
403
+ .with( '/var/www/proj', remote )
404
+ .and_return(manifest)
405
+
406
+ release_one = ( Time.now.to_i + 0 ).to_s
407
+ release_two = ( Time.now.to_i + 1 ).to_s
408
+ release_three = ( Time.now.to_i + 2 ).to_s
409
+
410
+ allow( manifest ).to receive(:data)
411
+ .and_return([
412
+ {release: release_one},
413
+ {release: release_two},
414
+ {release: release_three}
415
+ ])
416
+
417
+
418
+ allow( subject ).to receive(:symlink_release)
419
+ .with( remote, release_two )
420
+ allow( manifest ).to receive(:remove_if)
421
+ allow( manifest ).to receive(:save!)
422
+
423
+ allow(remote).to receive(:run)
424
+ .with("readlink /var/www/proj/current")
425
+ .and_return( HereOrThere::Response.new("",'',true) )
426
+ expect( remote ).to receive(:run)
427
+ .with("rm -rf /var/www/proj/releases/#{release_three}")
428
+
429
+ subject.rollback_release remote
430
+ end
431
+
432
+ it "removes the newest release from the manifest" do
433
+ config = double("Statistrano::Config", default_remote_config_responses )
434
+ remote = instance_double("Statistrano::Remote", config: config )
435
+ subject = described_class.new
436
+ manifest = Statistrano::Deployment::Manifest.new '/var/www/proj', remote
437
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
438
+ .with( '/var/www/proj', remote )
439
+ .and_return(manifest)
440
+
441
+
442
+ release_one = ( Time.now.to_i + 0 ).to_s
443
+ release_two = ( Time.now.to_i + 1 ).to_s
444
+ release_three = ( Time.now.to_i + 2 ).to_s
445
+ releases = [release_three,release_two,release_one]
446
+ allow( remote ).to receive(:run)
447
+ allow(remote).to receive(:run)
448
+ .with("readlink /var/www/proj/current")
449
+ .and_return( HereOrThere::Response.new("",'',true) )
450
+
451
+
452
+ # this is gnarly but we need to test against
453
+ # the manifest release data because of the block in
454
+ # Manifest#remove_if
455
+ allow(remote).to receive(:run)
456
+ .with("cat /var/www/proj/manifest.json")
457
+ .and_return( HereOrThere::Response.new("[#{ releases.map{ |r| "{\"release\": \"#{r}\"}" }.join(',') }]",'',true) )
458
+
459
+ allow( subject ).to receive(:symlink_release)
460
+ .with( remote, release_two )
461
+
462
+ expect( manifest ).to receive(:save!)
463
+ subject.rollback_release remote
464
+ expect( manifest.data ).to eq releases[1..-1].map {|r| {release: r}}
465
+ end
466
+
467
+ it "errors if there is only one release" do
468
+ config = double("Statistrano::Config", default_remote_config_responses )
469
+ remote = instance_double("Statistrano::Remote", config: config )
470
+ subject = described_class.new
471
+ manifest = instance_double("Statistrano::Deployment::Manifest")
472
+ allow( Statistrano::Deployment::Manifest ).to receive(:new)
473
+ .and_return(manifest)
474
+
475
+ release_one = ( Time.now.to_i + 0 ).to_s
476
+ allow( manifest ).to receive(:data)
477
+ .and_return([
478
+ { release: release_one }
479
+ ])
480
+
481
+ expect( Statistrano::Log ).to receive(:error)
482
+ subject.rollback_release remote
483
+ end
484
+ end
485
+
486
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Statistrano::Deployment::Releaser::Single do
4
+
5
+ let(:default_remote_config_responses) do
6
+ {
7
+ remote_dir: '/var/www/proj',
8
+ local_dir: 'build'
9
+ }
10
+ end
11
+
12
+ describe "#initialize" do
13
+ it "creates a release_name based on current time" do
14
+ allow( Time ).to receive(:now).and_return(12345)
15
+ subject = described_class.new
16
+ expect( subject.release_name ).to eq "12345"
17
+ end
18
+ end
19
+
20
+ describe "#create_release" do
21
+ it "runs through the pipeline" do
22
+ remote = instance_double("Statistrano::Remote")
23
+ subject = described_class.new
24
+
25
+ expect(subject).to receive(:setup).with(remote)
26
+ expect(subject).to receive(:rsync_to_remote).with(remote)
27
+
28
+ subject.create_release remote, {}
29
+ end
30
+ end
31
+
32
+ describe "#setup" do
33
+ it "creates the remote_dir on the target" do
34
+ config = double("Statistrano::Config", default_remote_config_responses)
35
+ remote = instance_double("Statistrano::Remote", config: config )
36
+ subject = described_class.new
37
+
38
+ expect( remote ).to receive(:run)
39
+ .with("mkdir -p /var/www/proj")
40
+ .and_return( HereOrThere::Response.new("","",true) )
41
+ subject.setup remote
42
+ end
43
+ end
44
+
45
+ describe "#rsync_to_remote" do
46
+ it "calls rsync_to_remote on the remote with the local_dir & remote_dir" do
47
+ config = double("Statistrano::Config", default_remote_config_responses )
48
+ remote = instance_double("Statistrano::Remote", config: config )
49
+ subject = described_class.new
50
+
51
+ allow( Dir ).to receive(:pwd).and_return('/local')
52
+ expect( remote ).to receive(:rsync_to_remote)
53
+ .with( '/local/build', '/var/www/proj' )
54
+ .and_return( HereOrThere::Response.new("","",true) )
55
+ subject.rsync_to_remote remote
56
+ end
57
+ end
58
+
59
+ end