pogo 2.31.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/README.md +73 -0
  2. data/bin/pogo +22 -0
  3. data/data/cacert.pem +3988 -0
  4. data/lib/heroku.rb +22 -0
  5. data/lib/heroku/auth.rb +320 -0
  6. data/lib/heroku/cli.rb +38 -0
  7. data/lib/heroku/client.rb +764 -0
  8. data/lib/heroku/client/heroku_postgresql.rb +111 -0
  9. data/lib/heroku/client/pgbackups.rb +113 -0
  10. data/lib/heroku/client/rendezvous.rb +105 -0
  11. data/lib/heroku/client/ssl_endpoint.rb +25 -0
  12. data/lib/heroku/command.rb +273 -0
  13. data/lib/heroku/command/account.rb +23 -0
  14. data/lib/heroku/command/accounts.rb +34 -0
  15. data/lib/heroku/command/addons.rb +305 -0
  16. data/lib/heroku/command/apps.rb +311 -0
  17. data/lib/heroku/command/auth.rb +86 -0
  18. data/lib/heroku/command/base.rb +230 -0
  19. data/lib/heroku/command/certs.rb +148 -0
  20. data/lib/heroku/command/config.rb +137 -0
  21. data/lib/heroku/command/db.rb +218 -0
  22. data/lib/heroku/command/domains.rb +85 -0
  23. data/lib/heroku/command/drains.rb +46 -0
  24. data/lib/heroku/command/git.rb +65 -0
  25. data/lib/heroku/command/help.rb +163 -0
  26. data/lib/heroku/command/keys.rb +115 -0
  27. data/lib/heroku/command/labs.rb +161 -0
  28. data/lib/heroku/command/logs.rb +98 -0
  29. data/lib/heroku/command/maintenance.rb +61 -0
  30. data/lib/heroku/command/pg.rb +277 -0
  31. data/lib/heroku/command/pgbackups.rb +289 -0
  32. data/lib/heroku/command/plugins.rb +110 -0
  33. data/lib/heroku/command/ps.rb +232 -0
  34. data/lib/heroku/command/releases.rb +124 -0
  35. data/lib/heroku/command/run.rb +179 -0
  36. data/lib/heroku/command/sharing.rb +89 -0
  37. data/lib/heroku/command/ssl.rb +61 -0
  38. data/lib/heroku/command/stack.rb +62 -0
  39. data/lib/heroku/command/status.rb +51 -0
  40. data/lib/heroku/command/update.rb +47 -0
  41. data/lib/heroku/command/version.rb +23 -0
  42. data/lib/heroku/deprecated.rb +5 -0
  43. data/lib/heroku/deprecated/help.rb +38 -0
  44. data/lib/heroku/distribution.rb +9 -0
  45. data/lib/heroku/helpers.rb +517 -0
  46. data/lib/heroku/helpers/heroku_postgresql.rb +104 -0
  47. data/lib/heroku/plugin.rb +161 -0
  48. data/lib/heroku/updater.rb +158 -0
  49. data/lib/heroku/version.rb +3 -0
  50. data/lib/vendor/heroku/okjson.rb +598 -0
  51. data/spec/helper/legacy_help.rb +16 -0
  52. data/spec/heroku/auth_spec.rb +246 -0
  53. data/spec/heroku/client/heroku_postgresql_spec.rb +34 -0
  54. data/spec/heroku/client/pgbackups_spec.rb +43 -0
  55. data/spec/heroku/client/rendezvous_spec.rb +62 -0
  56. data/spec/heroku/client/ssl_endpoint_spec.rb +48 -0
  57. data/spec/heroku/client_spec.rb +564 -0
  58. data/spec/heroku/command/addons_spec.rb +585 -0
  59. data/spec/heroku/command/apps_spec.rb +351 -0
  60. data/spec/heroku/command/auth_spec.rb +38 -0
  61. data/spec/heroku/command/base_spec.rb +109 -0
  62. data/spec/heroku/command/certs_spec.rb +178 -0
  63. data/spec/heroku/command/config_spec.rb +144 -0
  64. data/spec/heroku/command/db_spec.rb +110 -0
  65. data/spec/heroku/command/domains_spec.rb +87 -0
  66. data/spec/heroku/command/drains_spec.rb +34 -0
  67. data/spec/heroku/command/git_spec.rb +116 -0
  68. data/spec/heroku/command/help_spec.rb +93 -0
  69. data/spec/heroku/command/keys_spec.rb +120 -0
  70. data/spec/heroku/command/labs_spec.rb +99 -0
  71. data/spec/heroku/command/logs_spec.rb +60 -0
  72. data/spec/heroku/command/maintenance_spec.rb +51 -0
  73. data/spec/heroku/command/pg_spec.rb +223 -0
  74. data/spec/heroku/command/pgbackups_spec.rb +280 -0
  75. data/spec/heroku/command/plugins_spec.rb +104 -0
  76. data/spec/heroku/command/ps_spec.rb +195 -0
  77. data/spec/heroku/command/releases_spec.rb +130 -0
  78. data/spec/heroku/command/run_spec.rb +86 -0
  79. data/spec/heroku/command/sharing_spec.rb +59 -0
  80. data/spec/heroku/command/ssl_spec.rb +32 -0
  81. data/spec/heroku/command/stack_spec.rb +46 -0
  82. data/spec/heroku/command/status_spec.rb +48 -0
  83. data/spec/heroku/command/version_spec.rb +16 -0
  84. data/spec/heroku/command_spec.rb +211 -0
  85. data/spec/heroku/helpers/heroku_postgresql_spec.rb +109 -0
  86. data/spec/heroku/helpers_spec.rb +48 -0
  87. data/spec/heroku/plugin_spec.rb +172 -0
  88. data/spec/heroku/updater_spec.rb +44 -0
  89. data/spec/spec.opts +1 -0
  90. data/spec/spec_helper.rb +209 -0
  91. data/spec/support/display_message_matcher.rb +49 -0
  92. data/spec/support/openssl_mock_helper.rb +8 -0
  93. metadata +220 -0
