fog-aws 3.33.2 → 3.33.4

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: ef6d261effc88541b56e982050d0003a3ed126e5380a3928f3a622eea66633b9
4
- data.tar.gz: 0ba63812241e5c5694e62271c22bf9e654db0ae96f8b481ddd18c3d6731aa295
3
+ metadata.gz: 5e72621a4d0466e68fac0029075cc8183e520b2ddf4cca61438061640545cbc7
4
+ data.tar.gz: e74de695ac7fc458f8d0b53862c967b34771b91871e962e607559a774de16fb0
5
5
  SHA512:
6
- metadata.gz: 0dab6002b97b2a17003ea277b62572ea04583f8dd109c77ef4dc8db17cec0d5b97e69fe475bba60791f08ee4ffb0bd14283880d3989f3a70bde4f6b5581ff52b
7
- data.tar.gz: cc0419e84ba54e19b5e4909f94e2133816e40ffd3fac80ec96257c1046071a331b777e11a184d5a88abe84dc1a348f3f9aecc9527f90b047d346e3e631a072be
6
+ metadata.gz: 15639793281c3325753b5e4bccedf7243228d636150a3449a3dedf09cff79803e3414955715c55b2572e19f628e4e831e85ab0ace7265a1d225bbfb6d5b56148
7
+ data.tar.gz: 44676104a31d040262c20eb86e9702fb16ed75b9e4f260864c9e3ed284bffe69ed4372b0cc72486bf5df1a54c38cb64468c6060e0839b9f22b8f37f3cfd04efe
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.33.4](https://github.com/fog/fog-aws/tree/v3.33.4) (2026-09-28)
4
+
5
+ [Full Changelog](https://github.com/fog/fog-aws/compare/v3.33.0...v3.33.1)
6
+
7
+ - drop codeclimate test reporter, which forced ancient simplecov and broke ci (geemus)
8
+
9
+ **Merged pull requests:**
10
+
11
+ - let connection set concurrency for multipart copy \#761 (mshibuya)
12
+
13
+ - bump rubyzip \#762 (dependabot)
14
+
15
+
16
+ ## [v3.33.3](https://github.com/fog/fog-aws/tree/v3.33.3) (2026-08-28)
17
+
18
+ [Full Changelog](https://github.com/fog/fog-aws/compare/v3.33.0...v3.33.1)
19
+
20
+ **Merged pull requests:**
21
+
22
+ - fix thread safety of shared Fog::Storage connections [\#759](https://github.com/fog/fog-aws/pull/759) ([stanhu](https://github.com/stanhu))
23
+
24
+ - fix flaky describe_spot_price_history [\#760](https://github.com/fog/fog-aws/pull/760) ([stanhu](https://github.com/stanhu))
25
+
26
+ - Bump actions/checkout from 6 to 7 [\#758](https://github.com/fog/fog-aws/pull/758) ([dependabot](https://github.com/apps/dependabot))
27
+
28
+ - Bump actions/dependency-review-action from 4 to 5 [\#757](https://github.com/fog/fog-aws/pull/757) ([dependabot](https://github.com/apps/dependabot))
29
+
30
+ - Bump fog/.github/.github/workflows/ci.yml from 1.5.0 to 1.6.0 [\#756](https://github.com/fog/fog-aws/pull/756) ([dependabot](https://github.com/apps/dependabot))
31
+
3
32
  ## [v3.33.2](https://github.com/fog/fog-aws/tree/v3.33.2) (2026-04-20)
4
33
 
5
34
  [Full Changelog](https://github.com/fog/fog-aws/compare/v3.33.0...v3.33.1)
data/fog-aws.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'bundler'
27
27
  spec.add_development_dependency 'github_changelog_generator', '~> 1.16'
28
28
  spec.add_development_dependency 'rake', '>= 12.3.3'
29
- spec.add_development_dependency 'rubyzip', '~> 3.0.0'
29
+ spec.add_development_dependency 'rubyzip', '~> 3.6.0'
30
30
  spec.add_development_dependency 'shindo', '~> 0.3'
31
31
 
32
32
  spec.add_dependency 'base64', '>= 0.2', '< 0.4'
@@ -80,7 +80,7 @@ module Fog
80
80
  end
81
81
 
82
82
  def concurrency
83
- @concurrency || 1
83
+ @concurrency || service.copy_concurrency
84
84
  end
85
85
 
86
86
  def acl
@@ -86,7 +86,9 @@ module Fog
86
86
  end
87
87
  zones = all_zones if zones.nil?
88
88
 
89
- max_results = params['MaxResults'] || Fog::Mock.random_numbers(3).to_i
89
+ # Fog::Mock.random_numbers(3) can return "000", which .to_i turns
90
+ # into 0 and then trips the validation below, so guarantee at least 1.
91
+ max_results = params['MaxResults'] || [Fog::Mock.random_numbers(3).to_i, 1].max
90
92
  if !(max_results.is_a?(Integer) && max_results > 0)
91
93
  max_results_error = "InvalidParameterValue => Invalid value '#{max_results}' for maxResults"
92
94
  raise Fog::AWS::Compute::Error, max_results_error
@@ -25,15 +25,18 @@ module Fog
25
25
  def post_object_hidden_fields(options = {})
26
26
  options = options.dup
27
27
  if policy = options['policy']
28
+ # Grab the correlated credential state once so the credential,
29
+ # security token, and signature all come from the same snapshot.
30
+ credentials = @credentials
28
31
  date = Fog::Time.now
29
- credential = "#{@aws_access_key_id}/#{@signer.credential_scope(date)}"
32
+ credential = "#{credentials.aws_access_key_id}/#{credentials.signer.credential_scope(date)}"
30
33
  extra_conditions = [
31
34
  {'x-amz-date' => date.to_iso8601_basic},
32
35
  {'x-amz-credential' => credential},
33
36
  {'x-amz-algorithm' => Fog::AWS::SignatureV4::ALGORITHM}
34
37
  ]
35
38
 
36
- extra_conditions << {'x-amz-security-token' => @aws_session_token } if @aws_session_token
39
+ extra_conditions << {'x-amz-security-token' => credentials.aws_session_token } if credentials.aws_session_token
37
40
 
38
41
  policy_with_auth_fields = policy.merge('conditions' => policy['conditions'] + extra_conditions)
39
42
 
@@ -41,10 +44,10 @@ module Fog
41
44
  options['X-Amz-Credential'] = credential
42
45
  options['X-Amz-Date'] = date.to_iso8601_basic
43
46
  options['X-Amz-Algorithm'] = Fog::AWS::SignatureV4::ALGORITHM
44
- if @aws_session_token
45
- options['X-Amz-Security-Token'] = @aws_session_token
47
+ if credentials.aws_session_token
48
+ options['X-Amz-Security-Token'] = credentials.aws_session_token
46
49
  end
47
- options['X-Amz-Signature'] = @signer.derived_hmac(date).sign(options['policy']).unpack('H*').first
50
+ options['X-Amz-Signature'] = credentials.signer.derived_hmac(date).sign(options['policy']).unpack('H*').first
48
51
  end
49
52
  options
50
53
  end
@@ -57,7 +57,7 @@ module Fog
57
57
  ]
58
58
 
59
59
  requires :aws_access_key_id, :aws_secret_access_key
60
- recognizes :endpoint, :region, :host, :port, :scheme, :persistent, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at, :path_style, :acceleration, :instrumentor, :instrumentor_name, :aws_signature_version, :enable_signature_v4_streaming, :virtual_host, :cname, :max_put_chunk_size, :max_copy_chunk_size, :aws_credentials_refresh_threshold_seconds, :disable_content_md5_validation, :sts_endpoint
60
+ recognizes :endpoint, :region, :host, :port, :scheme, :persistent, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at, :path_style, :acceleration, :instrumentor, :instrumentor_name, :aws_signature_version, :enable_signature_v4_streaming, :virtual_host, :cname, :max_put_chunk_size, :max_copy_chunk_size, :copy_concurrency, :aws_credentials_refresh_threshold_seconds, :disable_content_md5_validation, :sts_endpoint
61
61
 
62
62
  secrets :aws_secret_access_key, :hmac
63
63
 
@@ -129,6 +129,32 @@ module Fog
129
129
  request :upload_part
130
130
  request :upload_part_copy
131
131
 
132
+ # Immutable snapshot of the credential-derived state needed to sign a
133
+ # request: the access key id, secret, session token, and the matching
134
+ # signer (v4) or HMAC (v2). These values are correlated. Under
135
+ # +use_iam_profile+ they are all replaced together whenever credentials
136
+ # are refreshed.
137
+ #
138
+ # Bundling them into one frozen object stored in a single instance
139
+ # variable lets a caller read a consistent set with one atomic
140
+ # reference read (see Real#request). Reading the fields as separate
141
+ # instance variables lets a refresh land between two reads and pair, for
142
+ # example, an old session token with a new signer, which S3 rejects with
143
+ # a 403. Keeping this connection safe to share across threads depends on
144
+ # never mutating an instance; build a new one and swap the reference.
145
+ class Credentials
146
+ attr_reader :aws_access_key_id, :aws_secret_access_key, :aws_session_token, :signer, :hmac
147
+
148
+ def initialize(aws_access_key_id: nil, aws_secret_access_key: nil, aws_session_token: nil, signer: nil, hmac: nil)
149
+ @aws_access_key_id = aws_access_key_id
150
+ @aws_secret_access_key = aws_secret_access_key
151
+ @aws_session_token = aws_session_token
152
+ @signer = signer
153
+ @hmac = hmac
154
+ freeze
155
+ end
156
+ end
157
+
132
158
  module Utils
133
159
  attr_accessor :region
134
160
  attr_accessor :disable_content_md5_validation
@@ -144,10 +170,16 @@ module Fog
144
170
  attr_reader :max_put_chunk_size
145
171
  attr_reader :max_copy_chunk_size
146
172
 
173
+ # Number of threads a multipart copy uses, unless the file sets its own.
174
+ #
175
+ # @return [Integer]
176
+ attr_reader :copy_concurrency
177
+
147
178
  def cdn
179
+ credentials = @credentials
148
180
  @cdn ||= Fog::AWS::CDN.new(
149
- :aws_access_key_id => @aws_access_key_id,
150
- :aws_secret_access_key => @aws_secret_access_key,
181
+ :aws_access_key_id => credentials.aws_access_key_id,
182
+ :aws_secret_access_key => credentials.aws_secret_access_key,
151
183
  :use_iam_profile => @use_iam_profile
152
184
  )
153
185
  end
@@ -222,7 +254,16 @@ module Fog
222
254
  validate_chunk_size(@max_copy_chunk_size, 'max_copy_chunk_size')
223
255
  end
224
256
 
257
+ def init_copy_concurrency!(options = {})
258
+ @copy_concurrency = options.fetch(:copy_concurrency, 1).to_i
259
+ raise ArgumentError.new('minimum copy_concurrency is 1') if @copy_concurrency < 1
260
+ end
261
+
225
262
  def v4_signed_params_for_url(params, expires)
263
+ # Read the correlated credential state once, as a single reference,
264
+ # so a concurrent refresh cannot pair a stale session token with a
265
+ # freshly rebuilt signer.
266
+ credentials = @credentials
226
267
  now = Fog::Time.now
227
268
 
228
269
  expires = expires - now.to_i
@@ -232,35 +273,36 @@ module Fog
232
273
  params[:query]['X-Amz-Expires'] = expires
233
274
  params[:query]['X-Amz-Date'] = now.to_iso8601_basic
234
275
 
235
- if @aws_session_token
236
- params[:query]['X-Amz-Security-Token'] = @aws_session_token
276
+ if credentials.aws_session_token
277
+ params[:query]['X-Amz-Security-Token'] = credentials.aws_session_token
237
278
  end
238
279
 
239
280
  params = request_params(params)
240
281
  params[:headers][:host] = params[:host]
241
282
  params[:headers][:host] += ":#{params[:port]}" if params.fetch(:port, nil)
242
283
 
243
- signature_query_params = @signer.signature_parameters(params, now, "UNSIGNED-PAYLOAD")
284
+ signature_query_params = credentials.signer.signature_parameters(params, now, "UNSIGNED-PAYLOAD")
244
285
  params[:query] = (params[:query] || {}).merge(signature_query_params)
245
286
  params
246
287
  end
247
288
 
248
289
  def v2_signed_params_for_url(params, expires)
249
- if @aws_session_token
290
+ credentials = @credentials
291
+ if credentials.aws_session_token
250
292
  params[:headers]||= {}
251
- params[:headers]['x-amz-security-token'] = @aws_session_token
293
+ params[:headers]['x-amz-security-token'] = credentials.aws_session_token
252
294
  end
253
- signature = signature_v2(params, expires)
295
+ signature = signature_v2(params, expires, credentials.hmac)
254
296
 
255
297
  params = request_params(params)
256
298
 
257
299
  signature_query_params = {
258
- 'AWSAccessKeyId' => @aws_access_key_id,
300
+ 'AWSAccessKeyId' => credentials.aws_access_key_id,
259
301
  'Signature' => signature,
260
302
  'Expires' => expires,
261
303
  }
262
304
  params[:query] = (params[:query] || {}).merge(signature_query_params)
263
- params[:query]['x-amz-security-token'] = @aws_session_token if @aws_session_token
305
+ params[:query]['x-amz-security-token'] = credentials.aws_session_token if credentials.aws_session_token
264
306
  params
265
307
  end
266
308
 
@@ -502,6 +544,7 @@ module Fog
502
544
 
503
545
  init_max_put_chunk_size!(options)
504
546
  init_max_copy_chunk_size!(options)
547
+ init_copy_concurrency!(options)
505
548
 
506
549
  @disable_content_md5_validation = options[:disable_content_md5_validation] || false
507
550
 
@@ -511,25 +554,32 @@ module Fog
511
554
  end
512
555
 
513
556
  def data
514
- self.class.data[@region][@aws_access_key_id]
557
+ self.class.data[@region][@credentials.aws_access_key_id]
515
558
  end
516
559
 
517
560
  def reset_data
518
- self.class.data[@region].delete(@aws_access_key_id)
561
+ self.class.data[@region].delete(@credentials.aws_access_key_id)
519
562
  end
520
563
 
521
564
  def setup_credentials(options)
522
565
  @aws_credentials_refresh_threshold_seconds = options[:aws_credentials_refresh_threshold_seconds]
523
-
524
- @aws_access_key_id = options[:aws_access_key_id]
525
- @aws_secret_access_key = options[:aws_secret_access_key]
526
- @aws_session_token = options[:aws_session_token]
527
566
  @aws_credentials_expire_at = options[:aws_credentials_expire_at]
528
567
 
529
- @signer = Fog::AWS::SignatureV4.new( @aws_access_key_id, @aws_secret_access_key, @region, 's3')
568
+ aws_access_key_id = options[:aws_access_key_id]
569
+ aws_secret_access_key = options[:aws_secret_access_key]
570
+ signer = Fog::AWS::SignatureV4.new(aws_access_key_id, aws_secret_access_key, @region, 's3')
571
+
572
+ # Swap the whole credential set as one reference so concurrent
573
+ # readers always observe a complete, self-consistent snapshot.
574
+ @credentials = Credentials.new(
575
+ :aws_access_key_id => aws_access_key_id,
576
+ :aws_secret_access_key => aws_secret_access_key,
577
+ :aws_session_token => options[:aws_session_token],
578
+ :signer => signer
579
+ )
530
580
  end
531
581
 
532
- def signature_v2(params, expires)
582
+ def signature_v2(params, expires, hmac = nil)
533
583
  'foo'
534
584
  end
535
585
 
@@ -573,6 +623,7 @@ module Fog
573
623
 
574
624
  init_max_put_chunk_size!(options)
575
625
  init_max_copy_chunk_size!(options)
626
+ init_copy_concurrency!(options)
576
627
 
577
628
  @disable_content_md5_validation = options[:disable_content_md5_validation] || false
578
629
 
@@ -612,16 +663,30 @@ module Fog
612
663
  @aws_credentials_refresh_threshold_seconds = options[:aws_credentials_refresh_threshold_seconds]
613
664
  end
614
665
 
615
- @aws_access_key_id = options[:aws_access_key_id]
616
- @aws_secret_access_key = options[:aws_secret_access_key]
617
- @aws_session_token = options[:aws_session_token]
618
666
  @aws_credentials_expire_at = options[:aws_credentials_expire_at]
619
667
 
668
+ aws_access_key_id = options[:aws_access_key_id]
669
+ aws_secret_access_key = options[:aws_secret_access_key]
670
+
671
+ signer = hmac = nil
620
672
  if @signature_version == 4
621
- @signer = Fog::AWS::SignatureV4.new(@aws_access_key_id, @aws_secret_access_key, @region, 's3')
673
+ signer = Fog::AWS::SignatureV4.new(aws_access_key_id, aws_secret_access_key, @region, 's3')
622
674
  elsif @signature_version == 2
623
- @hmac = Fog::HMAC.new('sha1', @aws_secret_access_key)
624
- end
675
+ hmac = Fog::HMAC.new('sha1', aws_secret_access_key)
676
+ end
677
+
678
+ # Publish the new credentials as a single frozen reference. This is
679
+ # the only write to the correlated credential state, so a reader
680
+ # that grabs @credentials once (see #request) can never observe a
681
+ # torn mix of old and new values, even while a refresh is running
682
+ # on another thread.
683
+ @credentials = Credentials.new(
684
+ :aws_access_key_id => aws_access_key_id,
685
+ :aws_secret_access_key => aws_secret_access_key,
686
+ :aws_session_token => options[:aws_session_token],
687
+ :signer => signer,
688
+ :hmac => hmac
689
+ )
625
690
  end
626
691
 
627
692
  def connection(scheme, host, port)
@@ -641,18 +706,33 @@ module Fog
641
706
  def request(params, &block)
642
707
  refresh_credentials_if_expired
643
708
 
709
+ # Read the correlated credential state exactly once, into a local, so
710
+ # a concurrent refresh cannot swap the signer out from under the
711
+ # session token partway through this request. See the Credentials
712
+ # class for the full rationale.
713
+ perform_request(params, @credentials, &block)
714
+ end
715
+
716
+ # Signs and dispatches a single request using the given credential
717
+ # snapshot. Splitting this out of #request lets the redirect retry in
718
+ # #_request reuse one consistent snapshot (with a signer rebuilt for
719
+ # the new region) instead of re-reading, and keeps #request's public
720
+ # signature unchanged.
721
+ def perform_request(params, credentials, &block)
722
+ signer = credentials.signer
723
+
644
724
  date = Fog::Time.now
645
725
 
646
726
  params = params.dup
647
727
  stringify_query_keys(params)
648
728
  params[:headers] = (params[:headers] || {}).dup
649
729
 
650
- params[:headers]['x-amz-security-token'] = @aws_session_token if @aws_session_token
730
+ params[:headers]['x-amz-security-token'] = credentials.aws_session_token if credentials.aws_session_token
651
731
 
652
732
  if @signature_version == 2
653
733
  expires = date.to_date_header
654
734
  params[:headers]['Date'] = expires
655
- params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature_v2(params, expires)}"
735
+ params[:headers]['Authorization'] = "AWS #{credentials.aws_access_key_id}:#{signature_v2(params, expires, credentials.hmac)}"
656
736
  end
657
737
 
658
738
  params = request_params(params)
@@ -680,12 +760,12 @@ module Fog
680
760
  else
681
761
  params[:headers]['x-amz-content-sha256'] ||= OpenSSL::Digest::SHA256.hexdigest(params[:body] || '')
682
762
  end
683
- signature_components = @signer.signature_components(params, date, params[:headers]['x-amz-content-sha256'])
684
- params[:headers]['Authorization'] = @signer.components_to_header(signature_components)
763
+ signature_components = signer.signature_components(params, date, params[:headers]['x-amz-content-sha256'])
764
+ params[:headers]['Authorization'] = signer.components_to_header(signature_components)
685
765
 
686
766
  if params[:body].respond_to?(:read) && @enable_signature_v4_streaming
687
767
  body = params.delete :body
688
- params[:request_block] = S3Streamer.new(body, signature_components['X-Amz-Signature'], @signer, date)
768
+ params[:request_block] = S3Streamer.new(body, signature_components['X-Amz-Signature'], signer, date)
689
769
  end
690
770
  end
691
771
  # FIXME: ToHashParser should make this not needed
@@ -713,23 +793,32 @@ module Fog
713
793
  new_params[:bucket_name] = %r{<Bucket>([^<]*)</Bucket>}.match(body).captures.first
714
794
  new_params[:host] = %r{<Endpoint>([^<]*)</Endpoint>}.match(body).captures.first
715
795
  # some errors provide it directly
716
- @new_region = %r{<Region>([^<]*)</Region>}.match(body) ? Regexp.last_match.captures.first : nil
796
+ new_region = %r{<Region>([^<]*)</Region>}.match(body) ? Regexp.last_match.captures.first : nil
717
797
  end
718
798
  Fog::Logger.warning("fog: followed redirect to #{host}, connecting to the matching region will be more performant")
719
- original_region, original_signer = @region, @signer
720
- @region = @new_region || case new_params[:host]
799
+ new_region ||= case new_params[:host]
721
800
  when /s3.amazonaws.com/, /s3-external-1.amazonaws.com/
722
801
  DEFAULT_REGION
723
802
  else
724
803
  %r{s3[\.\-]([^\.]*).amazonaws.com}.match(new_params[:host]).captures.first
725
804
  end
805
+ # Take a fresh, self-consistent snapshot and build a signer for the
806
+ # redirect region locally, then re-dispatch with it. We deliberately
807
+ # do not mutate @region/@signer: doing so would corrupt any
808
+ # concurrent request sharing this connection.
809
+ credentials = @credentials
726
810
  if @signature_version == 4
727
- @signer = Fog::AWS::SignatureV4.new(@aws_access_key_id, @aws_secret_access_key, @region, 's3')
811
+ redirect_signer = Fog::AWS::SignatureV4.new(credentials.aws_access_key_id, credentials.aws_secret_access_key, new_region, 's3')
812
+ credentials = Credentials.new(
813
+ :aws_access_key_id => credentials.aws_access_key_id,
814
+ :aws_secret_access_key => credentials.aws_secret_access_key,
815
+ :aws_session_token => credentials.aws_session_token,
816
+ :signer => redirect_signer,
817
+ :hmac => credentials.hmac
818
+ )
728
819
  original_params[:headers].delete('Authorization')
729
820
  end
730
- response = request(original_params.merge(new_params), &block)
731
- @region, @signer = original_region, original_signer
732
- response
821
+ perform_request(original_params.merge(new_params), credentials, &block)
733
822
  end
734
823
 
735
824
  # See http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
@@ -793,7 +882,7 @@ DATA
793
882
  end
794
883
  end
795
884
 
796
- def signature_v2(params, expires)
885
+ def signature_v2(params, expires, hmac = @credentials.hmac)
797
886
  headers = params[:headers] || {}
798
887
 
799
888
  string_to_sign =
@@ -844,7 +933,7 @@ DATA
844
933
  end
845
934
  canonical_resource << query_string
846
935
  string_to_sign << canonical_resource
847
- signed_string = @hmac.sign(string_to_sign)
936
+ signed_string = hmac.sign(string_to_sign)
848
937
  Base64.encode64(signed_string).chomp!
849
938
  end
850
939
 
@@ -1,5 +1,5 @@
1
1
  module Fog
2
2
  module AWS
3
- VERSION = '3.33.2'.freeze
3
+ VERSION = '3.33.4'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fog-aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.33.2
4
+ version: 3.33.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Lane
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 3.0.0
75
+ version: 3.6.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 3.0.0
82
+ version: 3.6.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: shindo
85
85
  requirement: !ruby/object:Gem::Requirement