dependabot-common 0.381.0 → 0.383.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: d1bc1965fa1a1b0995cf9a71e47f098f471c2727513d3a69d7c8cf35b82a3cc3
4
- data.tar.gz: 4273d18c7f649c135c28db5f864d82dc120f56a683a2b5fd823a09a486fc530b
3
+ metadata.gz: 1fb1a4d6243812d664a20067d226c2eec111080d39e7337217744a58a9d8f0b6
4
+ data.tar.gz: 609737fc8b6040222a744d58ea03f8587fec15136cb835d319c31b798f7ebf32
5
5
  SHA512:
6
- metadata.gz: 72acbd4917a61c50117d9014f26ac4cae89567215682764b01506c1a36f4eb80df3245f4ca4cad9ad7ab0f5d834e9e4540a67a6ea0dab5441d2d9c0a62f6acff
7
- data.tar.gz: d2acef41e94afb6b5d0d171139e615adbbcc491e89a35ec78d7852b2d745e301ea3f9ec212b035452f7898cdfd791c8e2ff25decf021e5bc2fbda58011879688
6
+ metadata.gz: 5e3042cb52f78e0396e75a3c6707a6706a1e2f4f1e12fc16310079eae0ae08336f688a75a92795cc6a8ba50ba62b9d0b9168b3ca4d5780439c7bccf574ab4179
7
+ data.tar.gz: a15d7f181d2d4afd8b5bf3e72f0450827d435f8e61e98f7863bfa0c254e8e2b983bb831901f1c95d27da8abfe791bf673d6c6e7fcc29b8d5e61c0e3c631ea9e6
@@ -52,7 +52,7 @@ module Dependabot
52
52
  update_types.map(&:downcase).filter_map(&:strip)
53
53
  end
54
54
 
55
- sig { params(dependency: Dependency).returns(T::Array[T.untyped]) }
55
+ sig { params(dependency: Dependency).returns(T::Array[String]) }
56
56
  def versions_by_type(dependency)
57
57
  version = correct_version_for(dependency)
58
58
  return [] unless version
@@ -11,10 +11,20 @@ module Dependabot
11
11
 
12
12
  def_delegators :@credential, :fetch, :keys, :[]=, :delete, :slice, :values, :entries
13
13
 
14
- sig { params(credential: T::Hash[String, T.any(T::Boolean, String)]).void }
14
+ sig { params(credential: T::Hash[String, T.any(T::Boolean, String, T::Array[String])]).void }
15
15
  def initialize(credential)
16
16
  @replaces_base = T.let(credential["replaces-base"] == true, T::Boolean)
17
17
  credential.delete("replaces-base")
18
+
19
+ raw_scope = credential.delete("scope")
20
+ @scope = T.let(
21
+ case raw_scope
22
+ when String then [raw_scope]
23
+ when Array then raw_scope
24
+ end,
25
+ T.nilable(T::Array[String])
26
+ )
27
+
18
28
  @credential = T.let(T.unsafe(credential), T::Hash[String, String])
19
29
  end
20
30
 
@@ -23,6 +33,9 @@ module Dependabot
23
33
  @replaces_base
24
34
  end
25
35
 
36
+ sig { returns(T.nilable(T::Array[String])) }
37
+ attr_reader :scope
38
+
26
39
  sig { params(key: String).returns(T.nilable(String)) }
27
40
  def [](key)
28
41
  @credential[key]
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "dependabot/dependency_requirement"
5
6
  require "dependabot/version"
6
7
 
7
8
  module Dependabot
@@ -90,7 +91,7 @@ module Dependabot
90
91
  sig { returns(T.nilable(String)) }
91
92
  attr_reader :version
92
93
 
93
- sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
94
+ sig { returns(T::Array[Dependabot::DependencyRequirement]) }
94
95
  attr_reader :requirements
95
96
 
96
97
  sig { returns(String) }
@@ -99,7 +100,7 @@ module Dependabot
99
100
  sig { returns(T.nilable(String)) }
100
101
  attr_reader :previous_version
101
102
 
