dependabot-common 0.386.0 → 0.388.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: 4e4c26ec485eed8ebb4e35d9edfa4ab996658f450708439941ff8ca253ce0457
4
- data.tar.gz: '045951912dda5a814efcf305b11ae28e5c6cd97a1e7f72f1c610650d2f9031cb'
3
+ metadata.gz: 29f29c96dbb5e187d5cdecfe015ecd60456ea004a7553a97b812416034451ebf
4
+ data.tar.gz: 1d6e5d04baaf18853476f074e742eb8a60113e2fdfa457db4b3019b1055b30ea
5
5
  SHA512:
6
- metadata.gz: bb49cbf946335c722ca445b0c314909eb0cf1790b241b68461881873cf254afddb7b7b10a168879ded348af64bdb30f5093882c1221b8f3c7e3ca69bbf6bd59b
7
- data.tar.gz: ed0b8a8a019adeed81707ee2aae27e1e7e50d8f4c0da2d66cfca4a952df713173706d091062d02cb5a5aa4c18b819747b76ec5f114595fdbb804c4bef9df208b
6
+ metadata.gz: 2c2d09853e0044b7dd6ed6ca9f56501ee8436f113eecec3d85506d1543b79994cb3544cb5718b6f55960f7257af74848e33bc330d956275f98a2d8220f09d4d6
7
+ data.tar.gz: 343ab8ddca784ef0e497987de868f0b405aeeb3bfb8f11c92ad24da3b6d067981fde09397e4d6a5633e2556bfa527f594f432dc4ca9aa79cd9b2b556be930e52
@@ -0,0 +1,61 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sawyer"
5
+ require "sorbet-runtime"
6
+ require "time"
7
+
8
+ module Dependabot
9
+ module Clients
10
+ # Typed view over the release fields returned by Octokit.
11
+ class GithubRelease < T::ImmutableStruct
12
+ extend T::Sig
13
+
14
+ const :id, T.nilable(Integer)
15
+ const :name, T.nilable(String)
16
+ const :tag_name, String
17
+ const :body, T.nilable(String)
18
+ const :html_url, T.nilable(String)
19
+ const :prerelease, T::Boolean
20
+ const :published_at, T.nilable(Time)
21
+
22
+ sig { params(resource: Sawyer::Resource).returns(T.nilable(GithubRelease)) }
23
+ def self.from_resource(resource)
24
+ tag_name = T.cast(resource[:tag_name], Object)
25
+ return unless tag_name.is_a?(String)
26
+
27
+ id = T.cast(resource[:id], Object)
28
+ prerelease = T.cast(resource[:prerelease], Object)
29
+
30
+ new(
31
+ id: id.is_a?(Integer) ? id : nil,
32
+ name: string_value(resource, :name),
33
+ tag_name: tag_name,
34
+ body: string_value(resource, :body),
35
+ html_url: string_value(resource, :html_url),
36
+ prerelease: prerelease == true,
37
+ published_at: time_value(resource, :published_at)
38
+ )
39
+ end
40
+
41
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(String)) }
42
+ def self.string_value(resource, key)
43
+ value = T.cast(resource[key], Object)
44
+ value.is_a?(String) ? value : nil
45
+ end
46
+ private_class_method :string_value
47
+
48
+ sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(Time)) }
49
+ def self.time_value(resource, key)
50
+ value = T.cast(resource[key], Object)
51
+ return value if value.is_a?(Time)
52
+ return unless value.is_a?(String)
53
+
54
+ Time.parse(value)
55
+ rescue ArgumentError
56
+ nil
57
+ end
58
+ private_class_method :time_value
59
+ end
60
+ end
61
+ end
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -33,18 +33,23 @@ module Dependabot
33
33
  extend T::Sig
34
34
  extend T::Generic
35
35
 
36
- # The values of a requirement entry are heterogeneous and
37
- # ecosystem-specific, so this bridge class is necessarily untyped at
38
- # the Hash level; the typed readers below are the migration path.
39
- # rubocop:disable Sorbet/ForbidTUntyped
36
+ Group = T.type_alias { T.any(String, Symbol) }
37
+ ObjectHash = T.type_alias { T::Hash[T.any(Symbol, String), Object] }
38
+ Requirement = T.type_alias { T.any(String, Symbol) }
39
+ Input = T.type_alias { T.any(DependencyRequirement, ObjectHash) }
40
+
40
41
  K = type_member { { fixed: Symbol } }
42
+ # Hash-style access remains dynamic until ecosystem callers migrate to the
43
+ # typed readers. Keeping that compatibility here avoids a flag-day change.
44
+ # rubocop:disable Sorbet/ForbidTUntyped
41
45
  V = type_member { { fixed: T.untyped } }
42
46
  Elem = type_member { { fixed: [Symbol, T.untyped] } }
47
+ # rubocop:enable Sorbet/ForbidTUntyped
43
48
 
44
49
  # Builds a DependencyRequirement from a requirement hash, symbolising
45
50
  # top-level keys. Accepts both plain hashes and existing
46
51
  # DependencyRequirement instances and always returns a new instance.
47
- sig { params(hash: T::Hash[T.any(Symbol, String), T.untyped]).returns(DependencyRequirement) }
52
+ sig { params(hash: Input).returns(DependencyRequirement) }
48
53
  def self.create(hash)
49
54
  requirement = new
50
55
  requirement.replace(hash.keys.to_h { |k| [k.to_sym, hash[k]] })
@@ -52,16 +57,21 @@ module Dependabot
52
57
  end
53
58
 
54
59
  # The version constraint string, e.g. ">= 1.0, < 2.0". Nil when the
55
- # dependency is pinned by a lockfile rather than a manifest constraint.
56
- sig { returns(T.nilable(String)) }
60
+ # dependency is pinned by a lockfile rather than a manifest constraint,
61
+ # or :unfixable when no valid updated requirement can be generated.
62
+ sig { returns(T.nilable(Requirement)) }
57
63
  def requirement
