heroku_hatchet 3.1.1 → 4.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2363b4e6ad949febf29929396202ad62215d4b8442fab1a5701273dc6356e0e8
4
- data.tar.gz: 2834674840abffb9e1bdcf944629c6b00dc987fb17db61685a5b4e15f29bbd71
3
+ metadata.gz: cb7387bbf480ee1443a1985b8bfd3681e6126605786c0ee102be9a691cd37f01
4
+ data.tar.gz: 59876cc8324d6397434dbb03492a6d1231023c343aa6a70a61502cc5d8059fdc
5
5
  SHA512:
6
- metadata.gz: 0ba670133988a7297d8573c69c488bfdb4450cb5420778f372475a1523bd482cf40bd5fb1e4299e6118dd46fb3cb78cabed819099bdd56a0caa71c57ff6e8692
7
- data.tar.gz: 4bf38e97b2afd7524e1ff81cac1fa1b4fb0cb8dbcd4fe62e49039aacdf79706def80b2ee8e8fe4a079355db559471895a42deec37e7304a94d31f4cc2ca985ff
6
+ metadata.gz: 95998e8ab947245d1ebc799fd6bf54b377bffda5e4b63f608aa2663e55e278d71d307754a6d2a623e66e8ff07dcbd04d1b30f4d3ab666b5b1e9856b78f22e645
7
+ data.tar.gz: 7554ca9897c18aa4022526a5e9deba8f05475797f57b5635dc51658797c92390556ea12fcdc66bad6567cbf8a3fb3ab72ae1408d41345bba95fe0d904f2851df
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## HEAD
2
2
 
