jw-heroku-rails 0.4.6

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.
@@ -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