58
- self[:requirement]
64
+ value = T.cast(self[:requirement], T.nilable(Object))
65
+ return if value.nil?
66
+ return value if value.is_a?(String) || value == :unfixable
67
+
68
+ raise TypeError, "requirement must be a string, :unfixable, or nil"
59
69
  end
60
70
 
61
71
  # The manifest file this requirement was declared in, e.g. "Gemfile".
62
72
  sig { returns(T.nilable(String)) }
63
73
  def file
64
- self[:file]
74
+ optional_string(:file)
65
75
  end
66
76
 
67
77
  # The dependency groups this requirement belongs to, e.g. ["dev"] or
@@ -69,26 +79,61 @@ module Dependabot
69
79
  # Nilable because some requirement entries are constructed with
70
80
  # groups: nil, and the reader must reflect that to stay a drop-in for
71
81
  # the underlying hash access under sorbet-runtime.
72
- sig { returns(T.nilable(T::Array[T.untyped])) }
82
+ sig { returns(T.nilable(T::Array[Group])) }
73
83
  def groups
74
- self[:groups]
84
+ value = T.cast(self[:groups], T.nilable(Object))
85
+ return if value.nil?
86
+ raise TypeError, "groups must be an array of strings or symbols, or nil" unless value.is_a?(Array)
87
+
88
+ value.each do |raw_group|
89
+ group = T.cast(raw_group, Object)
90
+ next if group.is_a?(String) || group.is_a?(Symbol)
91
+
92
+ raise TypeError, "groups must be an array of strings or symbols, or nil"
93
+ end
94
+ value
75
95
  end
76
96
 
77
97
  # The source details for the dependency, e.g.
78
98
  # { type: "git", url: "https://github.com/..." }. Keys may be symbols
79
99
  # or strings depending on whether the requirement was built by a file
80
100
  # parser or deserialised from a job definition.
81
- sig { returns(T.nilable(T::Hash[T.any(Symbol, String), T.untyped])) }
101
+ sig { returns(T.nilable(ObjectHash)) }
82
102
  def source
83
- self[:source]
103
+ optional_object_hash(:source)
84
104
  end
85
105
 
86
106
  # Optional ecosystem-specific metadata about the requirement, e.g.
87
107
  # { property_name: "rails.version" }.
88
- sig { returns(T.nilable(T::Hash[T.any(Symbol, String), T.untyped])) }
108
+ sig { returns(T.nilable(ObjectHash)) }
89
109
  def metadata
90
- self[:metadata]
110
+ optional_object_hash(:metadata)
111
+ end
112
+
113
+ private
114
+
115
+ sig { params(key: Symbol).returns(T.nilable(String)) }
116
+ def optional_string(key)
117
+ value = T.cast(self[key], T.nilable(Object))
118
+ return if value.nil?
119
+ return value if value.is_a?(String)
120
+
121
+ raise TypeError, "#{key} must be a string or nil"
122
+ end
123
+
124
+ sig { params(key: Symbol).returns(T.nilable(ObjectHash)) }
125
+ def optional_object_hash(key)
126
+ value = T.cast(self[key], T.nilable(Object))
127
+ return if value.nil?
128
+ raise TypeError, "#{key} must be a hash with string or symbol keys, or nil" unless value.is_a?(Hash)
129
+
130
+ value.each_key do |raw_nested_key|
131
+ nested_key = T.cast(raw_nested_key, Object)
132
+ next if nested_key.is_a?(String) || nested_key.is_a?(Symbol)
133
+
134
+ raise TypeError, "#{key} must be a hash with string or symbol keys, or nil"
135
+ end
136
+ value
91
137
  end
92
- # rubocop:enable Sorbet/ForbidTUntyped
93
138
  end
94
139
  end
@@ -0,0 +1,51 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ class ErrorDetails < T::ImmutableStruct
8
+ extend T::Sig
9
+
10
+ DetailHash = T.type_alias { T::Hash[T.any(String, Symbol), T.anything] }
11
+ Detail = T.type_alias { T.any(String, DetailHash) }
12
+
13
+ const :error_type, String
14
+ const :error_detail, T.nilable(Detail)
15
+
16
+ sig { params(hash: T::Hash[Symbol, T.anything]).returns(ErrorDetails) }
17
+ def self.from_hash(hash)
18
+ raw_type = T.cast(hash[:"error-type"], Object)
19
+ raise TypeError, "error-type must be a string" unless raw_type.is_a?(String)
20
+
21
+ new(
22
+ error_type: raw_type,
23
+ error_detail: parse_detail(T.cast(hash[:"error-detail"], T.nilable(Object)))
24
+ )
25
+ end
26
+
27
+ sig { returns(T::Hash[Symbol, Object]) }
28
+ def to_h
29
+ result = T.let({ "error-type": error_type }, T::Hash[Symbol, Object])
30
+ result[:"error-detail"] = error_detail if error_detail
31
+ result
32
+ end
33
+
34
+ sig { params(value: T.nilable(Object)).returns(T.nilable(Detail)) }
35
+ def self.parse_detail(value)
36
+ return if value.nil?
37
+ return value if value.is_a?(String)
38
+ raise TypeError, "error-detail must be a string or hash" unless value.is_a?(Hash)
39
+
40
+ result = T.let({}, DetailHash)
41
+ value.each do |raw_key, raw_value|
42
+ key = T.cast(raw_key, Object)
43
+ raise TypeError, "error-detail keys must be strings or symbols" unless key.is_a?(String) || key.is_a?(Symbol)
44
+
45
+ result[key] = T.cast(raw_value, Object)
46
+ end
47
+ result
48
+ end
49
+ private_class_method :parse_detail
50
+ end
51
+ end
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "dependabot/error_details"
5
6
  require "dependabot/utils"
6
7
 
7
8
  # rubocop:disable Metrics/ModuleLength
@@ -23,7 +24,7 @@ module Dependabot
23
24
 
24
25
  # rubocop:disable Metrics/MethodLength
25
26
  # rubocop:disable Metrics/CyclomaticComplexity