102
- sig { returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
103
+ sig { returns(T.nilable(T::Array[Dependabot::DependencyRequirement])) }
103
104
  attr_reader :previous_requirements
104
105
 
105
106
  sig { returns(T.nilable(String)) }
@@ -124,7 +125,6 @@ module Dependabot
124
125
  sig { returns(T.nilable(Time)) }
125
126
  attr_accessor :attribution_timestamp
126
127
 
127
- # rubocop:disable Metrics/AbcSize
128
128
  # rubocop:disable Metrics/PerceivedComplexity
129
129
  sig do
130
130
  params(
@@ -162,12 +162,15 @@ module Dependabot
162
162
  T.nilable(String)
163
163
  )
164
164
  @version = nil if @version == ""
165
- @requirements = T.let(requirements.map { |req| symbolize_keys(req) }, T::Array[T::Hash[Symbol, T.untyped]])
165
+ @requirements = T.let(
166
+ requirements.map { |req| DependencyRequirement.create(req) },
167
+ T::Array[Dependabot::DependencyRequirement]
168
+ )
166
169
  @previous_version = previous_version
167
170
  @previous_version = nil if @previous_version == ""
168
171
  @previous_requirements = T.let(
169
- previous_requirements&.map { |req| symbolize_keys(req) },
170
- T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
172
+ previous_requirements&.map { |req| DependencyRequirement.create(req) },
173
+ T.nilable(T::Array[Dependabot::DependencyRequirement])
171
174
  )
172
175
  @package_manager = package_manager
173
176
  @directory = directory
@@ -181,7 +184,6 @@ module Dependabot
181
184
  @metadata = T.let(symbolize_keys(metadata || {}), T::Hash[Symbol, T.untyped])
182
185
  check_values
183
186
  end
184
- # rubocop:enable Metrics/AbcSize
185
187
  # rubocop:enable Metrics/PerceivedComplexity
186
188
 
187
189
  sig { returns(T::Boolean) }
@@ -272,7 +274,7 @@ module Dependabot
272
274
  end
273
275
  end
274
276
 
275
- sig { params(requirements: T::Array[T::Hash[Symbol, T.untyped]]).returns(T.nilable(String)) }
277
+ sig { params(requirements: T::Array[Dependabot::DependencyRequirement]).returns(T.nilable(String)) }
276
278
  def docker_digest_from_reqs(requirements)
277
279
  requirements
278
280
  .filter_map { |r| r.dig(:source, "digest") || r.dig(:source, :digest) }
@@ -340,9 +342,9 @@ module Dependabot
340
342
  self == other
341
343
  end
342
344
 
343
- sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
345
+ sig { returns(T::Array[Dependabot::DependencyRequirement]) }
344
346
  def specific_requirements
345
- requirements.select { |r| requirement_class.new(r[:requirement]).specific? }
347
+ requirements.select { |r| requirement_class.new(r.requirement).specific? }
346
348
  end
347
349
 
348
350
  sig { returns(T.class_of(Dependabot::Requirement)) }
@@ -123,7 +123,7 @@ module Dependabot
123
123
  raise "Only symlinked files must specify a target!" if symlink_target
124
124
  end
125
125
 
126
- sig { returns(T::Hash[String, T.untyped]) }
126
+ sig { returns(T::Hash[String, T.nilable(T.any(String, T::Boolean))]) }
127
127
  def to_h
128
128
  details = {
129
129
  "name" => name,
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/experiments"
@@ -28,10 +28,23 @@ module Dependabot
28
28
  sig { returns(T.nilable(String)) }
29
29
  attr_reader :group_by
30
30
 
31
+ # The following readers are parsed once from the raw rules hash so callers
32
+ # get typed access instead of repeatedly casting rules["patterns"] and
33
+ # friends at every use site. A nil value means the rule is absent (which the
34
+ # matching logic treats differently from an empty list).
35
+ sig { returns(T.nilable(T::Array[String])) }
36
+ attr_reader :patterns
37
+
38
+ sig { returns(T.nilable(T::Array[String])) }
39
+ attr_reader :exclude_patterns
40
+
41
+ sig { returns(T.nilable(T::Array[String])) }
42
+ attr_reader :update_types
43
+
31
44
  sig do
32
45
  params(
33
46
  name: String,
34
- rules: T::Hash[String, T.untyped],
47
+ rules: T::Hash[String, T.any(String, T::Array[String])],
35
48
  applies_to: T.nilable(String)
36
49
  )
37
50
  .void
@@ -41,7 +54,10 @@ module Dependabot
41
54
  # For backwards compatibility, if no applies_to is provided, default to "version-updates"
42
55
  @applies_to = T.let(applies_to || "version-updates", String)
43
56
  @rules = rules
44
- @group_by = T.let(rules["group-by"], T.nilable(String))
57
+ @group_by = T.let(string_rule(rules, "group-by"), T.nilable(String))
58
+ @patterns = T.let(string_array_rule(rules, "patterns"), T.nilable(T::Array[String]))
59
+ @exclude_patterns = T.let(string_array_rule(rules, "exclude-patterns"), T.nilable(T::Array[String]))
60
+ @update_types = T.let(string_array_rule(rules, "update-types"), T.nilable(T::Array[String]))
45
61
  @dependencies = T.let([], T::Array[Dependabot::Dependency])
46
62
  end
47
63
 
@@ -75,16 +91,18 @@ module Dependabot
75
91
 
76
92
  sig { params(dependency_name: String).returns(T::Boolean) }
77
93
  def matches_pattern?(dependency_name)
78
- return true unless rules.key?("patterns") # If no patterns are defined, we pass this check by default
94
+ patterns = self.patterns
95
+ return true if patterns.nil? # If no patterns are defined, we pass this check by default
79
96
 
80
- T.unsafe(rules["patterns"]).any? { |rule| WildcardMatcher.match?(rule, dependency_name) }
97
+ patterns.any? { |rule| WildcardMatcher.match?(rule, dependency_name) }
81
98
  end
82
99
 
83
100
  sig { params(dependency_name: String).returns(T::Boolean) }
84
101
  def matches_excluded_pattern?(dependency_name)
85
- return false unless rules.key?("exclude-patterns") # If there are no exclusions, fail by default
102
+ exclude_patterns = self.exclude_patterns
103
+ return false if exclude_patterns.nil? # If there are no exclusions, fail by default
86
104
 
87
- T.unsafe(rules["exclude-patterns"]).any? { |rule| WildcardMatcher.match?(rule, dependency_name) }
105
+ exclude_patterns.any? { |rule| WildcardMatcher.match?(rule, dependency_name) }
88
106
  end
89
107
 
90
108
  sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
@@ -102,5 +120,42 @@ module Dependabot
102
120
  def experimental_rules_enabled?
103
121
  Dependabot::Experiments.enabled?(:grouped_updates_experimental_rules)
104
122
  end
123
+
124
+ # Reads a rule whose value is expected to be a single string (e.g.
125
+ # "group-by"), returning nil when the key is absent or the value is not a
126
+ # string.
127
+ sig do
128
+ params(
129
+ rules: T::Hash[String, T.any(String, T::Array[String])],
130
+ key: String
131
+ )
132
+ .returns(T.nilable(String))
133
+ end
134
+ def string_rule(rules, key)
135
+ value = rules[key]
136
+ value.is_a?(String) ? value : nil
137
+ end
138
+
139
+ # Reads a rule whose value is expected to be a list of strings (e.g.
140
+ # "patterns", "exclude-patterns", "update-types"). Returns nil when the key
141
+ # is absent so callers can distinguish "rule not set" from "empty list", and
142
+ # coerces a lone string into a single-element list.
143
+ sig do
144
+ params(
145
+ rules: T::Hash[String, T.any(String, T::Array[String])],
146
+ key: String
147
+ )
148
+ .returns(T.nilable(T::Array[String]))
149
+ end
150
+ def string_array_rule(rules, key)
151
+ return nil unless rules.key?(key)
152
+
153
+ value = rules[key]
154
+ case value
155
+ when Array then value.grep(String)
156
+ when String then [value]
157
+ else []
158
+ end
159
+ end
105
160
  end
106
161
  end
@@ -0,0 +1,94 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ # A single requirement entry within Dependency#requirements, e.g.:
8
+ #
9
+ # {
10
+ # requirement: ">= 1.0, < 2.0",
11
+ # file: "Gemfile",
12
+ # groups: [:default],
13
+ # source: { type: "rubygems", url: "https://rubygems.org" },
14
+ # metadata: { property_name: "rails.version" } # optional
15
+ # }
16
+ #
17
+ # Subclasses Hash so it is a drop-in replacement at call sites (and in
18
+ # type annotations) that treat requirement entries as
19
+ # T::Hash[Symbol, T.untyped], while exposing typed readers for the
20
+ # well-known keys. New code should prefer the typed readers; hash-style
21
+ # access remains supported while call sites are migrated gradually.
22
+ #
23
+ # Wire compatibility: instances serialise to JSON exactly like the plain
24
+ # hash they were created from, and compare equal (==/eql?/#hash) to plain
25
+ # hashes with the same content, so existing comparisons, Array/Set
26
+ # operations, and API payloads are unaffected.
27
+ #
28
+ # Note on Hash methods: in Ruby 3+, #merge, #dup and #compact preserve
29
+ # this class, while #select, #reject, #except, #transform_values and
30
+ # #to_h return plain Hash instances. Dependency#initialize re-wraps
31
+ # whatever it is given, so both styles remain safe.
32
+ class DependencyRequirement < Hash
33
+ extend T::Sig
34
+ extend T::Generic
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
40
+ K = type_member { { fixed: Symbol } }
41
+ V = type_member { { fixed: T.untyped } }
42
+ Elem = type_member { { fixed: [Symbol, T.untyped] } }
43
+
44
+ # Builds a DependencyRequirement from a requirement hash, symbolising
45
+ # top-level keys. Accepts both plain hashes and existing
46
+ # DependencyRequirement instances and always returns a new instance.
47
+ sig { params(hash: T::Hash[T.any(Symbol, String), T.untyped]).returns(DependencyRequirement) }
48
+ def self.create(hash)
49
+ requirement = new
50
+ requirement.replace(hash.keys.to_h { |k| [k.to_sym, hash[k]] })
51
+ requirement
52
+ end
53
+
54
+ # 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)) }
57
+ def requirement
58
+ self[:requirement]
59
+ end
60
+
61
+ # The manifest file this requirement was declared in, e.g. "Gemfile".
62
+ sig { returns(T.nilable(String)) }
63
+ def file
64
+ self[:file]
65
+ end
66
+
67
+ # The dependency groups this requirement belongs to, e.g. ["dev"] or
68
+ # [:default]. Element types vary by ecosystem (strings or symbols).
69
+ # Nilable because some requirement entries are constructed with
70
+ # groups: nil, and the reader must reflect that to stay a drop-in for
71
+ # the underlying hash access under sorbet-runtime.
72
+ sig { returns(T.nilable(T::Array[T.untyped])) }
73
+ def groups
74
+ self[:groups]
75
+ end
76
+
77
+ # The source details for the dependency, e.g.
78
+ # { type: "git", url: "https://github.com/..." }. Keys may be symbols
79
+ # or strings depending on whether the requirement was built by a file
80
+ # parser or deserialised from a job definition.
81
+ sig { returns(T.nilable(T::Hash[T.any(Symbol, String), T.untyped])) }
82
+ def source
83
+ self[:source]
84
+ end
85
+
86
+ # Optional ecosystem-specific metadata about the requirement, e.g.
87
+ # { property_name: "rails.version" }.
88
+ sig { returns(T.nilable(T::Hash[T.any(Symbol, String), T.untyped])) }
89
+ def metadata
90
+ self[:metadata]
91
+ end
92
+ # rubocop:enable Sorbet/ForbidTUntyped
93
+ end
94
+ end
@@ -97,6 +97,11 @@ module Dependabot
97
97
  "error-type": "path_dependencies_not_reachable",
98
98
  "error-detail": { dependencies: error.dependencies }
99
99
  }
100
+ when Dependabot::PrivateRegistryConfigNotFound
101
+ {
102
+ "error-type": "private_registry_config_not_found",
103
+ "error-detail": { source: error.source }
104
+ }
100
105
  when Dependabot::PrivateSourceAuthenticationFailure
101
106
  {
102
107
  "error-type": "private_source_authentication_failure",
@@ -238,6 +243,16 @@ module Dependabot
238
243
  "error-type": "dependency_file_not_resolvable",
239
244
  "error-detail": { message: error.message }
240
245
  }
246
+ when Dependabot::BlockedDependencyVersion
247
+ {
248
+ "error-type": "blocked_dependency_version",
249
+ "error-detail": {
250
+ "dependency-name": error.dependency_name,
251
+ "blocked-version": error.blocked_version,
252
+ "version-requirement": error.version_requirement,
253
+ reason: error.reason
254
+ }.compact
255
+ }
241
256
  when Dependabot::DependencyFileNotEvaluatable
242
257
  {
243
258
  "error-type": "dependency_file_not_evaluatable",
@@ -687,6 +702,22 @@ module Dependabot
687
702
  # Source level errors #
688
703
  #######################
689
704
 
705
+ class PrivateRegistryConfigNotFound < DependabotError
706
+ extend T::Sig
707
+
708
+ sig { returns(String) }
709
+ attr_reader :source
710
+
711
+ sig { params(source: String).void }
712
+ def initialize(source)
713
+ @source = T.let(sanitize_source(source), String)
714
+ msg = "Private npm registries require either a .npmrc file in your repository, " \
715
+ "or explicit `scope`/`replaces-base` configuration in dependabot.yml. " \
716
+ "Registry: #{@source}"
717
+ super(msg)
718
+ end
719
+ end
720
+
690
721
  class PrivateSourceAuthenticationFailure < DependabotError
691
722
  extend T::Sig
692
723
 
@@ -709,10 +740,10 @@ module Dependabot
709
740
  sig { returns(String) }
710
741
  attr_reader :source
711
742
 
712
- sig { params(source: T.nilable(String)).void }
713
- def initialize(source)
743
+ sig { params(source: T.nilable(String), error_message: T.nilable(String)).void }
744
+ def initialize(source, error_message = nil)
714
745
  @source = T.let(sanitize_source(T.must(source)), String)
715
- msg = "Bad response error while accessing source: #{@source}"
746
+ msg = error_message ? sanitize_source(error_message) : "Bad response error while accessing source: #{@source}"
716
747
  super(msg)
717
748
  end
718
749
  end
@@ -901,6 +932,46 @@ module Dependabot
901
932
  # Raised by UpdateChecker if all candidate updates are ignored
902
933
  class AllVersionsIgnored < DependabotError; end
903
934
 
935
+ # Raised when regenerating a lockfile would introduce or change a transitive
936
+ # (indirect) dependency to a version that matches a configured blocked version.
937
+ # The offending change is rejected so the blocked version is never shipped,
938
+ # while other dependencies are still allowed to update.
939
+ class BlockedDependencyVersion < DependabotError
940
+ extend T::Sig
941
+
942
+ sig { returns(String) }
943
+ attr_reader :dependency_name
944
+
945
+ sig { returns(String) }
946
+ attr_reader :blocked_version
947
+
948
+ sig { returns(String) }
949
+ attr_reader :version_requirement
950
+
951
+ sig { returns(T.nilable(String)) }
952
+ attr_reader :reason
953
+
954
+ sig do
955
+ params(
956
+ dependency_name: String,
957
+ blocked_version: String,
958
+ version_requirement: String,
959
+ reason: T.nilable(String)
960
+ ).void
961
+ end
962
+ def initialize(dependency_name:, blocked_version:, version_requirement:, reason: nil)
963
+ @dependency_name = dependency_name
964
+ @blocked_version = blocked_version
965
+ @version_requirement = version_requirement
966
+ @reason = reason
967
+
968
+ msg = "Update blocked: transitive dependency #{dependency_name} #{blocked_version} " \
969
+ "matches blocked version requirement '#{version_requirement}'"
970
+ msg += " (reason: #{reason})" if reason && !reason.empty?
971
+ super(msg)
972
+ end
973
+ end
974
+
904
975
  # Raised by FileParser if processing may execute external code in the update context
905
976
  class UnexpectedExternalCode < DependabotError; end
906
977
 
@@ -33,7 +33,7 @@ module Dependabot
33
33
  @dependencies.values.filter_map(&:combined)
34
34
  end
35
35
 
36
- sig { params(dep: Dependabot::Dependency).returns(T.untyped) }
36
+ sig { params(dep: Dependabot::Dependency).returns(T.self_type) }
37
37
  def <<(dep)
38
38
  T.must(@dependencies[key_for_dependency(dep)]) << dep
39
39
  self
@@ -28,6 +28,11 @@ module Dependabot
28
28
  sig { returns(T::Hash[Symbol, T.untyped]) }
29
29
  attr_reader :options
30
30
 
31
+ sig { returns(T::Boolean) }
32
+ def reject_external_code?
33
+ @reject_external_code
34
+ end
35
+
31
36
  sig do
32
37
  params(
33
38
  dependency_files: T::Array[Dependabot::DependencyFile],
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -120,25 +120,46 @@ module Dependabot
120
120
 
121
121
  sig do
122
122
  overridable
123
- .params(parameters: T::Hash[Symbol, T.untyped])
123
+ .params(
124
+ name: String,
125
+ content: T.nilable(String),
126
+ directory: String,
127
+ type: String,
128
+ support_file: T::Boolean,
129
+ vendored_file: T::Boolean,
130
+ symlink_target: T.nilable(String),
131
+ content_encoding: String,
132
+ deleted: T::Boolean,
133
+ operation: String,
134
+ mode: T.nilable(String)
135
+ )
124
136
  .returns(Dependabot::DependencyFile)
125
137
  end
126
- def create_dependency_file(parameters)
138
+ def create_dependency_file(
139
+ name:,
140
+ content: nil,
141
+ directory: "/",
142
+ type: "file",
143
+ support_file: false,
144
+ vendored_file: false,
145
+ symlink_target: nil,
146
+ content_encoding: Dependabot::DependencyFile::ContentEncoding::UTF_8,
147
+ deleted: false,
148
+ operation: Dependabot::DependencyFile::Operation::UPDATE,
149
+ mode: nil
150
+ )
127
151
  Dependabot::DependencyFile.new(
128
- name: parameters.fetch(:name),
129
- content: parameters[:content],
130
- directory: parameters.fetch(:directory, "/"),
131
- type: parameters.fetch(:type, "file"),
132
- support_file: parameters.fetch(:support_file, false),
133
- vendored_file: parameters.fetch(:vendored_file, false),
134
- symlink_target: parameters[:symlink_target],
135
- content_encoding: parameters.fetch(
136
- :content_encoding,
137
- Dependabot::DependencyFile::ContentEncoding::UTF_8
138
- ),
139
- deleted: parameters.fetch(:deleted, false),
140
- operation: parameters.fetch(:operation, Dependabot::DependencyFile::Operation::UPDATE),
141
- mode: parameters[:mode]
152
+ name: name,
153
+ content: content,
154
+ directory: directory,
155
+ type: type,
156
+ support_file: support_file,
157
+ vendored_file: vendored_file,
158
+ symlink_target: symlink_target,
159
+ content_encoding: content_encoding,
160
+ deleted: deleted,
161
+ operation: operation,
162
+ mode: mode
142
163
  )
143
164
  end
144
165
  end
@@ -31,27 +31,51 @@ module Dependabot
31
31
 
32
32
  private
33
33
 
34
+ # VendorUpdater always flags files as vendored, so it accepts but ignores
35
+ # the vendored_file argument. The parameter must stay to keep the override
36
+ # signature compatible with ArtifactUpdater#create_dependency_file.
34
37
  sig do
35
38
  override
36
- .params(parameters: T::Hash[Symbol, T.untyped])
39
+ .params(
40
+ name: String,
41
+ content: T.nilable(String),
42
+ directory: String,
43
+ type: String,
44
+ support_file: T::Boolean,
45
+ vendored_file: T::Boolean,
46
+ symlink_target: T.nilable(String),
47
+ content_encoding: String,
48
+ deleted: T::Boolean,
49
+ operation: String,
50
+ mode: T.nilable(String)
51
+ )
37
52
  .returns(Dependabot::DependencyFile)
38
53
  end
39
- def create_dependency_file(parameters)
40
- Dependabot::DependencyFile.new(
41
- name: parameters.fetch(:name),
42
- content: parameters[:content],
43
- directory: parameters.fetch(:directory, "/"),
44
- type: parameters.fetch(:type, "file"),
45
- support_file: parameters.fetch(:support_file, false),
54
+ def create_dependency_file(
55
+ name:,
56
+ content: nil,
57
+ directory: "/",
58
+ type: "file",
59
+ support_file: false,
60
+ vendored_file: false, # rubocop:disable Lint/UnusedMethodArgument
61
+ symlink_target: nil,
62
+ content_encoding: Dependabot::DependencyFile::ContentEncoding::UTF_8,
63
+ deleted: false,
64
+ operation: Dependabot::DependencyFile::Operation::UPDATE,
65
+ mode: nil
66
+ )
67
+ super(
68
+ name: name,
69
+ content: content,
70
+ directory: directory,
71
+ type: type,
72
+ support_file: support_file,
46
73
  vendored_file: true,
47
- symlink_target: parameters[:symlink_target],
48
- content_encoding: parameters.fetch(
49
- :content_encoding,
50
- Dependabot::DependencyFile::ContentEncoding::UTF_8
51
- ),
52
- deleted: parameters.fetch(:deleted, false),
53
- operation: parameters.fetch(:operation, Dependabot::DependencyFile::Operation::UPDATE),
54
- mode: parameters[:mode]
74
+ symlink_target: symlink_target,
75
+ content_encoding: content_encoding,
76
+ deleted: deleted,
77
+ operation: operation,
78
+ mode: mode
55
79
  )
56
80
  end
57
81
  end
@@ -14,6 +14,7 @@ require "dependabot/source"
14
14
  require "dependabot/dependency"
15
15
  require "dependabot/credential"
16
16
  require "dependabot/git_metadata_fetcher"
17
+ require "dependabot/git_tag_details"
17
18
  module Dependabot
18
19
  # rubocop:disable Metrics/ClassLength
19
20
  class GitCommitChecker
@@ -159,31 +160,31 @@ module Dependabot
159
160
  local_repo_git_metadata_fetcher.head_commit_for_ref(name)
160
161
  end
161
162
 
162
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
163
+ sig { returns(T.nilable(Dependabot::GitTagDetails)) }
163
164
  def local_ref_for_latest_version_matching_existing_precision
164
165
  allowed_refs = local_tag_for_pinned_sha ? allowed_version_tags : allowed_version_refs
165
166
 
166
167
  max_local_tag_for_current_precision(allowed_refs)
167
168
  end
168
169
 
169
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
170
+ sig { returns(T.nilable(Dependabot::GitTagDetails)) }
170
171
  def local_ref_for_latest_version_lower_precision
171
172
  allowed_refs = local_tag_for_pinned_sha ? allowed_version_tags : allowed_version_refs
172
173
 
173
174
  max_local_tag_for_lower_precision(allowed_refs)
174
175
  end
175
176
 
176
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
177
+ sig { returns(T.nilable(Dependabot::GitTagDetails)) }
177
178
  def local_tag_for_latest_version
178
179
  max_local_tag(allowed_version_tags)
179
180
  end
180
181
 
181
- sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
182
+ sig { returns(T::Array[Dependabot::GitTagDetails]) }
182
183
  def local_tags_for_allowed_versions_matching_existing_precision
183
184
  select_matching_existing_precision(allowed_version_tags).filter_map { |t| to_local_tag(t) }
184
185
  end
185
186
 
186
- sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
187
+ sig { returns(T::Array[Dependabot::GitTagDetails]) }
187
188
  def local_tags_for_allowed_versions
188
189
  allowed_version_tags.filter_map { |t| to_local_tag(t) }
189
190
  end
@@ -272,7 +273,7 @@ module Dependabot
272
273
  local_tags_matching_sha(commit_sha).map(&:name)
273
274
  end
274
275
 
275
- sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
276
+ sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(Dependabot::GitTagDetails)) }
276
277
  def max_local_tag(tags)
277
278
  max_version_tag = tags.max_by { |t| version_from_tag(t) }
278
279
 
@@ -295,12 +296,12 @@ module Dependabot
295
296
  sig { returns(T::Array[String]) }
296
297
  attr_reader :ignored_versions
297
298
 
298
- sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
299
+ sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(Dependabot::GitTagDetails)) }
299
300
  def max_local_tag_for_current_precision(tags)
