heroku-rails 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,4 +1,14 @@
1
1
  Heroku Rails
2
+
3
+ v0.2.0
4
+ ============================================
5
+ Added Heroku Settings tasks, and rails generators
6
+
7
+ Cleaned out lots of tasks that are now handled by editing config/heroku.yml and running `rake heroku:setup`
8
+
9
+ Namespaced tasks such as heroku:deploy, heroku:console, etc. The only top level task now is `rake all ...` which sets all the environments. Individual environment tasks (e.g. `rake production ...`) are still generated without a namespace.
10
+
11
+
2
12
  v0.0.1
3
13
  ============================================
4
14
  Initial fork and reorganization
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- heroku-rails (0.0.1)
4
+ heroku-rails (0.2.0)
5
5
  heroku
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -35,76 +35,119 @@ Rake tasks are not automatically loaded from gems, so you’ll need to add the f
35
35
 
36
36
  In config/heroku.yml you will need add the Heroku apps that you would like to attach to this project. You can generate this file and edit it by running:
37
37
 
38
- rake heroku:create_config
38
+ rails generate heroku:config
39
39
 
40
- If this is a fresh project, heroku_san can create all the applications for
41
- you, and set the RACK_ENV.
40
+ Then edit config/heroku.yml with your project's heroku configurations. To persist the changes onto Heroku, just run:
42
41
 
43
- rake all heroku:create heroku:rack_env
42
+ rake all heroku:setup
43
+
44
+ This will create the heroku apps you have defined, and create the settings for each.
44
45
 
45
46
  ## Usage
46
47
 
47
48
  After configuring your Heroku apps you can use rake tasks to control the
48
49
  apps.
49
50
 
50
- rake production deploy
51
+ rake production heroku:deploy
51
52
 
52
53
  A rake task with the shorthand name of each app is now available and adds that
53
54
  server to the list that subsequent commands will execute on. Because this list
54
55
  is additive, you can easily select which servers to run a command on.
55
56
 
56
- rake demo staging restart
57
+ rake demo staging heroku:restart
57
58
 
58
59
  A special rake task 'all' is created that causes any further commands to
59
60
  execute on all heroku apps.
60
61
 
61
- Manipulate collaborators on all this project's apps (prompts for email
62
- address):
63
-
64
- rake all heroku:share
65
- rake all heroku:unshare
66
-
67
62
  Need to add remotes for each app?
68
63
 
69
64
  rake all heroku:remotes
70
65
 
71
66
  A full list of tasks provided:
72
67
 
73
- rake all # Select all Heroku apps for later command
74
- rake console # Opens a remote console
75
- rake deploy # Deploys, migrates and restarts latest code.
76
- rake after_deploy # Callback run after the overall deploy
77
- rake after_each_deploy # Callback run after each environment's deploy
78
- rake before_deploy # Callback run before the overall deploy
79
- rake before_each_deploy # Callback run before each environment's deploy
80
- rake capture # Captures a bundle on Heroku
81
- rake heroku:apps # Lists configured apps
82
- rake heroku:create # Creates the Heroku app
83
- rake heroku:create_config # Creates an example configuration file
84
- rake heroku:gems # Generate the Heroku gems manifest from gem dependencies
85
- rake heroku:remotes # Add git remotes for all apps in this project
86
- rake heroku:rack_env # Add proper RACK_ENV to each application
87
- rake heroku:share # Adds a collaborator
88
- rake heroku:unshare # Removes a collaborator
89
- rake migrate # Migrates and restarts remote servers
90
- rake restart # Restarts remote servers
91
-
92
- Frequently used tasks are not namespaced, everything else lives under heroku.
68
+ rake all # Select all Heroku apps for later command
69
+ rake heroku:deploy # Deploys, migrates and restarts latest code.
70
+ rake heroku:apps # Lists configured apps
71
+ rake heroku:info # Queries the heroku status info on each app
72
+ rake heroku:console # Opens a remote console
73
+ rake heroku:capture # Captures a bundle on Heroku
74
+ rake heroku:remotes # Add git remotes for all apps in this project
75
+ rake heroku:migrate # Migrates and restarts remote servers
76
+ rake heroku:restart # Restarts remote servers
77
+
78
+ rake heroku:setup # runs all heroku setup scripts
79
+ rake heroku:setup:addons # sets up the heroku addons
80
+ rake heroku:setup:collaborators # sets up the heroku collaborators
81
+ rake heroku:setup:config # sets up the heroku config env variables
82
+ rake heroku:setup:domains # sets up the heroku domains
83
+ rake heroku:setup:stacks # sets the correct stack for each heroku app
84
+
85
+ rake heroku:db:setup # Migrates and restarts remote servers
86
+
87
+ You can easily alias frequently used tasks within your application's Rakefile:
88
+
89
+ task :deploy => ["heroku:deploy"]
90
+ task :console => ["heroku:console"]
91
+ task :capture => ["heroku:capture"]
92
+
93
+ With this in place, you can be a bit more terse:
94
+
95
+ rake staging console
96
+ rake all deploy
97
+
98
+ ### Deploy Hooks
99
+
100
+ You can easily hook into the deploy process by defining any of the following rake tasks:
101
+
102
+ You can generate these automatically within lib/tasks/heroku.rake by running:
103
+
104
+ rails generate heroku:callbacks
105
+
106
+
107
+ Then edit the rake tasks to provide custom logic for each of these steps
108
+
109
+ namespace :heroku do
110
+
111
+ # runs before all the deploys complete
112
+ task :before_deploy do
113
+
114
+ end
115
+
116
+ # runs before each push to a particular heroku deploy environment
117
+ task :before_each_deploy do
118
+
119
+ end
120
+
121
+ # runs after each push to a particular heroku deploy environment
122
+ task :after_each_deploy do
123
+
124
+ end
125
+
126
+ # runs after all the deploys complete
127
+ task :after_deploy do
128
+
129
+ end
130
+
131
+ end
132
+
133
+
134
+
93
135
 
