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