puppet_forge 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0e379f885f305dfc82f87bfea6ae668d6467595f0083355cbe95c63e9864019
4
- data.tar.gz: 1cf23ce28776267459e01bedfed935750650378542b54e1ec3155efb9b67ba60
3
+ metadata.gz: 2eed3dead78d19c535ee9a2b372a89e17efcf7b24f01c752bb38096a2ed52e37
4
+ data.tar.gz: 2036388fc19159a57d425383aa6c89326d222a900f9fd61fbbb529e62d004f28
5
5
  SHA512:
6
- metadata.gz: b32e4f0758fe84f28339cc8a3f57dad01a21abc26a490e7d1b8ac1a4ecdc6e52b961e4c24f0f4a5b476c4fc1ed849bfb42594314a120f6198c06261036702457
7
- data.tar.gz: 044a092e9f8e15f078043281bac236c8694c037a1f71bb34a4d0e392379811e88ac9981afa6d75661cc5483a7a3966af26def433fe2eeb370c9f2d80499fbcba
6
+ metadata.gz: ca288c45d5329d1656221858c03a1125e0e29eb1e6701b95da90baa2cd6ba254907e15fafbf4f1474b69a43014a3c9f10fbcaed620d3913d4bf221194b3a2cc5
7
+ data.tar.gz: 8f5a441289712e2cb9ef9c7d34a17cef347b2b09ff0f60fc1430cceae3780229201aecee3b0baa601a3fd343060ed1976fad247e8e54abbbfb7d5a9a37f30fc0
@@ -15,13 +15,14 @@ jobs:
15
15
  strategy:
16
16
  matrix:
17
17
  ruby:
18
+ - '3.2'
18
19
  - '3.1'
19
20
  - '3.0'
20
21
  - '2.7'
21
22
  - '2.6'
22
23
 
23
24
  steps:
24
- - uses: actions/checkout@v1
25
+ - uses: actions/checkout@v3
25
26
  - name: Set up Ruby ${{ matrix.ruby }}
26
27
  uses: ruby/setup-ruby@v1
27
28
  with:
data/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  Starting with v2.0.0, all notable changes to this project will be documented in this file.
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## v5.0.0 - 2023-05-07
7
+
8
+ * Ruby 3.2 support.
9
+ * LRU caching for HTTP response caching.
10
+ * Raise a ModuleNotFound error instead of just nil when a module is not found.
11
+
12
+ ## v4.1.0 - 2023-02-21
13
+
14
+ * Add upload method functionality.
15
+ * Allows the user to search by an array of endorsements.
16
+
6
17
  ## v4.0.0 - 2022-11-30
7
18
 
8
19
  * Breaking: The `puppet_forge` gem now requires at least Ruby 2.6.0
data/CODEOWNERS CHANGED
@@ -1 +1 @@
1
- * @puppetlabs/forge-team
1
+ * @puppetlabs/forge-team @bastelfreak
data/README.md CHANGED
@@ -136,7 +136,23 @@ release.verify(Pathname(release_tarball))
136
136
  # @raise RuntimeError if it fails to extract the contents of the release tarball
137
137
  PuppetForge::Unpacker.unpack(release_tarball, dest_dir, tmp_dir)
