puppet_forge 4.0.0 → 5.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: 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