26
- sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.anything])) }
27
+ sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
27
28
  def self.fetcher_error_details(error)
28
29
  case error
29
30
  when Dependabot::ToolVersionNotSupported
@@ -138,11 +139,12 @@ module Dependabot
138
139
  "rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
139
140
  }
140
141
  }
141
- end
142
+ end => details
143
+ Dependabot::ErrorDetails.from_hash(details) if details
142
144
  end
143
145
  # rubocop:enable Metrics/CyclomaticComplexity
144
146
 
145
- sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.anything])) }
147
+ sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
146
148
  def self.parser_error_details(error)
147
149
  case error
148
150
  when Dependabot::ToolFeatureNotSupported
@@ -220,13 +222,14 @@ module Dependabot
220
222
  # and responsibility for fixing it is on them, not us. As a result we
221
223
  # quietly log these as errors
222
224
  { "error-type": "server_error" }
223
- end
225
+ end => details
226
+ Dependabot::ErrorDetails.from_hash(details) if details
224
227
  end
225
228
 
226
229
  # rubocop:disable Lint/RedundantCopDisableDirective
227
230
  # rubocop:disable Metrics/CyclomaticComplexity
228
231
  # rubocop:disable Metrics/AbcSize
229
- sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.anything])) }
232
+ sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
230
233
  def self.updater_error_details(error)
231
234
  case error
232
235
  when Dependabot::ToolFeatureNotSupported
@@ -400,7 +403,8 @@ module Dependabot
400
403
  "rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
401
404
  }
402
405
  }
403
- end
406
+ end => details
407
+ Dependabot::ErrorDetails.from_hash(details) if details
404
408
  end
405
409
 
406
410
  # rubocop:enable Metrics/MethodLength
@@ -70,7 +70,7 @@ module Dependabot
70
70
  sig { returns(T.nilable(String)) }
71
71
  attr_reader :repo_contents_path
72
72
 
73
- sig { returns(T::Hash[String, String]) }
73
+ sig { returns(T::Hash[Symbol, Object]) }
74
74
  attr_reader :options
75
75
 
76
76
  CLIENT_NOT_FOUND_ERRORS = T.let(
@@ -135,7 +135,7 @@ module Dependabot
135
135
  source: Dependabot::Source,
136
136
  credentials: T::Array[Dependabot::Credential],
137
137
  repo_contents_path: T.nilable(String),
138
- options: T::Hash[String, String],
138
+ options: T::Hash[Symbol, Object],
139
139
  update_config: T.nilable(Dependabot::Config::UpdateConfig)
140
140
  )
141
141
  .void
@@ -0,0 +1,42 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ class GitCommitChecker
8
+ # Typed view over the source hash attached to a git dependency.
9
+ class SourceDetails < T::ImmutableStruct
10
+ extend T::Sig
11
+
12
+ const :type, T.nilable(String)
13
+ const :url, T.nilable(String)
14
+ const :branch, T.nilable(String)
15
+ const :ref, T.nilable(String)
16
+
17
+ sig do
18
+ params(details: T::Hash[T.any(String, Symbol), Object]).returns(SourceDetails)
19
+ end
20
+ def self.from_hash(details)
21
+ new(
22
+ type: string_value(details, :type),
23
+ url: string_value(details, :url),
24
+ branch: string_value(details, :branch),
25
+ ref: string_value(details, :ref)
26
+ )
27
+ end
28
+
29
+ sig do
30
+ params(
31
+ details: T::Hash[T.any(String, Symbol), Object],
32
+ key: Symbol
33
+ ).returns(T.nilable(String))
34
+ end
35
+ def self.string_value(details, key)
36
+ value = details.key?(key) ? details[key] : details[key.to_s]
37
+ value.is_a?(String) ? value : nil
38
+ end
39
+ private_class_method :string_value
40
+ end
41
+ end
42
+ end
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
@@ -14,7 +14,9 @@ require "dependabot/utils"
14
14
  require "dependabot/source"
15
15
  require "dependabot/dependency"
16
16
  require "dependabot/credential"
17
+ require "dependabot/clients/github_release"
17
18
  require "dependabot/git_metadata_fetcher"
19
+ require "dependabot/git_commit_checker/source_details"
18
20
  require "dependabot/git_tag_details"
19
21
  require "dependabot/package/package_release"
20
22
  require "dependabot/package/release_cooldown_options"
@@ -46,7 +48,7 @@ module Dependabot
46
48
  ignored_versions: T::Array[String],
47
49
  raise_on_ignored: T::Boolean,
48
50
  consider_version_branches_pinned: T::Boolean,
49
- dependency_source_details: T.nilable(T::Hash[Symbol, String])
51
+ dependency_source_details: T.nilable(T::Hash[Symbol, Object])
50
52
  )
51
53
  .void
52
54
  end
@@ -63,14 +65,15 @@ module Dependabot
63
65
  @ignored_versions = ignored_versions
64
66
  @raise_on_ignored = raise_on_ignored
65
67
  @consider_version_branches_pinned = consider_version_branches_pinned
66
- @dependency_source_details = dependency_source_details
68
+ @dependency_source_details = T.let(
69
+ dependency_source_details && SourceDetails.from_hash(dependency_source_details),
70
+ T.nilable(SourceDetails)
71
+ )
67
72
  end
68
73
 
69
74
  sig { returns(T::Boolean) }
70
75
  def git_dependency?
71
- return false if dependency_source_details.nil?
72
-
73
- dependency_source_details&.fetch(:type) == "git"
76
+ dependency_source_details&.type == "git"
74
77
  end
75
78
 
76
79
  # rubocop:disable Metrics/PerceivedComplexity
@@ -78,7 +81,7 @@ module Dependabot
78
81
  def pinned?
79
82
  raise "Not a git dependency!" unless git_dependency?
80
83
 
81
- branch = dependency_source_details&.fetch(:branch)
84
+ branch = dependency_source_details&.branch
82
85
 
83
86
  return false if ref.nil?
84
87
  return false if branch == ref
