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.
- data/README.md +73 -0
- data/bin/pogo +22 -0
- data/data/cacert.pem +3988 -0
- data/lib/heroku.rb +22 -0
- data/lib/heroku/auth.rb +320 -0
- data/lib/heroku/cli.rb +38 -0
- data/lib/heroku/client.rb +764 -0
- data/lib/heroku/client/heroku_postgresql.rb +111 -0
- data/lib/heroku/client/pgbackups.rb +113 -0
- data/lib/heroku/client/rendezvous.rb +105 -0
- data/lib/heroku/client/ssl_endpoint.rb +25 -0
- data/lib/heroku/command.rb +273 -0
- data/lib/heroku/command/account.rb +23 -0
- data/lib/heroku/command/accounts.rb +34 -0
- data/lib/heroku/command/addons.rb +305 -0
- data/lib/heroku/command/apps.rb +311 -0
- data/lib/heroku/command/auth.rb +86 -0
- data/lib/heroku/command/base.rb +230 -0
- data/lib/heroku/command/certs.rb +148 -0
- data/lib/heroku/command/config.rb +137 -0
- data/lib/heroku/command/db.rb +218 -0
- data/lib/heroku/command/domains.rb +85 -0
- data/lib/heroku/command/drains.rb +46 -0
- data/lib/heroku/command/git.rb +65 -0
- data/lib/heroku/command/help.rb +163 -0
- data/lib/heroku/command/keys.rb +115 -0
- data/lib/heroku/command/labs.rb +161 -0
- data/lib/heroku/command/logs.rb +98 -0
- data/lib/heroku/command/maintenance.rb +61 -0
- data/lib/heroku/command/pg.rb +277 -0
- data/lib/heroku/command/pgbackups.rb +289 -0
- data/lib/heroku/command/plugins.rb +110 -0
- data/lib/heroku/command/ps.rb +232 -0
- data/lib/heroku/command/releases.rb +124 -0
- data/lib/heroku/command/run.rb +179 -0
- data/lib/heroku/command/sharing.rb +89 -0
- data/lib/heroku/command/ssl.rb +61 -0
- data/lib/heroku/command/stack.rb +62 -0
- data/lib/heroku/command/status.rb +51 -0
- data/lib/heroku/command/update.rb +47 -0
- data/lib/heroku/command/version.rb +23 -0
- data/lib/heroku/deprecated.rb +5 -0
- data/lib/heroku/deprecated/help.rb +38 -0
- data/lib/heroku/distribution.rb +9 -0
- data/lib/heroku/helpers.rb +517 -0
- data/lib/heroku/helpers/heroku_postgresql.rb +104 -0
- data/lib/heroku/plugin.rb +161 -0
- data/lib/heroku/updater.rb +158 -0
- data/lib/heroku/version.rb +3 -0
- data/lib/vendor/heroku/okjson.rb +598 -0
- data/spec/helper/legacy_help.rb +16 -0
- data/spec/heroku/auth_spec.rb +246 -0
- data/spec/heroku/client/heroku_postgresql_spec.rb +34 -0
- data/spec/heroku/client/pgbackups_spec.rb +43 -0
- data/spec/heroku/client/rendezvous_spec.rb +62 -0
- data/spec/heroku/client/ssl_endpoint_spec.rb +48 -0
- data/spec/heroku/client_spec.rb +564 -0
- data/spec/heroku/command/addons_spec.rb +585 -0
- data/spec/heroku/command/apps_spec.rb +351 -0
- data/spec/heroku/command/auth_spec.rb +38 -0
- data/spec/heroku/command/base_spec.rb +109 -0
- data/spec/heroku/command/certs_spec.rb +178 -0
- data/spec/heroku/command/config_spec.rb +144 -0
- data/spec/heroku/command/db_spec.rb +110 -0
- data/spec/heroku/command/domains_spec.rb +87 -0
- data/spec/heroku/command/drains_spec.rb +34 -0
- data/spec/heroku/command/git_spec.rb +116 -0
- data/spec/heroku/command/help_spec.rb +93 -0
- data/spec/heroku/command/keys_spec.rb +120 -0
- data/spec/heroku/command/labs_spec.rb +99 -0
- data/spec/heroku/command/logs_spec.rb +60 -0
- data/spec/heroku/command/maintenance_spec.rb +51 -0
- data/spec/heroku/command/pg_spec.rb +223 -0
- data/spec/heroku/command/pgbackups_spec.rb +280 -0
- data/spec/heroku/command/plugins_spec.rb +104 -0
- data/spec/heroku/command/ps_spec.rb +195 -0
- data/spec/heroku/command/releases_spec.rb +130 -0
- data/spec/heroku/command/run_spec.rb +86 -0
- data/spec/heroku/command/sharing_spec.rb +59 -0
- data/spec/heroku/command/ssl_spec.rb +32 -0
- data/spec/heroku/command/stack_spec.rb +46 -0
- data/spec/heroku/command/status_spec.rb +48 -0
- data/spec/heroku/command/version_spec.rb +16 -0
- data/spec/heroku/command_spec.rb +211 -0
- data/spec/heroku/helpers/heroku_postgresql_spec.rb +109 -0
- data/spec/heroku/helpers_spec.rb +48 -0
- data/spec/heroku/plugin_spec.rb +172 -0
- data/spec/heroku/updater_spec.rb +44 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +209 -0
- data/spec/support/display_message_matcher.rb +49 -0
- data/spec/support/openssl_mock_helper.rb +8 -0
- 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
|