3
+ ## 4.0.0
4
+
5
+ - Introduce API rate limiting (#46)
6
+ - Deprecate App#platform_api method (#46)
7
+
3
8
  ## 3.1.1
4
9
 
5
10
  - Better errors when no lockfile is found #43
data/bin/hatchet CHANGED
@@ -22,9 +22,11 @@ class HatchetCLI < Thor
22
22
  warn_dot_ignore!
23
23
  lock_hash = load_lockfile
24
24
  puts "Installing repos for hatchet"
25
+
26
+ missing_commit = false
25
27
  dirs.map do |directory, git_repo|
26
28
  Threaded.later do
27
- commit = lock_hash.fetch(directory) { raise "expected #{directory} to be locked but it is not, please run `hatchet lock` and commit the results"}
29
+ commit = lock_hash[directory]
28
30
  directory = File.expand_path(directory)
29
31
  if Dir[directory].present?
30
32
  puts "== pulling '#{git_repo}' into '#{directory}'\n"
@@ -33,9 +35,15 @@ class HatchetCLI < Thor
33
35
  puts "== cloning '#{git_repo}' into '#{directory}'\n"
34
36
  clone(directory, git_repo)
35
37
  end
36
- checkout_commit(directory, commit)
38
+ if commit
39
+ checkout_commit(directory, commit)
40
+ else
41
+ missing_commit = true
42
+ end
37
43
  end
38
44
  end.map(&:join)
45
+
46
+ self.lock if missing_commit
39
47
  end
40
48
 
41
49
  def load_lockfile
data/lib/hatchet.rb CHANGED
@@ -17,6 +17,7 @@ require 'hatchet/app'
17
17
  require 'hatchet/anvil_app'
18
18
  require 'hatchet/git_app'
19
19
  require 'hatchet/config'
20
+ require 'hatchet/api_rate_limit'
20
21
 
21
22
  module Hatchet
22
23
  RETRIES = Integer(ENV['HATCHET_RETRIES'] || 1)
@@ -0,0 +1,40 @@
1
+ # Wraps platform-api and adds API rate limits
2
+ #
3
+ # Instead of:
4
+ #
5
+ # platform_api.pipeline.create(name: @name)
6
+ #
7
+ # Use:
8
+ #
9
+ # api_rate_limit = ApiRateLimit.new(platform_api)
10
+ # api_rate_limit.call.pipeline.create(name: @name)
11
+ #
12
+ class ApiRateLimit
13
+ def initialize(platform_api)
14
+ @platform_api = platform_api
15
+ @capacity = 1
16
+ @called = 0
17
+ end
18
+
19
+
20
+ # Sleeps for progressively longer when api rate limit capacity
21
+ # is lower.
22
+ #
23
+ # Unfortunatley `@platform_api.rate_limit` is an extra API
24
+ # call, so by checking our limit, we also are using our limit 😬
25
+ # to partially mitigate this, only check capacity every 5
26
+ # api calls, or if the
27
+ def call
28
+ @called += 1
29
+
30
+ if @called > 5 || @capacity < 1000
31
+ @called = 0
32
+ @capacity = @platform_api.rate_limit.info["remaining"]
33
+ end
34
+
35
+ sleep_time = (60/@capacity) if @capacity > 0.1 # no divide by zero
36
+ sleep(sleep_time || 60)
37
+
38
+ return @platform_api
39
+ end
40
+ end
data/lib/hatchet/app.rb CHANGED
@@ -31,7 +31,7 @@ module Hatchet
31
31
  @labs = ([] << options[:labs]).flatten.compact
32
32
  @buildpacks = options[:buildpack] || options[:buildpacks] || options[:buildpack_url] || self.class.default_buildpack
33
33
  @buildpacks = Array(@buildpacks)
34
- @reaper = Reaper.new(platform_api: platform_api)
34
+ @reaper = Reaper.new(api_rate_limit: api_rate_limit)
35
35
  end
36
36
 
37
37
  def self.default_buildpack
@@ -54,13 +54,13 @@ module Hatchet
54
54
  def set_config(options = {})
55
55
  options.each do |key, value|
56
56
  # heroku.put_config_vars(name, key => value)
57
- platform_api.config_var.update(name, key => value)
57
+ api_rate_limit.call.config_var.update(name, key => value)
58
58
  end
59
59
  end
60
60
 
61
61
  def get_config
62
62
  # heroku.get_config_vars(name).body
63
- platform_api.config_var.info_for_app(name)
63
+ api_rate_limit.call.config_var.info_for_app(name)
64
64
  end
65
65
 
66
66
  def lab_is_installed?(lab)
@@ -69,7 +69,7 @@ module Hatchet
69
69
 
70
70
  def get_labs
71
71
  # heroku.get_features(name).body
72
- platform_api.app_feature.list(name)
72
+ api_rate_limit.call.app_feature.list(name)
73
73
  end
74
74
 
75
75
  def set_labs!
@@ -78,13 +78,13 @@ module Hatchet
78
78
 
79
79
  def set_lab(lab)
80
80
  # heroku.post_feature(lab, name)
81
- platform_api.app_feature.update(name, lab, enabled: true)
81
+ api_rate_limit.call.app_feature.update(name, lab, enabled: true)
82
82
  end
83
83
 
84
84
  def add_database(plan_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL")
85
85
  Hatchet::RETRIES.times.retry do
86
86
  # heroku.post_addon(name, plan_name)
87
- platform_api.addon.create(name, plan: plan_name )
87
+ api_rate_limit.call.addon.create(name, plan: plan_name )
88
88
  _, value = get_config.detect {|k, v| k.match(/#{match_val}/) }
89
89
  set_config('DATABASE_URL' => value)
90
90
  end
@@ -120,7 +120,7 @@ module Hatchet
120
120
 
121
121
  def deployed?
122
122
  # !heroku.get_ps(name).body.detect {|ps| ps["process"].include?("web") }.nil?
123
- platform_api.formation.list(name).detect {|ps| ps["type"] == "web"}
123
+ api_rate_limit.call.formation.list(name).detect {|ps| ps["type"] == "web"}
124
124
  end
125
125
 
126
126
  def create_app
@@ -129,7 +129,7 @@ module Hatchet
129
129
  # heroku.post_app({ name: name, stack: stack }.delete_if {|k,v| v.nil? })
130
130
  hash = { name: name, stack: stack }
131
131
  hash.delete_if { |k,v| v.nil? }
132
- platform_api.app.create(hash)
132
+ api_rate_limit.call.app.create(hash)
133
133
  rescue
134
134
  @reaper.cycle
135
135
  raise e
@@ -139,7 +139,7 @@ module Hatchet
139
139
 
140
140
  def update_stack(stack_name)
141
141
  @stack = stack_name
142
- platform_api.app.update(name, build_stack: @stack)
142
+ api_rate_limit.call.app.update(name, build_stack: @stack)
143
143
  end
144
144
 
145
145
  # creates a new heroku app via the API
@@ -150,7 +150,7 @@ module Hatchet
150
150
  set_labs!
151
151
  # heroku.put_config_vars(name, 'BUILDPACK_URL' => @buildpack)
152
152
  buildpack_list = @buildpacks.map {|pack| { buildpack: pack }}
153
- platform_api.buildpack_installation.update(name, updates: buildpack_list)
153
+ api_rate_limit.call.buildpack_installation.update(name, updates: buildpack_list)
154
154
  @app_is_setup = true
155
155
  self
156
156
  end
@@ -186,7 +186,7 @@ module Hatchet
186
186
  in_directory do
187
187
  self.setup!
188
188
  self.push_with_retry!
189
- block.call(self, platform_api, output) if block_given?
189
+ block.call(self, api_rate_limit.call, output) if block_given?
190
190
  end
191
191
  ensure
192
192
  self.teardown!
@@ -255,7 +255,7 @@ module Hatchet
255
255
  end
256
256
 
257
257
  def create_pipeline
258
- platform_api.pipeline.create(name: @name)
258
+ api_rate_limit.call.pipeline.create(name: @name)
259
259
  end
260
260
 
261
261
  def source_get_url
@@ -265,7 +265,7 @@ module Hatchet
265
265
 
266
266
  def create_source
267
267
  @create_source ||= begin
268
- result = platform_api.source.create
268
+ result = api_rate_limit.call.source.create
269
269
  @source_get_url = result["source_blob"]["get_url"]
270
270
  @source_put_url = result["source_blob"]["put_url"]
271
271
  @source_put_url
@@ -273,12 +273,18 @@ module Hatchet
273
273
  end
274
274
 
275
275
  def delete_pipeline(pipeline_id)
276
- platform_api.pipeline.delete(pipeline_id)
276
+ api_rate_limit.call.pipeline.delete(pipeline_id)
277
277
  end
278
278
 
279
279
  def platform_api
280
- # We have to not use a cache due to https://github.com/heroku/platform-api/issues/73
281
- @platform_api ||= PlatformAPI.connect_oauth(api_key, cache: Moneta.new(:Null))
280
+ puts "Deprecated: use `api_rate_limit.call` instead of platform_api"
281
+ api_rate_limit
282
+ return @platform_api
283
+ end
284
+
285
+ def api_rate_limit
286
+ @platform_api ||= PlatformAPI.connect_oauth(api_key, cache: Moneta.new(:Null))
287
+ @api_rate_limit ||= ApiRateLimit.new(@platform_api)
282
288
  end
283
289
 
284
290
  private
@@ -11,14 +11,14 @@ module Hatchet
11
11
  attr_accessor :apps
12
12
 
13
13
 
14
- def initialize(platform_api:, regex: DEFAULT_REGEX)
15
- @platform_api = platform_api
14
+ def initialize(api_rate_limit: , regex: DEFAULT_REGEX)
15
+ @api_rate_limit = api_rate_limit
16
16
  @regex = regex
17
17
  end
18
18
 
19
19
  # Ascending order, oldest is last
20
20
  def get_apps
21
- apps = @platform_api.app.list.sort_by { |app| DateTime.parse(app["created_at"]) }.reverse
21
+ apps = @api_rate_limit.call.app.list.sort_by { |app| DateTime.parse(app["created_at"]) }.reverse
22
22
  @app_count = apps.count
23
23
  @hatchet_apps = apps.select {|app| app["name"].match(@regex) }
24
24
  end
@@ -63,7 +63,7 @@ module Hatchet
63
63
  def destroy_by_id(name:, id:, details: "")
64
64
  @message = "Destroying #{name.inspect}: #{id}. #{details}"
65
65
  puts @message
66
- @platform_api.app.delete(id)
66
+ @api_rate_limit.call.app.delete(id)
67
67
  end
68
68
 
69
69
  private
@@ -1,3 +1,3 @@
1
1
  module Hatchet
2
- VERSION = "3.1.1"
2
+ VERSION = "4.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroku_hatchet
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Schneeman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-03 00:00:00.000000000 Z
11
+ date: 2018-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: platform-api
@@ -199,6 +199,7 @@ files:
199
199
  - hatchet.lock
200
200
  - lib/hatchet.rb
201
201
  - lib/hatchet/anvil_app.rb
202
+ - lib/hatchet/api_rate_limit.rb
202
203
  - lib/hatchet/app.rb
203
204
  - lib/hatchet/config.rb
204
205
  - lib/hatchet/git_app.rb