@@ -118,7 +121,7 @@ module Dependabot
118
121
  sig { returns(T::Array[GitRef]) }
119
122
  def tags
120
123
  GitMetadataFetcher.new(
121
- url: dependency.source_details&.fetch(:url, nil),
124
+ url: T.must(source_details&.url),
122
125
  credentials: credentials
123
126
  ).tags
124
127
  end
@@ -128,7 +131,7 @@ module Dependabot
128
131
  T.must(
129
132
  T.let(
130
133
  GitMetadataFetcher.new(
131
- url: dependency.source_details&.fetch(:url, nil),
134
+ url: T.must(source_details&.url),
132
135
  credentials: credentials
133
136
  ).ref_details_for_pinned_ref(ref),
134
137
  T.nilable(Excon::Response)
@@ -282,9 +285,12 @@ module Dependabot
282
285
  false
283
286
  end
284
287
 
285
- sig { returns(T.nilable(T::Hash[T.any(Symbol, String), T.untyped])) }
288
+ sig { returns(T.nilable(SourceDetails)) }
286
289
  def dependency_source_details
287
- @dependency_source_details || dependency.source_details(allowed_types: ["git"])
290
+ @dependency_source_details ||= begin
291
+ details = dependency.source_details(allowed_types: ["git"])
292
+ SourceDetails.from_hash(details) if details
293
+ end
288
294
  end
289
295
 
290
296
  sig { returns(T::Array[Dependabot::GitTagWithDetail]) }
@@ -333,7 +339,7 @@ module Dependabot
333
339
 
334
340
  sig { override.returns(T.nilable(String)) }
335
341
  def cooldown_source_url
336
- dependency_source_details&.fetch(:url, nil)
342
+ dependency_source_details&.url
337
343
  end
338
344
 
339
345
  sig { override.returns(T::Array[Dependabot::Credential]) }
@@ -478,9 +484,11 @@ module Dependabot
478
484
 
479
485
  sig { params(tags: T::Array[Dependabot::GitRef]).returns(T::Array[Dependabot::GitRef]) }
480
486
  def handle_tag_prefix(tags)
481
- if dependency_source_details&.fetch(:ref, nil)&.start_with?("tags/")
487
+ if dependency_source_details&.ref&.start_with?("tags/")
482
488
  tags = tags.map do |tag|
483
- tag.dup.tap { |t| t.name = "tags/#{tag.name}" }
489
+ duplicated_tag = tag.dup
490
+ duplicated_tag.name = "tags/#{tag.name}"
491
+ duplicated_tag
484
492
  end
485
493
  end
486
494
 
@@ -519,8 +527,11 @@ module Dependabot
519
527
  client = Clients::GithubWithRetries
520
528
  .for_github_dot_com(credentials: credentials)
521
529
 
522
- # TODO: create this method instead of relying on method_missing
523
- T.unsafe(client.compare(T.must(listing_source_repo), ref1, ref2)).status
530
+ comparison = client.compare(T.must(listing_source_repo), ref1, ref2)
531
+ status = T.cast(comparison[:status], Object)
532
+ raise TypeError, "GitHub comparison status must be a string" unless status.is_a?(String)
533
+
534
+ status
524
535
  end
525
536
 
526
537
  sig { params(ref1: String, ref2: String).returns(String) }
@@ -529,9 +540,11 @@ module Dependabot
529
540
  .for_gitlab_dot_com(credentials: credentials)
530
541
 
531
542
  comparison = client.compare(T.must(listing_source_repo), ref1, ref2)
543
+ commits = T.cast(comparison["commits"], Object)
544
+ raise TypeError, "GitLab comparison commits must be an array" unless commits.is_a?(Array)
532
545
 
533
- if T.unsafe(comparison).commits.none? then "behind"
534
- elsif T.unsafe(comparison).compare_same_ref then "identical"
546
+ if commits.empty? then "behind"
547
+ elsif T.cast(comparison["compare_same_ref"], Object) == true then "identical"
535
548
  else
536
549
  "ahead"
537
550
  end
@@ -550,7 +563,13 @@ module Dependabot
550
563
 
551
564
  # Conservatively assume that ref2 is ahead in the equality case, of
552
565
  # if we get an unexpected format (e.g., due to a 404)
553
- if JSON.parse(response.body).fetch("values", ["x"]).none? then "behind"
566
+ comparison = T.cast(JSON.parse(response.body), Object)
567
+ raise TypeError, "Bitbucket comparison must be a hash" unless comparison.is_a?(Hash)
568
+
569
+ values = T.cast(comparison.fetch("values", ["x"]), Object)
570
+ raise TypeError, "Bitbucket comparison values must be an array" unless values.is_a?(Array)
571
+
572
+ if values.empty? then "behind"
554
573
  else
555
574
  "ahead"
556
575
  end
@@ -558,12 +577,12 @@ module Dependabot
558
577
 
559
578
  sig { returns(T.nilable(String)) }
560
579
  def ref_or_branch
561
- ref || dependency_source_details&.fetch(:branch)
580
+ ref || dependency_source_details&.branch
562
581
  end
563
582
 
564
583
  sig { returns(T.nilable(String)) }
565
584
  def ref
566
- dependency_source_details&.fetch(:ref)
585
+ dependency_source_details&.ref
567
586
  end
568
587
 
569
588
  sig { params(tag: String).returns(T::Boolean) }
@@ -667,9 +686,11 @@ module Dependabot
667
686
  begin
668
687
  tags = listing_repo_git_metadata_fetcher.tags
669
688
 
670
- if dependency_source_details&.fetch(:ref, nil)&.start_with?("tags/")
689
+ if dependency_source_details&.ref&.start_with?("tags/")
671
690
  tags = tags.map do |tag|
672
- tag.dup.tap { |t| t.name = "tags/#{tag.name}" }
691
+ duplicated_tag = tag.dup
692
+ duplicated_tag.name = "tags/#{tag.name}"
693
+ duplicated_tag
673
694
  end
674
695
  end
675
696
 
@@ -695,7 +716,7 @@ module Dependabot
695
716
 
696
717
  sig { returns(T::Boolean) }
697
718
  def wants_prerelease?
698
- return false unless dependency_source_details&.fetch(:ref, nil)
719
+ return false unless dependency_source_details&.ref
699
720
  return false unless pinned_ref_looks_like_version?
700
721
 
701
722
  version = version_from_ref(T.must(ref))
@@ -732,7 +753,7 @@ module Dependabot
732
753
  false
733
754
  end
734
755
 
735
- sig { returns(T::Array[T.untyped]) }
756
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
736
757
  def github_releases
737
758
  @github_releases ||= T.let(
738
759
  begin
@@ -747,11 +768,17 @@ module Dependabot
747
768
  )
748
769
  # The Octokit RBI types this as non-nil, but it can be nil at runtime
749
770
  # (e.g. an empty/unexpected registry response), so guard against it.
750
- T.unsafe(client.releases(T.must(source).repo, per_page: 100)) || []
771
+ releases = T.let(
772
+ client.releases(T.must(source).repo, per_page: 100),
773
+ T.nilable(T::Array[Sawyer::Resource])
774
+ )
775
+ (releases || []).filter_map do |release|
776
+ Dependabot::Clients::GithubRelease.from_resource(release)
777
+ end
751
778
  rescue Octokit::Error
752
779
  []
753
780
  end,
754
- T.nilable(T::Array[T.untyped])
781
+ T.nilable(T::Array[Dependabot::Clients::GithubRelease])
755
782
  )
756
783
  end
757
784
 
@@ -892,7 +919,7 @@ module Dependabot
892
919
  @local_repo_git_metadata_fetcher ||=
893
920
  T.let(
894
921
  GitMetadataFetcher.new(
895
- url: dependency_source_details&.fetch(:url),
922
+ url: T.must(dependency_source_details&.url),
896
923
  credentials: credentials
897
924
  ),
898
925
  T.nilable(Dependabot::GitMetadataFetcher)
@@ -913,8 +940,14 @@ module Dependabot
913
940
 
914
941
  sig { returns(String) }
915
942
  def ref_pinned
916
- dependency.source_details&.fetch(:ref, nil) ||
917
- dependency.source_details&.fetch(:branch, nil) || "HEAD"
943
+ details = source_details
944
+ details&.ref || details&.branch || "HEAD"
945
+ end
946
+
947
+ sig { returns(T.nilable(SourceDetails)) }
948
+ def source_details
949
+ details = dependency.source_details
950
+ SourceDetails.from_hash(details) if details
918
951
  end
919
952
  end
920
953
  # rubocop:enable Metrics/ClassLength
@@ -1,7 +1,8 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "dependabot/clients/github_release"
5
6
  require "dependabot/clients/github_with_retries"
6
7
  require "dependabot/shared_helpers"
7
8
  require "dependabot/source"
@@ -45,7 +46,8 @@ module Dependabot
45
46
  releases = cached_github_releases
46
47
  unless releases.empty?
47
48
  release = releases.find { |r| r.tag_name == tag_name }
48
- return release.published_at if release&.published_at
49
+ published_at = release&.published_at
50
+ return published_at if published_at
49
51
  end
50
52
 
51
53
  tag_creation_date(tag_name, commit_sha)
@@ -88,7 +90,7 @@ module Dependabot
88
90
 
89
91
  # Fetches and caches GitHub releases for the dependency source.
90
92
  # Returns an empty array for non-GitHub sources.
91
- sig { returns(T::Array[T.untyped]) } # rubocop:disable Sorbet/ForbidTUntyped
93
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
92
94
  def cached_github_releases
93
95
  @cached_github_releases ||= T.let(
94
96
  begin
@@ -99,7 +101,13 @@ module Dependabot
99
101
  source: T.must(source),
100
102
  credentials: cooldown_credentials
101
103
  )
102
- client.releases(T.must(source).repo, per_page: 100)
104
+ releases = T.let(
105
+ client.releases(T.must(source).repo, per_page: 100),
106
+ T.nilable(T::Array[Sawyer::Resource])
107
+ )
108
+ (releases || []).filter_map do |release|
109
+ Dependabot::Clients::GithubRelease.from_resource(release)
110
+ end
103
111
  else
104
112
  []
105
113
  end
@@ -107,7 +115,7 @@ module Dependabot
107
115
  Dependabot.logger.debug("Error fetching GitHub releases: #{e.message}")
108
116
  []
109
117
  end,
110
- T.nilable(T::Array[T.untyped]) # rubocop:disable Sorbet/ForbidTUntyped
118
+ T.nilable(T::Array[Dependabot::Clients::GithubRelease])
111
119
  )
112
120
  end
113
121
  end
@@ -147,12 +147,6 @@ module Dependabot
147
147
  raise "Unexpected response: #{response_with_git.status} - #{response_with_git.body}"
148
148
  end
149
149
 
150
- if uri.match?(/github\.com/i)
151
- response = response_with_git.data
152
- response[:response_headers] = response[:headers] unless response.nil?
153
- raise Octokit::Error.from_response(response)
154
- end
155
-
156
150
  raise "Server error at #{uri}: #{response_with_git.body}" if response_with_git.status >= 500
157
151
 
158
152
  raise Dependabot::GitDependenciesNotReachable, [uri]
@@ -213,7 +207,7 @@ module Dependabot
213
207
  )