94
136
  ## About Heroku Rails
95
137
 
96
138
  ### Links
97
139
 
98
- Homepage:: http://github.com/railsjedi/heroku-rails
99
- Issue Tracker:: http://github.com/railsjedi/heroku-rails/issues
140
+ Homepage:: <http://github.com/railsjedi/heroku-rails>
141
+
142
+ Issue Tracker:: <http://github.com/railsjedi/heroku-rails/issues>
100
143
 
101
144
  ### License
102
145
 
103
- License:: Copyright (c) 2010 [Jacques Crocker](mailto:railsjedi@gmail.com), released under the MIT license.
146
+ License:: Copyright (c) 2010 Jacques Crocker <railsjedi@gmail.com> released under the MIT license.
104
147
 
105
148
  ## Forked from Heroku Sans
106
149
 
107
- Heroku Rails is a fork and rewrite/reorganiziation of Heroku Sans, and a nice simple set of Rake tasks for managing Heroku environments. Check out that project here: http://github.com/fastestforward/heroku_san
150
+ Heroku Rails is a fork and rewrite/reorganiziation of Heroku Sans, and a nice simple set of Rake tasks for managing Heroku environments. Check out that project here: <http://github.com/fastestforward/heroku_san>
108
151
 
109
152
  ### Heroku Sans Contributors
110
153
 
@@ -114,4 +157,4 @@ Heroku Rails is a fork and rewrite/reorganiziation of Heroku Sans, and a nice si
114
157
 
115
158
  ### Heroku Sans License
116
159
 
117
- License:: Copyright (c) 2009 Elijah Miller <mailto:elijah.miller@gmail.com>, released under the MIT license.
160
+ License:: Copyright (c) 2009 Elijah Miller <elijah.miller@gmail.com>, released under the MIT license.
data/TODO CHANGED
@@ -1,2 +1,5 @@
1
- - refactor config/helpers out of tasks.rb into own file(s)
2
- -
1
+ refactor config/helpers out of tasks.rb into own file(s)
2
+ add config setup tasks (DONE)
3
+ √ add proper rails3 generator support
4
+ √ add hooks to allow other precompilations before deploy
5
+ - add sass/coffeescript precompile + jammit-s3 support
data/heroku-rails.gemspec CHANGED
@@ -1,10 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "heroku-rails"
3
- s.version = "0.0.1"
3
+ s.version = "0.2.0"
4
4
 
5
5
  s.authors = ["Elijah Miller", "Glenn Roberts", "Jacques Crocker"]
6
- s.summary = "A bunch of useful Rake tasks for managing your Heroku apps"
7
- s.description = "Manage multiple Heroku instances/apps for a single Rails app using Rake"
6
+ s.summary = "Deployment and configuration tools for Heroku/Rails"
7
+ s.description = "Manage multiple Heroku instances/apps for a single Rails app using Rake. It's the Capistrano for Heroku, without the suck."
8
8
 
