heroku-rails 0.0.1 → 0.2.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 +10 -0
- data/Gemfile.lock +1 -1
- data/README.md +80 -37
- data/TODO +5 -2
- data/heroku-rails.gemspec +3 -3
- data/lib/generators/heroku/callbacks_generator.rb +15 -0
- data/lib/generators/heroku/config_generator.rb +15 -0
- data/lib/generators/templates/heroku.rake +24 -0
- data/lib/generators/templates/heroku.yml +51 -0
- data/lib/heroku-rails.rb +3 -9
- data/lib/heroku/rails/heroku_config.rb +69 -0
- data/lib/heroku/rails/heroku_runner.rb +242 -0
- data/lib/heroku/rails/railtie.rb +9 -0
- data/lib/heroku/rails/tasks.rb +129 -173
- data/spec/fixtures/heroku-config.yml +48 -0
- data/spec/heroku/rails/heroku_config_spec.rb +131 -0
- data/spec/heroku/rails/heroku_runner_spec.rb +1 -0
- data/spec/spec_helper.rb +18 -0
- metadata +21 -9
- data/lib/heroku/rails/config.rb +0 -42
- data/lib/templates/heroku.example.yml +0 -5
data/lib/heroku/rails/tasks.rb
CHANGED
@@ -1,224 +1,180 @@
|
|
1
1
|
HEROKU_CONFIG_FILE = Rails.root.join('config', 'heroku.yml')
|
2
|
+
HEROKU_CONFIG = Heroku::Rails::HerokuConfig.new(HEROKU_CONFIG_FILE)
|
3
|
+
HEROKU_RUNNER = Heroku::Rails::HerokuRunner.new(HEROKU_CONFIG)
|
2
4
|
|
3
|
-
|
4
|
-
HEROKU_CONFIG
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
task name do
|
9
|
-
@heroku_apps ||= []
|
10
|
-
@heroku_apps << name
|
5
|
+
# create all the the environment specific tasks
|
6
|
+
(HEROKU_CONFIG.apps).each do |heroku_env, app_name|
|
7
|
+
desc "Select #{heroku_env} Heroku app for later commands"
|
8
|
+
task heroku_env do
|
9
|
+
HEROKU_RUNNER.add_environment(heroku_env)
|
11
10
|
end
|
12
11
|
end
|
13
12
|
|
14
13
|
desc 'Select all Heroku apps for later command'
|
15
14
|
task :all do
|
16
|
-
|
15
|
+
HEROKU_RUNNER.all_environments
|
17
16
|
end
|
18
17
|
|
19
18
|
namespace :heroku do
|
20
|
-
|
21
|
-
|
22
|
-
each_heroku_app do |name, app, repo|
|
23
|
-
system_with_echo "heroku create #{app}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
desc "Generate the Heroku gems manifest from gem dependencies"
|
28
|
-
task :gems => 'gems:base' do
|
29
|
-
RAILS_ENV='production'
|
30
|
-
Rake::Task[:environment].invoke
|
31
|
-
gems = Rails.configuration.gems.reject { |g| g.frozen? && !g.framework_gem? }
|
32
|
-
list = gems.collect do |g|
|
33
|
-
command, *options = g.send(:install_command)
|
34
|
-
options.join(" ")
|
35
|
-
end
|
36
|
-
|
37
|
-
list.unshift(%Q{rails --version "= #{Rails.version}"})
|
38
|
-
|
39
|
-
File.open(Rails.root.join('.gems'), 'w') do |f|
|
40
|
-
f.write(list.join("\n"))
|
41
|
-
end
|
19
|
+
def system_with_echo(*args)
|
20
|
+
HEROKU_RUNNER.system_with_echo(*args)
|
42
21
|
end
|
43
22
|
|
44
23
|
desc 'Add git remotes for all apps in this project'
|
45
24
|
task :remotes do
|
46
|
-
each_heroku_app do |
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
desc 'Adds a collaborator'
|
52
|
-
task :share do
|
53
|
-
print "Email address of collaborator to add: "
|
54
|
-
$stdout.flush
|
55
|
-
email = $stdin.gets
|
56
|
-
each_heroku_app do |name, app, repo|
|
57
|
-
system_with_echo "heroku sharing:add --app #{app} #{email}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
desc 'Adds a collaborator'
|
62
|
-
task :unshare do
|
63
|
-
print "Email address of collaborator to remove: "
|
64
|
-
$stdout.flush
|
65
|
-
email = $stdin.gets
|
66
|
-
each_heroku_app do |name, app, repo|
|
67
|
-
system_with_echo "heroku sharing:remove --app #{app} #{email}"
|
25
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
26
|
+
system_with_echo("git remote add #{app_name} #{repo}")
|
68
27
|
end
|
69
28
|
end
|
70
29
|
|
71
30
|
desc 'Lists configured apps'
|
72
31
|
task :apps => :all do
|
73
|
-
|
74
|
-
|
32
|
+
puts "\n"
|
33
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
34
|
+
puts "#{heroku_env} maps to the Heroku app #{app_name} located at:"
|
75
35
|
puts " #{repo}"
|
76
36
|
puts
|
77
37
|
end
|
78
38
|
end
|
79
39
|
|
80
|
-
desc
|
81
|
-
task :
|
82
|
-
each_heroku_app do |
|
83
|
-
|
84
|
-
puts
|
85
|
-
config = Hash[`#{command}`.scan(/^(.+?)\s*=>\s*(.+)$/)]
|
86
|
-
if config['RACK_ENV'] != name
|
87
|
-
system_with_echo "heroku config:add --app #{app} RACK_ENV=#{name}"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
desc 'Creates an example configuration file'
|
93
|
-
task :create_config do
|
94
|
-
example = File.join(File.dirname(__FILE__), '..', 'templates', 'heroku.example.yml')
|
95
|
-
if File.exists?(HEROKU_CONFIG_FILE)
|
96
|
-
puts "config/heroku.yml already exists"
|
97
|
-
else
|
98
|
-
puts "Copied example config to config/heroku.yml"
|
99
|
-
FileUtils.cp(example, HEROKU_CONFIG_FILE)
|
100
|
-
system_with_echo("#{ENV['EDITOR']} #{HEROKU_CONFIG_FILE}")
|
40
|
+
desc "Get remote server information on the heroku app"
|
41
|
+
task :info do
|
42
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
43
|
+
system_with_echo "heroku info --app #{app_name}"
|
44
|
+
puts "\n"
|
101
45
|
end
|
102
46
|
end
|
103
47
|
|
104
|
-
|
105
|
-
|
106
|
-
|
48
|
+
desc "Deploys, migrates and restarts latest code"
|
49
|
+
task :deploy => "heroku:before_deploy" do
|
50
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
107
51
|
|
108
|
-
|
52
|
+
# set the current heroku_app so that callbacks can read the data
|
53
|
+
@heroku_app = {:env => heroku_env, :app_name => app_name, :repo => repo}
|
54
|
+
Rake::Task["heroku:before_each_deploy"].invoke
|
109
55
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
else
|
120
|
-
puts "Unable to determine the current git branch, please checkout the branch you'd like to deploy"
|
121
|
-
exit(1)
|
56
|
+
branch = `git branch`.scan(/^\* (.*)\n/).flatten.first.to_s
|
57
|
+
if branch.present?
|
58
|
+
@git_push_arguments ||= []
|
59
|
+
system_with_echo "git push #{repo} #{@git_push_arguments.join(' ')} #{branch}:master && heroku rake --app #{app_name} db:migrate && heroku restart --app #{app_name}"
|
60
|
+
else
|
61
|
+
puts "Unable to determine the current git branch, please checkout the branch you'd like to deploy"
|
62
|
+
exit(1)
|
63
|
+
end
|
64
|
+
Rake::Task["heroku:after_each_deploy"].invoke
|
122
65
|
end
|
123
|
-
Rake::Task[:
|
66
|
+
Rake::Task["heroku:after_deploy"].execute
|
124
67
|
end
|
125
|
-
Rake::Task[:after_deploy].execute
|
126
|
-
end
|
127
68
|
|
128
|
-
|
129
|
-
task :before_deploy do
|
130
|
-
end
|
131
|
-
|
132
|
-
desc "Callback after all deploys"
|
133
|
-
task :after_deploy do
|
134
|
-
end
|
135
|
-
|
136
|
-
desc "Callback before each deploy"
|
137
|
-
task :before_each_deploy do
|
138
|
-
end
|
139
|
-
|
140
|
-
desc "Callback after each deploy"
|
141
|
-
task :after_each_deploy do
|
142
|
-
end
|
69
|
+
# Callback before all deploys
|
70
|
+
task :before_deploy do
|
71
|
+
end
|
143
72
|
|
73
|
+
# Callback after all deploys
|
74
|
+
task :after_deploy do
|
75
|
+
end
|
144
76
|
|
145
|
-
|
146
|
-
task :
|
147
|
-
|
148
|
-
@git_push_arguments << '--force'
|
149
|
-
Rake::Task[:deploy].execute
|
150
|
-
end
|
77
|
+
# Callback before each deploy
|
78
|
+
task :before_each_deploy do
|
79
|
+
end
|
151
80
|
|
152
|
-
|
153
|
-
task :
|
154
|
-
each_heroku_app do |name, app, repo|
|
155
|
-
system_with_echo "heroku bundles:capture --app #{app}"
|
81
|
+
# Callback after each deploy
|
82
|
+
task :after_each_deploy do
|
156
83
|
end
|
157
|
-
end
|
158
84
|
|
159
|
-
desc "
|
160
|
-
task :
|
161
|
-
|
162
|
-
|
85
|
+
desc "Force deploys, migrates and restarts latest code"
|
86
|
+
task :force_deploy do
|
87
|
+
@git_push_arguments ||= []
|
88
|
+
@git_push_arguments << '--force'
|
89
|
+
Rake::Task["heroku:deploy"].execute
|
163
90
|
end
|
164
|
-
end
|
165
91
|
|
166
|
-
desc "
|
167
|
-
task :
|
168
|
-
|
169
|
-
|
92
|
+
desc "Captures a bundle on Heroku"
|
93
|
+
task :capture do
|
94
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
95
|
+
system_with_echo "heroku bundles:capture --app #{app_name}"
|
96
|
+
end
|
170
97
|
end
|
171
|
-
end
|
172
98
|
|
173
|
-
desc "
|
174
|
-
task :
|
175
|
-
|
176
|
-
|
99
|
+
desc "Opens a remote console"
|
100
|
+
task :console do
|
101
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
102
|
+
system_with_echo "heroku console --app #{app_name}"
|
103
|
+
end
|
177
104
|
end
|
178
|
-
end
|
179
105
|
|
180
|
-
|
181
|
-
task :
|
182
|
-
each_heroku_app do |
|
183
|
-
system_with_echo "heroku
|
184
|
-
dump = `heroku pgdumps --app #{app}`.split("\n").last.split(" ").first
|
185
|
-
system_with_echo "mkdir -p #{Rails.root}/db/dumps"
|
186
|
-
file = "#{Rails.root}/db/dumps/#{dump}.sql.gz"
|
187
|
-
url = `heroku pgdumps:url --app #{app} #{dump}`.chomp
|
188
|
-
system_with_echo "wget", url, "-O", file
|
189
|
-
system_with_echo "rake db:drop db:create"
|
190
|
-
system_with_echo "gunzip -c #{file} | #{Rails.root}/script/dbconsole"
|
191
|
-
system_with_echo "rake jobs:clear"
|
106
|
+
desc "Restarts remote servers"
|
107
|
+
task :restart do
|
108
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
109
|
+
system_with_echo "heroku restart --app #{app_name}"
|
192
110
|
end
|
193
111
|
end
|
194
|
-
end
|
195
112
|
|
196
|
-
|
197
|
-
puts args.join(' ')
|
198
|
-
system(*args)
|
199
|
-
end
|
113
|
+
namespace :setup do
|
200
114
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
115
|
+
desc "Creates the apps on Heroku"
|
116
|
+
task :create_apps do
|
117
|
+
HEROKU_RUNNER.create_apps
|
118
|
+
end
|
119
|
+
|
120
|
+
desc "Setup the Heroku stacks from heroku.yml config"
|
121
|
+
task :stacks do
|
122
|
+
HEROKU_RUNNER.setup_stacks
|
123
|
+
end
|
124
|
+
|
125
|
+
desc "Setup the Heroku collaborators from heroku.yml config"
|
126
|
+
task :collaborators do
|
127
|
+
HEROKU_RUNNER.setup_collaborators
|
128
|
+
end
|
129
|
+
|
130
|
+
desc "Setup the Heroku environment config variables from heroku.yml config"
|
131
|
+
task :config do
|
132
|
+
HEROKU_RUNNER.setup_config
|
211
133
|
end
|
212
|
-
puts
|
213
|
-
else
|
214
|
-
puts "You must first specify at least one Heroku app:
|
215
|
-
rake <app> [<app>] <command>
|
216
|
-
rake production restart
|
217
|
-
rake demo staging deploy"
|
218
134
|
|
219
|
-
|
220
|
-
|
135
|
+
desc "Setup the Heroku addons from heroku.yml config"
|
136
|
+
task :addons do
|
137
|
+
HEROKU_RUNNER.setup_addons
|
138
|
+
end
|
221
139
|
|
222
|
-
|
140
|
+
desc "Setup the Heroku domains from heroku.yml config"
|
141
|
+
task :domains do
|
142
|
+
HEROKU_RUNNER.setup_domains
|
143
|
+
end
|
223
144
|
end
|
224
|
-
|
145
|
+
|
146
|
+
desc "Setup Heroku deploy environment from heroku.yml config"
|
147
|
+
task :setup => [
|
148
|
+
"heroku:setup:stacks",
|
149
|
+
"heroku:setup:collaborators",
|
150
|
+
"heroku:setup:config",
|
151
|
+
"heroku:setup:addons",
|
152
|
+
"heroku:setup:domains",
|
153
|
+
]
|
154
|
+
|
155
|
+
namespace :db do
|
156
|
+
desc "Migrates and restarts remote servers"
|
157
|
+
task :migrate do
|
158
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
159
|
+
system_with_echo "heroku rake --app #{app_name} db:migrate && heroku restart --app #{app_name}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
desc "Pulls the database from heroku and stores it into db/dumps/"
|
164
|
+
task :pull do
|
165
|
+
HEROKU_RUNNER.each_heroku_app do |heroku_env, app_name, repo|
|
166
|
+
system_with_echo "heroku pgdumps:capture --app #{app_name}"
|
167
|
+
dump = `heroku pgdumps --app #{app_name}`.split("\n").last.split(" ").first
|
168
|
+
system_with_echo "mkdir -p #{Rails.root}/db/dumps"
|
169
|
+
file = "#{Rails.root}/db/dumps/#{dump}.sql.gz"
|
170
|
+
url = `heroku pgdumps:url --app #{app_name} #{dump}`.chomp
|
171
|
+
system_with_echo "wget", url, "-O", file
|
172
|
+
|
173
|
+
# TODO: these are a bit distructive...
|
174
|
+
# system_with_echo "rake db:drop db:create"
|
175
|
+
# system_with_echo "gunzip -c #{file} | #{Rails.root}/script/dbconsole"
|
176
|
+
# system_with_echo "rake jobs:clear"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
apps:
|
2
|
+
production: awesomeapp
|
3
|
+
staging: awesomeapp-staging
|
4
|
+
|
5
|
+
stacks:
|
6
|
+
all: bamboo-mri-1.9.2
|
7
|
+
staging: bamboo-ree-1.8.7
|
8
|
+
|
9
|
+
config:
|
10
|
+
all:
|
11
|
+
BUNDLE_WITHOUT: "test development"
|
12
|
+
CONFIG_VAR1: "config1"
|
13
|
+
CONFIG_VAR2: "config2"
|
14
|
+
|
15
|
+
production:
|
16
|
+
CONFIG_VAR1: "config1-production"
|
17
|
+
|
18
|
+
staging:
|
19
|
+
CONFIG_VAR1: "config1-staging"
|
20
|
+
STAGING_CONFIG: "special-staging"
|
21
|
+
|
22
|
+
collaborators:
|
23
|
+
all:
|
24
|
+
- "all-user1@somedomain.com"
|
25
|
+
- "all-user2@somedomain.com"
|
26
|
+
- "all-user2@somedomain.com"
|
27
|
+
staging:
|
28
|
+
- "staging-user@somedomain.com"
|
29
|
+
production:
|
30
|
+
- "production-user@somedomain.com"
|
31
|
+
|
32
|
+
domains:
|
33
|
+
staging:
|
34
|
+
- "staging.awesomeapp.com"
|
35
|
+
production:
|
36
|
+
- "awesomeapp.com"
|
37
|
+
- "www.awesomeapp.com"
|
38
|
+
|
39
|
+
addons:
|
40
|
+
all:
|
41
|
+
# add any other addons here
|
42
|
+
- custom_domains:basic
|
43
|
+
- newrelic:bronze
|
44
|
+
|
45
|
+
production:
|
46
|
+
# list production env specific addons here
|
47
|
+
- ssl:piggyback
|
48
|
+
- cron:hourly
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Heroku::Rails
|
4
|
+
describe HerokuConfig do
|
5
|
+
before(:each) do
|
6
|
+
@config = HerokuConfig.new(config_path("heroku-config.yml"))
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should read the configuration file" do
|
10
|
+
@config.settings.should_not be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#apps" do
|
14
|
+
it "should return the list of apps defined" do
|
15
|
+
@config.apps.should have(2).apps
|
16
|
+
@config.apps.should include("production" => "awesomeapp")
|
17
|
+
@config.apps.should include("staging" => "awesomeapp-staging")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#app_names" do
|
22
|
+
it "should return the list of apps defined" do
|
23
|
+
@config.app_names.should have(2).names
|
24
|
+
@config.app_names.should include("awesomeapp")
|
25
|
+
@config.app_names.should include("awesomeapp-staging")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#app_environments" do
|
30
|
+
it "should return a list of the environments defined" do
|
31
|
+
@config.app_environments.should have(2).environments
|
32
|
+
@config.app_environments.should include("production")
|
33
|
+
@config.app_environments.should include("staging")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#stack" do
|
38
|
+
it "should return the associated stack for an environment" do
|
39
|
+
@config.stack("staging").should == "bamboo-ree-1.8.7"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should default to the all setting if not explicitly defined" do
|
43
|
+
@config.stack("production").should == "bamboo-mri-1.9.2"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#config" do
|
48
|
+
context "staging environment" do
|
49
|
+
before(:each) do
|
50
|
+
@config = @config.config("staging")
|
51
|
+
end
|
52
|
+
it "should include configs defined in 'staging'" do
|
53
|
+
@config["STAGING_CONFIG"].should == "special-staging"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should include configs defined in 'all'" do
|
57
|
+
@config["BUNDLE_WITHOUT"].should == "test development"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should use configs defined in 'staging' ahead of configs defined in 'all'" do
|
61
|
+
@config["CONFIG_VAR1"].should == "config1-staging"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#collaborators" do
|
67
|
+
context "staging environment" do
|
68
|
+
before(:each) do
|
69
|
+
@collaborators = @config.collaborators('staging')
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should include the collaborators defined in 'all'" do
|
73
|
+
@collaborators.should include('all-user1@somedomain.com')
|
74
|
+
@collaborators.should include('all-user2@somedomain.com')
|
75
|
+
@collaborators.should have(3).collaborators
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should include collaborators defined in 'staging'" do
|
79
|
+
@collaborators.should include('staging-user@somedomain.com')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not include collaborators defined in 'production'" do
|
83
|
+
@collaborators.should_not include('production-user@somedomain.com')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#domains" do
|
89
|
+
context "staging environment" do
|
90
|
+
before(:each) do
|
91
|
+
@domains = @config.domains('staging')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should include the domains defined in 'staging'" do
|
95
|
+
@domains.should include('staging.awesomeapp.com')
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should not include the domains defined in 'production'" do
|
99
|
+
@domains.should_not include('awesomeapp.com')
|
100
|
+
@domains.should_not include('www.awesomeapp.com')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "production environment" do
|
105
|
+
it "should include the domains defined in 'production'" do
|
106
|
+
@domains = @config.domains('production')
|
107
|
+
@domains.should include('awesomeapp.com')
|
108
|
+
@domains.should include('www.awesomeapp.com')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#addons" do
|
114
|
+
context "staging environment" do
|
115
|
+
before(:each) do
|
116
|
+
@addons = @config.addons('staging')
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should include addons defined in 'all'" do
|
120
|
+
@addons.should include('custom_domains:basic')
|
121
|
+
@addons.should include('newrelic:bronze')
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should not include addons defined in 'production'" do
|
125
|
+
@addons.should_not include('ssl:piggyback')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|