214
208
  end
215
209
 
216
- sig { params(uri: String).returns(T.untyped) }
210
+ sig { params(uri: String).returns(GitResponse) }
217
211
  def fetch_raw_upload_pack_with_git_for(uri)
218
212
  service_pack_uri = uri
219
213
  service_pack_uri += ".git" unless service_pack_uri.end_with?(".git") || skip_git_suffix(uri)
@@ -329,7 +323,7 @@ module Dependabot
329
323
  T.must(line.split.first).chars.last(40).join
330
324
  end
331
325
 
332
- sig { returns(T::Hash[Symbol, T.untyped]) }
326
+ sig { returns(T::Hash[Symbol, Object]) }
333
327
  def excon_defaults
334
328
  # Some git hosts are slow when returning a large number of tags
335
329
  SharedHelpers.excon_defaults(read_timeout: 20)
@@ -344,7 +338,7 @@ module Dependabot
344
338
 
345
339
  # Added method to fetch tags with their creation dates from a git repository. In case
346
340
  # private registry is used, it will clone the repository and fetch tags with their creation dates.
347
- sig { params(uri: String).returns(T.untyped) }
341
+ sig { params(uri: String).returns(GitResponse) }
348
342
  def fetch_tags_with_detail_from_git_for(uri)
349
343
  uri_ending_with_git = uri
