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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56e1f38d2b16ea6a3a7ea3f638ca46299dbb593d
4
- data.tar.gz: 52ca5fd168124dffdec952b953e56bbbeac73479
3
+ metadata.gz: 84bd6c187cb422b1e72b4dc30aa60012f46600b3
4
+ data.tar.gz: 3d14dd42f2b029cf97418d1508a84b250e6750c4
5
5
  SHA512:
6
- metadata.gz: 5363c5346e761369f59b7d1dc57568dd0a18f6bc47f9125d7a4b898111e82153fd4aba5fe20005d3f6a1502ce9906d48cd99241a8bb8de51bf490a159e1251c7
7
- data.tar.gz: c3ed3e7e73606aca077b6264a2313863f66dacdca8b5428c1d085072f130d25fda734f1323e420cb56f3cd3487b7b4d3124b13f539e2c15942aa394d975508ad
6
+ metadata.gz: c29d2b06b09efe69089e18e791509d30affa865e0bc9a22dc1a8ed7db3fba3bd30142c54238fa31761f433b8039e793e99d0a1bed8566e65c8ff800e590250d5
7
+ data.tar.gz: e05a117d41e9811b65621b54ca91501573b7a4503a645ec7fa618bfdeb2c37798c353ca4245b47fab73c1c2737a66bf5d3ada33238c3277d55f760bdc480bcac
data/.gitignore CHANGED
@@ -1,3 +1,8 @@
1
1
  *.gem
2
2
  bin/*
3
3
  Gemfile.lock
4
+
5
+ .bundle
6
+ .rspec
7
+ .ruby-version
8
+ .ruby-gemset
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
  ![Paratrooper](http://f.cl.ly/items/0Z1v1P1l1B1h1k1l2q0E/paratrooper_header.png)
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/paratrooper.png)](http://badge.fury.io/rb/paratrooper)
4
- [![Build Status](https://travis-ci.org/mattpolito/paratrooper.png?branch=master)](https://travis-ci.org/mattpolito/paratrooper)
5
- [![Code Climate](https://codeclimate.com/github/mattpolito/paratrooper.png)](https://codeclimate.com/github/mattpolito/paratrooper)
6
- [![Gitter chat](https://badges.gitter.im/mattpolito/paratrooper.png)](https://gitter.im/mattpolito/paratrooper)
3
+ [![Gem Version](http://img.shields.io/gem/v/paratrooper.svg?style=flat)](http://badge.fury.io/rb/paratrooper)
4
+ [![Build Status](http://img.shields.io/travis/mattpolito/paratrooper/master.svg?style=flat)](https://travis-ci.org/mattpolito/paratrooper)
5
+ [![Code Climate](http://img.shields.io/codeclimate/github/mattpolito/paratrooper.svg?style=flat)](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
@@ -2,4 +2,7 @@ require 'paratrooper/version'
2
2
  require 'paratrooper/deploy'
3
3
 
4
4
  module Paratrooper
5
+ def self.deploy(app_name, options = {}, &block)
6
+ Deploy.call(app_name, options, &block)
7
+ end
5
8
  end
@@ -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
@@ -1,24 +1,22 @@
1
- require 'paratrooper/heroku_wrapper'
2
- require 'paratrooper/system_caller'
3
- require 'paratrooper/notifiers/screen_notifier'
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
- include Callbacks
10
+ extend Forwardable
11
+ delegate [:system_caller, :migration_check, :notifiers,
12
+ :deloyment_host, :heroku, :source_control, :screen_notifier
13
+ ] => :config
14
14
 
15
- attr_accessor :app_name, :notifiers, :system_caller, :heroku, :tag_name,
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
- alias_method :tag=, :tag_name=
20
- alias_method :match_tag=, :match_tag_name=
21
- alias_method :branch=, :branch_name=
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
- @app_name = app_name
59
- @screen_notifier = options[:screen_notifier] || Notifiers::ScreenNotifier.new
60
- @notifiers = options[:notifiers] || [@screen_notifier]
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
- block.call(self) if block_given?
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 maintenance && pending_migrations?
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 pending_migrations?
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 / tag_name / 'master'
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, reference_point: reference_point)
140
- system_call "git push -f #{deployment_remote} #{reference_point}:refs/heads/master"
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 app_url
212
- heroku.app_url.sub(/\A\*\./, 'www.')
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
- app_url: app_url,
225
- deployment_remote: deployment_remote,
226
- tag_name: tag_name,
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
- git_remote(deployment_host, app_name)
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
@@ -0,0 +1,3 @@
1
+ module Paratrooper
2
+ class Error < StandardError; end
3
+ 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
- heroku_api.post_ps_restart(app_name)
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 = heroku_api.post_ps(app_name, task_name, attach: 'true').body
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 = heroku_api.get_releases(app_name).body
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
- heroku_api.post_app_maintenance(app_name, flag)
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 custom_domain_response
66
- @custom_domain_response ||= heroku_api.get_domains(app_name).body.last
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