jw-heroku-rails 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ module Heroku
2
+ module Generators
3
+ class ConfigGenerator < ::Rails::Generators::Base
4
+ desc "Generates a Heroku Config file at config/heroku.yml"
5
+
6
+ def self.source_root
7
+ @_heroku_gen_source_root ||= File.expand_path("../../templates", __FILE__)
8
+ end
9
+
10
+ def create_config_file
11
+ template 'heroku.yml', File.join('config', "heroku.yml")
12
+ end
13
+
14
+ def create_rake_file
15
+ template 'heroku.rake', File.join('lib', 'tasks', "heroku.rake")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ # ### Shortcuts: uncomment these for easier to type deployments
2
+ # ### e.g. rake deploy (instead of rake heroku:deploy)
3
+ # ###
4
+ # task :deploy => ["heroku:deploy"]
5
+ # task :console => ["heroku:console"]
6
+ # task :setup => ["heroku:setup"]
7
+ # task :logs => ["heroku:logs"]
8
+ # task :restart => ["heroku:restart"]
9
+
10
+ # Heroku Deploy Callbacks
11
+ namespace :heroku do
12
+
13
+ # runs before all the deploys complete
14
+ task :before_deploy do
15
+
16
+ end
17
+
18
+ # runs before each push to a particular heroku deploy environment
19
+ task :before_each_deploy, [:app_name] do |t,args|
20
+
21
+ end
22
+
23
+ # runs after each push to a particular heroku deploy environment
24
+ task :after_each_deploy, [:app_name] do |t,args|
25
+
26
+ end
27
+
28
+ # runs after all the deploys complete
29
+ task :after_deploy do
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,53 @@
1
+ apps:
2
+ # map the environments to your desired heroku app names
3
+ # for example,
4
+ # production: awesomeapp
5
+ # would create the RACK_ENV=production
6
+ # and deploy to http://awesomeapp.heroku.com
7
+ production: awesomeapp
8
+ staging: awesomeapp-staging
9
+ legacy: awesomeapp-legacy
10
+
11
+ stacks:
12
+ # choose the stacks you want to use for each app.
13
+ # the all: setting sets the default
14
+ all: bamboo-mri-1.9.2
15
+
16
+ # override on a per environment basis
17
+ legacy: bamboo-ree-1.8.7
18
+
19
+ config:
20
+ # choose the configuration settings for all environments
21
+ all:
22
+ BUNDLE_WITHOUT: "test:development"
23
+ CONFIG_VAR1: "config1"
24
+ CONFIG_VAR2: "config2"
25
+
26
+ # you can also override configuration settings for each environment
27
+ production:
28
+ CONFIG_VAR1: "config1-production"
29
+ staging:
30
+ CONFIG_VAR1: "config1-staging"
31
+
32
+ collaborators:
33
+ # Be sure to add yourself as a collaborator, otherwise your
34
+ # access to the app will be revoked.
35
+ all:
36
+ - "my-heroku-email@somedomain.com"
37
+ - "another-heroku-email@somedomain.com"
38
+
39
+ domains:
40
+ production:
41
+ - "awesomeapp.com"
42
+ - "www.awesomeapp.com"
43
+
44
+ addons:
45
+ all:
46
+ - custom_domains:basic
47
+ # add any other addons here
48
+
49
+ production:
50
+ - ssl:piggyback
51
+ - cron:daily
52
+ # - newrelic:bronze
53
+ # production env specific addons here
@@ -0,0 +1,3 @@
1
+ require 'heroku-rails/config'
2
+ require 'heroku-rails/runner'
3
+ require 'heroku-rails/railtie' if defined?(::Rails::Railtie)
@@ -0,0 +1,86 @@
1
+ require 'erb'
2
+
3
+ module HerokuRails
4
+ class Config
5
+
6
+ class << self
7
+ def root
8
+ @heroku_rails_root || ENV["RAILS_ROOT"] || "."
9
+ end
10
+ def root=(root)
11
+ @heroku_rails_root = root
12
+ end
13
+ end
14
+
15
+ attr_accessor :settings
16
+
17
+ def initialize(config_filepath)
18
+ if File.exists?(config_filepath)
19
+ self.settings = YAML.load(ERB.new(File.read(config_filepath)).result) || {}
20
+ else
21
+ self.settings = {}
22
+ end
23
+ end
24
+
25
+ def apps
26
+ self.settings['apps'] || {}
27
+ end
28
+
29
+ def app_names
30
+ apps.values
31
+ end
32
+
33
+ def app_environments
34
+ apps.keys
35
+ end
36
+
37
+ # return the stack setting for a particular app environment
38
+ def stack(app_env)
39
+ stacks = self.settings['stacks'] || {}
40
+ stacks[app_env] || stacks['all']
41
+ end
42
+
43
+ def rake_cmd(app_env)
44
+ if self.stack(app_env) =~ /cedar/i
45
+ 'heroku run rake'
46
+ else
47
+ 'heroku rake'
48
+ end
49
+ end
50
+
51
+ # pull out the config setting hash for a particular app environment
52
+ def config(app_env)
53
+ config = self.settings['config'] || {}
54
+ all = config['all'] || {}
55
+
56
+ # overwrite all configs with the environment specific ones
57
+ all.merge(config[app_env] || {})
58
+ end
59
+
60
+ # return a list of domains for a particular app environment
61
+ def domains(app_env)
62
+ domains = self.settings['domains'] || {}
63
+ domains[app_env] || []
64
+ end
65
+
66
+ # return a list of collaborators for a particular app environment
67
+ def collaborators(app_env)
68
+ app_setting_list('collaborators', app_env)
69
+ end
70
+
71
+ # return a list of addons for a particular app environment
72
+ def addons(app_env)
73
+ app_setting_list('addons', app_env)
74
+ end
75
+
76
+ protected
77
+
78
+ def app_setting_list(setting_key, app)
79
+ setting = self.settings[setting_key] || {}
80
+ all = setting['all'] || []
81
+
82
+ # add in collaborators from app environment to the ones defined in all
83
+ (all + (setting[app] || [])).uniq
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,8 @@
1
+ module HerokuRails
2
+ class Railtie < ::Rails::Railtie
3
+ rake_tasks do
4
+ HerokuRails::Config.root = ::Rails.root
5
+ load 'heroku/rails/tasks.rb'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,273 @@
1
+ require 'heroku-api'
2
+
3
+ module HerokuRails
4
+ class Runner
5
+ def initialize(config)
6
+ @config = config
7
+ @environments = []
8
+ end
9
+
10
+ def authorize
11
+ @heroku ||= Heroku::Auth.client
12
+ end
13
+
14
+ # add a specific environment to the run list
15
+ def add_environment(env)
16
+ @environments << env
17
+ end
18
+
19
+ # use all environments
20
+ def all_environments
21
+ @environments = @config.app_environments
22
+ end
23
+
24
+ # setup apps (create if necessary)
25
+ def setup_apps
26
+ authorize
27
+
28
+ # get a list of all my current apps on Heroku (so we don't create dupes)
29
+ @my_apps = @heroku.list.map{|a| a.first}
30
+
31
+ each_heroku_app do |heroku_env, app_name, repo|
32
+ next if @my_apps.include?(app_name)
33
+
34
+ stack = @config.stack(heroku_env)
35
+ stack_option = " --stack #{stack}" if stack.to_s.size > 0
36
+ creation_command "heroku create #{app_name}#{stack_option} --remote #{app_name}"
37
+ end
38
+ end
39
+
40
+ # setup the stacks for each app (migrating if necessary)
41
+ def setup_stacks
42
+ authorize
43
+ each_heroku_app do |heroku_env, app_name, repo|
44
+ # get the intended stack setting
45
+ stack = @config.stack(heroku_env)
46
+
47
+ # get the remote info about the app from heroku
48
+ heroku_app_info = @heroku.info(app_name) || {}
49
+
50
+ # if the stacks don't match, then perform a migration
51
+ if stack != heroku_app_info[:stack]
52
+ puts "Migrating the app: #{app_name} to the stack: #{stack}"
53
+ creation_command "heroku stack:migrate #{stack} --app #{app_name}"
54
+ end
55
+ end
56
+ end
57
+
58
+ # setup the list of collaborators
59
+ def setup_collaborators
60
+ authorize
61
+ each_heroku_app do |heroku_env, app_name, repo|
62
+ # get the remote info about the app from heroku
63
+ heroku_app_info = @heroku.info(app_name) || {}
64
+
65
+ # get the intended list of collaborators to add
66
+ collaborator_emails = @config.collaborators(heroku_env)
67
+
68
+ # add current user to collaborator list (always)
69
+ collaborator_emails << @heroku.user unless collaborator_emails.include?(@heroku.user)
70
+ collaborator_emails << heroku_app_info[:owner] unless collaborator_emails.include?(heroku_app_info[:owner])
71
+
72
+ # get existing collaborators
73
+ existing_emails = heroku_app_info[:collaborators].to_a.map{|c| c[:email]}
74
+
75
+ # get the list of collaborators to delete
76
+ existing_emails.each do |existing_email|
77
+ # check to see if we need to delete this person
78
+ unless collaborator_emails.include?(existing_email)
79
+ # delete that collaborator if they arent on the approved list
80
+ destroy_command "heroku sharing:remove #{existing_email} --app #{app_name}"
81
+ end
82
+ end
83
+
84
+ # get the list of collaborators to add
85
+ collaborator_emails.each do |collaborator_email|
86
+ # check to see if we need to add this person
87
+ unless existing_emails.include?(collaborator_email)
88
+ # add the collaborator if they are not already on the server
89
+ creation_command "heroku sharing:add #{collaborator_email} --app #{app_name}"
90
+ end
91
+ end
92
+
93
+ # display the destructive commands
94
+ output_destroy_commands(app_name)
95
+ end
96
+ end
97
+
98
+ # setup configuration
99
+ def setup_config
100
+ authorize
101
+ each_heroku_app do |heroku_env, app_name, repo|
102
+ # get the configuration that we are aiming towards
103
+ new_config = @config.config(heroku_env)
104
+
105
+ # default RACK_ENV to the heroku_env (unless its manually set to something else)
106
+ unless new_config["RACK_ENV"].to_s.length > 0
107
+ new_config["RACK_ENV"] = heroku_env
108
+ end
109
+
110
+ # get the existing config from heroku's servers
111
+ existing_config = @heroku.config_vars(app_name) || {}
112
+
113
+ # find the config variables to add
114
+ add_config = {}
115
+ new_config.each do |new_key, new_val|
116
+ add_config[new_key] = new_val unless existing_config[new_key] == new_val
117
+ end
118
+
119
+ # persist the changes onto heroku
120
+ unless add_config.empty?
121
+ # add the config
122
+ set_config = ""
123
+ add_config.each do |key, val|
124
+ set_config << "#{key}='#{val}' "
125
+ end
126
+
127
+ creation_command "heroku config:add #{set_config} --app #{app_name}"
128
+ end
129
+ end
130
+ end
131
+
132
+ # setup the addons for heroku
133
+ def setup_addons
134
+ authorize
135
+ each_heroku_app do |heroku_env, app_name, repo|
136
+ # get the addons that we are aiming towards
137
+ addons = @config.addons(heroku_env)
138
+
139
+ # get the addons that are already on the servers
140
+ existing_addons = (@heroku.installed_addons(app_name) || []).map{|a| a["name"]}
141
+
142
+ # all apps need the shared database
143
+ addons << "shared-database:5mb" unless addons.index("shared-database:5mb") || addons.index("shared-database:20gb")
144
+
145
+ # add "custom_domains" if that addon doesnt already exist
146
+ # and we have domains configured for this app
147
+ addons << "custom_domains:basic" unless @config.domains(heroku_env).empty? or
148
+ addons.any?{|a| a =~ /custom_domains/} or
149
+ existing_addons.any?{|a| a =~ /custom_domains/}
150
+
151
+ # remove the addons that need to be removed
152
+ existing_addons.each do |existing_addon|
153
+ # check to see if we need to delete this addon
154
+ unless addons.include?(existing_addon)
155
+ # delete this addon if they arent on the approved list
156
+ destroy_command "heroku addons:remove #{existing_addon} --app #{app_name}"
157
+ end
158
+ end
159
+
160
+ # add the addons that dont exist already
161
+ addons.each do |addon|
162
+ # check to see if we need to add this addon
163
+ unless existing_addons.include?(addon)
164
+ # add this addon if they are not already added
165
+ creation_command "heroku addons:add #{addon} --app #{app_name}"
166
+ end
167
+ end
168
+
169
+ # display the destructive commands
170
+ output_destroy_commands(app_name)
171
+ end
172
+ end
173
+
174
+ # setup the domains for heroku
175
+ def setup_domains
176
+ authorize
177
+ each_heroku_app do |heroku_env, app_name, repo|
178
+ # get the domains that we are aiming towards
179
+ domains = @config.domains(heroku_env)
180
+
181
+ # get the domains that are already on the servers
182
+ existing_domains = (@heroku.list_domains(app_name) || []).map{|a| a[:domain]}
183
+
184
+ # remove the domains that need to be removed
185
+ existing_domains.each do |existing_domain|
186
+ # check to see if we need to delete this domain
187
+ unless domains.include?(existing_domain)
188
+ # delete this domain if they arent on the approved list
189
+ destroy_command "heroku domains:remove #{existing_domain} --app #{app_name}"
190
+ end
191
+ end
192
+
193
+ # add the domains that dont exist already
194
+ domains.each do |domain|
195
+ # check to see if we need to add this domain
196
+ unless existing_domains.include?(domain)
197
+ # add this domain if they are not already added
198
+ creation_command "heroku domains:add #{domain} --app #{app_name}"
199
+ end
200
+ end
201
+
202
+ # display the destructive commands
203
+ output_destroy_commands(app_name)
204
+ end
205
+ end
206
+
207
+ # cycles through each configured heroku app
208
+ # yields the environment name, the app name, and the repo url
209
+ def each_heroku_app
210
+
211
+ if @config.apps.size == 0
212
+ puts "\nNo heroku apps are configured. Run:
213
+ rails generate heroku:config\n\n"
214
+ puts "this will generate a default config/heroku.yml that you should edit"
215
+ puts "and then try running this command again"
216
+
217
+ exit(1)
218
+ end
219
+
220
+ if @environments.blank? && @config.apps.size == 1
221
+ @environments = [@config.app_environments.first]
222
+ end
223
+
224
+ if @environments.present?
225
+ @environments.each do |heroku_env|
226
+ app_name = @config.apps[heroku_env]
227
+ yield(heroku_env, app_name, "git@heroku.com:#{app_name}.git")
228
+ end
229
+ else
230
+ puts "\nYou must first specify at least one Heroku app:
231
+ rake <app> [<app>] <command>
232
+ rake production restart
233
+ rake demo staging deploy"
234
+
235
+ puts "\n\nYou can use also command all Heroku apps for this project:
236
+ rake all heroku:setup\n"
237
+
238
+ exit(1)
239
+ end
240
+ end
241
+
242
+ def system_with_echo(*args)
243
+ puts args.join(' ')
244
+ command(*args)
245
+ end
246
+
247
+ def creation_command(*args)
248
+ system_with_echo(*args)
249
+ end
250
+
251
+ def destroy_command(*args)
252
+ # puts args.join(' ')
253
+ @destroy_commands ||= []
254
+ @destroy_commands << args.join(' ')
255
+ end
256
+
257
+ def output_destroy_commands(app)
258
+ puts "The #{app} had a few things removed from the heroku.yml."
259
+ puts "If they are no longer neccessary, then run the following commands:\n\n"
260
+ (@destroy_commands || []).each do |destroy_command|
261
+ puts destroy_command
262
+ end
263
+ puts "\n\nthese commands may cause data loss so make sure you know that these are necessary"
264
+ # clear destroy commands
265
+ @destroy_commands = []
266
+ end
267
+
268
+ def command(*args)
269
+ system(*args)
270
+ end
271
+
272
+ end
273
+ end