350
344
  uri_ending_with_git += ".git" unless uri_ending_with_git.end_with?(".git") || skip_git_suffix(uri)
@@ -1,9 +1,11 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "time"
5
6
 
6
7
  require "dependabot/credential"
8
+ require "dependabot/clients/github_release"
7
9
  require "dependabot/clients/github_with_retries"
8
10
  require "dependabot/clients/gitlab_with_retries"
9
11
  require "dependabot/metadata_finders/base"
@@ -12,40 +14,62 @@ require "dependabot/utils"
12
14
  module Dependabot
13
15
  module MetadataFinders
14
16
  class Base
15
- class GitLabRelease
17
+ class GitLabRelease < T::ImmutableStruct
16
18
  extend T::Sig
17
19
 
18
- sig { returns(String) }
19
- attr_reader :name
20
-
21
- sig { returns(String) }
22
- attr_reader :tag_name
23
-
24
- sig { returns(String) }
25
- attr_reader :body
26
-
27
- sig { returns(String) }
28
- attr_reader :html_url
20
+ const :name, String
21
+ const :tag_name, String
22
+ const :body, T.nilable(String)
23
+ const :html_url, String
24
+ const :authored_at, Time
29
25
 
30
26
  sig do
31
27
  params(
32
- name: String,
33
- tag_name: String,
34
- body: String,
35
- html_url: String
36
- ).void
37
- end
38
- def initialize(name:, tag_name:, body:, html_url:)
39
- @name = name
40
- @tag_name = tag_name
41
- @body = body
42
- @html_url = html_url
28
+ tag: Gitlab::ObjectifiedHash,
29
+ source_url: String
30
+ ).returns(T.nilable(GitLabRelease))
43
31
  end
32
+ def self.from_tag(tag, source_url:)
33
+ name = T.cast(tag["name"], Object)
34
+ release = T.cast(tag["release"], Object)
35
+ commit = T.cast(tag["commit"], Object)
36
+ return unless name.is_a?(String)
37
+ return unless release.is_a?(Gitlab::ObjectifiedHash)
38
+ return unless commit.is_a?(Gitlab::ObjectifiedHash)
39
+
40
+ tag_name = T.cast(release["tag_name"], Object)
41
+ return unless tag_name.is_a?(String)
42
+
43
+ authored_at = time_value(T.cast(commit["authored_date"], Object))
44
+ return unless authored_at
45
+
46
+ description = T.cast(release["description"], Object)
47
+ new(
48
+ name: name,
49
+ tag_name: tag_name,
50
+ body: description.is_a?(String) ? description : nil,
51
+ html_url: "#{source_url}/tags/#{name}",
52
+ authored_at: authored_at
53
+ )
54
+ end
55
+
56
+ sig { params(value: Object).returns(T.nilable(Time)) }
57
+ def self.time_value(value)
58
+ return value if value.is_a?(Time)
59
+ return unless value.is_a?(String)
60
+
61
+ Time.parse(value)
62
+ rescue ArgumentError
63
+ nil
64
+ end
65
+ private_class_method :time_value
44
66
  end
45
67
 
46
68
  class ReleaseFinder
47
69
  extend T::Sig
48
70
 
71
+ ReleaseType = T.type_alias { T.any(Dependabot::Clients::GithubRelease, GitLabRelease) }
72
+
49
73
  sig { returns(Dependabot::Dependency) }
50
74
  attr_reader :dependency
51
75
 
@@ -98,14 +122,13 @@ module Dependabot
98
122
 
99
123
  private
100
124
 
101
- sig { returns(T::Array[T.untyped]) }
125
+ sig { returns(T::Array[ReleaseType]) }
102
126
  def all_dep_releases
103
127
  releases = all_releases
104
128
  dep_prefix = dependency.name.downcase
105
129
 
106
130
  releases_with_dependency_name =
107
131
  releases
108
- .reject { |r| r.tag_name.nil? }
109
132
  .select { |r| r.tag_name.start_with?(dep_prefix) }
110
133
 
111
134
  return releases unless releases_with_dependency_name.any?
@@ -113,12 +136,12 @@ module Dependabot
113
136
  releases_with_dependency_name
114
137
  end
115
138
 
116
- sig { returns(T::Array[T.untyped]) }
139
+ sig { returns(T::Array[ReleaseType]) }
117
140
  def all_releases
118
- @all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[T.untyped]))
141
+ @all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[ReleaseType]))
119
142
  end
120
143
 
121
- sig { returns(T.nilable(T::Array[T.untyped])) }
144
+ sig { returns(T.nilable(T::Array[ReleaseType])) }
122
145
  def relevant_releases
123
146
  releases = releases_since_previous_version
124
147
 
@@ -139,7 +162,7 @@ module Dependabot
139
162
  end
140
163
  end
141
164
 
142
- sig { returns(T.nilable(T::Array[T.untyped])) }
165
+ sig { returns(T.nilable(T::Array[ReleaseType])) }
143
166
  def releases_since_previous_version
144
167
  return [updated_release].compact unless previous_version
145
168
 
@@ -158,21 +181,21 @@ module Dependabot
158
181
  end
159
182
  end
160
183
 
161
- sig { params(releases: T::Array[T.untyped]).returns(T.nilable(T::Array[T.untyped])) }
184
+ sig { params(releases: T::Array[ReleaseType]).returns(T.nilable(T::Array[ReleaseType])) }
162
185
  def filter_releases_using_previous_release(releases)