138
138
  ```
139
+ ### Uploading a module release
139
140
 
141
+ You can upload new module versions to the forge by following the steps below.
142
+
143
+ > Note: This API requires authorization. See [Authorization](#authorization) for more information.
144
+
145
+ ```ruby
146
+ release_tarball = 'pkg/puppetlabs-apache-1.6.0.tar.gz'
147
+
148
+ # Upload a module tarball to the Puppet Forge
149
+ # Returns an instance of V3::Release class and the response from the forge upload
150
+ # @raise PuppetForge::ReleaseForbidden if a 403 response is recieved from the server
151
+ # @raise PuppetForge::ReleaseBadContent if the module to upload is not valid
152
+ # @raise Faraday::ClientError if any errors encountered in the upload
153
+ # @raise PuppetForge::FileNotFound if the given tarball cannot be found
154
+ release, response = PuppetForge::V3::Release.upload(release_tarball)
155
+ ```
140
156
 
141
157
  ### Paginated Collections
142
158
 
@@ -28,16 +28,32 @@ Could not install package
28
28
  end
29
29
  end
30
30
 
31
+
32
+ class ErrorWithDetail < PuppetForge::Error
33
+ def self.from_response(response)
34
+ body = JSON.parse(response[:body])
35
+
36
+ message = body['message']
37
+ if body.key?('errors') && !body['errors']&.empty?
38
+ message << "\nThe following errors were returned from the server:\n - #{body['errors'].join("\n - ")}"
39
+ end
40
+
41
+ new(message)
42
+ end
43
+ end
44
+
45
+ class FileNotFound < PuppetForge::Error
46
+ end
47
+
31
48
  class ModuleNotFound < PuppetForge::Error
32
49
  end
33
50
 
34
51
  class ReleaseNotFound < PuppetForge::Error
35
52
  end
36
53
 
37
- class ReleaseForbidden < PuppetForge::Error
38
- def self.from_response(response)
39
- body = JSON.parse(response[:body])
40
- new(body["message"])
41
- end
54
+ class ReleaseForbidden < PuppetForge::ErrorWithDetail
55
+ end
56
+
57
+ class ReleaseBadContent < PuppetForge::ErrorWithDetail
42
58
  end
43
59
  end
@@ -0,0 +1,78 @@
1
+ require 'digest'
2
+
3
+ module PuppetForge
4
+ # Implements a simple LRU cache. This is used internally by the
5
+ # {PuppetForge::V3::Base} class to cache API responses.
6
+ class LruCache
7
+ # Takes a list of strings (or objects that respond to #to_s) and
8
+ # returns a SHA256 hash of the strings joined with colons. This is
9
+ # a convenience method for generating cache keys. Cache keys do not
10
+ # have to be SHA256 hashes, but they must be unique.
11
+ def self.new_key(*string_args)
12
+ Digest::SHA256.hexdigest(string_args.map(&:to_s).join(':'))
13
+ end
14
+
15
+ # @return [Integer] the maximum number of items to cache.
16
+ attr_reader :max_size
17
+
18
+ # @param max_size [Integer] the maximum number of items to cache. This can
19
+ # be overridden by setting the PUPPET_FORGE_MAX_CACHE_SIZE environment
20
+ # variable.
21
+ def initialize(max_size = 30)
22
+ raise ArgumentError, "max_size must be a positive integer" unless max_size.is_a?(Integer) && max_size > 0
23
+
24
+ @max_size = ENV['PUPPET_FORGE_MAX_CACHE_SIZE'] ? ENV['PUPPET_FORGE_MAX_CACHE_SIZE'].to_i : max_size
25
+ @cache = {}
26
+ @lru = []
27
+ @semaphore = Mutex.new
28
+ end
29
+
30
+ # Retrieves a value from the cache.
31
+ # @param key [Object] the key to look up in the cache
32
+ # @return [Object] the cached value for the given key, or nil if
33
+ # the key is not present in the cache.
34
+ def get(key)
35
+ if cache.key?(key)
36
+ semaphore.synchronize do
37
+ # If the key is present, move it to the front of the LRU
38
+ # list.
39
+ lru.delete(key)
40
+ lru.unshift(key)
41
+ end
42
+ cache[key]
43
+ end
44
+ end
45
+
46
+ # Adds a value to the cache.
47
+ # @param key [Object] the key to add to the cache
48
+ # @param value [Object] the value to add to the cache
49
+ def put(key, value)
50
+ semaphore.synchronize do
51
+ if cache.key?(key)
52
+ # If the key is already present, delete it from the LRU list.
53
+ lru.delete(key)
54
+ elsif cache.size >= max_size
55
+ # If the cache is full, remove the least recently used item.
56
+ cache.delete(lru.pop)
57
+ end
58
+ # Add the key to the front of the LRU list and add the value
59
+ # to the cache.
60
+ lru.unshift(key)
61
+ cache[key] = value
62
+ end
63
+ end
64
+
65
+ # Clears the cache.
66
+ def clear
67
+ semaphore.synchronize do
68
+ cache.clear
69
+ lru.clear
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ # Makes testing easier as these can be accessed directly with #send.
76
+ attr_reader :cache, :lru, :semaphore
77
+ end
78
+ end
@@ -4,6 +4,7 @@ require 'puppet_forge/error'
4
4
 
5
5
  require 'puppet_forge/lazy_accessors'
6
6
  require 'puppet_forge/lazy_relations'
7
+ require 'puppet_forge/lru_cache'
7
8
 
8
9
  module PuppetForge
9
10
  module V3
@@ -53,8 +54,22 @@ module PuppetForge
53
54
  API_VERSION
54
55
  end
55
56
 
57
+ # @private
58
+ def lru_cache
59
+ @lru_cache ||= PuppetForge::LruCache.new
60
+ end
61
+
62
+ # @private
63
+ def lru_cache_key(*args)
64
+ PuppetForge::LruCache.new_key(*args)
65
+ end
66
+
56
67
  # @private
57
68
  def request(resource, item = nil, params = {}, reset_connection = false, conn_opts = {})
69
+ cache_key = lru_cache_key(resource, item, params)
70
+ cached = lru_cache.get(cache_key)
71
+ return cached unless cached.nil?
72
+
58
73
  conn(reset_connection, conn_opts) if reset_connection
59
74
  unless conn.url_prefix.to_s =~ /^#{PuppetForge.host}/
60
75
  conn.url_prefix = "#{PuppetForge.host}"
@@ -66,7 +81,12 @@ module PuppetForge
66
81
  uri_path = "v3/#{resource}/#{item}"
67
82
  end
68
83
 
69
- PuppetForge::V3::Base.conn.get uri_path, params
84
+ # The API expects a space separated string. This allows the user to invoke it with a more natural feeling array.
85
+ params['endorsements'] = params['endorsements'].join(' ') if params['endorsements'].is_a? Array
86
+
87
+ result = PuppetForge::V3::Base.conn.get uri_path, params
88
+ lru_cache.put(cache_key, result)
89
+ result
70
90
  end
71
91
 
72
92
  # @private
@@ -11,6 +11,11 @@ module PuppetForge
11
11
  lazy :current_release, 'Release'
12
12
  lazy_collection :releases, 'Release'
13
13
 
14
+ def self.find(slug)
15
+ super
16
+ rescue Faraday::ResourceNotFound
17
+ raise PuppetForge::ModuleNotFound, "Module #{slug} not found"
18
+ end
14
19
  end
15
20
  end
16
21
  end
@@ -2,6 +2,7 @@ require 'puppet_forge/v3/base'
2
2
  require 'puppet_forge/v3/module'
3
3
 
4
4
  require 'digest'
5
+ require 'base64'
5
6
 
6
7
  module PuppetForge
7
8
  module V3
@@ -38,6 +39,38 @@ module PuppetForge
38
39
  end
39
40
  end
40
41
 
42
+ # Uploads the tarbarll to the forge
43
+ #
44
+ # @param path [Pathname] tarball file path
45
+ # @return resp
46
+ def self.upload(path)
47
+ # We want to make sure that the file exists before trying to upload it
48
+ raise PuppetForge::FileNotFound, "The file '#{path}' does not exist." unless File.file?(path)
49
+
50
+ file = File.open(path, 'rb')
51
+ encoded_string = Base64.encode64(file.read)
52
+ data = { file: encoded_string }
53
+
54
+ resp = conn.post do |req|
55
+ req.url '/v3/releases'
56
+ req.headers['Content-Type'] = 'application/json'
57
+ req.body = data.to_json
58
+ end
59
+
60
+ [self, resp]
61
+ rescue Faraday::ClientError => e
62
+ if e.response
63
+ case e.response[:status]
64
+ when 403
65
+ raise PuppetForge::ReleaseForbidden.from_response(e.response)
66
+ when 400
67
+ raise PuppetForge::ReleaseBadContent.from_response(e.response)
68
+ end
69
+ end
70
+
71
+ raise e
72
+ end
73
+
41
74
  # Verify that a downloaded module matches the best available checksum in the metadata for this release,
42
75
  # validates SHA-256 checksum if available, otherwise validates MD5 checksum
43
76
  #
@@ -1,3 +1,3 @@
1
1
  module PuppetForge
2
- VERSION = '4.0.0' # Library version
2
+ VERSION = '5.0.0' # Library version
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -12,7 +12,10 @@ require 'puppet_forge'
12
12
 
13
13
  module StubbingFaraday
14
14
 
15
- def stub_api_for(klass, base_url = "http://api.example.com")
15
+ def stub_api_for(klass, base_url = "http://api.example.com", lru_cache: false)
16
+ unless lru_cache # Disable LRU cache by default
17
+ allow(klass).to receive(:lru_cache).and_return(instance_double('PuppetForge::LruCache', get: nil, put: nil, clear: nil))
18
+ end
16
19
  allow(klass).to receive(:conn) do
17
20
  Faraday.new :url => base_url do |builder|
18
21
  builder.response(:json, :content_type => /\bjson$/, :parser_options => { :symbolize_names => true })
@@ -38,7 +41,7 @@ module StubbingFaraday
38
41
  [ 404 ].tap do |response|
39
42
  local = File.join(PROJECT_ROOT, 'spec', 'fixtures', xplatform_path)
40
43
 
41
- if File.exists?("#{local}.headers") && File.exists?("#{local}.json")
44
+ if File.exist?("#{local}.headers") && File.exist?("#{local}.json")
42
45
  File.open("#{local}.headers") do |file|
43
46
  response[0] = file.readline[/\d{3}/].to_i
44
47
  response[1] = headers = {}
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetForge::LruCache do
4
+ it 'creates a cache key from a list of strings' do
5
+ expect { subject.class.new_key('foo', 'bar', 'baz') }.not_to raise_error
6
+ end
7
+
8
+ it 'creates a new instance' do
9
+ expect { PuppetForge::LruCache.new(1) }.not_to raise_error
10
+ end
11
+
12
+ it 'raises an error if max_size is not a positive integer' do
13
+ expect { PuppetForge::LruCache.new(-1) }.to raise_error(ArgumentError)
14
+ expect { PuppetForge::LruCache.new(0) }.to raise_error(ArgumentError)
15
+ expect { PuppetForge::LruCache.new(1.5) }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'defaults to a max_size of 30' do
19
+ expect(PuppetForge::LruCache.new.max_size).to eq(30)
20
+ end
21
+
22
+ it 'allows max_size to be set via the max_size parameter' do
23
+ expect(PuppetForge::LruCache.new(42).max_size).to eq(42)
24
+ end
25
+
26
+ it 'provides a #get method' do
27
+ expect(PuppetForge::LruCache.new).to respond_to(:get)
28
+ end
29
+
30
+ it 'provides a #put method' do
31
+ expect(PuppetForge::LruCache.new).to respond_to(:put)
32
+ end
33
+
34
+ it 'provides a #clear method' do
35
+ expect(PuppetForge::LruCache.new).to respond_to(:clear)
36
+ end
37
+
38
+ context 'with environment variables' do
39
+ around(:each) do |example|
40
+ @old_max_size = ENV['PUPPET_FORGE_MAX_CACHE_SIZE']
41
+ ENV['PUPPET_FORGE_MAX_CACHE_SIZE'] = '42'
42
+ example.run
43
+ ENV['PUPPET_FORGE_MAX_CACHE_SIZE'] = @old_max_size
44
+ end
45
+
46
+ it 'uses the value of the PUPPET_FORGE_MAX_CACHE_SIZE environment variable if present' do
47
+ expect(PuppetForge::LruCache.new.max_size).to eq(42)
48
+ end
49
+ end
50
+
51
+ context '#get' do
52
+ it 'returns nil if the key is not present in the cache' do
53
+ expect(PuppetForge::LruCache.new.get('foo')).to be_nil
54
+ end
55
+
56
+ it 'returns the cached value for the given key' do
57
+ cache = PuppetForge::LruCache.new
58
+ cache.put('foo', 'bar')
59
+ expect(cache.get('foo')).to eq('bar')
60
+ end
61
+
62
+ it 'moves the key to the front of the LRU list' do
63
+ cache = PuppetForge::LruCache.new
64
+ cache.put('foo', 'bar')
65
+ cache.put('baz', 'qux')
66
+ cache.get('foo')
67
+ expect(cache.send(:lru)).to eq(['foo', 'baz'])
68
+ end
69
+
70
+ # The below test is non-deterministic but I'm not sure how to unit
71
+ # test thread-safety.
72
+ # it 'is thread-safe' do
73
+ # cache = PuppetForge::LruCache.new
74
+ # cache.put('foo', 'bar')
75
+ # cache.put('baz', 'qux')
76
+ # threads = []
77
+ # threads << Thread.new { 100.times { cache.get('foo') } }
78
+ # threads << Thread.new { 100.times { cache.get('baz') } }
79
+ # threads.each(&:join)
80
+ # expect(cache.send(:lru)).to eq(['baz', 'foo'])
81
+ # end
82
+ end
83
+
84
+ context '#put' do
85
+ it 'adds the key to the front of the LRU list' do
86
+ cache = PuppetForge::LruCache.new
87
+ cache.put('foo', 'bar')
88
+ expect(cache.send(:lru)).to eq(['foo'])
89
+ end
90
+
91
+ it 'adds the value to the cache' do
92
+ cache = PuppetForge::LruCache.new
93
+ cache.put('foo', 'bar')
94
+ expect(cache.send(:cache)).to eq({ 'foo' => 'bar' })
95
+ end
96
+
97
+ it 'removes the least recently used item if the cache is full' do
98
+ cache = PuppetForge::LruCache.new(2)
99
+ cache.put('foo', 'bar')
100
+ cache.put('baz', 'qux')
101
+ cache.put('quux', 'corge')
102
+ expect(cache.send(:lru)).to eq(['quux', 'baz'])
103
+ end
104
+
105
+ # The below test is non-deterministic but I'm not sure how to unit
106
+ # test thread-safety.
107
+ # it 'is thread-safe' do
108
+ # cache = PuppetForge::LruCache.new
109
+ # threads = []
110
+ # threads << Thread.new { 100.times { cache.put('foo', 'bar') } }
111
+ # threads << Thread.new { 100.times { cache.put('baz', 'qux') } }
112
+ # threads.each(&:join)
113
+ # expect(cache.send(:lru)).to eq(['baz', 'foo'])
114
+ # end
115
+ end
116
+
117
+ context '#clear' do
118
+ it 'clears the cache' do
119
+ cache = PuppetForge::LruCache.new
120
+ cache.put('foo', 'bar')
121
+ cache.put('baz', 'qux')
122
+ cache.clear
123
+ expect(cache.send(:lru).empty?).to be_truthy
124
+ expect(cache.send(:cache).empty?).to be_truthy
125
+ end
126
+ end
127
+ end
@@ -61,6 +61,7 @@ describe PuppetForge::V3::Base do
61
61
  describe 'the host url setting' do
62
62
  context 'without a path prefix' do
63
63
  before(:each) do
64
+ PuppetForge::V3::Base.lru_cache.clear # We test the cache later, so clear it now
64
65
  @orig_host = PuppetForge.host
65
66
  PuppetForge.host = 'https://api.example.com'
66
67
 
@@ -83,10 +84,25 @@ describe PuppetForge::V3::Base do
83
84
  base = PuppetForge::V3::Base.find 'puppet'
84
85
  expect(base.username).to eq('foo')
85
86
  end
87
+
88
+ it 'caches responses' do
89
+ stub_api_for(PuppetForge::V3::Base, lru_cache: true) do |stubs|
90
+ stub_fixture(stubs, :get, '/v3/bases/puppet')
91
+ end
92
+ allow(PuppetForge::V3::Base.lru_cache).to receive(:put).and_call_original
93
+ allow(PuppetForge::V3::Base.lru_cache).to receive(:get).and_call_original
94
+
95
+ PuppetForge::V3::Base.find 'puppet'
96
+ PuppetForge::V3::Base.find 'puppet'
97
+ PuppetForge::V3::Base.find 'puppet'
98
+ expect(PuppetForge::V3::Base.lru_cache).to have_received(:put).once
99
+ expect(PuppetForge::V3::Base.lru_cache).to have_received(:get).exactly(3).times
100
+ end
86
101
  end
87
102
 
88
103
  context 'with a path prefix' do
89
104
  before(:each) do
105
+ PuppetForge::V3::Base.lru_cache.clear # We test the cache later, so clear it now
90
106
  @orig_host = PuppetForge.host
91
107
  PuppetForge.host = 'https://api.example.com/uri/prefix'
92
108
 
@@ -109,6 +125,20 @@ describe PuppetForge::V3::Base do
109
125
  base = PuppetForge::V3::Base.find 'puppet'
110
126
  expect(base.username).to eq('bar')
111
127
  end
128
+
129
+ it 'caches responses' do
130
+ stub_api_for(PuppetForge::V3::Base, PuppetForge.host, lru_cache: true) do |stubs|
131
+ stub_fixture(stubs, :get, '/uri/prefix/v3/bases/puppet')
132
+ end
133
+ allow(PuppetForge::V3::Base.lru_cache).to receive(:put).and_call_original
134
+ allow(PuppetForge::V3::Base.lru_cache).to receive(:get).and_call_original
135
+
136
+ PuppetForge::V3::Base.find 'puppet'
137
+ PuppetForge::V3::Base.find 'puppet'
138
+ PuppetForge::V3::Base.find 'puppet'
139
+ expect(PuppetForge::V3::Base.lru_cache).to have_received(:put).once
140
+ expect(PuppetForge::V3::Base.lru_cache).to have_received(:get).exactly(3).times
141
+ end
112
142
  end
113
143
  end
114
144
  end
@@ -28,8 +28,8 @@ describe PuppetForge::V3::Module do
28
28
  expect(mod_stateless.name).to eq('apache')
29
29
  end
30
30
 
31
- it 'returns nil for non-existent modules' do
32
- expect { missing_mod }.to raise_error(Faraday::ResourceNotFound)
31
+ it 'raises exception for non-existent modules' do
32
+ expect { missing_mod }.to raise_error(PuppetForge::ModuleNotFound, 'Module absent-apache not found')
33
33
  end
34
34
  end
35
35
 
@@ -218,5 +218,43 @@ describe PuppetForge::V3::Release do
218
218
  expect(release.created_at).to_not be nil
219
219
  end
220
220
  end
221
+
222
+ describe '#upload' do
223
+ let(:tarball) { "#{PROJECT_ROOT}/spec/tmp/module.tgz" }
224
+ let(:file_object) { double('file', read: 'file contents') }
225
+
226
+ let(:release) { PuppetForge::V3::Release.upload(tarball) }
227
+ let(:mock_conn) { instance_double('PuppetForge::V3::Connection', url_prefix: PuppetForge.host) }
228
+
229
+ context 'when there is no auth token provided' do
230
+ it 'raises PuppetForge::ReleaseForbidden' do
231
+ allow(File).to receive(:file?).and_return(true)
232
+ allow(File).to receive(:open).and_return(file_object)
233
+ allow(described_class).to receive(:conn).and_return(mock_conn)
234
+
235
+ response = { status: 403, body: { 'message' => 'Forbidden' }.to_json }
236
+ expect(mock_conn).to receive(:post).and_raise(Faraday::ClientError.new('Forbidden', response))
237
+ expect { release }.to raise_error(PuppetForge::ReleaseForbidden)
238
+ end
239
+ end
240
+
241
+ context 'when the module is not valid' do
242
+ it 'raises PuppetForge::ReleaseBadRequest' do
243
+ allow(File).to receive(:file?).and_return(true)
244
+ allow(File).to receive(:open).and_return(file_object)
245
+ allow(described_class).to receive(:conn).and_return(mock_conn)
246
+
247
+ response = { status: 400, body: { message: 'Bad Content' }.to_json }
248
+ expect(mock_conn).to receive(:post).and_raise(Faraday::ClientError.new('400', response))
249
+ expect { release }.to raise_error(PuppetForge::ReleaseBadContent)
250
+ end
251
+ end
252
+
253
+ context 'when the tarball does not exist' do
254
+ it 'raises PuppetForge::FileNotFound' do
255
+ expect { PuppetForge::V3::Release.upload(tarball) }.to raise_error(PuppetForge::FileNotFound)
256
+ end
257
+ end
258
+ end
221
259
  end
222
260
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet Labs
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-01 00:00:00.000000000 Z
11
+ date: 2023-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -187,6 +187,7 @@ files:
187
187
  - lib/puppet_forge/error.rb
188
188
  - lib/puppet_forge/lazy_accessors.rb
189
189
  - lib/puppet_forge/lazy_relations.rb
190
+ - lib/puppet_forge/lru_cache.rb
190
191
  - lib/puppet_forge/tar.rb
191
192
  - lib/puppet_forge/tar/mini.rb
192
193
  - lib/puppet_forge/unpacker.rb
@@ -236,6 +237,7 @@ files:
236
237
  - spec/unit/forge/connection_spec.rb
237
238
  - spec/unit/forge/lazy_accessors_spec.rb
238
239
  - spec/unit/forge/lazy_relations_spec.rb
240
+ - spec/unit/forge/lru_cache_spec.rb
239
241
  - spec/unit/forge/tar/mini_spec.rb
240
242
  - spec/unit/forge/tar_spec.rb
241
243
  - spec/unit/forge/unpacker_spec.rb
@@ -251,7 +253,7 @@ homepage: https://github.com/puppetlabs/forge-ruby
251
253
  licenses:
252
254
  - Apache-2.0
253
255
  metadata: {}
254
- post_install_message:
256
+ post_install_message:
255
257
  rdoc_options: []
256
258
  require_paths:
257
259
  - lib
@@ -267,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
269
  version: '0'
268
270
  requirements: []
269
271
  rubygems_version: 3.1.6
270
- signing_key:
272
+ signing_key:
271
273
  specification_version: 4
272
274
  summary: Access the Puppet Forge API from Ruby for resource information and to download
273
275
  releases.
@@ -308,6 +310,7 @@ test_files:
308
310
  - spec/unit/forge/connection_spec.rb
309
311
  - spec/unit/forge/lazy_accessors_spec.rb
310
312
  - spec/unit/forge/lazy_relations_spec.rb
313
+ - spec/unit/forge/lru_cache_spec.rb
311
314
  - spec/unit/forge/tar/mini_spec.rb
312
315
  - spec/unit/forge/tar_spec.rb
313
316
  - spec/unit/forge/unpacker_spec.rb