paratrooper 2.4.1 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,9 @@
1
1
  require 'netrc'
2
+ require 'paratrooper/error'
2
3
 
3
4
  module Paratrooper
4
5
  class LocalApiKeyExtractor
5
- class NetrcFileDoesNotExist < StandardError; end
6
+ class NetrcFileDoesNotExist < Paratrooper::Error; end
6
7
 
7
8
  attr_reader :file_path, :netrc_klass
8
9
 
@@ -48,7 +48,12 @@ module Paratrooper
48
48
  end
49
49
 
50
50
  def push_repo(options = {})
51
- display("Pushing #{options[:reference_point]} to Heroku")
51
+ desc = "#{options[:reference_point]} to #{options[:app_name]} on Heroku"
52
+ if options[:force]
53
+ display("Force pushing #{desc}")
54
+ else
55
+ display("Pushing #{desc}")
56
+ end
52
57
  end
53
58
 
54
59
  def run_migrations(options = {})
@@ -58,10 +63,6 @@ module Paratrooper
58
63
  def app_restart(options = {})
59
64
  display("Restarting application")
60
65
  end
61
-
62
- def warm_instance(options = {})
63
- display("Accessing #{options[:app_url]} to warm up your application")
64
- end
65
66
  end
66
67
  end
67
68
  end
@@ -2,16 +2,17 @@ require 'paratrooper/system_caller'
2
2
 
3
3
  module Paratrooper
4
4
  class PendingMigrationCheck
5
- attr_accessor :diff, :heroku, :match_tag_name, :system_caller
5
+ attr_accessor :diff, :heroku, :deployment_sha, :system_caller
6
6
 
7
- def initialize(match_tag_name, heroku_wrapper, system_caller)
7
+ def initialize(deployment_sha, heroku_wrapper, system_caller)
8
8
  self.heroku = heroku_wrapper
9
- self.match_tag_name = match_tag_name
9
+ self.deployment_sha = deployment_sha
10
10
  self.system_caller = system_caller
11
11
  end
12
12
 
13
13
  def migrations_waiting?
14
- @migrations_waiting ||= check_for_pending_migrations
14
+ defined?(@migrations_waiting) or @migrations_waiting = check_for_pending_migrations
15
+ @migrations_waiting
15
16
  end
16
17
 
17
18
  def last_deployed_commit
@@ -21,8 +22,8 @@ module Paratrooper
21
22
  private
22
23
 
23
24
  def check_for_pending_migrations
