pogo 2.31.2 → 2.32.14

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/bin/pogo CHANGED
@@ -2,10 +2,10 @@
2
2
  # encoding: UTF-8
3
3
 
4
4
  # pogo defaults
5
- ENV['HEROKU_HOST'] ||= 'http://api.pogoapp.com'
6
- ENV['HEROKU_STATUS_HOST'] ||= 'http://status.pogoapp.com'
7
- ENV['HEROKU_SSL_VERIFY'] ||= 'disable'
8
- ENV['HEROKU_GIT_HOST'] ||= 'git-i.pogoapp.com'
5
+ ENV['HEROKU_HOST'] ||= 'pogoapp.com'
6
+ ENV['HEROKU_STATUS_HOST'] ||= 'status.pogoapp.com'
7
+ ENV['HEROKU_GIT_HOST'] ||= 'git.pogoapp.com'
8
+ ENV['HEROKU_SSL_VERIFY'] ||= 'disable' # StartCom cert + RestClient = :(
9
9
 
10
10
  # resolve bin path, ignoring symlinks
11
11
  require "pathname"
@@ -19,4 +19,5 @@ Heroku::Updater.disable("`heroku update` is only available from Heroku Toolbelt.
19
19
 
20
20
  # start up the CLI
21
21
  require "heroku/cli"
22
+ Heroku.user_agent = "heroku-gem/#{Heroku::VERSION} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}"
22
23
  Heroku::CLI.start(*ARGV)
data/lib/heroku.rb CHANGED
@@ -4,19 +4,14 @@ require "heroku/version"
4
4
 
5
5
  module Heroku
6
6
 
7
- USER_AGENT = 'legacy' # left for backwards compatibility with old toolbelt bin files
7
+ USER_AGENT = "heroku-gem/#{Heroku::VERSION} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}"
8
8
 
9
9
  def self.user_agent
10
- type = if ENV['GEM_HOME'] && __FILE__.include?(ENV['GEM_HOME'])
11
- 'gem'
12
- else
13
- 'toolbelt'
14
- end
15
- user_agent = "heroku-#{type}/#{Heroku::Updater.latest_local_version} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}"
16
- if Heroku::Updater.autoupdate?
17
- user_agent << ' autoupdate'
18
- end
19
- user_agent
10
+ @@user_agent ||= USER_AGENT
11
+ end
12
+
13
+ def self.user_agent=(agent)
14
+ @@user_agent = agent
20
15
  end
21
16
 
22
17
  end
data/lib/heroku/auth.rb CHANGED
@@ -178,7 +178,7 @@ class Heroku::Auth
178
178
  end
179
179
 
180
180
  def ask_for_credentials
181
- puts "Enter your Heroku credentials."
181
+ puts "Enter your #{host_name} credentials."
182
182
 
183
183
  print "Email: "
184
184
  user = ask
@@ -300,12 +300,29 @@ class Heroku::Auth
300
300
  @login_attempts < 3
301
301
  end
302
302
 
303
+ def verified_hosts
304
+ %w( heroku.com heroku-shadow.com )
305
+ end
306
+
307
+ def base_host(host)
308
+ URI.parse(full_host(host)).host.split(".")[-2..-1].join(".")
309
+ end
310
+
311
+ def full_host(host)
312
+ (host =~ /^http/) ? host : "https://api.#{host}"
313
+ end
314
+
315
+ def verify_host?(host)
316
+ hostname = base_host(host)
317
+ verified = verified_hosts.include?(hostname)
318
+ verified = false if ENV["HEROKU_SSL_VERIFY"] == "disable"
319
+ verified
320
+ end
321
+
303
322
  protected
304
323
 
305
324
  def default_params
306
- full_host = (host =~ /^http/) ? host : "https://api.#{host}"
307
- verify_ssl = ENV['HEROKU_SSL_VERIFY'] != 'disable' && full_host =~ %r|^https://api.heroku.com|
308
- uri = URI(full_host)
325
+ uri = URI.parse(full_host(host))
309
326
  {
310
327
  :headers => {
311
328
  'User-Agent' => Heroku.user_agent
@@ -313,7 +330,7 @@ class Heroku::Auth
313
330
  :host => uri.host,
314
331
  :port => uri.port.to_s,
315
332
  :scheme => uri.scheme,
316
- :ssl_verify_peer => verify_ssl
333
+ :ssl_verify_peer => verify_host?(host)
317
334
  }
318
335
  end
319
336
  end
@@ -1,51 +1,81 @@
1
1
  require "heroku/client"
2
- require "digest/sha2"
3
2
 
4
3
  class Heroku::Client::HerokuPostgresql
5
- Version = 10
4
+ Version = 11
6
5
 
7
6
  include Heroku::Helpers
8
7
 
9
- def initialize(url)
8
+ @headers = { :x_heroku_gem_version => Heroku::Client.version }
9
+
10
+ def self.add_headers(headers)
11
+ @headers.merge! headers
12
+ end
13
+
14
+ def self.headers
15
+ @headers
16
+ end
17
+
18
+ attr_reader :attachment
19
+ def initialize(attachment)
20
+ @attachment = attachment
21
+ if attachment.resource_name == 'SHARED_DATABASE'
22
+ error('This command is not available for shared database')
23
+ end
10
24
  require 'rest_client'
11
- @heroku_postgresql_host = ENV["HEROKU_POSTGRESQL_HOST"] || "https://shogun.heroku.com"
12
- @database_sha = sha(url)
13
- @heroku_postgresql_resource = RestClient::Resource.new(
14
- "#{@heroku_postgresql_host}/client/v10/databases",
15
- :headers => { :x_heroku_gem_version => Heroku::Client.version }
25
+ end
26
+
27
+ def heroku_postgresql_host
28
+ if attachment.starter_plan?
29
+ ENV["HEROKU_POSTGRESQL_HOST"] || "postgres-starter-api"
30
+ else
31
+ if ENV['SHOGUN']
32
+ "shogun-#{ENV['SHOGUN']}"
33
+ else
34
+ ENV["HEROKU_POSTGRESQL_HOST"] || "postgres-api"
35
+ end
36
+ end
37
+ end
38
+
39
+ def resource_name
40
+ attachment.resource_name
41
+ end
42
+
43
+ def heroku_postgresql_resource
44
+ RestClient::Resource.new(
45
+ "https://#{heroku_postgresql_host}.heroku.com/client/v11/databases",
46
+ :user => Heroku::Auth.user,
47
+ :password => Heroku::Auth.password,
48
+ :headers => self.class.headers
16
49
  )
17
50
  end
18
51
 
19
52
  def ingress
20
- http_put "#{@database_sha}/ingress"
53
+ http_put "#{resource_name}/ingress"
21
54
  end
22
55
 
23
56
  def reset
24
- http_put "#{@database_sha}/reset"
57
+ http_put "#{resource_name}/reset"
25
58
  end
26
59
 
27
60
  def rotate_credentials
28
- http_post "#{@database_sha}/credentials_rotation"
61
+ http_post "#{resource_name}/credentials_rotation"
29
62
  end
30
63
 
31
- def get_database
32
- http_get @database_sha
64
+ def get_database(extended=false)
65
+ query = extended ? '?extended=true' : ''
66
+ http_get resource_name + query
33
67
  end
34
68
 
35
69
  def get_wait_status
36
- http_get "#{@database_sha}/wait_status"
70
+ http_get "#{resource_name}/wait_status"
37
71
  end
38
72
 
39
73
  def unfollow
40
- http_put "#{@database_sha}/unfollow"
74
+ http_put "#{resource_name}/unfollow"
41
75
  end
42
76
 
43
77
  protected
44
78
 
45
- def sha(url)
46
- Digest::SHA2.hexdigest url
47
- end
48
-
49
79
  def sym_keys(c)
50
80
  if c.is_a?(Array)
51
81
  c.map { |e| sym_keys(e) }
@@ -77,7 +107,7 @@ class Heroku::Client::HerokuPostgresql
77
107
  def http_get(path)
78
108
  checking_client_version do
79
109
  retry_on_exception(RestClient::Exception) do
80
- response = @heroku_postgresql_resource[path].get
110
+ response = heroku_postgresql_resource[path].get
81
111
  display_heroku_warning response
82
112
  sym_keys(json_decode(response.to_s))
83
113
  end
@@ -86,7 +116,7 @@ class Heroku::Client::HerokuPostgresql
86
116
 
87
117
  def http_post(path, payload = {})
88
118
  checking_client_version do
89
- response = @heroku_postgresql_resource[path].post(json_encode(payload))
119
+ response = heroku_postgresql_resource[path].post(json_encode(payload))
90
120
  display_heroku_warning response
91
121
  sym_keys(json_decode(response.to_s))
92
122
  end
@@ -94,7 +124,7 @@ class Heroku::Client::HerokuPostgresql
94
124
 
95
125
  def http_put(path, payload = {})
96
126
  checking_client_version do
97
- response = @heroku_postgresql_resource[path].put(json_encode(payload))
127
+ response = heroku_postgresql_resource[path].put(json_encode(payload))
98
128
  display_heroku_warning response
99
129
  sym_keys(json_decode(response.to_s))
100
130
  end
@@ -1,6 +1,7 @@
1
1
  require "timeout"
2
2
  require "socket"
3
3
  require "uri"
4
+ require "heroku/auth"
4
5
  require "heroku/client"
5
6
  require "heroku/helpers"
6
7
 
@@ -29,10 +30,12 @@ class Heroku::Client::Rendezvous
29
30
 
30
31
  ssl_socket = Timeout.timeout(connect_timeout) do
31
32
  ssl_context = OpenSSL::SSL::SSLContext.new
32
- if ((host =~ /heroku\.com$/) && !(ENV["HEROKU_SSL_VERIFY"] == "disable"))
33
- ssl_context.ca_file = File.expand_path("../../../../data/cacert.pem", __FILE__)
33
+
34
+ if Heroku::Auth.verify_host?(host)
35
+ ssl_context.ca_file = File.expand_path("../../../../data/cacert.pem", __FILE__)
34
36
  ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
35
37
  end
38
+
36
39
  tcp_socket = TCPSocket.open(host, port)
37
40
  ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
38
41
  ssl_socket.connect
@@ -115,20 +115,12 @@ module Heroku
115
115
  if args.include?('-h') || args.include?('--help')
116
116
  args.unshift(cmd) unless cmd =~ /^-.*/
117
117
  cmd = 'help'
118
- command = parse('help')
118
+ command = parse(cmd)
119
119
  end
120
120
 
121
- unless command
122
- if cmd == '--version'
123
- cmd = 'version'
124
- command = parse(cmd)
125
- else
126
- error([
127
- "`#{cmd}` is not a heroku command.",
128
- suggestion(cmd, commands.keys + command_aliases.keys),
129
- "See `heroku help` for a list of available commands."
130
- ].compact.join("\n"))
131
- end
121
+ if cmd == '--version'
122
+ cmd = 'version'
123
+ command = parse(cmd)
132
124
  end
133
125
 
134
126
  @current_command = cmd
@@ -141,7 +133,7 @@ module Heroku
141
133
  # remove OptionParsers Officious['version'] to avoid conflicts
142
134
  # see: https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb#L814
143
135
  parser.base.long.delete('version')
144
- (global_options + command[:options]).each do |option|
136
+ (global_options + (command && command[:options] || [])).each do |option|
145
137
  parser.on(*option[:args]) do |value|
146
138
  if option[:proc]
147
139
  option[:proc].call(value)
@@ -173,16 +165,39 @@ module Heroku
173
165
  @current_options = opts
174
166
  @invalid_arguments = invalid_options
175
167
 
176
- command_instance = command[:klass].new(args.dup, opts.dup)
177
-
178
168
  @anonymous_command = [ARGV.first, *@anonymized_args].join(' ')
179
-
180
- if !@normalized_args.include?('--app _') && (implied_app = command_instance.app rescue nil)
181
- @normalized_args << '--app _'
169
+ begin
170
+ usage_directory = "#{home_directory}/.heroku/usage"
171
+ FileUtils.mkdir_p(usage_directory)
172
+ usage_file = usage_directory << "/#{Heroku::VERSION}"
173
+ usage = if File.exists?(usage_file)
174
+ json_decode(File.read(usage_file))
175
+ else
176
+ {}
177
+ end
178
+ usage[@anonymous_command] ||= 0
179
+ usage[@anonymous_command] += 1
180
+ File.write(usage_file, json_encode(usage) + "\n")
181
+ rescue
182
+ # usage writing is not important, allow failures
182
183
  end
183
- @normalized_command = [ARGV.first, @normalized_args.sort_by {|arg| arg.gsub('-', '')}].join(' ')
184
184
 
185
- [ command_instance, command[:method] ]
185
+ if command
186
+ command_instance = command[:klass].new(args.dup, opts.dup)
187
+
188
+ if !@normalized_args.include?('--app _') && (implied_app = command_instance.app rescue nil)
189
+ @normalized_args << '--app _'
190
+ end
191
+ @normalized_command = [ARGV.first, @normalized_args.sort_by {|arg| arg.gsub('-', '')}].join(' ')
192
+
193
+ [ command_instance, command[:method] ]
194
+ else
195
+ error([
196
+ "`#{cmd}` is not a heroku command.",
197
+ suggestion(cmd, commands.keys + command_aliases.keys),
198
+ "See `heroku help` for a list of available commands."
199
+ ].compact.join("\n"))
200
+ end
186
201
  end
187
202
 
188
203
  def self.run(cmd, arguments=[])
@@ -233,6 +248,12 @@ module Heroku
233
248
  error e.message
234
249
  rescue OptionParser::ParseError
235
250
  commands[cmd] ? run("help", [cmd]) : run("help")
251
+ rescue Excon::Errors::SocketError => e
252
+ if e.message == 'getaddrinfo: nodename nor servname provided, or not known (SocketError)'
253
+ error("Unable to connect to Heroku API, please check internet connectivity and try again.")
254
+ else
255
+ raise(e)
256
+ end
236
257
  ensure
237
258
  display_warnings
238
259
  end
@@ -242,7 +263,7 @@ module Heroku
242
263
  end
243
264
 
244
265
  def self.extract_error(body, options={})
245
- default_error = block_given? ? yield : "Internal server error.\nRun 'heroku status' to check for known platform issues."
266
+ default_error = block_given? ? yield : "Internal server error.\nRun `heroku status` to check for known platform issues."
246
267
  parse_error_xml(body) || parse_error_json(body) || parse_error_plain(body) || default_error
247
268
  end
248
269
 
@@ -175,7 +175,7 @@ class Heroku::Command::Apps < Heroku::Command::Base
175
175
  # $ heroku apps:create myapp-staging --remote staging
176
176
  #
177
177
  def create
178
- name = shift_argument || options[:app]
178
+ name = shift_argument || options[:app] || ENV['HEROKU_APP']
179
179
  validate_arguments!
180
180
 
181
181
  info = api.post_app({ "name" => name, "stack" => options[:stack] }).body
@@ -206,7 +206,7 @@ class Heroku::Command::Apps < Heroku::Command::Base
206
206
 
207
207
  hputs([ info["web_url"], info["git_url"] ].join(" | "))
208
208
  rescue Timeout::Error
209
- hputs("Timed Out! Check heroku status for known issues.")
209
+ hputs("Timed Out! Run `heroku status` to check for known platform issues.")
210
210
  end
211
211
 
212
212
  unless options[:no_remote].is_a? FalseClass
@@ -4,29 +4,39 @@ require "heroku/command/base"
4
4
  #
5
5
  class Heroku::Command::Labs < Heroku::Command::Base
6
6
 
7
- # labs [APP]
7
+ # labs
8
8
  #
9
- # lists enabled features for an app
9
+ # list experimental features
10
10
  #
11
11
  #Example:
12
12
  #
13
- # $ heroku labs -a myapp
14
- # === myapp Enabled Features
15
- # sigterm-all: When stopping a dyno, send SIGTERM to all processes rather than only to the root process.
13
+ # === User Features (david@heroku.com)
14
+ # [+] dashboard Use Heroku Dashboard by default
16
15
  #
17
- # === email@example.com Enabled Features
18
- # sumo-rankings: Heroku Sumo ranks and visualizes the scale of your app, and suggests the optimum combination of dynos and add-ons to take it to the next level.
16
+ # === App Features (glacial-retreat-5913)
17
+ # [ ] preboot Provide seamless web dyno deploys
18
+ # [ ] user-env-compile Add user config vars to the environment during slug compilation # $ heroku labs -a myapp
19
19
  #
20
20
  def index
21
21
  validate_arguments!
22
22
 
23
- if app
24
- display_features(app, 'enabled', { 'enabled' => true, 'kind' => 'app' })
23
+ user_features, app_features = api.get_features(app).body.sort_by do |feature|
24
+ feature["name"]
25
+ end.partition do |feature|
26
+ feature["kind"] == "user"
25
27
  end
26
28
 
27
- display_features(Heroku::Auth.user, 'enabled', { 'enabled' => true, 'kind' => 'user' })
29
+ display_app = app || "no app specified"
30
+
31
+ styled_header "User Features (#{Heroku::Auth.user})"
32
+ display_features user_features
33
+ display
34
+ styled_header "App Features (#{display_app})"
35
+ display_features app_features
28
36
  end
29
37
 
38
+ alias_command "labs:list", "labs"
39
+
30
40
  # labs:info FEATURE
31
41
  #
32
42
  # displays additional information about FEATURE
@@ -54,107 +64,83 @@ class Heroku::Command::Labs < Heroku::Command::Base
54
64
 
55
65
  # labs:disable FEATURE
56
66
  #
57
- # disables FEATURE on an app
67
+ # disables an experimental feature
58
68
  #
59
69
  #Example:
60
70
  #
61
- # $ heroku labs:disable user_env_compile
62
- # Disabling user_env_compile for myapp... done
71
+ # $ heroku labs:disable ninja-power
72
+ # Disabling ninja-power feature for me@example.org... done
63
73
  #
64
74
  def disable
65
- unless feature_name = shift_argument
66
- error("Usage: heroku labs:disable FEATURE\nMust specify FEATURE to disable.")
67
- end
75
+ feature_name = shift_argument
76
+ error "Usage: heroku labs:disable FEATURE\nMust specify FEATURE to disable." unless feature_name
68
77
  validate_arguments!
69
78
 
70
- message = "Disabling #{feature_name}"
71
- message += " for #{app}" if app
72
- action(message) do
73
- api.delete_feature(feature_name, app)
79
+ feature = api.get_features(app).body.detect { |f| f["name"] == feature_name }
80
+ message = "Disabling #{feature_name} "
81
+
82
+ error "No such feature: #{feature_name}" unless feature
83
+
84
+ if feature["kind"] == "user"
85
+ message += "for #{Heroku::Auth.user}"
86
+ else
87
+ error "Must specify an app" unless app
88
+ message += "for #{app}"
89
+ end
90
+
91
+ action message do
92
+ api.delete_feature feature_name, app
74
93
  end
75
94
  end
76
95
 
77
96
  # labs:enable FEATURE
78
97
  #
79
- # enables FEATURE on an app
98
+ # enables an experimental feature
80
99
  #
81
100
  #Example:
82
101
  #
83
- # $ heroku labs:enable user_env_compile
84
- # Enabling user_env_compile for myapp... done
85
- # For more information see: http://devcenter.heroku.com/articles/labs-user-env-compile
102
+ # $ heroku labs:enable ninja-power
103
+ # Enabling ninja-power feature for me@example.org... done
86
104
  #
87
105
  def enable
88
- unless feature_name = shift_argument
89
- error("Usage: heroku labs:enable FEATURE\nMust specify FEATURE to enable.")
90
- end
106
+ feature_name = shift_argument
107
+ error "Usage: heroku labs:enable FEATURE\nMust specify FEATURE to enable." unless feature_name
91
108
  validate_arguments!
92
109
 
93
- message = "Enabling #{feature_name}"
94
- message += " for #{app}" if app
95
- feature_data = nil
96
- action(message) do
97
- feature_data = api.post_feature(feature_name, app).body
110
+ feature = api.get_features.body.detect { |f| f["name"] == feature_name }
111
+ message = "Enabling #{feature_name} "
112
+
113
+ error "No such feature: #{feature_name}" unless feature
114
+
115
+ if feature["kind"] == "user"
116
+ message += "for #{Heroku::Auth.user}"
117
+ else
118
+ error "Must specify an app" unless app
119
+ message += "for #{app}"
98
120
  end
99
- display("WARNING: This feature is experimental and may change or be removed without notice.")
100
- display("For more information see: #{feature_data['docs']}")
101
- end
102
121
 
103
- # labs:list
104
- #
105
- # lists available features
106
- #
107
- #Example:
108
- #
109
- # $ heroku labs:list
110
- # === App Available Features
111
- # dot-profile: Source .profile during dyno startup
112
- # preboot: Provide seamless deploys by booting web dynos with new code before killing existing web dynos.
113
- # user_env_compile: Add user config vars to the environment during slug compilation
114
- #
115
- # === User Available Features
116
- # default-heroku-postgresql-dev: Use the new heroku-postgresql:dev add-on as the default database for Cedar apps.
117
- #
118
- def list
119
- validate_arguments!
122
+ feature_data = action(message) do
123
+ api.post_feature(feature_name, app).body
124
+ end
120
125
 
121
- display_features('App', 'available', { 'kind' => 'app' })
122
- display_features('User', 'available', { 'kind' => 'user' })
126
+ display "WARNING: This feature is experimental and may change or be removed without notice."
127
+ display "For more information see: #{feature_data["docs"]}" if feature_data["docs"]
123
128
  end
124
129
 
125
130
  private
126
131
 
132
+ # app is not required for these commands, so rescue if there is none
127
133
  def app
128
- # app is not required for these commands, so rescue if there is none
129
134
  super
130
135
  rescue Heroku::Command::CommandFailed
131
136
  nil
132
137
  end
133
138
 
134
- def display_features(type, status, attributes)
135
- @features ||= api.get_features(app).body
136
-
137
- selected_features = @features.dup
138
- attributes.each do |key, value|
139
- selected_features = selected_features.select {|feature| feature[key] == value}
140
- end
141
-
142
- feature_hash = {}
143
- selected_features.each do |feature|
144
- feature_hash[feature['name']] = feature['summary']
145
- end
146
-
147
- if feature_hash.empty?
148
- case status
149
- when 'available'
150
- display("There are no #{type} features available.")
151
- when 'enabled'
152
- display("#{type} has no enabled features.")
153
- end
154
- else
155
- styled_header("#{type} #{status.capitalize} Features")
156
- styled_hash(feature_hash)
157
- display
139
+ def display_features(features)
140
+ longest_name = features.map { |f| f["name"].to_s.length }.sort.last
141
+ features.each do |feature|
142
+ toggle = feature["enabled"] ? "[+]" : "[ ]"
143
+ display "%s %-#{longest_name}s %s" % [ toggle, feature["name"], feature["summary"] ]
158
144
  end
159
145
  end
160
146