pogo 2.31.2

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