heroku_san 2.0.0 → 2.1.0

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.
data/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Change log (curated)
2
+
3
+ ## v2.1.0
4
+
5
+ * Documentation update
6
+ * Push `REVISION` to Heroku example
7
+ * Bug fixes
8
+
9
+ ### New tasks
10
+
11
+ * rake logs:tail
12
+ * rake shell
13
+ * All HerokuSan tasks inside heroku: namespace, with aliases in the global namespace
14
+
15
+ ### New methods
16
+
17
+ * `Stage#deploy`
18
+ * `Stage#maintenance` can now take a block, and ensures that maintenance mode is off afterwards.
19
+ * `Stage#push_config`
20
+
21
+ ## v2.0.0
22
+
23
+ * Major rewrite into classes `Project` & `Stage`, with helper `Git` module
24
+ * Tests for _everything_
25
+ * Examples directory (e.g. `auto-tagger`)
26
+ * Removed dependencies on Rails
27
+ * `tasks.rb` is greatly simplified, mostly API calls into the `Stage` class
28
+ * Support for tagging releases and deploying apps using a tag glob
29
+ * Support for Heroku stacks (aspen, bamboo & cedar)
30
+
31
+ ## v1.3.0
32
+
33
+ N/A
data/README.rdoc CHANGED
@@ -68,37 +68,47 @@ Need to add remotes for each app?
68
68
 
69
69
  A full list of tasks provided:
70
70
 
71
- rake after_deploy # Callback after deploys
72
- rake all # Select all Heroku apps for later command
73
- rake before_deploy # Callback before deploys
74
- rake capture # Captures a bundle on Heroku
75
- rake console # Opens a remote console
76
- rake db:pull # Pull the Heroku database
77
- rake db:push # Push local database to Heroku database
78
- rake deploy[commit] # Pushes the given commit, migrates and restarts (default: HEAD)
79
- rake deploy:force[commit] # Force-pushes the given commit, migrates and restarts (default: HEAD)
80
- rake heroku:apps # Lists configured apps
81
- rake heroku:apps:local # Lists configured apps without hitting Heroku
82
- rake heroku:config # Add config:vars to each application
83
- rake heroku:config:list # Lists config variables as set on Heroku
84
- rake heroku:config:list:local # Lists local config variables without setting them
85
- rake heroku:create # Creates the Heroku app
86
- rake heroku:create_config # Creates an example configuration file
87
- rake heroku:gems # Generate the Heroku gems manifest from gem dependencies
88
- rake heroku:maintenance # Enable maintenance mode
89
- rake heroku:maintenance_off # Disable maintenance mode
90
- rake heroku:push[commit] # Pushes the given commit (default: HEAD)
91
- rake heroku:push:force[commit] # Force-pushes the given commit (default: HEAD)
92
- rake heroku:rack_env # Add proper RACK_ENV to each application
93
- rake heroku:rake[task] # Runs a rake task remotely
94
- rake heroku:remotes # Add git remotes for all apps in this project
95
- rake heroku:share # Adds a collaborator
96
- rake heroku:unshare # Removes a collaborator
97
- rake logs # Shows the Heroku logs
98
- rake migrate # Migrates and restarts remote servers
99
- rake restart # Restarts remote servers
100
-
101
- Frequently used tasks are not namespaced, everything else lives under heroku.
71
+ rake heroku:apps # Lists configured apps
72
+ rake heroku:apps:local # Lists configured apps without hitting heroku
73
+ rake heroku:config # Add config:vars to each application.
74
+ rake heroku:config:list # Lists config variables as set on Heroku
75
+ rake heroku:config:list:local # Lists local config variables without setting them
76
+ rake heroku:config:rack_env # Add proper RACK_ENV to each application
77
+ rake heroku:console # Opens a remote console
78
+ rake heroku:create # Creates the Heroku app
79
+ rake heroku:create_config # Creates an example configuration file
80
+ rake heroku:db:migrate # Migrates and restarts remote servers
81
+ rake heroku:db:pull # Pull database from stage to local dev database
82
+ rake heroku:deploy[commit] # Pushes the given commit, migrates and restarts (default: HEAD)
83
+ rake heroku:deploy:after # Callback after deploys
84
+ rake heroku:deploy:before # Callback before deploys
85
+ rake heroku:deploy:force[commit] # Force-pushes the given commit, migrates and restarts (default: HEAD)
86
+ rake heroku:logs # Shows the Heroku logs
87
+ rake heroku:logs:tail # Tail the Heroku logs (requires logging:expanded)
88
+ rake heroku:maintenance # Enable maintenance mode
89
+ rake heroku:maintenance_off # Disable maintenance mode
90
+ rake heroku:maintenance_on # Enable maintenance mode
91
+ rake heroku:push[commit] # Pushes the given commit (default: HEAD)
92
+ rake heroku:push:force[commit] # Force-pushes the given commit (default: HEAD)
93
+ rake heroku:rake[task] # Runs a rake task remotely
94
+ rake heroku:remotes # Add git remotes for all apps in this project
95
+ rake heroku:restart # Restarts remote servers
96
+ rake heroku:share # Adds a collaborator (asks for email)
97
+ rake heroku:unshare # Removes a collaborator (asks for email)
98
+ rake heroku:stage:all # Select all Heroku apps for later command
99
+
100
+ Frequently used tasks are aliased into the global namespace:
101
+
102
+ task :all => 'heroku:stage:all'
103
+ task :deploy => 'heroku:deploy'
104
+ task 'deploy:force' => 'heroku:deploy:force'
105
+ task :before_deploy => 'heroku:deploy:before'
106
+ task :after_deploy => 'heroku:deploy:after'
107
+ task :console => 'heroku:console'
108
+ task :restart => 'heroku:restart'
109
+ task :migrate => 'heroku:db:migrate'
110
+ task :logs => 'heroku:logs:default'
111
+ task 'logs:tail' => 'heroku:logs:tail'
102
112
 
