heroku_san 1.3.0 → 2.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/tasks.rb ADDED
@@ -0,0 +1,284 @@
1
+ require 'git'
2
+ include Git
3
+
4
+ @heroku_san = HerokuSan::Project.new(Rails.root.join('config', 'heroku.yml'))
5
+
6
+ @heroku_san.all.each do |stage|
7
+ desc "Select #{stage} Heroku app for later commands"
8
+ task stage do
9
+ @heroku_san << stage
10
+ end
11
+ end
12
+
13
+ desc 'Select all Heroku apps for later command'
14
+ task :all do
15
+ @heroku_san << @heroku_san.all
16
+ end
17
+
18
+ namespace :heroku do
19
+ desc "Creates the Heroku app"
20
+ task :create do
21
+ each_heroku_app do |stage|
22
+ stage.create
23
+ end
24
+ end
25
+
26
+ desc "Generate the Heroku gems manifest from gem dependencies"
27
+ task :gems => 'gems:base' do
28
+ raise HerokuSan::Deprecated
29
+ end
30
+
31
+ desc 'Add git remotes for all apps in this project'
32
+ task :remotes do
33
+ each_heroku_app do |stage|
34
+ sh "git remote add #{stage.name} #{stage.repo}"
35
+ end
36
+ end
37
+
38
+ desc 'Adds a collaborator'
39
+ task :share do
40
+ print "Email address of collaborator to add: "
41
+ $stdout.flush
42
+ email = $stdin.gets
43
+ each_heroku_app do |stage|
44
+ stage.sharing_add email
45
+ end
46
+ end
47
+
48
+ desc 'Removes a collaborator'
49
+ task :unshare do
50
+ print "Email address of collaborator to remove: "
51
+ $stdout.flush
52
+ email = $stdin.gets
53
+ each_heroku_app do |stage|
54
+ stage.sharing_remove email
55
+ end
56
+ end
57
+
58
+ desc 'Lists configured apps'
59
+ task :apps => :all do
60
+ each_heroku_app do |stage|
61
+ puts "#{stage.name} is shorthand for the Heroku app #{stage.app} located at:"
62
+ 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
70
+ puts
71
+ end
72
+ end
73
+
74
+ namespace :apps do
75
+ desc 'Lists configured apps without hitting heroku'
76
+ task :local => :all do
77
+ each_heroku_app do |stage|
78
+ puts "#{stage.name} is shorthand for the Heroku app #{stage.app} located at:"
79
+ puts " #{stage.repo}"
80
+ puts " the #{stage.name} TAG is '#{stage.tag}'" if stage.tag
81
+ puts
82
+ end
83
+ end
84
+ end
85
+
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
+ desc 'Add config:vars to each application.'
99
+ task :config do
100
+ 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)
106
+ end
107
+ end
108
+
109
+ desc 'Creates an example configuration file'
110
+ task :create_config do
111
+ filename = %Q{#{@heroku_san.config_file.to_s}}
112
+ if @heroku_san.create_config
113
+ puts "Copied example config to #{filename.inspect}"
114
+ if ENV['EDITOR'].present?
115
+ sh "#{ENV['EDITOR']} #{filename}"
116
+ else
117
+ puts "Please edit #{filename.inspect} with your application's settings."
118
+ end
119
+ else
120
+ puts "#{filename.inspect} already exists"
121
+ end
122
+ end
123
+
124
+ namespace :config do
125
+ desc "Lists config variables as set on Heroku"
126
+ task :list do
127
+ each_heroku_app do |stage|
128
+ puts "#{stage.name}:"
129
+ stage.long_config
130
+ end
131
+ end
132
+
133
+ namespace :list do
134
+ desc "Lists local config variables without setting them"
135
+ task :local do
136
+ each_heroku_app do |stage|
137
+ (stage.config).each do |var, value|
138
+ puts "#{stage.name} #{var}: '#{value}'"
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ desc 'Runs a rake task remotely'
146
+ task :rake, [:task] do |t, args|
147
+ each_heroku_app do |stage|
148
+ stage.run 'rake', args.task
149
+ end
150
+ end
151
+
152
+ desc "Pushes the given commit (default: HEAD)"
153
+ task :push, :commit do |t, args|
154
+ each_heroku_app do |stage|
155
+ git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo)
156
+ end
157
+ end
158
+
159
+ namespace :push do
160
+ desc "Force-pushes the given commit (default: HEAD)"
161
+ task :force, :commit do |t, args|
162
+ each_heroku_app do |stage|
163
+ git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo, %w[--force])
164
+ end
165
+ end
166
+ end
167
+
168
+ desc "Enable maintenance mode"
169
+ task :maintenance do
170
+ each_heroku_app do |stage|
171
+ stage.maintenance :on
172
+ end
173
+ end
174
+
175
+ desc "Enable maintenance mode"
176
+ task :maintenance_on do
177
+ each_heroku_app do |stage|
178
+ stage.maintenance :on
179
+ end
180
+ end
181
+
182
+ desc "Disable maintenance mode"
183
+ task :maintenance_off do
184
+ each_heroku_app do |stage|
185
+ stage.maintenance :off
186
+ end
187
+ 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
+
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|
202
+ each_heroku_app do |stage|
203
+ git_push(args[:commit] || git_parsed_tag(stage.tag), stage.repo, %w[--force])
204
+ stage.migrate
205
+ end
206
+ Rake::Task[:after_deploy].execute
207
+ end
208
+ end
209
+
210
+ task :force_deploy do
211
+ raise Deprecated
212
+ end
213
+
214
+ desc "Callback before deploys"
215
+ task :before_deploy do
216
+ end
217
+
218
+ desc "Callback after deploys"
219
+ task :after_deploy do
220
+ end
221
+
222
+ desc "Captures a bundle on Heroku"
223
+ task :capture do
224
+ raise Deprecated
225
+ end
226
+
227
+ desc "Opens a remote console"
228
+ task :console do
229
+ each_heroku_app do |stage|
230
+ stage.run 'console'
231
+ end
232
+ end
233
+
234
+ desc "Restarts remote servers"
235
+ task :restart do
236
+ each_heroku_app do |stage|
237
+ stage.restart
238
+ end
239
+ end
240
+
241
+ desc "Migrates and restarts remote servers"
242
+ task :migrate do
243
+ each_heroku_app do |stage|
244
+ stage.migrate
245
+ end
246
+ end
247
+
248
+ desc "Shows the Heroku logs"
249
+ task :logs do
250
+ each_heroku_app do |stage|
251
+ stage.logs
252
+ end
253
+ end
254
+
255
+ namespace :db do
256
+ task :pull do
257
+ 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"
267
+ end
268
+ end
269
+ end
270
+
271
+ def each_heroku_app(&block)
272
+ @heroku_san.each_app(&block)
273
+ puts
274
+ rescue HerokuSan::NoApps => e
275
+ puts "You must first specify at least one Heroku app:
276
+ rake <app> [<app>] <command>
277
+ rake production restart
278
+ rake demo staging deploy"
279
+
280
+ puts "\nYou can use also command all Heroku apps for this project:
281
+ rake all heroku:share"
282
+
283
+ exit(1)
284
+ end
@@ -1,15 +1,18 @@
1
1
  #