163
186
  return releases if releases.index(previous_release).nil?
164
187
 
165
188
  releases.first(T.must(releases.index(previous_release)))
166
189
  end
167
190
 
168
- sig { params(releases: T::Array[T.untyped]).returns(T.nilable(T::Array[T.untyped])) }
191
+ sig { params(releases: T::Array[ReleaseType]).returns(T.nilable(T::Array[ReleaseType])) }
169
192
  def filter_releases_using_updated_release(releases)
170
193
  return releases if releases.index(updated_release).nil?
171
194
 
172
195
  releases[releases.index(updated_release)..-1]
173
196
  end
174
197
 
175
- sig { params(releases: T::Array[T.untyped], conservative: T::Boolean).returns(T::Array[T.untyped]) }
198
+ sig { params(releases: T::Array[ReleaseType], conservative: T::Boolean).returns(T::Array[ReleaseType]) }
176
199
  def filter_releases_using_previous_version(releases, conservative:)
177
200
  releases.reject do |release|
178
201
  cleaned_tag = release.tag_name.gsub(/^[^0-9]*/, "")
@@ -193,7 +216,7 @@ module Dependabot
193
216
  end
194
217
  end
195
218
 
196
- sig { params(releases: T::Array[T.untyped], conservative: T::Boolean).returns(T::Array[T.untyped]) }
219
+ sig { params(releases: T::Array[ReleaseType], conservative: T::Boolean).returns(T::Array[ReleaseType]) }
197
220
  def filter_releases_using_updated_version(releases, conservative:)
198
221
  updated_version = version_class.new(new_version)
199
222
 
@@ -216,17 +239,17 @@ module Dependabot
216
239
  end
217
240
  end
218
241
 
219
- sig { returns(T.untyped) }
242
+ sig { returns(T.nilable(ReleaseType)) }
220
243
  def updated_release
221
244
  release_for_version(new_version)
222
245
  end
223
246
 
224
- sig { returns(T.untyped) }
247
+ sig { returns(T.nilable(ReleaseType)) }
225
248
  def previous_release
226
249
  release_for_version(previous_version)
227
250
  end
228
251
 
229
- sig { params(version: T.nilable(String)).returns(T.untyped) }
252
+ sig { params(version: T.nilable(String)).returns(T.nilable(ReleaseType)) }
230
253
  def release_for_version(version)
231
254
  return nil unless version
232
255
 
@@ -236,22 +259,23 @@ module Dependabot
236
259
  all_dep_releases.find { |r| release_regex.match?(r.name.to_s) }
237
260
  end
238
261
 
239
- sig { params(release: T.untyped).returns(String) }
262
+ sig { params(release: ReleaseType).returns(String) }
240
263
  def serialize_release(release)
241
- rel = release
242
- title = "## #{rel.name.to_s == '' ? rel.tag_name : rel.name}\n"
243
- body = if rel.body.to_s.gsub(/\n*\z/m, "") == ""
264
+ name = release.name
265
+ title = "## #{name.to_s == '' ? release.tag_name : name}\n"
266
+ body = if release.body.to_s.gsub(/\n*\z/m, "") == ""
244
267
  "No release notes provided."
245
268
  else
246
- rel.body.gsub(/\n*\z/m, "")
269
+ T.must(release.body).gsub(/\n*\z/m, "")
247
270
  end
248
271
 
249
- release_body_includes_title?(rel) ? body : title + body
272
+ release_body_includes_title?(release) ? body : title + body
250
273
  end
251
274
 
252
- sig { params(release: T.untyped).returns(T::Boolean) }
275
+ sig { params(release: ReleaseType).returns(T::Boolean) }
253
276
  def release_body_includes_title?(release)
