heroku_san 1.3.0 → 2.0.rc.1
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/.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')
|