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/.gitignore +1 -1
- data/.rvmrc +1 -1
- data/README.rdoc +2 -1
- data/Rakefile +10 -2
- data/autotest/discover.rb +1 -0
- data/cucumber.yml +2 -0
- data/examples/auto_tagger.rake +62 -0
- data/features/{command_line.feature → config.feature} +34 -10
- data/features/extended-config.feature +5 -18
- data/features/remote.feature +57 -0
- data/features/support/env.rb +9 -0
- data/heroku_san.gemspec +8 -6
- data/lib/git.rb +39 -0
- data/lib/heroku_san/project.rb +111 -0
- data/lib/heroku_san/stage.rb +78 -0
- data/lib/heroku_san/version.rb +3 -0
- data/lib/heroku_san.rb +9 -1
- data/lib/{heroku_san/railtie.rb → railtie.rb} +1 -1
- data/lib/tasks/heroku.rake +1 -1
- data/lib/tasks.rb +284 -0
- data/lib/templates/heroku.example.yml +4 -1
- data/spec/fixtures/example.yml +26 -0
- data/spec/fixtures/extended_config.yml +7 -0
- data/spec/fixtures/old_format.yml +10 -0
- data/spec/fixtures/single_app.yml +6 -0
- data/spec/git_spec.rb +55 -0
- data/spec/heroku_san/project_spec.rb +107 -0
- data/spec/heroku_san/stage_spec.rb +138 -0
- data/spec/spec_helper.rb +21 -0
- metadata +69 -24
- data/lib/heroku_san/tasks.rb +0 -419
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
|
-
# <
|
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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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')
|