@@ -0,0 +1,23 @@
1
+ require "heroku/command/base"
2
+
3
+ # manage heroku account options
4
+ #
5
+ class Heroku::Command::Account < Heroku::Command::Base
6
+
7
+ # account:confirm_billing
8
+ #
9
+ # Confirm that your account can be billed at the end of the month
10
+ #
11
+ #Example:
12
+ #
13
+ # $ heroku account:confirm_billing
14
+ # This action will cause your account to be billed at the end of the month
15
+ # For more information, see http://docs.heroku.com/billing
16
+ # Are you sure you want to do this? (y/n)
17
+ #
18
+ def confirm_billing
19
+ validate_arguments!
20
+ Heroku::Helpers.confirm_billing
21
+ end
22
+
23
+ end
@@ -0,0 +1,34 @@
1
+ if Heroku::Plugin.list.include?('heroku-accounts')
2
+
3
+ require "heroku/command/base"
4
+
5
+ # manage multiple heroku accounts
6
+ #
7
+ class Heroku::Command::Accounts < Heroku::Command::Base
8
+
9
+ # accounts:default
10
+ # set a system-wide default account
11
+ def default
12
+ name = shift_argument
13
+ validate_arguments!
14
+
15
+ unless name
16
+ error("Please specify an account name.")
17
+ end
18
+
19
+ unless account_exists?(name)
20
+ error("That account does not exist.")
21
+ end
22
+
23
+ result = %x{ git config --global heroku.account #{name} }
24
+
25
+ # update netrc
26
+ Heroku::Auth.instance_variable_set(:@account, nil) # kill memoization
27
+ Heroku::Auth.credentials = [Heroku::Auth.user, Heroku::Auth.password]
28
+ Heroku::Auth.write_credentials
29
+
30
+ result
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,305 @@
1
+ require "heroku/command/base"
2
+ require "heroku/helpers/heroku_postgresql"
3
+
4
+ module Heroku::Command
5
+
6
+ # manage addon resources
7
+ #
8
+ class Addons < Base
9
+
10
+ include Heroku::Helpers::HerokuPostgresql
11
+
12
+ # addons
13
+ #
14
+ # list installed addons
15
+ #
16
+ def index
17
+ validate_arguments!
18
+
19
+ installed = api.get_addons(app).body
20
+ if installed.empty?
21
+ display("#{app} has no add-ons.")
22
+ else
23
+ available, pending = installed.partition { |a| a['configured'] }
24
+
25
+ unless available.empty?
26
+ styled_header("#{app} Configured Add-ons")
27
+ styled_array(available.map do |a|
28
+ [a['name'], a['attachment_name'] || '']
29
+ end)
30
+ end
31
+
32
+ unless pending.empty?
33
+ styled_header("#{app} Add-ons to Configure")
34
+ styled_array(pending.map do |a|
35
+ [a['name'], app_addon_url(a['name'])]
36
+ end)
37
+ end
38
+ end
39
+ end
40
+
41
+ # addons:list
42
+ #
43
+ # list all available addons
44
+ #
45
+ def list
46
+ addons = heroku.addons
47
+ if addons.empty?
48
+ display "No addons available currently"
49
+ else
50
+ partitioned_addons = partition_addons(addons)
51
+ partitioned_addons.each do |key, addons|
52
+ partitioned_addons[key] = format_for_display(addons)
53
+ end
54
+ display_object(partitioned_addons)
55
+ end
56
+ end
57
+
58
+ # addons:add ADDON
59
+ #
60
+ # install an addon
61
+ #
62
+ def add
63
+ configure_addon('Adding') do |addon, config|
64
+ heroku.install_addon(app, addon, config)
65
+ end
66
+ end
67
+
68
+ # addons:upgrade ADDON
69
+ #
70
+ # upgrade an existing addon
71
+ #
72
+ def upgrade
73
+ configure_addon('Upgrading to') do |addon, config|
74
+ heroku.upgrade_addon(app, addon, config)
75
+ end
76
+ end
77
+
78
+ # addons:downgrade ADDON
79
+ #
80
+ # downgrade an existing addon
81
+ #
82
+ def downgrade
83
+ configure_addon('Downgrading to') do |addon, config|
84
+ heroku.upgrade_addon(app, addon, config)
85
+ end
86
+ end
87
+
88
+ # addons:remove ADDON1 [ADDON2 ...]
89
+ #
90
+ # uninstall one or more addons
91
+ #
92
+ def remove
93
+ return unless confirm_command
94
+
95
+ args.each do |name|
96
+ messages = nil
97
+ action("Removing #{name} on #{app}") do
98
+ messages = addon_run { heroku.uninstall_addon(app, name, :confirm => app) }
99
+ end
100
+ display(messages[:attachment]) if messages[:attachment]
101
+ display(messages[:message]) if messages[:message]
102
+ end
103
+ end
104
+
105
+ # addons:docs ADDON
106
+ #
107
+ # open an addon's documentation in your browser
108
+ #
109
+ def docs
110
+ unless addon = shift_argument
111
+ error("Usage: heroku addons:docs ADDON\nMust specify ADDON to open docs for.")
112
+ end
113
+ validate_arguments!
114
+
115
+ addon_names = api.get_addons.body.map {|a| a['name']}
116
+ addon_types = addon_names.map {|name| name.split(':').first}.uniq
117
+
118
+ name_matches = addon_names.select {|name| name =~ /^#{addon}/}
119
+ type_matches = addon_types.select {|name| name =~ /^#{addon}/}
120
+
121
+ if name_matches.include?(addon) || type_matches.include?(addon)
122
+ type_matches = [addon]
123
+ end
124
+
125
+ case type_matches.length
126
+ when 0 then
127
+ error([
128
+ "`#{addon}` is not a heroku add-on.",
129
+ suggestion(addon, addon_names + addon_types),
130
+ "See `heroku addons:list` for all available addons."
131
+ ].compact.join("\n"))
132
+ when 1
133
+ addon_type = type_matches.first
134
+ launchy("Opening #{addon_type} docs", addon_docs_url(addon_type))
135
+ else
136
+ error("Ambiguous addon name: #{addon}\nPerhaps you meant #{name_matches[0...-1].map {|match| "`#{match}`"}.join(', ')} or `#{name_matches.last}`.\n")
137
+ end
138
+ end
139
+
140
+ # addons:open ADDON
141
+ #
142
+ # open an addon's dashboard in your browser
143
+ #
144
+ def open
145
+ unless addon = shift_argument
146
+ error("Usage: heroku addons:open ADDON\nMust specify ADDON to open.")
147
+ end
148
+ validate_arguments!
149
+
150
+ app_addons = api.get_addons(app).body.map {|a| a['name']}
151
+ matches = app_addons.select {|a| a =~ /^#{addon}/}.sort
152
+
153
+ case matches.length
154
+ when 0 then
155
+ addon_names = api.get_addons.body.map {|a| a['name']}
156
+ if addon_names.any? {|name| name =~ /^#{addon}/}
157
+ error("Addon not installed: #{addon}")
158
+ else
159
+ error([
160
+ "`#{addon}` is not a heroku add-on.",
161
+ suggestion(addon, addon_names + addon_names.map {|name| name.split(':').first}.uniq),
162
+ "See `heroku addons:list` for all available addons."
163
+ ].compact.join("\n"))
164
+ end
165
+ when 1 then
166
+ addon_to_open = matches.first
167
+ launchy("Opening #{addon_to_open} for #{app}", app_addon_url(addon_to_open))
168
+ else
169
+ error("Ambiguous addon name: #{addon}\nPerhaps you meant #{matches[0...-1].map {|match| "`#{match}`"}.join(', ')} or `#{matches.last}`.\n")
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def addon_docs_url(addon)
176
+ "https://devcenter.#{heroku.host}/articles/#{addon.split(':').first}"
177
+ end
178
+
179
+ def app_addon_url(addon)
180
+ "https://api.#{heroku.host}/myapps/#{app}/addons/#{addon}"
181
+ end
182
+
183
+ def partition_addons(addons)
184
+ addons.group_by{ |a| (a["state"] == "public" ? "available" : a["state"]) }
185
+ end
186
+
187
+ def format_for_display(addons)
188
+ grouped = addons.inject({}) do |base, addon|
189
+ group, short = addon['name'].split(':')
190
+ base[group] ||= []
191
+ base[group] << addon.merge('short' => short)
192
+ base
193
+ end
194
+ grouped.keys.sort.map do |name|
195
+ addons = grouped[name]
196
+ row = name.dup
197
+ if addons.any? { |a| a['short'] }
198
+ row << ':'
199
+ size = row.size
200
+ stop = false
201
+ row << addons.map { |a| a['short'] }.compact.sort.map do |short|
202
+ size += short.size
203
+ if size < 31
204
+ short
205
+ else
206
+ stop = true
207
+ nil
208
+ end
209
+ end.compact.join(', ')
210
+ row << '...' if stop
211
+ end
212
+ row
213
+ end
214
+ end
215
+
216
+ def addon_run
217
+ response = yield
218
+
219
+ if response
220
+ price = "(#{ response['price'] })" if response['price']
221
+
222
+ if response['message'] =~ /(Attached as [A-Z0-9_]+)\n(.*)/m
223
+ attachment = $1
224
+ message = $2
225
+ else
226
+ attachment = nil
227
+ message = response['message']
228
+ end
229
+
230
+ begin
231
+ release = api.get_release(app, 'current').body
232
+ release = release['name']
233
+ rescue Heroku::API::Errors::Error
234
+ release = nil
235
+ end
236
+ end
237
+
238
+ status [ release, price ].compact.join(' ')
239
+ { :attachment => attachment, :message => message }
240
+ rescue RestClient::ResourceNotFound => e
241
+ error Heroku::Command.extract_error(e.http_body) {
242
+ e.http_body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found"
243
+ }
244
+ rescue RestClient::Locked => ex
245
+ raise
246
+ rescue RestClient::RequestFailed => e
247
+ retry if e.http_code == 402 && confirm_billing
248
+ error Heroku::Command.extract_error(e.http_body)
249
+ end
250
+
251
+ def configure_addon(label, &install_or_upgrade)
252
+ addon = args.shift
253
+ raise CommandFailed.new("Missing add-on name") if addon.nil? || ["--fork", "--follow"].include?(addon)
254
+
255
+ config = parse_options(args)
256
+ config.merge!(:confirm => app) if app == options[:confirm]
257
+ raise CommandFailed.new("Unexpected arguments: #{args.join(' ')}") unless args.empty?
258
+
259
+ hpg_translate_fork_and_follow(addon, config)
260
+
261
+ messages = nil
262
+ action("#{label} #{addon} on #{app}") do
263
+ messages = addon_run { install_or_upgrade.call(addon, config) }
264
+ end
265
+ display(messages[:attachment]) unless messages[:attachment].to_s.strip == ""
266
+ display(messages[:message]) unless messages[:message].to_s.strip == ""
267
+
268
+ display("Use `heroku addons:docs #{addon}` to view documentation.")
269
+ end
270
+
271
+ #this will clean up when we officially deprecate
272
+ def parse_options(args)
273
+ config = {}
274
+ deprecated_args = []
275
+ flag = /^--/
276
+
277
+ args.size.times do
278
+ break if args.empty?
279
+ peek = args.first
280
+ next unless peek && (peek.match(flag) || peek.match(/=/))
281
+ arg = args.shift
282
+ peek = args.first
283
+ key = arg
284
+ if key.match(/=/)
285
+ deprecated_args << key unless key.match(flag)
286
+ key, value = key.split('=', 2)
287
+ elsif peek.nil? || peek.match(flag)
288
+ value = true
289
+ else
290
+ value = args.shift
291
+ end
292
+ value = true if value == 'true'
293
+ config[key.sub(flag, '')] = value
294
+
295
+ if !deprecated_args.empty?
296
+ out_string = deprecated_args.map{|a| "--#{a}"}.join(' ')
297
+ display("Warning: non-unix style params have been deprecated, use #{out_string} instead")
298
+ end
299
+ end
300
+
301
+ config
302
+ end
303
+
304
+ end
305
+ end
@@ -0,0 +1,311 @@
1
+ require "heroku/command/base"
2
+
3
+ # manage apps (create, destroy)
4
+ #
5
+ class Heroku::Command::Apps < Heroku::Command::Base
6
+
7
+ # apps
8
+ #
9
+ # list your apps
10
+ #
11
+ #Example:
12
+ #
13
+ # $ heroku apps
14
+ # === My Apps
15
+ # myapp1
16
+ # myapp2
17
+ #
18
+ # === Collaborated Apps
19
+ # theirapp1 other@owner.name
20
+ #
21
+ def index
22
+ validate_arguments!
23
+ apps = api.get_apps.body
24
+ unless apps.empty?
25
+ my_apps, collaborated_apps = apps.partition do |app|
26
+ app["owner_email"] == Heroku::Auth.user
27
+ end
28
+
29
+ unless my_apps.empty?
30
+ styled_header("My Apps")
31
+ styled_array(my_apps.map { |app| app["name"] })
32
+ end
33
+
34
+ unless collaborated_apps.empty?
35
+ styled_header("Collaborated Apps")
36
+ styled_array(collaborated_apps.map { |app| [app["name"], app["owner_email"]] })
37
+ end
38
+ else
39
+ display("You have no apps.")
40
+ end
41
+ end
42
+
43
+ alias_command "list", "apps"
44
+
45
+ # apps:info
46
+ #
47
+ # show detailed app information
48
+ #
49
+ # -s, --shell # output more shell friendly key/value pairs
50
+ #
51
+ #Examples:
52
+ #
53
+ # $ heroku apps:info
54
+ # === myapp
55
+ # Git URL: git@heroku.com:myapp.git
56
+ # Repo Size: 5M
57
+ # ...
58
+ #
59
+ # $ heroku apps:info --shell
60
+ # git_url=git@heroku.com:myapp.git
61
+ # repo_size=5000000
62
+ # ...
63
+ #
64
+ def info
65
+ validate_arguments!
66
+ app_data = api.get_app(app).body
67
+
68
+ unless options[:shell]
69
+ styled_header(app_data["name"])
70
+ end
71
+
72
+ addons_data = api.get_addons(app).body.map {|addon| addon['name']}.sort
73
+ collaborators_data = api.get_collaborators(app).body.map {|collaborator| collaborator["email"]}.sort
74
+ collaborators_data.reject! {|email| email == app_data["owner_email"]}
75
+
76
+ if options[:shell]
77
+ if app_data['domain_name']
78
+ app_data['domain_name'] = app_data['domain_name']['domain']
79
+ end
80
+ unless addons_data.empty?
81
+ app_data['addons'] = addons_data.join(',')
82
+ end
83
+ unless collaborators_data.empty?
84
+ app_data['collaborators'] = collaborators_data.join(',')
85
+ end
86
+ app_data.keys.sort_by { |a| a.to_s }.each do |key|
87
+ hputs("#{key}=#{app_data[key]}")
88
+ end
89
+ else
90
+ data = {}
91
+
92
+ unless addons_data.empty?
93
+ data["Addons"] = addons_data
94
+ end
95
+
96
+ data["Collaborators"] = collaborators_data
97
+
98
+ if app_data["create_status"] && app_data["create_status"] != "complete"
99
+ data["Create Status"] = app_data["create_status"]
100
+ end
101
+
102
+ if app_data["cron_finished_at"]
103
+ data["Cron Finished At"] = format_date(app_data["cron_finished_at"])
104
+ end
105
+
106
+ if app_data["cron_next_run"]
107
+ data["Cron Next Run"] = format_date(app_data["cron_next_run"])
108
+ end
109
+
110
+ if app_data["database_size"]
111
+ data["Database Size"] = format_bytes(app_data["database_size"])
112
+ end
113
+
114
+ data["Git URL"] = app_data["git_url"]
115
+
116
+ if app_data["database_tables"]
117
+ data["Database Size"].gsub!('(empty)', '0K') + " in #{quantify("table", app_data["database_tables"])}"
118
+ end
119
+
120
+ if app_data["dyno_hours"].is_a?(Hash)
121
+ data["Dyno Hours"] = app_data["dyno_hours"].keys.map do |type|
122
+ "%s - %0.2f dyno-hours" % [ type.to_s.capitalize, app_data["dyno_hours"][type] ]
123
+ end
124
+ end
125
+
126
+ data["Owner Email"] = app_data["owner_email"]
127
+
128
+ if app_data["repo_size"]
129
+ data["Repo Size"] = format_bytes(app_data["repo_size"])
130
+ end
131
+
132
+ if app_data["slug_size"]
133
+ data["Slug Size"] = format_bytes(app_data["slug_size"])
134
+ end
135
+
136
+ data["Stack"] = app_data["stack"]
137
+ if data["Stack"] != "cedar"
138
+ data.merge!("Dynos" => app_data["dynos"], "Workers" => app_data["workers"])
139
+ end
140
+
141
+ data["Web URL"] = app_data["web_url"]
142
+
143
+ styled_hash(data)
144
+ end
145
+ end
146
+
147
+ alias_command "info", "apps:info"
148
+
149
+ # apps:create [NAME]
150
+ #
151
+ # create a new app
152
+ #
153
+ # --addons ADDONS # a comma-delimited list of addons to install
154
+ # -b, --buildpack BUILDPACK # a buildpack url to use for this app
155
+ # -n, --no-remote # don't create a git remote
156
+ # -r, --remote REMOTE # the git remote to create, default "heroku"
157
+ # -s, --stack STACK # the stack on which to create the app
158
+ #
159
+ #Examples:
160
+ #
161
+ # $ heroku apps:create
162
+ # Creating floating-dragon-42... done, stack is cedar
163
+ # http://floating-dragon-42.heroku.com/ | git@heroku.com:floating-dragon-42.git
164
+ #
165
+ # $ heroku apps:create -s bamboo
166
+ # Creating floating-dragon-42... done, stack is bamboo-mri-1.9.2
167
+ # http://floating-dragon-42.herokuapp.com/ | git@heroku.com:floating-dragon-42.git
168
+ #
169
+ # # specify a name
170
+ # $ heroku apps:create myapp
171
+ # Creating myapp... done, stack is cedar
172
+ # http://myapp.heroku.com/ | git@heroku.com:myapp.git
173
+ #
174
+ # # create a staging app
175
+ # $ heroku apps:create myapp-staging --remote staging
176
+ #
177
+ def create
178
+ name = shift_argument || options[:app]
179
+ validate_arguments!
180
+
181
+ info = api.post_app({ "name" => name, "stack" => options[:stack] }).body
182
+ begin
183
+ action("Creating #{info['name']}") do
184
+ if info['create_status'] == 'creating'
185
+ Timeout::timeout(options[:timeout].to_i) do
186
+ loop do
187
+ break if api.get_app(info['name']).body['create_status'] == 'complete'
188
+ sleep 1
189
+ end
190
+ end
191
+ end
192
+ status("stack is #{info['stack']}")
193
+ end
194
+
195
+ (options[:addons] || "").split(",").each do |addon|
196
+ addon.strip!
197
+ action("Adding #{addon} to #{info["name"]}") do
198
+ api.post_addon(info["name"], addon)
199
+ end
200
+ end
201
+
202
+ if buildpack = options[:buildpack]
203
+ api.put_config_vars(info["name"], "BUILDPACK_URL" => buildpack)
204
+ display("BUILDPACK_URL=#{buildpack}")
205
+ end
206
+
207
+ hputs([ info["web_url"], info["git_url"] ].join(" | "))
208
+ rescue Timeout::Error
209
+ hputs("Timed Out! Check heroku status for known issues.")
210
+ end
211
+
212
+ unless options[:no_remote].is_a? FalseClass
213
+ create_git_remote(options[:remote] || "heroku", info["git_url"])
214
+ end
215
+ end
216
+
217
+ alias_command "create", "apps:create"
218
+
219
+ # apps:rename NEWNAME
220
+ #
221
+ # rename the app
222
+ #
223
+ #Example:
224
+ #
225
+ # $ heroku apps:rename myapp-newname
226
+ # http://myapp-newname.herokuapp.com/ | git@heroku.com:myapp-newname.git
227
+ # Git remote heroku updated
228
+ #
229
+ def rename
230
+ newname = shift_argument
231
+ if newname.nil? || newname.empty?
232
+ error("Usage: heroku apps:rename NEWNAME\nMust specify NEWNAME to rename.")
233
+ end
234
+ validate_arguments!
235
+
236
+ action("Renaming #{app} to #{newname}") do
237
+ api.put_app(app, "name" => newname)
238
+ end
239
+
240
+ app_data = api.get_app(newname).body
241
+ hputs([ app_data["web_url"], app_data["git_url"] ].join(" | "))
242
+
243
+ if remotes = git_remotes(Dir.pwd)
244
+ remotes.each do |remote_name, remote_app|
245
+ next if remote_app != app
246
+ git "remote rm #{remote_name}"
247
+ git "remote add #{remote_name} #{app_data["git_url"]}"
248
+ hputs("Git remote #{remote_name} updated")
249
+ end
250
+ else
251
+ hputs("Don't forget to update your Git remotes on any local checkouts.")
252
+ end
253
+ end
254
+
255
+ alias_command "rename", "apps:rename"
256
+
257
+ # apps:open
258
+ #
259
+ # open the app in a web browser
260
+ #
261
+ #Example:
262
+ #
263
+ # $ heroku apps:open
264
+ # Opening myapp... done
265
+ #
266
+ def open
267
+ validate_arguments!
268
+
269
+ app_data = api.get_app(app).body
270
+ launchy("Opening #{app}", app_data['web_url'])
271
+ end
272
+
273
+ alias_command "open", "apps:open"
274
+
275
+ # apps:destroy
276
+ #
277
+ # permanently destroy an app
278
+ #
279
+ #Example:
280
+ #
281
+ # $ heroku apps:destroy -a myapp --confirm myapp
282
+ # Destroying myapp (including all add-ons)... done
283
+ #
284
+ def destroy
285
+ @app = shift_argument || options[:app] || options[:confirm]
286
+ validate_arguments!
287
+
288
+ unless @app
289
+ error("Usage: heroku apps:destroy --app APP\nMust specify APP to destroy.")
290
+ end
291
+
292
+ api.get_app(@app) # fail fast if no access or doesn't exist
293
+
294
+ message = "WARNING: Potentially Destructive Action\nThis command will destroy #{@app} (including all add-ons)."
295
+ if confirm_command(@app, message)
296
+ action("Destroying #{@app} (including all add-ons)") do
297
+ api.delete_app(@app)
298
+ if remotes = git_remotes(Dir.pwd)
299
+ remotes.each do |remote_name, remote_app|
300
+ next if @app != remote_app
301
+ git "remote rm #{remote_name}"
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+
308
+ alias_command "destroy", "apps:destroy"
309
+ alias_command "apps:delete", "apps:destroy"
310
+
311
+ end