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.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/CHANGELOG.md +9 -2
- data/README.md +3 -4
- data/lib/paratrooper.rb +3 -0
- data/lib/paratrooper/callbacks.rb +2 -2
- data/lib/paratrooper/configuration.rb +94 -0
- data/lib/paratrooper/deploy.rb +42 -105
- data/lib/paratrooper/error.rb +3 -0
- data/lib/paratrooper/heroku_wrapper.rb +16 -22
- data/lib/paratrooper/local_api_key_extractor.rb +2 -1
- data/lib/paratrooper/notifiers/screen_notifier.rb +6 -5
- data/lib/paratrooper/pending_migration_check.rb +7 -6
- data/lib/paratrooper/source_control.rb +49 -0
- data/lib/paratrooper/system_caller.rb +19 -6
- data/lib/paratrooper/version.rb +1 -1
- data/paratrooper.gemspec +2 -2
- data/spec/paratrooper/configuration_spec.rb +352 -0
- data/spec/paratrooper/deploy_spec.rb +77 -232
- data/spec/paratrooper/heroku_wrapper_spec.rb +8 -38
- data/spec/paratrooper/local_api_key_extractor_spec.rb +2 -3
- data/spec/paratrooper/notifier_spec.rb +1 -1
- data/spec/paratrooper/pending_migration_check_spec.rb +14 -7
- data/spec/paratrooper/source_control_spec.rb +233 -0
- metadata +32 -28
- data/.bundle/config +0 -2
- data/.rspec +0 -2
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84bd6c187cb422b1e72b4dc30aa60012f46600b3
|
4
|
+
data.tar.gz: 3d14dd42f2b029cf97418d1508a84b250e6750c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c29d2b06b09efe69089e18e791509d30affa865e0bc9a22dc1a8ed7db3fba3bd30142c54238fa31761f433b8039e793e99d0a1bed8566e65c8ff800e590250d5
|
7
|
+
data.tar.gz: e05a117d41e9811b65621b54ca91501573b7a4503a645ec7fa618bfdeb2c37798c353ca4245b47fab73c1c2737a66bf5d3ada33238c3277d55f760bdc480bcac
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 3.0.0.beta1
|
4
|
+
|
5
|
+
- Moved all state into configuration object
|
6
|
+
- Updated interface to start a deploy Ex: `Paratrooper.deploy('appname')`
|
7
|
+
- If any exception is thrown, the deploy process is aborted
|
8
|
+
- Stop deploy process if there is no access to Heroku
|
9
|
+
|
3
10
|
## 2.4.1
|
4
11
|
|
5
|
-
- Fix Deploy#app_url for wildcard domains
|
12
|
+
- Fix `Deploy#app_url` for wildcard domains
|
6
13
|
|
7
14
|
## 2.4.0
|
8
15
|
|
9
16
|
- Maintenance mode only runs around migrations now
|
10
|
-
- README updates around maintenance_mode
|
17
|
+
- README updates around `maintenance_mode=`
|
11
18
|
|
12
19
|
## 2.3.0
|
13
20
|
|
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|