103
113
  == Links
104
114
 
@@ -0,0 +1,8 @@
1
+ # Adding this to your :after_deploy task this will add an environment variable,
2
+ # in this case, "REVISION", to your Heroku environment with the current revision.
3
+ task :after_deploy do
4
+ each_heroku_app do |stage|
5
+ revision = stage.revision.split.first
6
+ stage.push_config('REVISION' => revision) if revision
7
+ end
8
+ end
@@ -18,6 +18,8 @@ Feature: Command Line
18
18
  app: awesomeapp-staging
19
19
  demo:
20
20
  app: awesomeapp-demo
21
+ development:
22
+ app:
21
23
  """
22
24
 
23
25
  When I run `rake --trace heroku:apps:local`
data/lib/git.rb CHANGED
@@ -23,17 +23,25 @@ module Git
23
23
  end
24
24
  end
25
25
 
26
- def git_tag(glob)
27
- return nil if glob.nil?
28
- %x{git tag -l '#{glob}'}.split("\n").last
26
+ def git_parsed_tag(tag)
27
+ git_rev_parse(git_tag(tag))
29
28
  end
30
29
 
31
30
  def git_rev_parse(ref)
32
31
  return nil if ref.nil?
33
32
  %x{git rev-parse #{ref}}.split("\n").first
33
+ end
34
+
35
+ def git_tag(glob)
36
+ return nil if glob.nil?
37
+ %x{git tag -l '#{glob}'}.split("\n").last
34
38
  end
35
39
 
36
- def git_parsed_tag(tag)
37
- git_rev_parse(git_tag(tag))
40
+ def git_revision(repo)
41
+ %x{git ls-remote --heads #{repo} master}.split.first
42
+ end
43
+
44
+ def git_named_rev(ref)
45
+ %x{git name-rev #{ref}}.chomp
38
46
  end
39
47
  end
data/lib/heroku_san.rb CHANGED
@@ -5,5 +5,6 @@ require 'heroku_san/project'
5
5
 
6
6
  module HerokuSan
7
7
  class NoApps < StandardError; end
8
+ class MissingApp < StandardError; end
8
9
  class Deprecated < StandardError; end
9
10
  end
@@ -1,6 +1,7 @@
1
1
  module HerokuSan
2
2
  class Stage
3
3
  attr_reader :name
4
+ include Git
4
5
 
5
6
  def initialize(stage, options = {})
6
7
  @name = stage
@@ -8,7 +9,7 @@ module HerokuSan
8
9
  end
9
10
 
10
11
  def app
11
- @options['app']
12
+ @options['app'] or raise MissingApp, "#{name}: is missing the app: configuration value. I don't know what to access on Heroku."
12
13
  end
13
14
 
14
15
  def repo
@@ -35,14 +36,28 @@ module HerokuSan
35
36
  end
36
37
  end
37
38
 
39
+ def deploy(sha = nil, force = false)
40
+ sha ||= git_parsed_tag(tag)
41
+ git_push(sha, repo, force ? %w[--force] : [])
42
+ end
43
+
38
44
  def migrate
39
45
  run 'rake', 'db:migrate'
40
46
  sh_heroku "restart"
41
47
  end
42
48
 
43
- def maintenance(action)
44
- raise ArgumentError, "Action #{action.inspect} must be one of (:on, :off)", caller if ![:on, :off].include?(action)
45
- sh_heroku "maintenance:#{action}"
49
+ def maintenance(action = nil)
50
+ if block_given?
51
+ sh_heroku "maintenance:on"
52
+ begin
53
+ yield
54
+ ensure
55
+ sh_heroku "maintenance:off"
56
+ end
57
+ else
58
+ raise ArgumentError, "Action #{action.inspect} must be one of (:on, :off)", caller if ![:on, :off].include?(action)
59
+ sh_heroku "maintenance:#{action}"
60
+ end
46
61
  end
47
62
 
48
63
  def create
@@ -60,18 +75,27 @@ module HerokuSan
60
75
  def long_config
61
76
  sh_heroku 'config --long'
62
77
  end
78
+
79
+ def push_config(options = {})
80
+ vars = (options == {} ? config : options).map {|var,value| "#{var}=#{Shellwords.escape(value)}"}.join(' ')
81
+ sh_heroku "config:add #{vars}"
82
+ end
63
83
 
64
84
  def restart
65
85
  sh_heroku 'restart'
66
86
  end
67
87
 
68
- def logs
69
- sh_heroku 'logs'
88
+ def logs(tail = false)
89
+ sh_heroku 'logs' + (tail ? ' --tail' : '')
90
+ end
91
+
92
+ def revision
93
+ git_named_rev(git_revision(repo))
70
94
  end
71
95
 
72
96
  private
73
97
 
74
- def sh_heroku command
98
+ def sh_heroku(command)
75
99
  sh "heroku #{command} --app #{app}"
76
100
  end
77
101
  end
@@ -1,3 +1,3 @@
1
1
  module HerokuSan
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
data/lib/tasks.rb CHANGED
@@ -5,17 +5,18 @@ include Git
5
5
 
6
6
  @heroku_san.all.each do |stage|
7
7
  desc "Select #{stage} Heroku app for later commands"
8
- task stage do
8
+ task "heroku:stage:#{stage}" do
9
9
  @heroku_san << stage
10
10
  end
11
- end
12
-
13
- desc 'Select all Heroku apps for later command'
14
- task :all do
15
- @heroku_san << @heroku_san.all
11
+ task stage => "heroku:stage:#{stage}"
16
12
  end
17
13
 
18
14
  namespace :heroku do
15
+ desc 'Select all Heroku apps for later command'
16
+ task 'stage:all' do
17
+ @heroku_san << @heroku_san.all
18
+ end
19
+
19
20
  desc "Creates the Heroku app"
20
21
  task :create do
21
22
  each_heroku_app do |stage|
@@ -23,7 +24,7 @@ namespace :heroku do
23
24
  end
24
25
  end
25
26
 
26
- desc "Generate the Heroku gems manifest from gem dependencies"
27
+ #desc "Generate the Heroku gems manifest from gem dependencies"
27
28
  task :gems => 'gems:base' do
28
29
  raise HerokuSan::Deprecated
29
30
  end
@@ -35,7 +36,7 @@ namespace :heroku do
35
36
  end
36
37
  end
37
38
 
38
- desc 'Adds a collaborator'
39
+ desc 'Adds a collaborator (asks for email)'
39
40
  task :share do
40
41
  print "Email address of collaborator to add: "
41
42
  $stdout.flush
@@ -45,7 +46,7 @@ namespace :heroku do
45
46
  end
46
47
  end
47
48
 
48
- desc 'Removes a collaborator'
49
+ desc 'Removes a collaborator (asks for email)'
49
50
  task :unshare do
50
51
  print "Email address of collaborator to remove: "
51
52
  $stdout.flush
@@ -58,15 +59,10 @@ namespace :heroku do
58
59
  desc 'Lists configured apps'
59
60
  task :apps => :all do
60
61
  each_heroku_app do |stage|
62
+ rev = stage.revision
61
63
  puts "#{stage.name} is shorthand for the Heroku app #{stage.app} located at:"
62
64
  puts " #{stage.repo}"
63
- print " @ "
64
- rev = `git ls-remote -h #{stage.repo}`.split(' ').first
65
- if rev.blank?
66
- puts 'not deployed'
67
- else
68
- puts `git name-rev #{rev}`
69
- end
65
+ puts " @ #{rev.blank? ? 'not deployed' : rev}"
70
66
  puts
71
67
  end
72
68
  end
@@ -83,26 +79,10 @@ namespace :heroku do
83
79
  end
84
80
  end
85
81
 
86
- desc 'Add proper RACK_ENV to each application'
87
- task :rack_env => :all do
88
- each_heroku_app do |stage|
89
- command = "heroku config --app #{stage.app}"
90
- puts command
91
- config = Hash[`#{command}`.scan(/^(.+?)\s*=>\s*(.+)$/)]
92
- if config['RACK_ENV'] != stage.name
93
- sh "heroku config:add --app #{stage.app} RACK_ENV=#{stage.name}"
94
- end
95
- end
96
- end
97
-
98
82
  desc 'Add config:vars to each application.'
99
83
  task :config do
100
84
  each_heroku_app do |stage|
101
- command = "heroku config:add --app #{stage.app}"
102
- stage.config.each do |var, value|
103
- command += " #{var}=#{value}"
104
- end
105
- sh(command)
85
+ stage.push_config
106
86
  end
107
87
  end
108
88
 
@@ -122,6 +102,18 @@ namespace :heroku do
122
102
  end
123
103
 
124
104
  namespace :config do
105
+ desc 'Add proper RACK_ENV to each application'
106
+ task :rack_env => :all do
107
+ each_heroku_app do |stage|
108
+ command = "heroku config --app #{stage.app}"
109
+ puts command
110
+ config = Hash[`#{command}`.scan(/^(.+?)\s*=>\s*(.+)$/)]
111
+ if config['RACK_ENV'] != stage.name
112
+ stage.push_config RACK_ENV: stage.name
113
+ end
114
+ end
115
+ end
116
+
125
117
  desc "Lists config variables as set on Heroku"
126
118
  task :list do
127
119
  each_heroku_app do |stage|
@@ -152,7 +144,7 @@ namespace :heroku do
152
144
  desc "Pushes the given commit (default: HEAD)"
153
145
  task :push, :commit do |t, args|
154
146
  each_heroku_app do |stage|
155
- git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo)
147
+ stage.deploy(args[:commit])
156
148
  end
157
149
  end
158
150
 
@@ -160,7 +152,7 @@ namespace :heroku do
160
152
  desc "Force-pushes the given commit (default: HEAD)"
161
153
  task :force, :commit do |t, args|
162
154
  each_heroku_app do |stage|
163
- git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo, %w[--force])
155
+ stage.deploy(args[:commit], :force)
164
156
  end
165
157
  end
166
158
  end
@@ -185,89 +177,122 @@ namespace :heroku do
185
177
  stage.maintenance :off
186
178
  end
187
179
  end
188
- end
189
-
190
- desc "Pushes the given commit, migrates and restarts (default: HEAD)"
191
- task :deploy, [:commit] => [:before_deploy] do |t, args|
192
- each_heroku_app do |stage|
193
- git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo)
194
- stage.migrate
195
- end
196
- Rake::Task[:after_deploy].execute
197
- end
198
180
 
199
- namespace :deploy do
200
- desc "Force-pushes the given commit, migrates and restarts (default: HEAD)"
201
- task :force, [:commit] => [:before_deploy] do |t, args|
181
+ desc "Pushes the given commit, migrates and restarts (default: HEAD)"
182
+ task :deploy, [:commit] => [:before_deploy] do |t, args|
202
183
  each_heroku_app do |stage|
203
- git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo, %w[--force])
184
+ stage.deploy(args[:commit])
204
185
  stage.migrate
205
186
  end
206
187
  Rake::Task[:after_deploy].execute
207
188
  end
208
- end
209
189
 
210
- task :force_deploy do
211
- raise Deprecated
212
- end
190
+ namespace :deploy do
191
+ desc "Force-pushes the given commit, migrates and restarts (default: HEAD)"
192
+ task :force, [:commit] => [:before_deploy] do |t, args|
193
+ each_heroku_app do |stage|
194
+ stage.deploy(args[:commit], :force)
195
+ stage.migrate
196
+ end
197
+ Rake::Task[:after_deploy].execute
198
+ end
213
199
 
214
- desc "Callback before deploys"
215
- task :before_deploy do
216
- end
200
+ desc "Callback before deploys"
201
+ task :before do
202
+ end
217
203
 
218
- desc "Callback after deploys"
219
- task :after_deploy do
220
- end
204
+ desc "Callback after deploys"
205
+ task :after do
206
+ end
221
207
 
222
- desc "Captures a bundle on Heroku"
223
- task :capture do
224
- raise Deprecated
225
- end
208
+ end
226
209
 
227
- desc "Opens a remote console"
228
- task :console do
229
- each_heroku_app do |stage|
230
- stage.run 'console'
210
+ task :force_deploy do
211
+ raise HerokuSan::Deprecated
231
212
  end
232
- end
233
213
 
234
- desc "Restarts remote servers"
235
- task :restart do
236
- each_heroku_app do |stage|
237
- stage.restart
214
+ #desc "Captures a bundle on Heroku"
215
+ task :capture do
216
+ raise HerokuSan::Deprecated
238
217
  end
239
- end
240
218
 
241
- desc "Migrates and restarts remote servers"
242
- task :migrate do
243
- each_heroku_app do |stage|
244
- stage.migrate
219
+ desc "Opens a remote console"
220
+ task :console do
221
+ each_heroku_app do |stage|
222
+ stage.run 'console'
223
+ end
245
224
  end
246
- end
247
225
 
248
- desc "Shows the Heroku logs"
249
- task :logs do
250
- each_heroku_app do |stage|
251
- stage.logs
226
+ desc "Restarts remote servers"
227
+ task :restart do
228
+ each_heroku_app do |stage|
229
+ stage.restart
230
+ end
252
231
  end
253
- end
254
232
 
255
- namespace :db do
256
- task :pull do
233
+ namespace :logs do
234
+ task :default do
235
+ each_heroku_app do |stage|
236
+ stage.logs
237
+ end
238
+ end
239
+
240
+ desc "Tail the Heroku logs (requires logging:expanded)"
241
+ task :tail do
242
+ each_heroku_app do |stage|
243
+ stage.logs(:tail)
244
+ end
245
+ end
246
+ end
247
+
248
+ desc "Shows the Heroku logs"
249
+ task :logs => 'logs:default'
250
+
251
+ namespace :db do
252
+ desc "Migrates and restarts remote servers"
253
+ task :migrate do
254
+ each_heroku_app do |stage|
255
+ stage.migrate
256
+ end
257
+ end
258
+
259
+ desc "Pull database from stage to local dev database"
260
+ task :pull do
261
+ each_heroku_app do |stage|
262
+ sh "heroku pgdumps:capture --app #{stage.app}"
263
+ dump = `heroku pgdumps --app #{stage.app}`.split("\n").last.split(" ").first
264
+ sh "mkdir -p #{Rails.root}/db/dumps"
265
+ file = "#{Rails.root}/db/dumps/#{dump}.sql.gz"
266
+ url = `heroku pgdumps:url --app #{stage.app} #{dump}`.chomp
267
+ sh "wget", url, "-O", file
268
+ sh "rake db:drop db:create"
269
+ sh "gunzip -c #{file} | #{Rails.root}/script/dbconsole"
270
+ sh "rake jobs:clear"
271
+ end
272
+ end
273
+ end
274
+
275
+ desc "Run a bash shell on Heroku"
276
+ task :shell do
257
277
  each_heroku_app do |stage|
258
- sh "heroku pgdumps:capture --app #{stage.app}"
259
- dump = `heroku pgdumps --app #{stage.app}`.split("\n").last.split(" ").first
260
- sh "mkdir -p #{Rails.root}/db/dumps"
261
- file = "#{Rails.root}/db/dumps/#{dump}.sql.gz"
262
- url = `heroku pgdumps:url --app #{stage.app} #{dump}`.chomp
263
- sh "wget", url, "-O", file
264
- sh "rake db:drop db:create"
265
- sh "gunzip -c #{file} | #{Rails.root}/script/dbconsole"
266
- sh "rake jobs:clear"
278
+ stage.run 'bash'
267
279
  end
268
280
  end
269
281
  end
270
282
 
283
+ task :all => 'heroku:stage:all'
284
+ task :deploy => 'heroku:deploy'
285
+ task 'deploy:force' => 'heroku:deploy:force'
286
+ task :before_deploy => 'heroku:deploy:before'
287
+ task :after_deploy => 'heroku:deploy:after'
288
+ task :console => 'heroku:console'
289
+ task :restart => 'heroku:restart'
290
+ task :migrate => 'heroku:db:migrate'
291
+ task :logs => 'heroku:logs:default'
292
+ task 'logs:tail' => 'heroku:logs:tail'
293
+ task 'heroku:rack_env' => 'heroku:config:rack_env'
294
+ task :shell => 'heroku:shell'
295
+
271
296
  def each_heroku_app(&block)
272
297
  @heroku_san.each_app(&block)
273
298
  puts
data/spec/git_spec.rb CHANGED
@@ -52,4 +52,23 @@ describe GitTest do
52
52
  subject.git_rev_parse(nil).should == nil
53
53
  end
54
54
  end
55
+
56
+ describe "#git_revision" do
57
+ it "returns the current revision of the repository (on Heroku)" do
58
+ subject.should_receive("`").with("git ls-remote --heads staging master") { "sha\n" }
59
+ subject.git_revision('staging').should == 'sha'
60
+ end
61
+
62
+ it "returns nil if there is no revision (i.e. not deployed yet)" do
63
+ subject.should_receive("`").with("git ls-remote --heads staging master") { "\n" }
64
+ subject.git_revision('staging').should == nil
65
+ end
66
+ end
67
+
68
+ describe "#git_named_rev" do
69
+ it "returns symbolic names for given rev" do
70
+ subject.should_receive("`").with("git name-rev sha") {"sha production/123456\n"}
71
+ subject.git_named_rev('sha').should == 'sha production/123456'
72
+ end
73
+ end
55
74
  end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe HerokuSan::Stage do
4
+ include Git
4
5
  subject { HerokuSan::Stage.new('production', {"app" => "awesomeapp", "stack" => "bamboo-ree-1.8.7"})}
5
6
 
6
7
  context "initializes" do
@@ -19,6 +20,16 @@ describe HerokuSan::Stage do
19
20
  its(:repo) { should == 'git@heroku.com:awesomeapp-demo.git' }
20
21
  end
21
22
 
23
+ describe "#app" do
24
+ its(:app) { should == 'awesomeapp'}
25
+ context "blank app" do
26
+ subject { HerokuSan::Stage.new('production') }
27
+ it "should raise an error" do
28
+ expect { subject.app }.to raise_error(HerokuSan::MissingApp, /production: is missing the app: configuration value\./)
29
+ end
30
+ end
31
+ end
32
+
22
33
  context "celadon cedar stack has a different API" do
23
34
  describe "#stack" do
24
35
  it "returns the name of the stack from Heroku" do
@@ -54,6 +65,28 @@ EOT
54
65
  end
55
66
  end
56
67
 
68
+ describe "#deploy" do
69
+ it "deploys to heroku" do
70
+ subject.should_receive(:git_push).with(git_parsed_tag(subject.tag), subject.repo, [])
71
+ subject.deploy
72
+ end
73
+
74
+ it "deploys with a custom sha" do
75
+ subject.should_receive(:git_push).with('deadbeef', subject.repo, [])
76
+ subject.deploy('deadbeef')
77
+ end
78
+
79
+ it "deploys with --force" do
80
+ subject.should_receive(:git_push).with(git_parsed_tag(subject.tag), subject.repo, %w[--force])
81
+ subject.deploy(nil, :force)
82
+ end
83
+
84
+ it "deploys with a custom sha & --force" do
85
+ subject.should_receive(:git_push).with('deadbeef', subject.repo, %w[--force])
86
+ subject.deploy('deadbeef', :force)
87
+ end
88
+ end
89
+
57
90
  describe "#migrate" do
58
91
  it "runs rake db:migrate" do
59
92
  subject.should_receive(:sh).with("heroku run:rake db:migrate --app awesomeapp")
@@ -62,7 +95,7 @@ EOT
62
95
  end
63
96
  end
64
97
 
65
- describe "#maintenance" do
98
+ describe "#maintenance" do
66
99
  it ":on" do
67
100
  subject.should_receive(:sh).with("heroku maintenance:on --app awesomeapp")
68
101
  subject.maintenance :on
@@ -78,6 +111,27 @@ EOT
78
111
  subject.maintenance :busy
79
112
  end.to raise_error ArgumentError, "Action #{:busy.inspect} must be one of (:on, :off)"
80
113
  end
114
+
115
+ context "with a block" do
116
+ it "wraps it in a maitenance mode" do
117
+ subject.should_receive(:sh).with("heroku maintenance:on --app awesomeapp")
118
+ reactor = mock("Reactor"); reactor.should_receive(:scram).with(:now)
119
+ subject.should_receive(:sh).with("heroku maintenance:off --app awesomeapp")
120
+ subject.maintenance do
121
+ reactor.scram(:now)
122
+ end
123
+ end
124
+ it "ensures that maintenance mode is turned off" do
125
+ subject.should_receive(:sh).with("heroku maintenance:on --app awesomeapp")
126
+ reactor = mock("Reactor"); reactor.should_receive(:scram).with(:now).and_raise(RuntimeError)
127
+ subject.should_receive(:sh).with("heroku maintenance:off --app awesomeapp")
128
+ expect {
129
+ subject.maintenance do
130
+ reactor.scram(:now)
131
+ end
132
+ }.to raise_error
133
+ end
134
+ end
81
135
  end
82
136
 
83
137
  describe "#create" do
@@ -133,6 +187,39 @@ EOT
133
187
  subject.should_receive(:sh).with("heroku logs --app awesomeapp")
134
188
  subject.logs
135
189
  end
190
+ it "tails log files" do
191
+ subject.should_receive(:sh).with("heroku logs --tail --app awesomeapp")
192
+ subject.logs(:tail)
193
+ end
136
194
  end
137
195
 
196
+ describe "#push_config" do
197
+ it "updates the configuration settings on Heroku" do
198
+ subject = HerokuSan::Stage.new('test', {"app" => "awesomeapp", "config" => {FOO: 'bar', DOG: 'emu'}})
199
+ subject.should_receive(:sh).with("heroku config:add FOO=bar DOG=emu --app awesomeapp")
200
+ subject.push_config
201
+ end
202
+ it "properly escapes variables" do
203
+ subject = HerokuSan::Stage.new('test', {"app" => "awesomeapp", "config" => {FOO: ' bar\emu bat zebra '}})
204
+ subject.should_receive(:sh).with("heroku config:add FOO=#{Shellwords.escape(' bar\emu bat zebra ')} --app awesomeapp")
205
+ subject.push_config
206
+ end
207
+ it "pushes the options hash" do
208
+ subject.should_receive(:sh).with("heroku config:add RACK_ENV=magic --app awesomeapp")
209
+ subject.push_config(RACK_ENV: 'magic')
210
+ end
211
+ end
212
+
213
+ describe "#revision" do
214
+ it "returns the named remote revision for the stage" do
215
+ subject.should_receive(:git_revision).with(subject.repo) {"sha"}
216
+ subject.should_receive(:git_named_rev).with('sha') {"sha production/123456"}
217
+ subject.revision.should == 'sha production/123456'
218
+ end
219
+ it "returns nil if the stage has never been deployed" do
220
+ subject.should_receive(:git_revision).with(subject.repo) {nil}
221
+ subject.should_receive(:git_named_rev).with(nil) {''}
222
+ subject.revision.should == ''
223
+ end
224
+ end
138
225
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroku_san
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,11 +12,11 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2012-03-03 00:00:00.000000000Z
15
+ date: 2012-03-06 00:00:00.000000000Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rails
19
- requirement: &2151831540 !ruby/object:Gem::Requirement
19
+ requirement: &2153413220 !ruby/object:Gem::Requirement
20
20
  none: false
21
21
  requirements:
22
22
  - - ! '>='
@@ -24,10 +24,10 @@ dependencies:
24
24
  version: '2'
25
25
  type: :runtime
26
26
  prerelease: false
27
- version_requirements: *2151831540
27
+ version_requirements: *2153413220
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: heroku
30
- requirement: &2151830840 !ruby/object:Gem::Requirement
30
+ requirement: &2153412680 !ruby/object:Gem::Requirement
31
31
  none: false
32
32
  requirements:
33
33
  - - ! '>='
@@ -35,10 +35,10 @@ dependencies:
35
35
  version: '2'
36
36
  type: :runtime
37
37
  prerelease: false
38
- version_requirements: *2151830840
38
+ version_requirements: *2153412680
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: rake
41
- requirement: &2151830320 !ruby/object:Gem::Requirement
41
+ requirement: &2153412280 !ruby/object:Gem::Requirement
42
42
  none: false
43
43
  requirements:
44
44
  - - ! '>='
@@ -46,10 +46,10 @@ dependencies:
46
46
  version: '0'
47
47
  type: :runtime
48
48
  prerelease: false
49
- version_requirements: *2151830320
49
+ version_requirements: *2153412280
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: aruba
52
- requirement: &2151829520 !ruby/object:Gem::Requirement
52
+ requirement: &2153411640 !ruby/object:Gem::Requirement
53
53
  none: false
54
54
  requirements:
55
55
  - - ! '>='
@@ -57,10 +57,10 @@ dependencies:
57
57
  version: '0'
58
58
  type: :development
59
59
  prerelease: false
60
- version_requirements: *2151829520
60
+ version_requirements: *2153411640
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: cucumber
63
- requirement: &2151829020 !ruby/object:Gem::Requirement
63
+ requirement: &2153411060 !ruby/object:Gem::Requirement
64
64
  none: false
65
65
  requirements:
66
66
  - - ! '>='
@@ -68,10 +68,10 @@ dependencies:
68
68
  version: '0'
69
69
  type: :development
70
70
  prerelease: false
71
- version_requirements: *2151829020
71
+ version_requirements: *2153411060
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: rake
74
- requirement: &2151828020 !ruby/object:Gem::Requirement
74
+ requirement: &2153410400 !ruby/object:Gem::Requirement
75
75
  none: false
76
76
  requirements:
77
77
  - - ! '>='
@@ -79,10 +79,10 @@ dependencies:
79
79
  version: '0'
80
80
  type: :development
81
81
  prerelease: false
82
- version_requirements: *2151828020
82
+ version_requirements: *2153410400
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: bundler
85
- requirement: &2151826960 !ruby/object:Gem::Requirement
85
+ requirement: &2153409500 !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
88
  - - ~>
@@ -90,7 +90,7 @@ dependencies:
90
90
  version: '1.0'
91
91
  type: :development
92
92
  prerelease: false
93
- version_requirements: *2151826960
93
+ version_requirements: *2153409500
94
94
  description: Manage multiple Heroku instances/apps for a single Rails app using Rake
95
95
  email: elijah.miller@gmail.com
96
96
  executables: []
@@ -101,6 +101,7 @@ files:
101
101
  - .gitignore
102
102
  - .gitmodules
103
103
  - .rvmrc
104
+ - CHANGELOG.md
104
105
  - Gemfile
105
106
  - LICENSE
106
107
  - README.rdoc
@@ -108,6 +109,7 @@ files:
108
109
  - autotest/discover.rb
109
110
  - cucumber.yml
110
111
  - examples/auto_tagger.rake
112
+ - examples/push_revision.rake
111
113
  - features/config.feature
112
114
  - features/extended-config.feature
113
115
  - features/remote.feature