300
301
  max_local_tag(select_matching_existing_precision(tags))
301
302
  end
302
303
 
303
- sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
304
+ sig { params(tags: T::Array[Dependabot::GitRef]).returns(T.nilable(Dependabot::GitTagDetails)) }
304
305
  def max_local_tag_for_lower_precision(tags)
305
306
  max_local_tag(select_lower_precision(tags))
306
307
  end
@@ -553,17 +554,17 @@ module Dependabot
553
554
  prefix.length > 1 ? prefix.gsub(/v$/i, "") : prefix.gsub(/^v$/i, "")
554
555
  end
555
556
 
556
- sig { params(tag: T.nilable(Dependabot::GitRef)).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
557
+ sig { params(tag: T.nilable(Dependabot::GitRef)).returns(T.nilable(Dependabot::GitTagDetails)) }
557
558
  def to_local_tag(tag)
558
559
  return unless tag
559
560
 
560
561
  version = version_from_tag(tag)
561
- {
562
+ GitTagDetails.new(
562
563
  tag: tag.name,
563
564
  version: version,
564
565
  commit_sha: tag.commit_sha,
565
566
  tag_sha: tag.ref_sha
566
- }
567
+ )
567
568
  end
568
569
 
569
570
  sig { returns(T.nilable(String)) }
@@ -0,0 +1,114 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+ require "dependabot/clients/github_with_retries"
6
+ require "dependabot/shared_helpers"
7
+ require "dependabot/source"
8
+
9
+ module Dependabot
10
+ # Shared logic for resolving release dates from git-based sources for cooldown
11
+ # purposes. Used by ecosystems that rely on git tags (pre-commit, GitHub Actions)
12
+ # rather than package registries.
13
+ #
14
+ # Priority: GitHub Release published_at > tag creation date (for-each-ref) > commit date.
15
+ #
16
+ # Including classes must implement:
17
+ # - `cooldown_source_url` — returns the git source URL
18
+ # - `cooldown_credentials` — returns the credentials array
19
+ module GitCooldownDateResolver
20
+ extend T::Sig
21
+ extend T::Helpers
22
+
23
+ abstract!
24
+
25
+ # The git source URL for the dependency (e.g. "https://github.com/owner/repo")
26
+ sig { abstract.returns(T.nilable(String)) }
27
+ def cooldown_source_url; end
28
+
29
+ # Credentials for GitHub API access
30
+ sig { abstract.returns(T::Array[Dependabot::Credential]) }
31
+ def cooldown_credentials; end
32
+
33
+ # Strips the `tags/` prefix that GitCommitChecker may add when the pinned
34
+ # ref starts with `tags/`, preventing construction of invalid refs like
35
+ # `refs/tags/tags/v1.0.0`.
36
+ sig { params(tag_name: String).returns(String) }
37
+ def normalize_tag_name(tag_name)
38
+ tag_name.delete_prefix("tags/")
39
+ end
40
+
41
+ # Resolves the best available date for a candidate tag.
42
+ # Priority: GitHub Release published_at > tag creation date > commit date.
43
+ sig { params(tag_name: String, commit_sha: String).returns(Time) }
44
+ def resolve_candidate_date(tag_name, commit_sha)
45
+ releases = cached_github_releases
46
+ unless releases.empty?
47
+ release = releases.find { |r| r.tag_name == tag_name }
48
+ return release.published_at if release&.published_at
49
+ end
50
+
51
+ tag_creation_date(tag_name, commit_sha)
52
+ end
53
+
54
+ # Looks up the GitHub Release published_at date for a given tag name.
55
+ # Returns nil if no release exists for this tag.
56
+ sig { params(tag_name: String).returns(T.nilable(Time)) }
57
+ def github_release_published_at(tag_name)
58
+ releases = cached_github_releases
59
+ return nil if releases.empty?
60
+
61
+ release = releases.find { |r| r.tag_name == tag_name }
62
+ return nil unless release&.published_at
63
+
64
+ release.published_at
65
+ rescue StandardError => e
66
+ Dependabot.logger.debug("Error fetching GitHub release date for #{tag_name}: #{e.message}")
67
+ nil
68
+ end
69
+
70
+ # Returns the tag creation date for cooldown purposes (used inside bare clone).
71
+ # Priority: tag creation date from for-each-ref > commit date fallback.
72
+ sig { params(tag_name: String, commit_sha: String).returns(Time) }
73
+ def tag_creation_date(tag_name, commit_sha)
74
+ tag_date_str = SharedHelpers.run_shell_command(
75
+ "git for-each-ref --format=\"%(creatordate:iso)\" \"refs/tags/#{tag_name}\"",
76
+ fingerprint: "git for-each-ref --format=\"%(creatordate:iso)\" \"refs/tags/<tag_name>\""
77
+ ).strip
78
+
79
+ if tag_date_str.empty?
80
+ tag_date_str = SharedHelpers.run_shell_command(
81
+ "git show --no-patch --format=\"%cd\" --date=iso #{commit_sha}",
82
+ fingerprint: "git show --no-patch --format=\"%cd\" --date=iso <commit_sha>"
83
+ ).strip
84
+ end
85
+
86
+ Time.parse(tag_date_str)
87
+ end
88
+
89
+ # Fetches and caches GitHub releases for the dependency source.
90
+ # Returns an empty array for non-GitHub sources.
91
+ sig { returns(T::Array[T.untyped]) } # rubocop:disable Sorbet/ForbidTUntyped
92
+ def cached_github_releases
93
+ @cached_github_releases ||= T.let(
94
+ begin
95
+ url = cooldown_source_url
96
+ source = Source.from_url(url)
97
+ if source&.provider == "github"
98
+ client = Dependabot::Clients::GithubWithRetries.for_source(
99
+ source: T.must(source),
100
+ credentials: cooldown_credentials
101
+ )
102
+ client.releases(T.must(source).repo, per_page: 100)
103
+ else
104
+ []
105
+ end
106
+ rescue StandardError => e
107
+ Dependabot.logger.debug("Error fetching GitHub releases: #{e.message}")
108
+ []
109
+ end,
110
+ T.nilable(T::Array[T.untyped]) # rubocop:disable Sorbet/ForbidTUntyped
111
+ )
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,78 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ # A git tag (or ref) resolved to a version, as produced by GitCommitChecker's
8
+ # local_tag_for_* / local_ref_for_* methods, e.g.:
9
+ #
10
+ # {
11
+ # tag: "v1.2.0",
12
+ # version: Dependabot::Version.new("1.2.0"),
13
+ # commit_sha: "a1b2c3...",
14
+ # tag_sha: "d4e5f6..." # nil for lightweight tags
15
+ # }
16
+ #
17
+ # Distinct from Dependabot::GitTagWithDetail, which carries a tag name and
18
+ # release date for cooldown handling.
19
+ #
20
+ # Subclasses Hash so it is a drop-in replacement at the many call sites that
21
+ # read entries with [:key] / fetch and treat them as
22
+ # T::Hash[Symbol, T.untyped], while exposing typed readers for the well-known
23
+ # keys. Instances compare equal (Hash#==) to plain hashes with the same
24
+ # content, so existing comparisons and API payloads are unaffected.
25
+ class GitTagDetails < Hash
26
+ extend T::Sig
27
+ extend T::Generic
28
+
29
+ # The values are heterogeneous (strings and a version object), so this
30
+ # bridge class is necessarily untyped at the Hash level; the typed readers
31
+ # below are the migration path.
32
+ # rubocop:disable Sorbet/ForbidTUntyped
33
+ K = type_member { { fixed: Symbol } }
34
+ V = type_member { { fixed: T.untyped } }
35
+ Elem = type_member { { fixed: [Symbol, T.untyped] } }
36
+ # rubocop:enable Sorbet/ForbidTUntyped
37
+
38
+ sig do
39
+ params(
40
+ tag: String,
41
+ version: T.nilable(Gem::Version),
42
+ commit_sha: T.nilable(String),
43
+ tag_sha: T.nilable(String)
44
+ ).void
45
+ end
46
+ def initialize(tag:, version: nil, commit_sha: nil, tag_sha: nil)
47
+ super()
48
+ self[:tag] = tag
49
+ self[:version] = version
50
+ self[:commit_sha] = commit_sha
51
+ self[:tag_sha] = tag_sha
52
+ end
53
+
54
+ # The tag or ref name, e.g. "v1.2.0".
55
+ sig { returns(String) }
56
+ def tag
57
+ self[:tag]
58
+ end
59
+
60
+ # The version parsed from the tag name.
61
+ sig { returns(T.nilable(Gem::Version)) }
62
+ def version
63
+ self[:version]
64
+ end
65
+
66
+ # The SHA of the commit the tag points at.
67
+ sig { returns(T.nilable(String)) }
68
+ def commit_sha
69
+ self[:commit_sha]
70
+ end
71
+
72
+ # The SHA of the tag object itself (nil for lightweight tags).
73
+ sig { returns(T.nilable(String)) }
74
+ def tag_sha
75
+ self[:tag_sha]
76
+ end
77
+ end
78
+ end
@@ -145,7 +145,7 @@ module Dependabot
145
145
  end
146
146
 
147
147
  # TODO: Refactor me so that Composer doesn't need to be special cased
148
- sig { params(requirements: T.nilable(T::Array[T::Hash[Symbol, T.untyped]])).returns(T::Boolean) }
148
+ sig { params(requirements: T.nilable(T::Array[Dependabot::DependencyRequirement])).returns(T::Boolean) }
149
149
  def git_source?(requirements)
150
150
  # Special case Composer, which uses git as a source but handles tags
151
151
  # internally
@@ -59,8 +59,8 @@ module Dependabot
59
59
 
60
60
  # Converts the Notice object to a hash.
61
61
  # @return [Hash] The hash representation of the notice.
62
- sig { returns(T::Hash[Symbol, T.untyped]) }
63
- def to_hash
62
+ sig { returns(T::Hash[Symbol, T.any(String, T::Boolean)]) }
63
+ def to_h
64
64
  {
65
65
  mode: @mode,
66
66
  type: @type,
@@ -1,6 +1,7 @@
1
1
  # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "digest"
4
5
  require "sorbet-runtime"
5
6
 
6
7
  module Dependabot
@@ -70,9 +71,10 @@ module Dependabot
70
71
  sanitized_name = sanitized_name.gsub("/", separator)
71
72
 
72
73
  # Shorten the ref in case users refs have length limits
73
- if max_length && (sanitized_name.length > T.must(max_length))
74
- sha = T.must(Digest::SHA1.hexdigest(sanitized_name)[0, T.must(max_length)])
75
- sanitized_name[[T.must(max_length) - sha.size, 0].max..] = sha
74
+ branch_name_max_length = max_length
75
+ if branch_name_max_length && (sanitized_name.length > branch_name_max_length)
76
+ sha = T.must(Digest::SHA1.hexdigest(sanitized_name)[0, branch_name_max_length])
77
+ sanitized_name[[branch_name_max_length - sha.size, 0].max..] = sha
76
78
  end
77
79
 
78
80
  sanitized_name
@@ -119,21 +119,21 @@ module Dependabot
119
119
 
120
120
  sig { params(dependency: Dependabot::Dependency).returns(String) }
121
121
  def sanitized_requirement(dependency)
122
- new_library_requirement(dependency)
123
- .delete(" ")
124
- .gsub("!=", "neq-")
125
- .gsub(">=", "gte-")
126
- .gsub("<=", "lte-")
127
- .gsub("~>", "tw-")
128
- .gsub("^", "tw-")
129
- .gsub("||", "or-")
130
- .gsub("~", "approx-")
131
- .gsub("~=", "tw-")
132
- .gsub(/==*/, "eq-")
133
- .gsub(">", "gt-")
134
- .gsub("<", "lt-")
135
- .gsub("*", "star")
136
- .gsub(",", "-and-")
122
+ T.must(new_library_requirement(dependency))
123
+ .delete(" ")
124
+ .gsub("!=", "neq-")
125
+ .gsub(">=", "gte-")
126
+ .gsub("<=", "lte-")
127
+ .gsub("~>", "tw-")
128
+ .gsub("^", "tw-")
129
+ .gsub("||", "or-")
130
+ .gsub("~", "approx-")
131
+ .gsub("~=", "tw-")
132
+ .gsub(/==*/, "eq-")
133
+ .gsub(">", "gt-")
134
+ .gsub("<", "lt-")
135
+ .gsub("*", "star")
136
+ .gsub(",", "-and-")
137
137
  end
138
138
 
139
139
  sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
@@ -176,7 +176,7 @@ module Dependabot
176
176
  previous_ref(dependency) != new_ref(dependency)
177
177
  end
178
178
 
179
- sig { params(dependency: Dependabot::Dependency).returns(T.untyped) }
179
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(String)) }
180
180
  def new_library_requirement(dependency)