9
9
  s.email = "railsjedi@gmail.com"
10
10
  s.homepage = "http://github.com/railsjedi/heroku-rails"
@@ -0,0 +1,15 @@
1
+ module Heroku
2
+ module Generators
3
+ class CallbacksGenerator < ::Rails::Generators::Base
4
+ desc "Generates the rake tasks (lib/tasks/heroku.rake) that let you override heroku deploy callbacks"
5
+
6
+ def self.source_root
7
+ @_heroku_gen_source_root ||= File.expand_path("../../templates", __FILE__)
8
+ end
9
+
10
+ def create_callbacks_rake_file
11
+ template 'heroku.rake', File.join('lib', 'tasks', "heroku.rake")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
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
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # Heroku Deploy Callbacks
2
+ namespace :heroku do
3
+
4
+ # runs before all the deploys complete
5
+ task :before_deploy do
6
+
7
+ end
8
+
9
+ # runs before each push to a particular heroku deploy environment
10
+ task :before_each_deploy do
11
+
12
+ end
13
+
14
+ # runs after each push to a particular heroku deploy environment
15
+ task :after_each_deploy do
16
+
17
+ end
18
+
19
+ # runs after all the deploys complete
20
+ task :after_deploy do
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,51 @@
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
+ all:
34
+ - "my-heroku-email@somedomain.com"
35
+ - "another-heroku-email@somedomain.com"
36
+
37
+ domains:
38
+ production:
39
+ - "awesomeapp.com"
40
+ - "www.awesomeapp.com"
41
+
42
+ addons:
43
+ all:
44
+ # add any other addons here
45
+ - custom_domains:basic
46
+ - newrelic:bronze
47
+
48
+ production:
49
+ # list production env specific addons here
50
+ - ssl:piggyback
51
+ - cron:hourly
data/lib/heroku-rails.rb CHANGED
@@ -1,9 +1,3 @@
1
- module Heroku
2
- module Rails
3
- class Railtie < ::Rails::Railtie
4
- rake_tasks do
5
- load 'heroku/rails/tasks.rb'
6
- end
7
- end
8
- end
9
- end
1
+ require 'heroku/rails/heroku_config'
2
+ require 'heroku/rails/heroku_runner'
3
+ require 'heroku/rails/railtie' if defined?(::Rails::Railtie)
@@ -0,0 +1,69 @@
1
+ module Heroku
2
+ module Rails
3
+ class HerokuConfig
4
+
5
+ attr_accessor :settings
6
+
7
+ def initialize(config_filepath)
8
+ if File.exists?(config_filepath)
9
+ self.settings = YAML.load_file(config_filepath) || {}
10
+ else
11
+ self.settings = {}
12
+ end
13
+ end
14
+
15
+ def apps
16
+ self.settings['apps']
17
+ end
18
+
19
+ def app_names
20
+ self.settings['apps'].values
21
+ end
22
+
23
+ def app_environments
24
+ self.settings['apps'].keys
25
+ end
26
+
27
+ # return the stack setting for a particular app environment
28
+ def stack(app_env)
29
+ stacks = self.settings['stacks'] || {}
30
+ stacks[app_env] || stacks['all']
31
+ end
32
+
33
+ # pull out the config setting hash for a particular app environment
34
+ def config(app_env)
35
+ config = self.settings['config'] || {}
36
+ all = config['all'] || {}
37
+
38
+ # overwrite all configs with the environment specific ones
39
+ all.merge(config[app_env] || {})
40
+ end
41
+
42
+ # return a list of domains for a particular app environment
43
+ def domains(app_env)
44
+ domains = self.settings['domains'] || {}
45
+ domains[app_env] || []
46
+ end
47
+
48
+ # return a list of collaborators for a particular app environment
49
+ def collaborators(app_env)
50
+ app_setting_list('collaborators', app_env)
51
+ end
52
+
53
+ # return a list of addons for a particular app environment
54
+ def addons(app_env)
55
+ app_setting_list('addons', app_env)
56
+ end
57
+
58
+ protected
59
+
60
+ def app_setting_list(setting_key, app)
61
+ setting = self.settings[setting_key] || {}
62
+ all = setting['all'] || []
63
+
64
+ # add in collaborators from app environment to the ones defined in all
65
+ (all + (setting[app] || [])).uniq
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,242 @@
1
+ require 'heroku/client'
2
+
3
+ module Heroku
4
+ module Rails
5
+ class HerokuRunner
6
+ def initialize(config)
7
+ @config = config
8
+ @environments = []
9
+ end
10
+
11
+ def authorize
12
+ return if @heroku
13
+
14
+ # setup heroku username and password so we can start up a heroku client
15
+ credentials_path = File.expand_path("~/.heroku/credentials")
16
+
17
+ # read in the username,password so we can build the client
18
+ if File.exists?(credentials_path)
19
+ auth = File.read(credentials_path)
20
+ username, password = auth.split("\n")
21
+ @heroku = Heroku::Client.new(username, password)
22
+ else
23
+ puts "Heroku not set up. Run `heroku list` in order to input your credentials and try again"
24
+ exit(1)
25
+ end
26
+ end
27
+
28
+ # add a specific environment to the run list
29
+ def add_environment(env)
30
+ @environments << env
31
+ end
32
+
33
+ # use all environments
34
+ def all_environments
35
+ @environments = @config.app_environments
36
+ end
37
+
38
+ # setup apps (create if necessary)
39
+ def setup_apps
40
+ authorize unless @heroku
41
+
42
+ # get a list of all my current apps on Heroku (so we don't create dupes)
43
+ @my_apps = @heroku.list.map{|a| a.first}
44
+
45
+ each_heroku_app do |heroku_env, app_name, repo|
46
+ next if @my_apps.include?(app_name)
47
+
48
+ stack = @config.stack(heroku_env)
49
+ stack_option = " --stack #{stack}" if stack.to_s.size > 0
50
+ system_with_echo "heroku create #{app_name}#{stack_option} --remote #{app_name}"
51
+ end
52
+ end
53
+
54
+ # setup the stacks for each app (migrating if necessary)
55
+ def setup_stacks
56
+ authorize unless @heroku
57
+ each_heroku_app do |heroku_env, app_name, repo|
58
+ # get the intended stack setting
59
+ stack = @config.stack(heroku_env)
60
+
61
+ # get the remote info about the app from heroku
62
+ heroku_app_info = @heroku.info(app_name) || {}
63
+
64
+ # if the stacks don't match, then perform a migration
65
+ if stack != heroku_app_info[:stack]
66
+ puts "Migrating the app: #{app_name} to the stack: #{stack}"
67
+ system_with_echo "heroku stack:migrate #{stack} --app #{app_name}"
68
+ end
69
+ end
70
+ end
71
+
72
+ # setup the list of collaborators
73
+ def setup_collaborators
74
+ authorize unless @heroku
75
+ each_heroku_app do |heroku_env, app_name, repo|
76
+ # get the remote info about the app from heroku
77
+ heroku_app_info = @heroku.info(app_name) || {}
78
+
79
+ # get the intended list of collaborators to add
80
+ collaborator_emails = @config.collaborators(heroku_env)
81
+
82
+ # get existing collaborators
83
+ existing_emails = heroku_app_info[:collaborators].to_a.map{|c| c[:email]}
84
+
85
+ # get the list of collaborators to delete
86
+ existing_emails.each do |existing_email|
87
+ # check to see if we need to delete this person
88
+ unless collaborator_emails.include?(existing_email)
89
+ # delete that collaborator if they arent on the approved list
90
+ system_with_echo "heroku sharing:remove #{existing_email} --app #{app_name}"
91
+ end
92
+ end
93
+
94
+ # get the list of collaborators to add
95
+ collaborator_emails.each do |collaborator_email|
96
+ # check to see if we need to add this person
97
+ unless existing_emails.include?(collaborator_email)
98
+ # add the collaborator if they are not already on the server
99
+ system_with_echo "heroku sharing:add #{collaborator_email} --app #{app_name}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # setup configuration
106
+ def setup_config
107
+ authorize unless @heroku
108
+ each_heroku_app do |heroku_env, app_name, repo|
109
+ # get the configuration that we are aiming towards
110
+ new_config = @config.config(heroku_env)
111
+
112
+ # default RACK_ENV to the heroku_env (unless its manually set to something else)
113
+ unless new_config["RACK_ENV"].to_s.length > 0
114
+ new_config["RACK_ENV"] = heroku_env
115
+ end
116
+
117
+ # get the existing config from heroku's servers
118
+ existing_config = @heroku.config_vars(app_name) || {}
119
+
120
+ # find the config variables to add
121
+ add_config = {}
122
+ new_config.each do |new_key, new_val|
123
+ add_config[new_key] = new_val unless existing_config[new_key] == new_val
124
+ end
125
+
126
+ # persist the changes onto heroku
127
+ unless add_config.empty?
128
+ # add the config
129
+ set_config = ""
130
+ add_config.each do |key, val|
131
+ set_config << "#{key}='#{val}' "
132
+ end
133
+
134
+ system_with_echo "heroku config:add #{set_config} --app #{app_name}"
135
+ end
136
+ end
137
+ end
138
+
139
+ # setup the addons for heroku
140
+ def setup_addons
141
+ authorize unless @heroku
142
+ each_heroku_app do |heroku_env, app_name, repo|
143
+ # get the addons that we are aiming towards
144
+ addons = @config.addons(heroku_env)
145
+
146
+ # get the addons that are already on the servers
147
+ existing_addons = (@heroku.installed_addons(app_name) || []).map{|a| a["name"]}
148
+
149
+ # add "custom_domains" if that addon doesnt already exist
150
+ # and we have domains configured for this app
151
+ addons << "custom_domains:basic" unless @config.domains(heroku_env).empty? or
152
+ addons.any?{|a| a =~ /custom_domains/} or
153
+ existing_addons.any?{|a| a =~ /custom_domains/}
154
+
155
+ # remove the addons that need to be removed
156
+ existing_addons.each do |existing_addon|
157
+ # check to see if we need to delete this addon
158
+ unless addons.include?(existing_addon)
159
+ # delete this addon if they arent on the approved list
160
+ system_with_echo "heroku addons:remove #{existing_addon} --app #{app_name}"
161
+ end
162
+ end
163
+
164
+ # add the addons that dont exist already
165
+ addons.each do |addon|
166
+ # check to see if we need to add this addon
167
+ unless existing_addons.include?(addon)
168
+ # add this addon if they are not already added
169
+ system_with_echo "heroku addons:add #{addon} --app #{app_name}"
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ # setup the domains for heroku
176
+ def setup_domains
177
+ authorize unless @heroku
178
+ each_heroku_app do |heroku_env, app_name, repo|
179
+ # get the domains that we are aiming towards
180
+ domains = @config.domains(heroku_env)
181
+
182
+ # get the domains that are already on the servers
183
+ existing_domains = (@heroku.list_domains(app_name) || []).map{|a| a[:domain]}
184
+
185
+ # remove the domains that need to be removed
186
+ existing_domains.each do |existing_domain|
187
+ # check to see if we need to delete this domain
188
+ unless domains.include?(existing_domain)
189
+ # delete this domain if they arent on the approved list
190
+ system_with_echo "heroku domains:remove #{existing_domain} --app #{app_name}"
191
+ end
192
+ end
193
+
194
+ # add the domains that dont exist already
195
+ domains.each do |domain|
196
+ # check to see if we need to add this domain
197
+ unless existing_domains.include?(domain)
198
+ # add this domain if they are not already added
199
+ system_with_echo "heroku domains:add #{domain} --app #{app_name}"
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ # cycles through each configured heroku app
206
+ # yields the environment name, the app name, and the repo url
207
+ def each_heroku_app
208
+ if @environments.blank? && @config.apps.size == 1
209
+ puts "Defaulting to #{env} app since only one app is defined"
210
+ @environments = [@config.app_environments.first]
211
+ end
212
+
213
+ if @environments.present?
214
+ @environments.each do |heroku_env|
215
+ app_name = @config.apps[heroku_env]
216
+ yield(heroku_env, app_name, "git@heroku.com:#{app_name}.git")
217
+ end
218
+ else
219
+ puts "You must first specify at least one Heroku app:
220
+ rake <app> [<app>] <command>
221
+ rake production restart
222
+ rake demo staging deploy"
223
+
224
+ puts "\nYou can use also command all Heroku apps for this project:
225
+ rake all heroku:share"
226
+
227
+ exit(1)
228
+ end
229
+ end
230
+
231
+ def system_with_echo(*args)
232
+ puts args.join(' ')
233
+ command(*args)
234
+ end
235
+
236
+ def command(*args)
237
+ system(*args)
238
+ end
239
+
240
+ end
241
+ end
242
+ end