paratrooper 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paratrooper (0.3.0)
4
+ paratrooper (1.1.3)
5
5
  heroku-api (~> 0.3)
6
6
  netrc (~> 0.7)
7
7
 
@@ -11,7 +11,7 @@ GEM
11
11
  coderay (1.0.8)
12
12
  diff-lcs (1.1.3)
13
13
  excon (0.16.10)
14
- heroku-api (0.3.7)
14
+ heroku-api (0.3.8)
15
15
  excon (~> 0.16.10)
16
16
  method_source (0.8.1)
17
17
  netrc (0.7.7)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ## Paratrooper
1
+ ![Paratrooper](http://f.cl.ly/items/0Z1v1P1l1B1h1k1l2q0E/paratrooper_header.png)
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/paratrooper.png)](http://badge.fury.io/rb/paratrooper)
4
4
  [![Build Status](https://travis-ci.org/mattpolito/paratrooper.png?branch=master)](https://travis-ci.org/mattpolito/paratrooper)
@@ -175,6 +175,11 @@ end
175
175
  4. Push to the branch (`git push origin my-new-feature`)
176
176
  5. Create new Pull Request
177
177
 
178
+ ## Thanks
179
+
180
+ * [Rye Mason][] for the fantastic heading image
181
+
178
182
  [Heroku]: http://heroku.com
179
183
  [Heroku Toolbelt]: http://toolbelt.heroku.com
180
184
  [New Relic]: http://newrelic.com
185
+ [Rye Mason]: https://github.com/ryenotbread
@@ -1,62 +1,119 @@
1
1
  require 'paratrooper/heroku_wrapper'
2
- require 'paratrooper/default_formatter'
3
2
  require 'paratrooper/system_caller'
3
+ require 'paratrooper/notifiers/screen_notifier'
4
4
 
5
5
  module Paratrooper
6
+
7
+ # Public: Entry point into the library.
8
+ #
6
9
  class Deploy
7
- attr_reader :app_name, :formatter, :system_caller, :heroku, :tag_name,
10
+ attr_reader :app_name, :notifiers, :system_caller, :heroku, :tag_name,
8
11
  :match_tag
9
12
 
13
+ # Public: Initializes a Deploy
14
+ #
15
+ # app_name - A String naming the Heroku application to be interacted with.
16
+ # options - The Hash options is used to provide additional functionality.
17
+ # :notifiers - Array of objects responsible for handling
18
+ # notifications (optional).
19
+ # :heroku - Object wrapper around heroku-api (optional).
20
+ # :tag - String name to be used as a git reference
21
+ # point (optional).
22
+ # :match_tag_to - String name of git reference point to match
23
+ # :tag to (optional).
24
+ # :system_caller - Object responsible for calling system
25
+ # commands (optional).
10
26
  def initialize(app_name, options = {})
11
27
  @app_name = app_name
12
- @formatter = options[:formatter] || DefaultFormatter.new
28
+ @notifiers = options[:notifiers] || [Notifiers::ScreenNotifier.new]
13
29
  @heroku = options[:heroku] || HerokuWrapper.new(app_name, options)
14
30
  @tag_name = options[:tag]
15
31
  @match_tag = options[:match_tag_to] || 'master'
16
32
  @system_caller = options[:system_caller] || SystemCaller.new
17
33
  end
18
34
 
35
+ def setup
36
+ notify(:setup)
37
+ end
38
+
39
+ def teardown
40
+ notify(:teardown)
41
+ end
42
+
43
+ def notify(step, options={})
44
+ notifiers.each do |notifier|
45
+ notifier.notify(step, default_payload.merge(options))
46
+ end
47
+ end
48
+
49
+ # Public: Activates Heroku maintenance mode.
50
+ #
19
51
  def activate_maintenance_mode
20
- notify_screen("Activating Maintenance Mode")
52
+ notify(:activate_maintenance_mode)
21
53
  heroku.app_maintenance_on
22
54
  end
23
55
 
56
+ # Public: Deactivates Heroku maintenance mode.
57
+ #
24
58
  def deactivate_maintenance_mode
25
- notify_screen("Deactivating Maintenance Mode")
59
+ notify(:deactivate_maintenance_mode)
26
60
  heroku.app_maintenance_off
27
61
  end
28
62
 
63
+ # Public: Creates a git tag and pushes it to repository.
64
+ #
29
65
  def update_repo_tag
30
66
  unless tag_name.nil? || tag_name.empty?
31
- notify_screen("Updating Repo Tag: #{tag_name}")
67
+ notify(:update_repo_tag)
32
68
  system_call "git tag #{tag_name} #{match_tag} -f"
33
69
  system_call "git push -f origin #{tag_name}"
34
70
  end
35
71
  end
36
72
 
73
+ # Public: Pushes repository to Heroku.
74
+ #
37
75
  def push_repo
38
76
  reference_point = tag_name || 'master'
39
- notify_screen("Pushing #{reference_point} to Heroku")
77
+ notify(:push_repo, reference_point: reference_point)
40
78
  system_call "git push -f #{git_remote} #{reference_point}:master"
41
79
  end
42
80
 
81
+ # Public: Runs rails database migrations on your application.
82
+ #
43
83
  def run_migrations
44
- notify_screen("Running database migrations")
84
+ notify(:run_migrations)
45
85
  system_call "heroku run rake db:migrate --app #{app_name}"
46
86
  end
47
87
 
88
+ # Public: Restarts application on Heroku.
89
+ #
48
90
  def app_restart
49
- notify_screen("Restarting application")
91
+ notify(:app_restart)
50
92
  heroku.app_restart
51
93
  end
52
94
 
95
+ # Public: cURL for application URL to start your Heroku dyno.
96
+ #
53
97
  def warm_instance(wait_time = 3)
54
98
  sleep wait_time
55
- notify_screen("Accessing #{app_url} to warm up your application")
99
+ notify(:warm_instance)
56
100
  system_call "curl -Il http://#{app_url}"
57
101
  end
58
102
 
103
+ # Public: Execute common deploy steps.
104
+ #
105
+ # Default deploy consists of:
106
+ # * Activating maintenance page
107
+ # * Updating repository tag
108
+ # * Pushing repository to Heroku
109
+ # * Running database migrations
110
+ # * Restarting application on Heroku
111
+ # * Deactivating maintenance page
112
+ # * cURL'ing application URL to warm Heroku dyno
113
+ #
114
+ # Alias: #deploy
59
115
  def default_deploy
116
+ setup
60
117
  activate_maintenance_mode
61
118
  update_repo_tag
62
119
  push_repo
@@ -64,6 +121,7 @@ module Paratrooper
64
121
  app_restart
65
122
  deactivate_maintenance_mode
66
123
  warm_instance
124
+ teardown
67
125
  end
68
126
  alias_method :deploy, :default_deploy
69
127
 
@@ -72,14 +130,23 @@ module Paratrooper
72
130
  heroku.app_url
73
131
  end
74
132
 
75
- def git_remote
76
- "git@heroku.com:#{app_name}.git"
133
+ def default_payload
134
+ {
135
+ app_name: app_name,
136
+ app_url: app_url,
137
+ git_remote: git_remote,
138
+ tag_name: tag_name,
139
+ match_tag: match_tag
140
+ }
77
141
  end
78
142
 
79
- def notify_screen(message)
80
- formatter.display(message)
143
+ def git_remote
144
+ "git@heroku.com:#{app_name}.git"
81
145
  end
82
146
 
147
+ # Internal: Calls commands meant to go to system
148
+ #
149
+ # call - String version of system command
83
150
  def system_call(call)
84
151
  system_caller.execute(call)
85
152
  end
@@ -0,0 +1,17 @@
1
+ module Paratrooper
2
+ class Notifier
3
+ def notify(step_name, options = {})
4
+ self.send(step_name, options)
5
+ end
6
+
7
+ def activate_maintenance_mode(options = {}); end
8
+ def deactivate_maintenance_mode(options = {}); end
9
+ def update_repo_tag(options = {}); end
10
+ def push_repo(options = {}); end
11
+ def run_migrations(options = {}); end
12
+ def app_restart(options = {}); end
13
+ def warm_instance(options = {}); end
14
+ def setup(options = {}); end
15
+ def teardown(options = {}); end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require 'paratrooper/notifier'
2
+
3
+ module Paratrooper
4
+ module Notifiers
5
+ class AirbrakeNotifier
6
+ def setup
7
+ `airbrake deploy`
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ require 'paratrooper/notifier'
2
+
3
+ module Paratrooper
4
+ module Notifiers
5
+
6
+ # Public: Sends notification to NewRelic to stop monitoring while deploy is
7
+ # happening
8
+ #
9
+ class NewRelicNotifier < Notifier
10
+ attr_reader :account_id, :api_key, :application_id
11
+
12
+ # Public: Initializes NewRelicNotifier
13
+ #
14
+ # api_key - String api key from NewRelic
15
+ # account_id - String NewRelic account id
16
+ # application_id - String NewRelic id of application
17
+ def initialize(api_key, account_id, application_id)
18
+ @api_key = api_key
19
+ @account_id = account_id
20
+ @application_id = application_id
21
+ end
22
+
23
+ def setup
24
+ %x[curl https://heroku.newrelic.com/accounts/#{account_id}/applications/#{application_id}/ping_targets/disable -X POST -H "X-Api-Key: #{api_key}"]
25
+ end
26
+
27
+ def teardown
28
+ %x[curl https://heroku.newrelic.com/accounts/#{account_id}/applications/#{application_id}/ping_targets/enable -X POST -H "X-Api-Key: #{api_key}"]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,67 @@
1
+ require 'paratrooper/notifier'
2
+
3
+ module Paratrooper
4
+ module Notifiers
5
+
6
+ # Public: Default notifier for outputting messages to screen.
7
+ #
8
+ class ScreenNotifier < Notifier
9
+ attr_reader :output
10
+
11
+ # Public: Initializes ScreenNotifier
12
+ #
13
+ # output - IO object (default: STDOUT)
14
+ def initialize(output = STDOUT)
15
+ @output = output
16
+ end
17
+
18
+ # Public: Displays message with decoration
19
+ #
20
+ # message - String message to be displayed
21
+ #
22
+ # Examples
23
+ #
24
+ # display("Excellent Message")
25
+ # # =>
26
+ # # => ==========================================================================
27
+ # # => >> Excellent Message
28
+ # # => ==========================================================================
29
+ # # =>
30
+ def display(message)
31
+ output.puts
32
+ output.puts "=" * 80
33
+ output.puts ">> #{message}"
34
+ output.puts "=" * 80
35
+ output.puts
36
+ end
37
+
38
+ def activate_maintenance_mode(options = {})
39
+ display("Activating Maintenance Mode")
40
+ end
41
+
42
+ def deactivate_maintenance_mode(options = {})
43
+ display("Deactivating Maintenance Mode")
44
+ end
45
+
46
+ def update_repo_tag(options = {})
47
+ display("Updating Repo Tag: #{options[:tag_name]}")
48
+ end
49
+
50
+ def push_repo(options = {})
51
+ display("Pushing #{options[:reference_point]} to Heroku")
52
+ end
53
+
54
+ def run_migrations(options = {})
55
+ display("Running database migrations")
56
+ end
57
+
58
+ def app_restart(options = {})
59
+ display("Restarting application")
60
+ end
61
+
62
+ def warm_instance(options = {})
63
+ display("Accessing #{options[:app_url]} to warm up your application")
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,3 +1,3 @@
1
1
  module Paratrooper
2
- VERSION = "1.1.3"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -9,7 +9,7 @@ describe Paratrooper::Deploy do
9
9
  let(:default_options) do
10
10
  {
11
11
  heroku: heroku,
12
- formatter: formatter,
12
+ notifiers: [],
13
13
  system_caller: system_caller
14
14
  }
15
15
  end
@@ -22,7 +22,6 @@ describe Paratrooper::Deploy do
22
22
  app_maintenance_off: true,
23
23
  )
24
24
  end
25
- let(:formatter) { double(:formatter, puts: '') }
26
25
  let(:system_caller) { double(:system_caller) }
27
26
  let(:domain_response) do
28
27
  double(:domain_response, body: [{'domain' => 'application_url'}])
@@ -46,26 +45,19 @@ describe Paratrooper::Deploy do
46
45
  end
47
46
  end
48
47
 
49
- context "accepts :formatter" do
50
- let(:options) { { formatter: formatter } }
51
- let(:formatter) { double(:formatter) }
48
+ context "accepts :notifiers" do
49
+ let(:options) { { notifiers: [notifiers] } }
50
+ let(:notifiers) { double(:notifier) }
52
51
 
53
- it "and responds to #formatter" do
54
- expect(deployer.formatter).to eq(formatter)
52
+ it "and responds to #notifiers" do
53
+ expect(deployer.notifiers).to eq([notifiers])
55
54
  end
56
55
  end
57
56
  end
58
57
 
59
58
  describe "#activate_maintenance_mode" do
60
- let(:options) { { formatter: formatter } }
61
- let(:formatter) { double(:formatter, puts: true) }
62
-
63
- before do
64
- formatter.stub(:display)
65
- end
66
-
67
- it "displays message" do
68
- formatter.should_receive(:display).with('Activating Maintenance Mode')
59
+ it 'sends notification' do
60
+ deployer.should_receive(:notify).with(:activate_maintenance_mode).once
69
61
  deployer.activate_maintenance_mode
70
62
  end
71
63
 
@@ -76,12 +68,8 @@ describe Paratrooper::Deploy do
76
68
  end
77
69
 
78
70
  describe "#deactivate_maintenance_mode" do
79
- before do
80
- formatter.stub(:display)
81
- end
82
-
83
- it "displays message" do
84
- formatter.should_receive(:display).with('Deactivating Maintenance Mode')
71
+ it 'sends notification' do
72
+ deployer.should_receive(:notify).with(:deactivate_maintenance_mode).once
85
73
  deployer.deactivate_maintenance_mode
86
74
  end
87
75
 
@@ -97,11 +85,10 @@ describe Paratrooper::Deploy do
97
85
 
98
86
  before do
99
87
  system_caller.stub(:execute)
100
- formatter.stub(:display)
101
88
  end
102
89
 
103
- it 'displays message' do
104
- formatter.should_receive(:display).with('Updating Repo Tag: awesome')
90
+ it 'sends notification' do
91
+ deployer.should_receive(:notify).with(:update_repo_tag).once
105
92
  deployer.update_repo_tag
106
93
  end
107
94
 
@@ -142,11 +129,11 @@ describe Paratrooper::Deploy do
142
129
  describe "#push_repo" do
143
130
  before do
144
131
  system_caller.stub(:execute)
145
- formatter.stub(:display)
146
132
  end
147
133
 
148
- it 'displays message' do
149
- formatter.should_receive(:display).with('Pushing master to Heroku')
134
+ it 'sends notification' do
135
+ deployer.should_receive(:notify)
136
+ .with(:push_repo, reference_point: 'master').once
150
137
  deployer.push_repo
151
138
  end
152
139
 
@@ -160,11 +147,10 @@ describe Paratrooper::Deploy do
160
147
  describe "#run_migrations" do
161
148
  before do
162
149
  system_caller.stub(:execute)
163
- formatter.stub(:display)
164
150
  end
165
151
 
166
- it 'displays message' do
167
- formatter.should_receive(:display).with('Running database migrations')
152
+ it 'sends notification' do
153
+ deployer.should_receive(:notify).with(:run_migrations).once
168
154
  deployer.run_migrations
169
155
  end
170
156
 
@@ -176,12 +162,8 @@ describe Paratrooper::Deploy do
176
162
  end
177
163
 
178
164
  describe "#app_restart" do
179
- before do
180
- formatter.stub(:display)
181
- end
182
-
183
- it 'displays message' do
184
- formatter.should_receive(:display).with('Restarting application')
165
+ it 'sends notification' do
166
+ deployer.should_receive(:notify).with(:app_restart).once
185
167
  deployer.app_restart
186
168
  end
187
169
 
@@ -194,12 +176,10 @@ describe Paratrooper::Deploy do
194
176
  describe "#warm_instance" do
195
177
  before do
196
178
  system_caller.stub(:execute)
197
- formatter.stub(:display)
198
179
  end
199
180
 
200
- it 'displays message' do
201
- expected_notice = 'Accessing application_url to warm up your application'
202
- formatter.should_receive(:display).with(expected_notice)
181
+ it 'sends notification' do
182
+ deployer.should_receive(:notify).with(:warm_instance).once
203
183
  deployer.warm_instance(0)
204
184
  end
205
185
 
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'paratrooper/notifier'
3
+
4
+ describe Paratrooper::Notifier do
5
+ let(:notifier) { described_class.new }
6
+ describe '#notify' do
7
+ it 'sends correct method options' do
8
+ notifier.should_receive(:update_repo_tag).with(test: 'blah')
9
+ notifier.notify(:update_repo_tag, test: 'blah')
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,8 @@
1
1
  require 'spec_helper'
2
- require 'paratrooper/default_formatter'
2
+ require 'paratrooper/notifiers/screen_notifier'
3
+ require 'stringio'
3
4
 
4
- describe Paratrooper::DefaultFormatter do
5
+ describe Paratrooper::Notifiers::ScreenNotifier do
5
6
  let(:formatter) { described_class.new(output_stub) }
6
7
  let(:output_stub) { StringIO.new }
7
8
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paratrooper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-20 00:00:00.000000000 Z
13
+ date: 2013-03-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -107,18 +107,22 @@ files:
107
107
  - README.md
108
108
  - Rakefile
109
109
  - lib/paratrooper.rb
110
- - lib/paratrooper/default_formatter.rb
111
110
  - lib/paratrooper/deploy.rb
112
111
  - lib/paratrooper/heroku_wrapper.rb
113
112
  - lib/paratrooper/local_api_key_extractor.rb
113
+ - lib/paratrooper/notifier.rb
114
+ - lib/paratrooper/notifiers/airbrake_notifier.rb
115
+ - lib/paratrooper/notifiers/new_relic_notifier.rb.rb
116
+ - lib/paratrooper/notifiers/screen_notifier.rb
114
117
  - lib/paratrooper/system_caller.rb
115
118
  - lib/paratrooper/version.rb
116
119
  - paratrooper.gemspec
117
120
  - spec/fixtures/netrc
118
- - spec/paratrooper/default_formatter_spec.rb
119
121
  - spec/paratrooper/deploy_spec.rb
120
122
  - spec/paratrooper/heroku_wrapper_spec.rb
121
123
  - spec/paratrooper/local_api_key_extractor_spec.rb
124
+ - spec/paratrooper/notifier_spec.rb
125
+ - spec/paratrooper/notifiers/screen_notifier_spec.rb
122
126
  - spec/spec_helper.rb
123
127
  homepage: http://github.com/mattpolito/paratrooper
124
128
  licenses: []
@@ -146,9 +150,10 @@ specification_version: 3
146
150
  summary: Library to create task for deployment to Heroku
147
151
  test_files:
148
152
  - spec/fixtures/netrc
149
- - spec/paratrooper/default_formatter_spec.rb
150
153
  - spec/paratrooper/deploy_spec.rb
151
154
  - spec/paratrooper/heroku_wrapper_spec.rb
152
155
  - spec/paratrooper/local_api_key_extractor_spec.rb
156
+ - spec/paratrooper/notifier_spec.rb
157
+ - spec/paratrooper/notifiers/screen_notifier_spec.rb
153
158
  - spec/spec_helper.rb
154
159
  has_rdoc:
@@ -1,19 +0,0 @@
1
- require 'stringio'
2
-
3
- module Paratrooper
4
- class DefaultFormatter
5
- attr_reader :output
6
-
7
- def initialize(output = STDOUT)
8
- @output = output
9
- end
10
-
11
- def display(message)
12
- output.puts
13
- output.puts "=" * 80
14
- output.puts ">> #{message}"
15
- output.puts "=" * 80
16
- output.puts
17
- end
18
- end
19
- end