181
181
  updated_reqs =
182
182
  dependency.requirements - T.must(dependency.previous_requirements)
@@ -197,7 +197,7 @@ module Dependabot
197
197
  milestone: nil,
198
198
  branch_name_separator: "/",
199
199
  branch_name_prefix: "dependabot",
200
- branch_name_max_length: nil,
200
+ branch_name_max_length: 100,
201
201
  label_language: false,
202
202
  automerge_candidate: false,
203
203
  github_redirection_service: DEFAULT_GITHUB_REDIRECTION_SERVICE,
@@ -40,7 +40,7 @@ module Dependabot
40
40
  sig { returns(T.nilable(String)) }
41
41
  attr_reader :commit_message
42
42
 
43
- sig { returns(T::Hash[Symbol, T.untyped]) }
43
+ sig { returns(T::Hash[Symbol, Integer]) }
44
44
  attr_reader :provider_metadata
45
45
 
46
46
  sig do
@@ -54,7 +54,7 @@ module Dependabot
54
54
  author_details: T.nilable(T::Hash[Symbol, String]),
55
55
  signature_key: T.nilable(String),
56
56
  commit_message: T.nilable(String),
57
- provider_metadata: T::Hash[Symbol, T.untyped]
57
+ provider_metadata: T::Hash[Symbol, Integer]
58
58
  )