254
- title = release.name.to_s == "" ? release.tag_name : release.name
277
+ name = release.name
278
+ title = name.nil? || name.empty? ? release.tag_name : name
255
279
  release.body.to_s.match?(/\A\s*\#*\s*#{Regexp.quote(title)}/m)
256
280
  end
257
281
 
@@ -265,7 +289,7 @@ module Dependabot
265
289
  dependency.version_class
266
290
  end
267
291
 
268
- sig { returns(T::Array[T.untyped]) }
292
+ sig { returns(T::Array[ReleaseType]) }
269
293
  def fetch_dependency_releases
270
294
  return [] unless source
271
295
 
@@ -279,47 +303,45 @@ module Dependabot
279
303
  end
280
304
  end
281
305
 
282
- sig { returns(T::Array[T.untyped]) }
306
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
283
307
  def fetch_github_releases
284
- releases = github_client.releases(T.must(source).repo, per_page: 100)
285
-
286
- # Remove any releases without a tag name. These are draft releases and
287
- # aren't yet associated with a tag, so shouldn't be used.
288
- releases = releases.reject { |r| T.unsafe(r).tag_name.nil? }
308
+ releases = parsed_github_releases
289
309
 
290
310
  clean_release_names =
291
- releases.map { |r| T.unsafe(r).tag_name.gsub(/^[^0-9\.]*/, "") }
311
+ releases.map { |release| release.tag_name.gsub(/^[^0-9\.]*/, "") }
292
312
 
293
313
  if clean_release_names.all? { |nm| version_class.correct?(nm) }
294
- releases.sort_by do |r|
295
- version_class.new(T.unsafe(r).tag_name.gsub(/^[^0-9\.]*/, ""))
314
+ releases.sort_by do |release|
315
+ version_class.new(release.tag_name.gsub(/^[^0-9\.]*/, ""))
296
316
  end.reverse
297
317
  else
298
- releases.sort_by { |r| T.unsafe(r).id }.reverse
318
+ releases.sort_by { |release| release.id || 0 }.reverse
299
319
  end
300
320
  rescue Octokit::NotFound, Octokit::UnavailableForLegalReasons
301
321
  []
302
322
  end
303
323
 
304
- sig { returns(T::Array[T.untyped]) }
324
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
325
+ def parsed_github_releases
326
+ resources = T.let(
327
+ github_client.releases(T.must(source).repo, per_page: 100),
328
+ T.nilable(T::Array[Sawyer::Resource])
329
+ )
330
+ releases = (resources || []).filter_map do |release|
331
+ Dependabot::Clients::GithubRelease.from_resource(release)
332
+ end
333
+ releases
334
+ end
335
+
336
+ sig { returns(T::Array[GitLabRelease]) }
305
337
  def fetch_gitlab_releases
306
- releases =
307
- T.unsafe(
308
- gitlab_client
309
- .tags(T.must(source).repo)
310
- )
311
- .select(&:release)
312
- .sort_by { |r| T.unsafe(r).commit.authored_date }
313
- .reverse
314
-
315
- releases.map do |tag|
316
- GitLabRelease.new(
317
- name: T.unsafe(tag).name,
318
- tag_name: T.unsafe(tag).release.tag_name,
319
- body: T.unsafe(tag).release.description,
320
- html_url: "#{T.must(source).url}/tags/#{T.unsafe(tag).name}"
321
- )
338
+ tags = gitlab_client.tags(T.must(source).repo)
339
+ releases = tags.filter_map do |tag|
340
+ next unless tag.is_a?(Gitlab::ObjectifiedHash)
341
+
342
+ GitLabRelease.from_tag(tag, source_url: T.must(source).url)
322
343
  end
344
+ releases.sort_by(&:authored_at).reverse
323
345
  rescue Gitlab::Error::NotFound
324
346
  []
325
347
  end
@@ -359,20 +381,32 @@ module Dependabot
359
381
 
360
382
  sig { returns(T.nilable(String)) }
361
383
  def previous_ref
362
- previous_refs = T.must(dependency.previous_requirements).filter_map do |r|
363
- r.dig(:source, "ref") || r.dig(:source, :ref)
384
+ previous_refs = T.must(dependency.previous_requirements).filter_map do |requirement|
385
+ requirement_ref(requirement)
364
386
  end.uniq
365
387
  previous_refs.first if previous_refs.one?
366
388
  end
367
389
 
368
390
  sig { returns(T.nilable(String)) }
369
391
  def new_ref
370
- new_refs = dependency.requirements.filter_map do |r|
371
- r.dig(:source, "ref") || r.dig(:source, :ref)
392
+ new_refs = dependency.requirements.filter_map do |requirement|
393
+ requirement_ref(requirement)
372
394
  end.uniq
373
395
  new_refs.first if new_refs.one?
374
396
  end
375
397
 
398
+ sig { params(requirement: Dependabot::DependencyRequirement).returns(T.nilable(String)) }
399
+ def requirement_ref(requirement)
400
+ source = requirement.source
401
+ return unless source
402
+
403
+ symbol_ref = source[:ref]
404
+ return symbol_ref if symbol_ref.is_a?(String)
405
+
406
+ string_ref = source["ref"]
407
+ string_ref if string_ref.is_a?(String)
408
+ end
409
+
376
410
  sig { returns(T::Boolean) }
377
411
  def ref_changed?
378
412
  # We could go from multiple previous refs (nil) to a single new ref
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -121,9 +121,9 @@ module Dependabot
121
121
 
122
122
  # Apply case transformation to content after prefix
123
123
  case branch_name_case
124
- when "lower"
124
+ when "lowercase"
125
125
  content = content.downcase
126
- when "upper"
126
+ when "uppercase"
127
127
  content = content.upcase
128
128
  end
129
129
 
@@ -24,6 +24,15 @@ module Dependabot
24
24
  T.cast(super, Dependabot::Version)
25
25
  end
26
26
 
27
+ sig do
28
+ overridable
29
+ .params(_from_version: String, _to_version: String)
30
+ .returns(T.nilable(String))
31
+ end
32
+ def self.update_type(_from_version, _to_version)
33
+ nil
34
+ end
35
+
27
36
  # Opt-in to Rubygems 4 behavior
28
37
  sig { override.overridable.params(version: VersionParameter).returns(T::Boolean) }
29
38
  def self.correct?(version)
data/lib/dependabot.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Dependabot
5
- VERSION = "0.386.0"
5
+ VERSION = "0.388.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.386.0
4
+ version: 0.388.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -518,6 +518,7 @@ files:
518
518
  - lib/dependabot/clients/bitbucket.rb
519
519
  - lib/dependabot/clients/bitbucket_with_retries.rb
520
520
  - lib/dependabot/clients/codecommit.rb
521
+ - lib/dependabot/clients/github_release.rb
521
522
  - lib/dependabot/clients/github_with_retries.rb
522
523
  - lib/dependabot/clients/gitlab_with_retries.rb
523
524
  - lib/dependabot/command_helpers.rb
@@ -536,6 +537,7 @@ files:
536
537
  - lib/dependabot/dependency_group.rb
537
538
  - lib/dependabot/dependency_requirement.rb
538
539
  - lib/dependabot/ecosystem.rb
540
+ - lib/dependabot/error_details.rb
539
541
  - lib/dependabot/errors.rb
540
542
  - lib/dependabot/experiments.rb
541
543
  - lib/dependabot/file_fetchers.rb
@@ -552,6 +554,7 @@ files:
552
554
  - lib/dependabot/file_updaters/base.rb
553
555
  - lib/dependabot/file_updaters/vendor_updater.rb
554
556
  - lib/dependabot/git_commit_checker.rb
557
+ - lib/dependabot/git_commit_checker/source_details.rb
555
558
  - lib/dependabot/git_cooldown_date_resolver.rb
556
559
  - lib/dependabot/git_metadata_fetcher.rb
557
560
  - lib/dependabot/git_ref.rb
@@ -622,7 +625,7 @@ licenses:
622
625
  - MIT
623
626
  metadata:
624
627
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
625
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.386.0
628
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.388.0
626
629
  rdoc_options: []
627
630
  require_paths:
628
631
  - lib