|
2
2
|
|
3
|
-
[](https://gitter.im/mattpolito/paratrooper)
|
3
|
+
[](http://badge.fury.io/rb/paratrooper)
|
4
|
+
[](https://travis-ci.org/mattpolito/paratrooper)
|
5
|
+
[](https://codeclimate.com/github/mattpolito/paratrooper)
|
7
6
|
|
8
7
|
Simplify your [Heroku][] deploy with quick and concise deployment rake tasks.
|
9
8
|
|
data/lib/paratrooper.rb
CHANGED
@@ -14,14 +14,14 @@ module Paratrooper
|
|
14
14
|
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
15
15
|
end
|
16
16
|
|
17
|
-
private
|
18
|
-
|
19
17
|
def build_callback(name, context = nil, &block)
|
20
18
|
execute_callback("before_#{name}".to_sym, context)
|
21
19
|
block.call if block_given?
|
22
20
|
execute_callback("after_#{name}".to_sym, context)
|
23
21
|
end
|
24
22
|
|
23
|
+
private
|
24
|
+
|
25
25
|
def execute_callback(name, context)
|
26
26
|
callbacks[name].each { |c| c.call(context) }
|
27
27
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'paratrooper/heroku_wrapper'
|
2
|
+
require 'paratrooper/http_client_wrapper'
|
3
|
+
require 'paratrooper/system_caller'
|
4
|
+
require 'paratrooper/callbacks'
|
5
|
+
require 'paratrooper/pending_migration_check'
|
6
|
+
require 'paratrooper/source_control'
|
7
|
+
require 'paratrooper/notifiers/screen_notifier'
|
8
|
+
|
9
|
+
module Paratrooper
|
10
|
+
class Configuration
|
11
|
+
include Callbacks
|
12
|
+
|
13
|
+
attr_accessor :branch_name, :app_name, :api_key
|
14
|
+
attr_writer :protocol, :heroku, :migration_check,
|
15
|
+
:system_caller, :deployment_host, :http_client, :screen_notifier,
|
16
|
+
:source_control
|
17
|
+
|
18
|
+
alias :branch= :branch_name=
|
19
|
+
|
20
|
+
def attributes=(attrs)
|
21
|
+
attrs.each do |method, value|
|
22
|
+
public_send("#{method}=", value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def branch_name?
|
27
|
+
!branch_name.to_s.strip.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def migration_check
|
31
|
+
@migration_check ||= PendingMigrationCheck.new(source_control.deployment_sha, heroku, system_caller)
|
32
|
+
end
|
33
|
+
|
34
|
+
def heroku
|
35
|
+
@heroku ||= HerokuWrapper.new(app_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def screen_notifier
|
39
|
+
@screen_notifier ||= Notifiers::ScreenNotifier.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def notifiers=(notifers)
|
43
|
+
@notifiers = Array(notifers)
|
44
|
+
end
|
45
|
+
|
46
|
+
def notifiers
|
47
|
+
@notifiers ||= [@screen_notifier]
|
48
|
+
end
|
49
|
+
|
50
|
+
def protocol
|
51
|
+
@protocol ||= 'http'
|
52
|
+
end
|
53
|
+
|
54
|
+
def deployment_host
|
55
|
+
@deployment_host ||= 'heroku.com'
|
56
|
+
end
|
57
|
+
|
58
|
+
def http_client
|
59
|
+
@http_client ||= HttpClientWrapper.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def maintenance
|
63
|
+
@maintenance ||= false
|
64
|
+
end
|
65
|
+
|
66
|
+
def maintenance=(val)
|
67
|
+
@maintenance = !!val
|
68
|
+
end
|
69
|
+
|
70
|
+
def maintenance?
|
71
|
+
@maintenance
|
72
|
+
end
|
73
|
+
|
74
|
+
def force_push=(val)
|
75
|
+
@force_push= !!val
|
76
|
+
end
|
77
|
+
|
78
|
+
def force_push
|
79
|
+
@force_push ||= false
|
80
|
+
end
|
81
|
+
|
82
|
+
def force_push?
|
83
|
+
@force_push
|
84
|
+
end
|
85
|
+
|
86
|
+
def system_caller
|
87
|
+
@system_caller ||= SystemCaller.new
|
88
|
+
end
|
89
|
+
|
90
|
+
def source_control
|
91
|
+
@source_control ||= SourceControl.new(self)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/paratrooper/deploy.rb
CHANGED
@@ -1,24 +1,22 @@
|
|
1
|
-
require '
|
2
|
-
require 'paratrooper/
|
3
|
-
require 'paratrooper/
|
4
|
-
require 'paratrooper/pending_migration_check'
|
5
|
-
require 'paratrooper/callbacks'
|
6
|
-
require 'paratrooper/http_client_wrapper'
|
1
|
+
require 'forwardable'
|
2
|
+
require 'paratrooper/configuration'
|
3
|
+
require 'paratrooper/error'
|
7
4
|
|
8
5
|
module Paratrooper
|
9
6
|
|
10
7
|
# Public: Entry point into the library.
|
11
8
|
#
|
12
9
|
class Deploy
|
13
|
-
|
10
|
+
extend Forwardable
|
11
|
+
delegate [:system_caller, :migration_check, :notifiers,
|
12
|
+
:deloyment_host, :heroku, :source_control, :screen_notifier
|
13
|
+
] => :config
|
14
14
|
|
15
|
-
|
16
|
-
:match_tag_name, :protocol, :deployment_host, :migration_check, :debug,
|
17
|
-
:screen_notifier, :branch_name, :http_client, :maintenance
|
15
|
+
attr_writer :config
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
def self.call(app_name, options = {}, &block)
|
18
|
+
new(app_name, options, &block).deploy
|
19
|
+
end
|
22
20
|
|
23
21
|
# Public: Initializes a Deploy
|
24
22
|
#
|
@@ -30,14 +28,12 @@ module Paratrooper
|
|
30
28
|
# notified of steps in deployment process
|
31
29
|
# (optional).
|
32
30
|
# :heroku - Object wrapper around heroku-api (optional).
|
33
|
-
# :tag - String name to be used as a git reference
|
34
|
-
# point for deploying from specific tag
|
35
|
-
# (optional).
|
36
|
-
# :match_tag - String name of git reference point to match
|
37
|
-
# :tag to (optional).
|
38
31
|
# :branch - String name to be used as a git reference
|
39
|
-
# point for deploying from specific branch
|
32
|
+
# point for deploying from specific branch.
|
33
|
+
# Use :head to deploy from current branch
|
40
34
|
# (optional).
|
35
|
+
# :force - Force deploy using (-f flag) on deploy
|
36
|
+
# (optional, default: false)
|
41
37
|
# :system_caller - Object responsible for calling system
|
42
38
|
# commands (optional).
|
43
39
|
# :protocol - String web protocol to be used when pinging
|
@@ -55,22 +51,12 @@ module Paratrooper
|
|
55
51
|
# (optional).
|
56
52
|
#
|
57
53
|
def initialize(app_name, options = {}, &block)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@heroku = options[:heroku] || HerokuWrapper.new(app_name, options)
|
62
|
-
@tag_name = options[:tag]
|
63
|
-
@branch_name = options[:branch]
|
64
|
-
@match_tag_name = options[:match_tag] || 'master'
|
65
|
-
@system_caller = options[:system_caller] || SystemCaller.new(debug)
|
66
|
-
@protocol = options[:protocol] || 'http'
|
67
|
-
@deployment_host = options[:deployment_host] || 'heroku.com'
|
68
|
-
@debug = options[:debug] || false
|
69
|
-
@migration_check = options[:migration_check] || PendingMigrationCheck.new(match_tag_name, heroku, system_caller)
|
70
|
-
@http_client = options[:http_client] || HttpClientWrapper.new
|
71
|
-
@maintenance = options[:maintenance] || false
|
54
|
+
config.attributes = options.merge(app_name: app_name)
|
55
|
+
block.call(config) if block_given?
|
56
|
+
end
|
72
57
|
|
73
|
-
|
58
|
+
def config
|
59
|
+
@config ||= Configuration.new
|
74
60
|
end
|
75
61
|
|
76
62
|
# Public: Hook method called first in the deploy process.
|
@@ -93,7 +79,7 @@ module Paratrooper
|
|
93
79
|
# Public: Activates Heroku maintenance mode.
|
94
80
|
#
|
95
81
|
def activate_maintenance_mode
|
96
|
-
return unless
|
82
|
+
return unless maintenance_necessary?
|
97
83
|
callback(:activate_maintenance_mode) do
|
98
84
|
notify(:activate_maintenance_mode)
|
99
85
|
heroku.app_maintenance_on
|
@@ -103,41 +89,22 @@ module Paratrooper
|
|
103
89
|
# Public: Deactivates Heroku maintenance mode.
|
104
90
|
#
|
105
91
|
def deactivate_maintenance_mode
|
106
|
-
return unless
|
92
|
+
return unless maintenance_necessary?
|
107
93
|
callback(:deactivate_maintenance_mode) do
|
108
94
|
notify(:deactivate_maintenance_mode)
|
109
95
|
heroku.app_maintenance_off
|
110
96
|
end
|
111
97
|
end
|
112
98
|
|
113
|
-
def maintenance_mode(&block)
|
114
|
-
activate_maintenance_mode
|
115
|
-
block.call if block_given?
|
116
|
-
deactivate_maintenance_mode
|
117
|
-
end
|
118
|
-
|
119
|
-
# Public: Creates a git tag and pushes it to repository.
|
120
|
-
#
|
121
|
-
def update_repo_tag
|
122
|
-
unless tag_name.nil? || tag_name.empty?
|
123
|
-
callback(:update_repo_tag) do
|
124
|
-
notify(:update_repo_tag)
|
125
|
-
system_call "git tag #{tag_name} #{match_tag_name} -f"
|
126
|
-
system_call "git push -f origin #{tag_name}"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
99
|
# Public: Pushes repository to Heroku.
|
132
100
|
#
|
133
101
|
# Based on the following precedence:
|
134
|
-
# branch_name /
|
102
|
+
# branch_name / 'master'
|
135
103
|
#
|
136
104
|
def push_repo
|
137
|
-
reference_point = git_branch_name || git_tag_name || 'master'
|
138
105
|
callback(:push_repo) do
|
139
|
-
notify(:push_repo
|
140
|
-
|
106
|
+
notify(:push_repo)
|
107
|
+
source_control.push_to_deploy
|
141
108
|
end
|
142
109
|
end
|
143
110
|
|
@@ -161,41 +128,26 @@ module Paratrooper
|
|
161
128
|
end
|
162
129
|
end
|
163
130
|
|
164
|
-
# Public: cURL for application URL to start your Heroku dyno.
|
165
|
-
#
|
166
|
-
# wait_time - Integer length of time (seconds) to wait before making call
|
167
|
-
# to app
|
168
|
-
#
|
169
|
-
def warm_instance(wait_time = 3)
|
170
|
-
callback(:warm_instance) do
|
171
|
-
notify(:warm_instance)
|
172
|
-
sleep wait_time
|
173
|
-
http_client.get("#{protocol}://#{app_url}")
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
131
|
# Public: Execute common deploy steps.
|
178
132
|
#
|
179
133
|
# Default deploy consists of:
|
180
134
|
# * Activating maintenance page
|
181
|
-
# * Updating repository tag
|
182
135
|
# * Pushing repository to Heroku
|
183
136
|
# * Running database migrations
|
184
137
|
# * Restarting application on Heroku
|
185
138
|
# * Deactivating maintenance page
|
186
|
-
# * Accessing application URL to warm Heroku dyno
|
187
139
|
#
|
188
140
|
# Alias: #deploy
|
189
141
|
def default_deploy
|
190
142
|
setup
|
191
|
-
update_repo_tag
|
192
143
|
push_repo
|
193
144
|
maintenance_mode do
|
194
145
|
run_migrations
|
195
146
|
app_restart
|
196
147
|
end
|
197
|
-
warm_instance
|
198
148
|
teardown
|
149
|
+
rescue Paratrooper::Error => e
|
150
|
+
abort(e.message)
|
199
151
|
end
|
200
152
|
alias_method :deploy, :default_deploy
|
201
153
|
|
@@ -208,40 +160,33 @@ module Paratrooper
|
|
208
160
|
end
|
209
161
|
|
210
162
|
private
|
211
|
-
def
|
212
|
-
|
163
|
+
def maintenance_mode(&block)
|
164
|
+
activate_maintenance_mode
|
165
|
+
block.call if block_given?
|
166
|
+
deactivate_maintenance_mode
|
167
|
+
end
|
168
|
+
|
169
|
+
def maintenance_necessary?
|
170
|
+
config.maintenance? && pending_migrations?
|
213
171
|
end
|
214
172
|
|
215
173
|
def callback(name, &block)
|
216
|
-
build_callback(name, screen_notifier, &block)
|
174
|
+
config.build_callback(name, screen_notifier, &block)
|
217
175
|
end
|
218
176
|
|
219
177
|
# Internal: Payload data to be sent with notifications
|
220
178
|
#
|
221
179
|
def default_payload
|
222
180
|
{
|
223
|
-
app_name: app_name,
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
match_tag: match_tag_name
|
181
|
+
app_name: config.app_name,
|
182
|
+
deployment_remote: source_control.remote,
|
183
|
+
force_push: config.force_push,
|
184
|
+
reference_point: source_control.reference_point,
|
228
185
|
}
|
229
186
|
end
|
230
187
|
|
231
|
-
def git_remote(host, name)
|
232
|
-
"git@#{host}:#{name}.git"
|
233
|
-
end
|
234
|
-
|
235
|
-
def git_branch_name
|
236
|
-
"refs/heads/#{branch_name}" if branch_name
|
237
|
-
end
|
238
|
-
|
239
|
-
def git_tag_name
|
240
|
-
"refs/tags/#{tag_name}" if tag_name
|
241
|
-
end
|
242
|
-
|
243
188
|
def deployment_remote
|
244
|
-
|
189
|
+
source_control.remote
|
245
190
|
end
|
246
191
|
|
247
192
|
# Internal: Notifies other objects that an event has occurred
|
@@ -256,19 +201,11 @@ module Paratrooper
|
|
256
201
|
end
|
257
202
|
|
258
203
|
def pending_migrations?
|
259
|
-
migration_check.migrations_waiting?
|
204
|
+
@pending_migrations ||= migration_check.migrations_waiting?
|
260
205
|
end
|
261
206
|
|
262
207
|
def restart_required?
|
263
208
|
pending_migrations?
|
264
209
|
end
|
265
|
-
|
266
|
-
# Internal: Calls commands meant to go to system
|
267
|
-
#
|
268
|
-
# call - String version of system command
|
269
|
-
#
|
270
|
-
def system_call(call)
|
271
|
-
system_caller.execute(call)
|
272
|
-
end
|
273
210
|
end
|
274
211
|
end
|
@@ -1,9 +1,17 @@
|
|
1
1
|
require 'heroku-api'
|
2
2
|
require 'rendezvous'
|
3
3
|
require 'paratrooper/local_api_key_extractor'
|
4
|
+
require 'paratrooper/error'
|
4
5
|
|
5
6
|
module Paratrooper
|
6
7
|
class HerokuWrapper
|
8
|
+
class ErrorNoAccess < Paratrooper::Error
|
9
|
+
def initialize(name)
|
10
|
+
msg = "It appears that you may not have access to #{name}"
|
11
|
+
super(msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
7
15
|
attr_reader :api_key, :app_name, :heroku_api, :key_extractor, :rendezvous
|
8
16
|
|
9
17
|
def initialize(app_name, options = {})
|
@@ -15,7 +23,7 @@ module Paratrooper
|
|
15
23
|
end
|
16
24
|
|
17
25
|
def app_restart
|
18
|
-
|
26
|
+
client(:post_ps_restart, app_name)
|
19
27
|
end
|
20
28
|
|
21
29
|
def app_maintenance_off
|
@@ -26,44 +34,30 @@ module Paratrooper
|
|
26
34
|
app_maintenance('1')
|
27
35
|
end
|
28
36
|
|
29
|
-
def app_url
|
30
|
-
app_domain_name
|
31
|
-
end
|
32
|
-
|
33
37
|
def run_migrations
|
34
38
|
run_task('rake db:migrate')
|
35
39
|
end
|
36
40
|
|
37
41
|
def run_task(task_name)
|
38
|
-
data =
|
42
|
+
data = client(:post_ps, app_name, task_name, attach: 'true').body
|
39
43
|
rendezvous.start(url: data['rendezvous_url'])
|
40
44
|
end
|
41
45
|
|
42
46
|
def last_deploy_commit
|
43
|
-
data =
|
47
|
+
data = client(:get_releases, app_name).body
|
44
48
|
return nil if data.empty?
|
45
49
|
data.last['commit']
|
46
50
|
end
|
47
51
|
|
48
52
|
private
|
49
|
-
def app_domain_name
|
50
|
-
if custom_domain_response
|
51
|
-
custom_domain_response['domain']
|
52
|
-
else
|
53
|
-
default_domain_name
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
53
|
def app_maintenance(flag)
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def default_domain_name
|
62
|
-
heroku_api.get_app(app_name).body['domain_name']['domain']
|
54
|
+
client(:post_app_maintenance, app_name, flag)
|
63
55
|
end
|
64
56
|
|
65
|
-
def
|
66
|
-
|
57
|
+
def client(method, *args)
|
58
|
+
heroku_api.public_send(method, *args)
|
59
|
+
rescue Heroku::API::Errors::Forbidden => e
|
60
|
+
raise ErrorNoAccess.new(app_name)
|
67
61
|
end
|
68
62
|
end
|
69
63
|
end
|