59
59
  .void
60
60
  end
@@ -84,7 +84,7 @@ module Dependabot
84
84
 
85
85
  # TODO: Each implementation returns a client-specific type.
86
86
  # We should standardise this to return a `Dependabot::Branch` type instead.
87
- sig { returns(T.untyped) }
87
+ sig { returns(T.untyped) } # rubocop:disable Sorbet/ForbidTUntyped
88
88
  def update
89
89
  case source.provider
90
90
  when "github" then github_updater.update
@@ -120,7 +120,7 @@ module Dependabot
120
120
  files: files,
121
121
  credentials: credentials,
122
122
  pull_request_number: pull_request_number,
123
- target_project_id: T.cast(provider_metadata[:target_project_id], T.nilable(Integer))
123
+ target_project_id: provider_metadata[:target_project_id]
124
124
  )
125
125
  end
126
126
 
@@ -3,6 +3,8 @@
3
3
 
4
4
  require "sorbet-runtime"
5
5
 
6
+ require "dependabot/dependency_requirement"
7
+
6
8
  module Dependabot
7
9
  module RequirementsUpdater
8
10
  module Base
@@ -15,7 +17,7 @@ module Dependabot
15
17
 
16
18
  interface!
17
19
 
18
- sig { abstract.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
20
+ sig { abstract.returns(T::Array[Dependabot::DependencyRequirement]) }
19
21
  def updated_requirements; end
20
22
 
21
23
  private
@@ -182,7 +182,7 @@ module Dependabot
182
182
  dependency.version
183
183
  end
184
184
 
185
- sig { overridable.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
185
+ sig { overridable.returns(T::Array[Dependabot::DependencyRequirement]) }
186
186
  def updated_requirements
187
187
  raise NotImplementedError
188
188
  end
@@ -226,6 +226,20 @@ module Dependabot
226
226
 
227
227
  private
228
228
 
229
+ # Wraps an array of raw requirement hashes (e.g. the output of an
230
+ # ecosystem RequirementsUpdater) into typed DependencyRequirement
231
+ # objects, so updated_requirements overrides can satisfy the typed
232
+ # return contract. Entries that are already DependencyRequirement
233
+ # instances are returned as-is; plain hashes are wrapped. Re-wrapping
234
+ # would produce a value-equal copy, so skipping it avoids needless
235
+ # allocations.
236
+ sig { params(requirements: T::Array[T::Hash[Symbol, T.untyped]]).returns(T::Array[Dependabot::DependencyRequirement]) }
237
+ def wrap_requirements(requirements)
238
+ requirements.map do |requirement|
239
+ requirement.is_a?(Dependabot::DependencyRequirement) ? requirement : Dependabot::DependencyRequirement.create(requirement)
240
+ end
241
+ end
242
+
229
243
  sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
230
244
  def active_advisories
231
245
  security_advisories.select { |a| a.vulnerable?(T.must(current_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.381.0"
5
+ VERSION = "0.383.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.381.0
4
+ version: 0.383.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -534,6 +534,7 @@ files:
534
534
  - lib/dependabot/dependency_graphers/base.rb
535
535
  - lib/dependabot/dependency_graphers/generic.rb
536
536
  - lib/dependabot/dependency_group.rb
537
+ - lib/dependabot/dependency_requirement.rb
537
538
  - lib/dependabot/ecosystem.rb
538
539
  - lib/dependabot/errors.rb
539
540
  - lib/dependabot/experiments.rb
@@ -551,8 +552,10 @@ files:
551
552
  - lib/dependabot/file_updaters/base.rb
552
553
  - lib/dependabot/file_updaters/vendor_updater.rb
553
554
  - lib/dependabot/git_commit_checker.rb
555
+ - lib/dependabot/git_cooldown_date_resolver.rb
554
556
  - lib/dependabot/git_metadata_fetcher.rb
555
557
  - lib/dependabot/git_ref.rb
558
+ - lib/dependabot/git_tag_details.rb
556
559
  - lib/dependabot/git_tag_with_detail.rb
557
560
  - lib/dependabot/logger.rb
558
561
  - lib/dependabot/metadata_finders.rb
@@ -617,7 +620,7 @@ licenses:
617
620
  - MIT
618
621
  metadata:
619
622
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
620
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.381.0
623
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.383.0
621
624
  rdoc_options: []
622
625
  require_paths:
623
626
  - lib