24
- call = %Q[git diff --shortstat #{last_deployed_commit} #{match_tag_name} -- db/migrate]
25
- self.diff = system_caller.execute(call)
25
+ cmd = %Q[git diff --shortstat #{last_deployed_commit} #{deployment_sha} -- db/migrate]
26
+ self.diff = system_caller.execute(cmd)
26
27
  !diff.strip.empty?
27
28
  end
28
29
  end
@@ -0,0 +1,49 @@
1
+ require 'forwardable'
2
+
3
+ module Paratrooper
4
+ class SourceControl
5
+ extend Forwardable
6
+ delegate [:system_caller] => :config
7
+
8
+ attr_accessor :config
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ def remote
15
+ "git@#{config.deployment_host}:#{config.app_name}.git"
16
+ end
17
+
18
+ def branch_name
19
+ if config.branch_name?
20
+ branch = config.branch_name.to_s
21
+ branch.upcase == "HEAD" ? "HEAD" : "refs/heads/#{config.branch_name}"
22
+ end
23
+ end
24
+
25
+ def push_to_deploy
26
+ system_call("git push #{force_flag}#{remote} #{reference_point}:refs/heads/master", :exit_code)
27
+ end
28
+
29
+ def reference_point
30
+ branch_name || 'HEAD'
31
+ end
32
+
33
+ def deployment_sha
34
+ system_call("git rev-parse #{reference_point}").strip
35
+ end
36
+
37
+ def force_flag
38
+ "-f " if config.force_push
39
+ end
40
+
41
+ # Internal: Calls commands meant to go to system
42
+ #
43
+ # cmd - String version of system command
44
+ #
45
+ def system_call(cmd, exit_code = false)
46
+ system_caller.execute(cmd, exit_code)
47
+ end
48
+ end
49
+ end
@@ -1,19 +1,32 @@
1
+ require 'paratrooper/error'
2
+
1
3
  module Paratrooper
2
4
  class SystemCaller
5
+ class ErrorSystemExit < Paratrooper::Error
6
+ def initialize(cmd)
7
+ msg = "The system command '#{cmd}' has exited unsuccessfully"
8
+ super(msg)
9
+ end
10
+ end
3
11
  attr_accessor :debug
4
12
 
5
13
  def initialize(debug = false)
6
- self.debug = debug
14
+ @debug = debug
7
15
  end
8
16
 
9
- def execute(call)
10
- debug_message_for(call)
11
- `#{call}`
17
+ def execute(cmd, exit_code = false)
18
+ debug_message_for(cmd)
19
+ result = %x[#{cmd}]
20
+ if exit_code && $? != 0
21
+ fail(ErrorSystemExit.new(cmd))
22
+ end
23
+ result
12
24
  end
13
25
 
14
26
  private
15
- def debug_message_for(call)
16
- p "DEBUG: #{call}" if debug
27
+
28
+ def debug_message_for(cmd)
29
+ puts "DEBUG: #{cmd}" if debug
17
30
  end
18
31
  end
19
32
  end
@@ -1,3 +1,3 @@
1
1
  module Paratrooper
2
- VERSION = "2.4.1".freeze
2
+ VERSION = "3.0.0.beta1".freeze
3
3
  end
data/paratrooper.gemspec CHANGED
@@ -20,10 +20,10 @@ Gem::Specification.new do |gem|
20
20
  gem.required_ruby_version = '>= 1.9.2'
21
21
 
22
22
  gem.add_development_dependency 'rake'
23
- gem.add_development_dependency 'rspec', '~> 2.12'
23
+ gem.add_development_dependency 'rspec', '~> 3.0'
24
24
  gem.add_development_dependency 'pry'
25
25
  gem.add_dependency 'heroku-api', '~> 0.3'
26
- gem.add_dependency 'rendezvous', '~> 0.0.1'
26
+ gem.add_dependency 'rendezvous', '~> 0.1'
27
27
  gem.add_dependency 'netrc', '~> 0.7'
28
28
  gem.add_dependency 'excon'
29
29
  end
@@ -0,0 +1,352 @@
1
+ require 'spec_helper'
2
+ require 'paratrooper/configuration'
3
+
4
+ describe Paratrooper::Configuration do
5
+ let(:configuration) { described_class.new }
6
+
7
+ describe "attributes=" do
8
+ it "takes hash of attributes and calls the key as a method setter" do
9
+ attrs = { app_name: 'APP_NAME', screen_notifier: 'SCREEN_NOTIFIER' }
10
+ configuration.attributes = attrs
11
+
12
+ expect(configuration.app_name).to eq('APP_NAME')
13
+ expect(configuration.screen_notifier).to eq('SCREEN_NOTIFIER')
14
+ end
15
+ end
16
+
17
+ describe "app_name" do
18
+ context "with passed value" do
19
+ it "returns passed value" do
20
+ configuration.app_name = "APP_NAME"
21
+ expect(configuration.app_name).to eq("APP_NAME")
22
+ end
23
+ end
24
+
25
+ context "with no value passed" do
26
+ it "returns nil" do
27
+ expect(configuration.app_name).to be_nil
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "branch_name?" do
33
+ context "when branch_name is a valid string" do
34
+ it "returns true" do
35
+ configuration.branch_name = 'BRANCH_NAME'
36
+ expect(configuration.branch_name?).to eq(true)
37
+ end
38
+ end
39
+
40
+ context "when branch_name is an invalid string" do
41
+ it "returns false" do
42
+ configuration.branch_name = " "
43
+ expect(configuration.branch_name?).to eq(false)
44
+ end
45
+ end
46
+
47
+ context "when branch_name is nil" do
48
+ it "returns false" do
49
+ configuration.branch_name = nil
50
+ expect(configuration.branch_name?).to eq(false)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "branch_name" do
56
+ context "with passed value" do
57
+ it "returns passed value" do
58
+ configuration.branch_name = "TAG_NAME"
59
+ expect(configuration.branch_name).to eq("TAG_NAME")
60
+ end
61
+ end
62
+
63
+ context "with no value passed" do
64
+ it "returns nil" do
65
+ expect(configuration.branch_name).to be_nil
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "branch=" do
71
+ specify "branch is set and @branch_name holds value" do
72
+ configuration.branch = "branch_name"
73
+
74
+ expect(configuration.branch_name).to eq("branch_name")
75
+ end
76
+ end
77
+
78
+ describe "protocol" do
79
+ context "with passed value" do
80
+ specify "passed value is returned" do
81
+ configuration.protocol = 'https'
82
+
83
+ expect(configuration.protocol).to eq('https')
84
+ end
85
+ end
86
+
87
+ context "no value passed" do
88
+ specify "default value of 'http' is returned" do
89
+ expect(configuration.protocol).to eq('http')
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "deployment_host" do
95
+ context "with passed value" do
96
+ it "returns the value passed" do
97
+ configuration.deployment_host = 'HOST_NAME'
98
+ expect(configuration.deployment_host).to eq('HOST_NAME')
99
+ end
100
+ end
101
+
102
+ context "with no passed value" do
103
+ it "returns the default value of 'heroku.com'" do
104
+ expect(configuration.deployment_host).to eq('heroku.com')
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "api_key" do
110
+ context "with passed value" do
111
+ it "returns the value passed" do
112
+ configuration.api_key = 'API_KEY'
113
+ expect(configuration.api_key).to eq('API_KEY')
114
+ end
115
+ end
116
+
117
+ context "with no passed value" do
118
+ it "returns nil" do
119
+ expect(configuration.api_key).to be_nil
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "maintenance" do
125
+ context "with passed value" do
126
+ it "returns the value passed" do
127
+ configuration.maintenance = 'true'
128
+ expect(configuration.maintenance).to eq(true)
129
+ end
130
+ end
131
+
132
+ context "with no passed value" do
133
+ it "returns false" do
134
+ expect(configuration.maintenance).to eq(false)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "maintenance?" do
140
+ context "when #maintenance is truthy" do
141
+ it "returns true" do
142
+ configuration.maintenance = "YUP"
143
+ expect(configuration.maintenance?).to eq(true)
144
+ end
145
+ end
146
+
147
+ context "when #maintenance is falsey" do
148
+ it "returns false" do
149
+ configuration.maintenance = false
150
+ expect(configuration.maintenance?).to eq(false)
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "force_push" do
156
+ context "with passed value" do
157
+ it "returns the value passed" do
158
+ configuration.force_push = "true"
159
+ expect(configuration.force_push).to eq(true)
160
+ end
161
+ end
162
+
163
+ context "with no passed value" do
164
+ it "returns false" do
165
+ expect(configuration.force_push).to eq(false)
166
+ end
167
+ end
168
+ end
169
+
170
+ describe "force_push?" do
171
+ context "when #force_push is truthy" do
172
+ it "returns true" do
173
+ configuration.force_push = "YUP"
174
+ expect(configuration.force_push?).to eq(true)
175
+ end
176
+ end
177
+
178
+ context "when #force_push is falsey" do
179
+ it "returns false" do
180
+ configuration.force_push = false
181
+ expect(configuration.force_push?).to eq(false)
182
+ end
183
+ end
184
+ end
185
+
186
+ describe "heroku" do
187
+ context "with passed value" do
188
+ it "returns passed value" do
189
+ heroku = double(:heroku)
190
+ configuration.heroku = heroku
191
+
192
+ expect(configuration.heroku).to eq(heroku)
193
+ end
194
+ end
195
+
196
+ context "with no value passed" do
197
+ it "returns internal heroku wrapper" do
198
+ heroku = class_double("HerokuWrapper")
199
+ stub_const("Paratrooper::HerokuWrapper", heroku)
200
+ wrapper_instance = double(:heroku)
201
+ expect(heroku).to receive(:new).with("APP_NAME").and_return(wrapper_instance)
202
+
203
+ configuration.app_name = "APP_NAME"
204
+ expect(configuration.heroku).to eq(wrapper_instance)
205
+ end
206
+ end
207
+ end
208
+
209
+ describe "migration_check" do
210
+ context "with passed value" do
211
+ it "returns passed value" do
212
+ migration_check = double(:pending_migration_check)
213
+ configuration.migration_check = migration_check
214
+
215
+ expect(configuration.migration_check).to eq(migration_check)
216
+ end
217
+ end
218
+
219
+ context "with no value passed" do
220
+ it "returns the default pending migration check object" do
221
+ migration_check_class = class_double("PendingMigrationCheck")
222
+ stub_const("Paratrooper::PendingMigrationCheck", migration_check_class)
223
+ migration_check = double(:heroku)
224
+ source_control = double(:source_control, deployment_sha: "SHA")
225
+
226
+ configuration.heroku = "HEROKU"
227
+ configuration.system_caller = "SYSTEM"
228
+ configuration.source_control = source_control
229
+
230
+ expect(migration_check_class).to receive(:new).with("SHA", "HEROKU", "SYSTEM")
231
+ .and_return(migration_check)
232
+ expect(configuration.migration_check).to eq(migration_check)
233
+ end
234
+ end
235
+ end
236
+
237
+ describe "system_caller" do
238
+ context "with passed value" do
239
+ it "returns passed value" do
240
+ system_caller = double(:system_caller)
241
+ configuration.system_caller = system_caller
242
+
243
+ expect(configuration.system_caller).to eq(system_caller)
244
+ end
245
+ end
246
+
247
+ context "with no value passed" do
248
+ it "returns default system_caller" do
249
+ system_caller = class_double("SystemCaller")
250
+ stub_const("Paratrooper::SystemCaller", system_caller)
251
+ wrapper_instance = double(:system_caller)
252
+ expect(system_caller).to receive(:new).and_return(wrapper_instance)
253
+
254
+ expect(configuration.system_caller).to eq(wrapper_instance)
255
+ end
256
+ end
257
+ end
258
+
259
+ describe "http_client" do
260
+ context "with passed value" do
261
+ it "returns passed value" do
262
+ http_client = double(:http_client)
263
+ configuration.http_client = http_client
264
+
265
+ expect(configuration.http_client).to eq(http_client)
266
+ end
267
+ end
268
+
269
+ context "with no value passed" do
270
+ it "returns internal http_client wrapper" do
271
+ http_client = class_double("HttpClientWrapper")
272
+ stub_const("Paratrooper::HttpClientWrapper", http_client)
273
+ wrapper_instance = double(:http_client)
274
+ expect(http_client).to receive(:new).and_return(wrapper_instance)
275
+
276
+ expect(configuration.http_client).to eq(wrapper_instance)
277
+ end
278
+ end
279
+ end
280
+
281
+ describe "screen_notifier" do
282
+ context "with passed value" do
283
+ it "returns passed value" do
284
+ screen_notifier = double(:screen_notifier)
285
+ configuration.screen_notifier = screen_notifier
286
+
287
+ expect(configuration.screen_notifier).to eq(screen_notifier)
288
+ end
289
+ end
290
+
291
+ context "with no value passed" do
292
+ it "returns default screen_notifier" do
293
+ screen_notifier = class_double("Notifiers::ScreenNotifier")
294
+ stub_const("Paratrooper::Notifiers::ScreenNotifier", screen_notifier)
295
+ notifier = double(:screen_notifier)
296
+ expect(screen_notifier).to receive(:new).and_return(notifier)
297
+
298
+ expect(configuration.screen_notifier).to eq(notifier)
299
+ end
300
+ end
301
+ end
302
+
303
+ describe "notifiers" do
304
+ context "with passed value as array" do
305
+ it "returns passed value" do
306
+ notifier = double(:notifier)
307
+ configuration.notifiers = [notifier]
308
+
309
+ expect(configuration.notifiers).to eq([notifier])
310
+ end
311
+ end
312
+
313
+ context "with passed value as single item" do
314
+ it "returns array containing passed value" do
315
+ notifier = double(:notifier)
316
+ configuration.notifiers = notifier
317
+
318
+ expect(configuration.notifiers).to eq([notifier])
319
+ end
320
+ end
321
+
322
+ context "with no value passed" do
323
+ it "returns array containing screen notifier" do
324
+ configuration.screen_notifier = "SCREEN_NOTIFIER"
325
+
326
+ expect(configuration.notifiers).to eq(["SCREEN_NOTIFIER"])
327
+ end
328
+ end
329
+ end
330
+
331
+ describe "source_control" do
332
+ context "with passed value" do
333
+ it "returns passed value" do
334
+ source_control = double(:source_control)
335
+ configuration.source_control = source_control
336
+
337
+ expect(configuration.source_control).to eq(source_control)
338
+ end
339
+ end
340
+
341
+ context "with no value passed" do
342
+ it "returns default source_control" do
343
+ source_control = class_double("SourceControl")
344
+ stub_const("Paratrooper::SourceControl", source_control)
345
+ controller = double(:source_control)
346
+ expect(source_control).to receive(:new).with(configuration).and_return(controller)
347
+
348
+ expect(configuration.source_control).to eq(controller)
349
+ end
350
+ end
351
+ end
352
+ end