paratrooper 2.4.1 → 3.0.0.beta1

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.
@@ -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