2
2
  # Format:
3
3
  #
4
- # <heroku_san shorthand name>:
4
+ # <stage name>:
5
5
  # app: <Heroku app name>
6
6
  # stack: <Heroku stack, optional>
7
+ # tag: <git tag pattern, optional>
8
+ # repo: <git repository, optional>
7
9
  # config:
8
10
  # - <Heroku config:var name>: <Heroku config:var value>
9
11
  #
10
12
  production:
11
13
  app: awesomeapp
12
14
  stack: bamboo-ree-1.8.7
15
+ tag: production/*
13
16
  config:
14
17
  BUNDLE_WITHOUT: "development:test"
15
18
  GOOGLE_ANALYTICS: "UA-12345678-1"
@@ -0,0 +1,26 @@
1
+ #
2
+ # Format:
3
+ #
4
+ # <heroku_san shorthand name>:
5
+ # app: <Heroku app name>
6
+ # tag: <git tag pattern>
7
+ # config:
8
+ # - <Heroku config:var name>: <Heroku config:var value>
9
+ #
10
+ production:
11
+ app: awesomeapp
12
+ tag: production/*
13
+ config:
14
+ BUNDLE_WITHOUT: "development:test"
15
+ GOOGLE_ANALYTICS: "UA-12345678-1"
16
+
17
+ staging:
18
+ app: awesomeapp-staging
19
+ stack: bamboo-ree-1.8.7
20
+ config: &default
21
+ BUNDLE_WITHOUT: "development:test"
22
+
23
+ demo:
24
+ app: awesomeapp-demo
25
+ stack: cedar
26
+ config: *default
@@ -0,0 +1,7 @@
1
+ config_repo: 'file:///<%= File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', 'features', 'data', 'test-config') %>'
2
+ production:
3
+ app: awesomeapp
4
+ staging:
5
+ app: awesomeapp-staging
6
+ demo:
7
+ app: awesomeapp-demo
@@ -0,0 +1,10 @@
1
+ #
2
+ # Format:
3
+ #
4
+ # apps:
5
+ # shorthand: <Heroku app name>
6
+ #
7
+ apps:
8
+ production: awesomeapp
9
+ staging: awesomeapp-staging
10
+ demo: awesomeapp-demo
@@ -0,0 +1,6 @@
1
+ production:
2
+ app: awesomeapp
3
+ tag: production/*
4
+ config:
5
+ BUNDLE_WITHOUT: "development:test"
6
+ GOOGLE_ANALYTICS: "UA-12345678-1"
data/spec/git_spec.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+ require 'git'
3
+
4
+ class GitTest; include Git; end
5
+
6
+ describe GitTest do
7
+ describe "#git_push" do
8
+ it "pushes to heroku" do
9
+ subject.should_receive(:sh).with("git update-ref refs/heroku_san/deploy HEAD")
10
+ subject.should_receive(:sh).with("git push git@heroku.com:awesomeapp.git refs/heroku_san/deploy:refs/heads/master")
11
+ subject.should_receive(:sh).with("git update-ref -d refs/heroku_san/deploy")
12
+ subject.git_push(nil, 'git@heroku.com:awesomeapp.git')
13
+ end
14
+
15
+ it "pushes a specific commit to heroku" do
16
+ subject.should_receive(:sh).with("git update-ref refs/heroku_san/deploy kommit")
17
+ subject.should_receive(:sh).with("git push git@heroku.com:awesomeapp.git refs/heroku_san/deploy:refs/heads/master")
18
+ subject.should_receive(:sh).with("git update-ref -d refs/heroku_san/deploy")
19
+ subject.git_push('kommit', 'git@heroku.com:awesomeapp.git')
20
+ end
21
+
22
+ it "includes options, too" do
23
+ subject.should_receive(:sh).with("git update-ref refs/heroku_san/deploy HEAD")
24
+ subject.should_receive(:sh).with("git push git@heroku.com:awesomeapp.git --force -v refs/heroku_san/deploy:refs/heads/master")
25
+ subject.should_receive(:sh).with("git update-ref -d refs/heroku_san/deploy")
26
+ subject.git_push(nil, 'git@heroku.com:awesomeapp.git', %w[--force -v])
27
+ end
28
+ end
29
+
30
+ describe "#git_tag" do
31
+ it "returns the latest tag that matches the pattern" do
32
+ subject.should_receive("`").with("git tag -l 'pattern*'") { "x\n\y\n\z\n" }
33
+ subject.git_tag('pattern*').should == "z"
34
+ end
35
+ it "returns nil if no tags match the pattern" do
36
+ subject.should_receive("`").with("git tag -l 'pattern*'") { "\n" }
37
+ subject.git_tag('pattern*').should == nil
38
+ end
39
+ it "returns nil for a nil tag" do
40
+ subject.should_not_receive("`").with("git tag -l ''") { "\n" }
41
+ subject.git_tag(nil).should == nil
42
+ end
43
+ end
44
+
45
+ describe "#git_rev_parse" do
46
+ it "returns the rev based on the tag" do
47
+ subject.should_receive("`").with("git rev-parse prod/1234567890") { "sha\n" }
48
+ subject.git_rev_parse('prod/1234567890').should == "sha"
49
+ end
50
+ it "returns nil for a blank tag" do
51
+ subject.should_not_receive("`").with("git rev-parse ") { "\n" }
52
+ subject.git_rev_parse(nil).should == nil
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ describe HerokuSan::Project do
5
+ specify ".new with a missing config file" do
6
+ heroku_san = HerokuSan::Project.new("/u/should/never/get/here")
7
+ heroku_san.all.should == []
8
+ end
9
+
10
+ context "using the example config file" do
11
+ let(:heroku_config_file) { File.join(SPEC_ROOT, "fixtures", "example.yml") }
12
+ let(:template_config_file) {
13
+ path = File.join(SPEC_ROOT, "..", "lib/templates", "heroku.example.yml")
14
+ (File.respond_to? :realpath) ? File.realpath(path) : path
15
+ }
16
+ let(:heroku_san) { HerokuSan::Project.new(heroku_config_file) }
17
+
18
+ it "#all" do
19
+ heroku_san.all.should =~ %w[production staging demo]
20
+ end
21
+
22
+ context "using the heroku_san format" do
23
+ let(:heroku_san) { HerokuSan::Project.new(File.join(SPEC_ROOT, "fixtures", "old_format.yml")) }
24
+
25
+ it "returns a list of apps" do
26
+ heroku_san.all.should =~ %w[production staging demo]
27
+ end
28
+ end
29
+
30
+ describe "Adding an app to the deploy list" do
31
+ it "appends known shorthands to apps" do
32
+ heroku_san.apps.should == []
33
+ heroku_san << 'production'
34
+ heroku_san.apps.should == %w[production]
35
+ heroku_san << 'staging'
36
+ heroku_san.apps.should == %w[production staging]
37
+ heroku_san << 'unknown'
38
+ heroku_san.apps.should == %w[production staging]
39
+ end
40
+
41
+ it "appends .all (or any array)" do
42
+ heroku_san << heroku_san.all
43
+ heroku_san.apps.should == heroku_san.all
44
+ end
45
+ end
46
+
47
+ describe "#apps extra default behaviors" do
48
+ specify "on a git branch that matches an app name" do
49
+ heroku_san.should_receive(:git_active_branch) { "staging" }
50
+ $stdout.should_receive(:puts).with("Defaulting to 'staging' as it matches the current branch")
51
+ heroku_san.apps.should == %w[staging]
52
+ end
53
+
54
+ specify "on a git branch that doesn't matches an app name" do
55
+ heroku_san.should_receive(:git_active_branch) { "master" }
56
+ heroku_san.apps.should == %w[]
57
+ end
58
+
59
+ context "but only a single configured app" do
60
+ let(:heroku_san) { HerokuSan::Project.new(File.join(SPEC_ROOT, "fixtures", "single_app.yml")) }
61
+ it "returns the app" do
62
+ $stdout.should_receive(:puts).with('Defaulting to "production" since only one app is defined')
63
+ heroku_san.apps.should == %w[production]
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "#each_app" do
69
+ it "raises an error is no apps were specified" do
70
+ expect { heroku_san.each_app do true; end }.to raise_error HerokuSan::NoApps
71
+ end
72
+
73
+ it "yields to a block with args" do
74
+ heroku_san << 'production'
75
+ block = double('block')
76
+ block.should_receive(:action).with(heroku_san['production'])
77
+ heroku_san.each_app do |stage|
78
+ block.action(stage)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "#[]" do
84
+ it "returns a config section" do
85
+ heroku_san.all.each do |app|
86
+ heroku_san[app].should be_a HerokuSan::Stage
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "#create_config" do
92
+ it "creates a new file using the example file" do
93
+ Dir.mktmpdir do |dir|
94
+ tmp_config_file = File.join dir, 'config.yml'
95
+ heroku_san = HerokuSan::Project.new(tmp_config_file)
96
+ FileUtils.should_receive(:cp).with(File.expand_path(template_config_file), tmp_config_file)
97
+ heroku_san.create_config.should be_true
98
+ end
99
+ end
100
+
101
+ it "does not overwrite an existing file" do
102
+ FileUtils.should_not_receive(:cp)
103
+ heroku_san.create_config.should be_false
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ describe HerokuSan::Stage do
4
+ subject { HerokuSan::Stage.new('production', {"app" => "awesomeapp", "stack" => "bamboo-ree-1.8.7"})}
5
+
6
+ context "initializes" do
7
+ subject { HerokuSan::Stage.new('production',
8
+ {"stack" => "cedar",
9
+ "app" => "awesomeapp-demo",
10
+ "tag" => "demo/*",
11
+ "config"=> {"BUNDLE_WITHOUT"=>"development:test"}
12
+ })}
13
+
14
+ its(:name) { should == 'production' }
15
+ its(:app) { should == 'awesomeapp-demo' }
16
+ its(:stack) { should == 'cedar' }
17
+ its(:tag) { should == "demo/*" }
18
+ its(:config) { should == {"BUNDLE_WITHOUT"=>"development:test"} }
19
+ its(:repo) { should == 'git@heroku.com:awesomeapp-demo.git' }
20
+ end
21
+
22
+ context "celadon cedar stack has a different API" do
23
+ describe "#stack" do
24
+ it "returns the name of the stack from Heroku" do
25
+ subject = HerokuSan::Stage.new('production', {"app" => "awesomeapp"})
26
+ subject.should_receive("`").with("heroku stack --app awesomeapp") {
27
+ <<EOT
28
+ aspen-mri-1.8.6
29
+ * bamboo-mri-1.9.2
30
+ bamboo-ree-1.8.7
31
+ cedar (beta)
32
+ EOT
33
+ }
34
+ subject.stack.should == 'bamboo-mri-1.9.2'
35
+ end
36
+
37
+ it "returns the stack name from the config if it is set there" do
38
+ subject = HerokuSan::Stage.new('production', {"app" => "awesomeapp", "stack" => "cedar"})
39
+ subject.should_not_receive("`")
40
+ subject.stack.should == 'cedar'
41
+ end
42
+ end
43
+
44
+ describe "#run" do
45
+ it "runs commands using the pre-cedar format" do
46
+ subject.should_receive(:sh).with("heroku run:rake foo bar bleh --app awesomeapp")
47
+ subject.run 'rake', 'foo bar bleh'
48
+ end
49
+ it "runs commands using the new cedar format" do
50
+ subject = HerokuSan::Stage.new('production', {"app" => "awesomeapp", "stack" => "cedar"})
51
+ subject.should_receive(:sh).with("heroku run worker foo bar bleh --app awesomeapp")
52
+ subject.run 'worker', 'foo bar bleh'
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "#migrate" do
58
+ it "runs rake db:migrate" do
59
+ subject.should_receive(:sh).with("heroku run:rake db:migrate --app awesomeapp")
60
+ subject.should_receive(:sh).with("heroku restart --app awesomeapp")
61
+ subject.migrate
62
+ end
63
+ end
64
+
65
+ describe "#maintenance" do
66
+ it ":on" do
67
+ subject.should_receive(:sh).with("heroku maintenance:on --app awesomeapp")
68
+ subject.maintenance :on
69
+ end
70
+
71
+ it ":off" do
72
+ subject.should_receive(:sh).with("heroku maintenance:off --app awesomeapp")
73
+ subject.maintenance :off
74
+ end
75
+
76
+ it "otherwise raises an ArgumentError" do
77
+ expect do
78
+ subject.maintenance :busy
79
+ end.to raise_error ArgumentError, "Action #{:busy.inspect} must be one of (:on, :off)"
80
+ end
81
+ end
82
+
83
+ describe "#create" do
84
+ it "creates an app on heroku" do
85
+ subject.should_receive(:sh).with("heroku apps:create awesomeapp --stack bamboo-ree-1.8.7")
86
+ subject.create
87
+ end
88
+ it "uses the default stack if none is given" do
89
+ subject = HerokuSan::Stage.new('production', {"app" => "awesomeapp"})
90
+ subject.should_receive(:sh).with("heroku apps:create awesomeapp")
91
+ subject.create
92
+ end
93
+ end
94
+
95
+ describe "#sharing_add" do
96
+ it "add collaborators" do
97
+ subject.should_receive(:sh).with("heroku sharing:add email@example.com --app awesomeapp")
98
+ subject.sharing_add 'email@example.com'
99
+ end
100
+ end
101
+
102
+ describe "#sharing_remove" do
103
+ it "removes collaborators" do
104
+ subject.should_receive(:sh).with("heroku sharing:remove email@example.com --app awesomeapp")
105
+ subject.sharing_remove 'email@example.com'
106
+ end
107
+ end
108
+
109
+ describe "#long_config" do
110
+ it "prints out the remote config" do
111
+ subject.should_receive(:sh).with("heroku config --long --app awesomeapp") {
112
+ <<EOT
113
+ BUNDLE_WITHOUT => development:test
114
+ DATABASE_URL => postgres://thnodhxrzn:T0-UwxLyFgXcnBSHmyhv@ec2-50-19-216-194.compute-1.amazonaws.com/thnodhxrzn
115
+ LANG => en_US.UTF-8
116
+ RACK_ENV => production
117
+ SHARED_DATABASE_URL => postgres://thnodhxrzn:T0-UwxLyFgXcnBSHmyhv@ec2-50-19-216-194.compute-1.amazonaws.com/thnodhxrzn
118
+ EOT
119
+ }
120
+ subject.long_config
121
+ end
122
+ end
123
+
124
+ describe "#restart" do
125
+ it "restarts an app" do
126
+ subject.should_receive(:sh).with("heroku restart --app awesomeapp")
127
+ subject.restart
128
+ end
129
+ end
130
+
131
+ describe "#logs" do
132
+ it "returns log files" do
133
+ subject.should_receive(:sh).with("heroku logs --app awesomeapp")
134
+ subject.logs
135
+ end
136
+ end
137
+
138
+ end
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ SPEC_ROOT = File.dirname(__FILE__)
5
+
6
+ # Requires supporting ruby files with custom matchers and macros, etc,
7
+ # in spec/support/ and its subdirectories.
8
+ Dir[File.join(SPEC_ROOT, "support/**/*.rb")].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ # == Mock Framework
12
+ #
13
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
14
+ #
15
+ # config.mock_with :mocha
16
+ # config.mock_with :flexmock
17
+ # config.mock_with :rr
18
+ config.mock_with :rspec
19
+ end
20
+
21
+ require File.join(SPEC_ROOT, '/../lib/heroku_san')