dependabot-npm_and_yarn 0.294.0 → 0.295.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dependabot/npm_and_yarn/constraint_helper.rb +306 -0
- data/lib/dependabot/npm_and_yarn/file_parser/bun_lock.rb +0 -1
- data/lib/dependabot/npm_and_yarn/file_parser.rb +38 -1
- data/lib/dependabot/npm_and_yarn/file_updater/package_json_updater.rb +25 -2
- data/lib/dependabot/npm_and_yarn/file_updater/pnpm_workspace_updater.rb +140 -0
- data/lib/dependabot/npm_and_yarn/file_updater.rb +38 -1
- data/lib/dependabot/npm_and_yarn/npm_package_manager.rb +4 -10
- data/lib/dependabot/npm_and_yarn/package_manager.rb +55 -22
- data/lib/dependabot/npm_and_yarn/version_selector.rb +32 -7
- data/lib/dependabot/npm_and_yarn.rb +19 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14edb941111a95dc07d83e6449c01bd3b0f4b92c4e3168acd19d31ce1fa96183
|
4
|
+
data.tar.gz: 0ec9a05bb2ebda169ad58028bfc2c0465ef0bbf192265f317570842f5aaa81b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5822f02ef7a153c079c70f91724efe53250ea696af5c0a995cfb86537be568e155344eb9cbb1481c2f2be01c080d486a0b98300dd318aa72c6271b36bd0d906e
|
7
|
+
data.tar.gz: 85bf7be8bb171f92b38a67d7c0b12b6c0e68056a6cdf09a0b20d8c2054517276e614e1bc7fd01bc366a53e917ba54c032ee816c977d47ca4eb11c22afda6c2ee
|
@@ -0,0 +1,306 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module NpmAndYarn
|
8
|
+
module ConstraintHelper
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
INVALID = "invalid" # Invalid constraint
|
12
|
+
# Regex Components for Semantic Versioning
|
13
|
+
DIGIT = "\\d+" # Matches a single number (e.g., "1")
|
14
|
+
PRERELEASE = "(?:-[a-zA-Z0-9.-]+)?" # Matches optional pre-release tag (e.g., "-alpha")
|
15
|
+
BUILD_METADATA = "(?:\\+[a-zA-Z0-9.-]+)?" # Matches optional build metadata (e.g., "+001")
|
16
|
+
DOT = "\\." # Matches a literal dot "."
|
17
|
+
|
18
|
+
# Matches semantic versions:
|
19
|
+
VERSION = T.let("#{DIGIT}(?:\\.#{DIGIT}){0,2}#{PRERELEASE}#{BUILD_METADATA}".freeze, String)
|
20
|
+
|
21
|
+
VERSION_REGEX = T.let(/\A#{VERSION}\z/o, Regexp)
|
22
|
+
|
23
|
+
# SemVer regex: major.minor.patch[-prerelease][+build]
|
24
|
+
SEMVER_REGEX = /^(?<version>\d+\.\d+\.\d+)(?:-(?<prerelease>[a-zA-Z0-9.-]+))?(?:\+(?<build>[a-zA-Z0-9.-]+))?$/
|
25
|
+
|
26
|
+
# Constraint Types as Constants
|
27
|
+
CARET_CONSTRAINT_REGEX = T.let(/^\^(#{VERSION})$/, Regexp)
|
28
|
+
TILDE_CONSTRAINT_REGEX = T.let(/^~(#{VERSION})$/, Regexp)
|
29
|
+
EXACT_CONSTRAINT_REGEX = T.let(/^(#{VERSION})$/, Regexp)
|
30
|
+
GREATER_THAN_EQUAL_REGEX = T.let(/^>=(#{VERSION})$/, Regexp)
|
31
|
+
LESS_THAN_EQUAL_REGEX = T.let(/^<=(#{VERSION})$/, Regexp)
|
32
|
+
GREATER_THAN_REGEX = T.let(/^>(#{VERSION})$/, Regexp)
|
33
|
+
LESS_THAN_REGEX = T.let(/^<(#{VERSION})$/, Regexp)
|
34
|
+
WILDCARD_REGEX = T.let(/^\*$/, Regexp)
|
35
|
+
|
36
|
+
# Unified Regex for Valid Constraints
|
37
|
+
VALID_CONSTRAINT_REGEX = T.let(Regexp.union(
|
38
|
+
CARET_CONSTRAINT_REGEX,
|
39
|
+
TILDE_CONSTRAINT_REGEX,
|
40
|
+
EXACT_CONSTRAINT_REGEX,
|
41
|
+
GREATER_THAN_EQUAL_REGEX,
|
42
|
+
LESS_THAN_EQUAL_REGEX,
|
43
|
+
GREATER_THAN_REGEX,
|
44
|
+
LESS_THAN_REGEX,
|
45
|
+
WILDCARD_REGEX
|
46
|
+
).freeze, Regexp)
|
47
|
+
|
48
|
+
# Validates if the provided semver constraint expression from a `package.json` is valid.
|
49
|
+
# A valid semver constraint expression in `package.json` can consist of multiple groups
|
50
|
+
# separated by logical OR (`||`). Within each group, space-separated constraints are treated
|
51
|
+
# as logical AND. Each individual constraint must conform to the semver rules defined in
|
52
|
+
# `VALID_CONSTRAINT_REGEX`.
|
53
|
+
#
|
54
|
+
# Example (valid `package.json` semver constraints):
|
55
|
+
# ">=1.2.3 <2.0.0 || ~3.4.5" → Valid (space-separated constraints are AND, `||` is OR)
|
56
|
+
# "^1.0.0 || >=2.0.0 <3.0.0" → Valid (caret and range constraints combined)
|
57
|
+
# "1.2.3" → Valid (exact version)
|
58
|
+
# "*" → Valid (wildcard allows any version)
|
59
|
+
#
|
60
|
+
# Example (invalid `package.json` semver constraints):
|
61
|
+
# ">=1.2.3 && <2.0.0" → Invalid (`&&` is not valid in semver)
|
62
|
+
# ">=x.y.z" → Invalid (non-numeric version parts are not valid)
|
63
|
+
# "1.2.3 ||" → Invalid (trailing OR operator)
|
64
|
+
#
|
65
|
+
# @param constraint_expression [String] The semver constraint expression from `package.json` to validate.
|
66
|
+
# @return [T::Boolean] Returns true if the constraint expression is valid semver, false otherwise.
|
67
|
+
sig { params(constraint_expression: T.nilable(String)).returns(T::Boolean) }
|
68
|
+
def self.valid_constraint_expression?(constraint_expression)
|
69
|
+
normalized_constraint = constraint_expression&.strip
|
70
|
+
|
71
|
+
# Treat nil or empty input as valid (no constraints)
|
72
|
+
return true if normalized_constraint.nil? || normalized_constraint.empty?
|
73
|
+
|
74
|
+
# Split the expression by logical OR (`||`) into groups
|
75
|
+
normalized_constraint.split("||").reject(&:empty?).all? do |or_group|
|
76
|
+
or_group.split(/\s+/).reject(&:empty?).all? do |and_constraint|
|
77
|
+
and_constraint.match?(VALID_CONSTRAINT_REGEX)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Extract unique constraints from the given constraint expression.
|
83
|
+
# @param constraint_expression [T.nilable(String)] The semver constraint expression.
|
84
|
+
# @return [T::Array[String]] The list of unique Ruby-compatible constraints.
|
85
|
+
sig do
|
86
|
+
params(
|
87
|
+
constraint_expression: T.nilable(String),
|
88
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version])
|
89
|
+
)
|
90
|
+
.returns(T.nilable(T::Array[String]))
|
91
|
+
end
|
92
|
+
def self.extract_constraints(constraint_expression, dependabot_versions = nil)
|
93
|
+
normalized_constraint = constraint_expression&.strip
|
94
|
+
return [] if normalized_constraint.nil? || normalized_constraint.empty?
|
95
|
+
|
96
|
+
parsed_constraints = parse_constraints(normalized_constraint, dependabot_versions)
|
97
|
+
|
98
|
+
return nil unless parsed_constraints
|
99
|
+
|
100
|
+
parsed_constraints.filter_map { |parsed| parsed[:constraint] }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Find the highest version from the given constraint expression.
|
104
|
+
# @param constraint_expression [T.nilable(String)] The semver constraint expression.
|
105
|
+
# @return [T.nilable(String)] The highest version, or nil if no versions are available.
|
106
|
+
sig do
|
107
|
+
params(
|
108
|
+
constraint_expression: T.nilable(String),
|
109
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version])
|
110
|
+
)
|
111
|
+
.returns(T.nilable(String))
|
112
|
+
end
|
113
|
+
def self.find_highest_version_from_constraint_expression(constraint_expression, dependabot_versions = nil)
|
114
|
+
normalized_constraint = constraint_expression&.strip
|
115
|
+
return nil if normalized_constraint.nil? || normalized_constraint.empty?
|
116
|
+
|
117
|
+
parsed_constraints = parse_constraints(normalized_constraint, dependabot_versions)
|
118
|
+
|
119
|
+
return nil unless parsed_constraints
|
120
|
+
|
121
|
+
parsed_constraints
|
122
|
+
.filter_map { |parsed| parsed[:version] } # Extract all versions
|
123
|
+
.max_by { |version| Version.new(version) }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Parse all constraints (split by logical OR `||`) and convert to Ruby-compatible constraints.
|
127
|
+
# Return:
|
128
|
+
# - `nil` if the constraint expression is invalid
|
129
|
+
# - `[]` if the constraint expression is valid but represents "no constraints"
|
130
|
+
# - An array of hashes for valid constraints with details about the constraint and version
|
131
|
+
sig do
|
132
|
+
params(
|
133
|
+
constraint_expression: T.nilable(String),
|
134
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version])
|
135
|
+
)
|
136
|
+
.returns(T.nilable(T::Array[T::Hash[Symbol, T.nilable(String)]]))
|
137
|
+
end
|
138
|
+
def self.parse_constraints(constraint_expression, dependabot_versions = nil)
|
139
|
+
normalized_constraint = constraint_expression&.strip
|
140
|
+
|
141
|
+
# Return an empty array for valid "no constraints" (nil or empty input)
|
142
|
+
return [] if normalized_constraint.nil? || normalized_constraint.empty?
|
143
|
+
|
144
|
+
# Return nil for invalid constraints
|
145
|
+
return nil unless valid_constraint_expression?(normalized_constraint)
|
146
|
+
|
147
|
+
# Parse valid constraints
|
148
|
+
constraints = normalized_constraint.split("||").flat_map do |or_group|
|
149
|
+
or_group.strip.split(/\s+/).map(&:strip)
|
150
|
+
end.then do |normalized_constraints| # rubocop:disable Style/MultilineBlockChain
|
151
|
+
to_ruby_constraints_with_versions(normalized_constraints, dependabot_versions)
|
152
|
+
end.uniq { |parsed| parsed[:constraint] } # Ensure uniqueness based on `:constraint` # rubocop:disable Style/MultilineBlockChain
|
153
|
+
constraints
|
154
|
+
end
|
155
|
+
|
156
|
+
sig do
|
157
|
+
params(
|
158
|
+
constraints: T::Array[String],
|
159
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version])
|
160
|
+
).returns(T::Array[T::Hash[Symbol, T.nilable(String)]])
|
161
|
+
end
|
162
|
+
def self.to_ruby_constraints_with_versions(constraints, dependabot_versions = [])
|
163
|
+
constraints.filter_map do |constraint|
|
164
|
+
parsed = to_ruby_constraint_with_version(constraint, dependabot_versions)
|
165
|
+
parsed if parsed && parsed[:constraint] # Only include valid constraints
|
166
|
+
end.uniq
|
167
|
+
end
|
168
|
+
|
169
|
+
# rubocop:disable Metrics/MethodLength
|
170
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
171
|
+
# rubocop:disable Metrics/AbcSize
|
172
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
173
|
+
# Converts a semver constraint to a Ruby-compatible constraint and extracts the version, if available.
|
174
|
+
# @param constraint [String] The semver constraint to parse.
|
175
|
+
# @return [T.nilable(T::Hash[Symbol, T.nilable(String)])] Returns the Ruby-compatible constraint and the version,
|
176
|
+
# if available, or nil if the constraint is invalid.
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# to_ruby_constraint_with_version("=1.2.3") # => { constraint: "=1.2.3", version: "1.2.3" }
|
180
|
+
# to_ruby_constraint_with_version("^1.2.3") # => { constraint: ">=1.2.3 <2.0.0", version: "1.2.3" }
|
181
|
+
# to_ruby_constraint_with_version("*") # => { constraint: nil, version: nil }
|
182
|
+
# to_ruby_constraint_with_version("invalid") # => nil
|
183
|
+
sig do
|
184
|
+
params(
|
185
|
+
constraint: String,
|
186
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version])
|
187
|
+
)
|
188
|
+
.returns(T.nilable(T::Hash[Symbol, T.nilable(String)]))
|
189
|
+
end
|
190
|
+
def self.to_ruby_constraint_with_version(constraint, dependabot_versions = [])
|
191
|
+
return nil if constraint.empty?
|
192
|
+
|
193
|
+
case constraint
|
194
|
+
when EXACT_CONSTRAINT_REGEX # Exact version, e.g., "1.2.3-alpha"
|
195
|
+
return unless Regexp.last_match
|
196
|
+
|
197
|
+
full_version = Regexp.last_match(1)
|
198
|
+
{ constraint: "=#{full_version}", version: full_version }
|
199
|
+
when CARET_CONSTRAINT_REGEX # Caret constraint, e.g., "^1.2.3"
|
200
|
+
return unless Regexp.last_match
|
201
|
+
|
202
|
+
full_version = Regexp.last_match(1)
|
203
|
+
_, major, minor = version_components(full_version)
|
204
|
+
return nil if major.nil?
|
205
|
+
|
206
|
+
ruby_constraint =
|
207
|
+
if major.to_i.zero?
|
208
|
+
minor.nil? ? ">=#{full_version} <1.0.0" : ">=#{full_version} <0.#{minor.to_i + 1}.0"
|
209
|
+
else
|
210
|
+
">=#{full_version} <#{major.to_i + 1}.0.0"
|
211
|
+
end
|
212
|
+
{ constraint: ruby_constraint, version: full_version }
|
213
|
+
when TILDE_CONSTRAINT_REGEX # Tilde constraint, e.g., "~1.2.3"
|
214
|
+
return unless Regexp.last_match
|
215
|
+
|
216
|
+
full_version = Regexp.last_match(1)
|
217
|
+
_, major, minor = version_components(full_version)
|
218
|
+
ruby_constraint =
|
219
|
+
if minor.nil?
|
220
|
+
">=#{full_version} <#{major.to_i + 1}.0.0"
|
221
|
+
else
|
222
|
+
">=#{full_version} <#{major}.#{minor.to_i + 1}.0"
|
223
|
+
end
|
224
|
+
{ constraint: ruby_constraint, version: full_version }
|
225
|
+
when GREATER_THAN_EQUAL_REGEX # Greater than or equal, e.g., ">=1.2.3"
|
226
|
+
|
227
|
+
return unless Regexp.last_match && Regexp.last_match(1)
|
228
|
+
|
229
|
+
found_version = highest_matching_version(
|
230
|
+
dependabot_versions,
|
231
|
+
T.must(Regexp.last_match(1))
|
232
|
+
) do |version, constraint_version|
|
233
|
+
version >= Version.new(constraint_version)
|
234
|
+
end
|
235
|
+
{ constraint: ">=#{Regexp.last_match(1)}", version: found_version&.to_s }
|
236
|
+
when LESS_THAN_EQUAL_REGEX # Less than or equal, e.g., "<=1.2.3"
|
237
|
+
return unless Regexp.last_match
|
238
|
+
|
239
|
+
full_version = Regexp.last_match(1)
|
240
|
+
{ constraint: "<=#{full_version}", version: full_version }
|
241
|
+
when GREATER_THAN_REGEX # Greater than, e.g., ">1.2.3"
|
242
|
+
return unless Regexp.last_match && Regexp.last_match(1)
|
243
|
+
|
244
|
+
found_version = highest_matching_version(
|
245
|
+
dependabot_versions,
|
246
|
+
T.must(Regexp.last_match(1))
|
247
|
+
) do |version, constraint_version|
|
248
|
+
version > Version.new(constraint_version)
|
249
|
+
end
|
250
|
+
{ constraint: ">#{Regexp.last_match(1)}", version: found_version&.to_s }
|
251
|
+
when LESS_THAN_REGEX # Less than, e.g., "<1.2.3"
|
252
|
+
return unless Regexp.last_match && Regexp.last_match(1)
|
253
|
+
|
254
|
+
found_version = highest_matching_version(
|
255
|
+
dependabot_versions,
|
256
|
+
T.must(Regexp.last_match(1))
|
257
|
+
) do |version, constraint_version|
|
258
|
+
version < Version.new(constraint_version)
|
259
|
+
end
|
260
|
+
{ constraint: "<#{Regexp.last_match(1)}", version: found_version&.to_s }
|
261
|
+
when WILDCARD_REGEX # Wildcard
|
262
|
+
{ constraint: nil, version: dependabot_versions&.max&.to_s } # Explicitly valid but no specific constraint
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
sig do
|
267
|
+
params(
|
268
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version]),
|
269
|
+
constraint_version: String,
|
270
|
+
condition: T.proc.params(version: Dependabot::Version, constraint: Dependabot::Version).returns(T::Boolean)
|
271
|
+
)
|
272
|
+
.returns(T.nilable(Dependabot::Version))
|
273
|
+
end
|
274
|
+
def self.highest_matching_version(dependabot_versions, constraint_version, &condition)
|
275
|
+
return unless dependabot_versions&.any?
|
276
|
+
|
277
|
+
# Returns the highest version that satisfies the condition, or nil if none.
|
278
|
+
dependabot_versions
|
279
|
+
.sort
|
280
|
+
.reverse
|
281
|
+
.find { |version| condition.call(version, Version.new(constraint_version)) } # rubocop:disable Performance/RedundantBlockCall
|
282
|
+
end
|
283
|
+
|
284
|
+
# rubocop:enable Metrics/MethodLength
|
285
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
286
|
+
# rubocop:enable Metrics/AbcSize
|
287
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
288
|
+
|
289
|
+
# Parses a semantic version string into its components as per the SemVer spec
|
290
|
+
# Example: "1.2.3-alpha+001" → ["1.2.3", "1", "2", "3", "alpha", "001"]
|
291
|
+
sig { params(full_version: T.nilable(String)).returns(T.nilable(T::Array[String])) }
|
292
|
+
def self.version_components(full_version)
|
293
|
+
return [] if full_version.nil?
|
294
|
+
|
295
|
+
match = full_version.match(SEMVER_REGEX)
|
296
|
+
return [] unless match
|
297
|
+
|
298
|
+
version = match[:version]
|
299
|
+
return [] unless version
|
300
|
+
|
301
|
+
major, minor, patch = version.split(".")
|
302
|
+
[version, major, minor, patch, match[:prerelease], match[:build]].compact
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
@@ -31,7 +31,6 @@ module Dependabot
|
|
31
31
|
version = content["lockfileVersion"]
|
32
32
|
raise_invalid!("expected 'lockfileVersion' to be an integer") unless version.is_a?(Integer)
|
33
33
|
raise_invalid!("expected 'lockfileVersion' to be >= 0") unless version >= 0
|
34
|
-
raise_invalid!("unsupported 'lockfileVersion' = #{version}") unless version.zero?
|
35
34
|
|
36
35
|
T.let(content, T.untyped)
|
37
36
|
end
|
@@ -55,10 +55,11 @@ module Dependabot
|
|
55
55
|
end
|
56
56
|
|
57
57
|
sig { override.returns(T::Array[Dependency]) }
|
58
|
-
def parse
|
58
|
+
def parse # rubocop:disable Metrics/PerceivedComplexity
|
59
59
|
dependency_set = DependencySet.new
|
60
60
|
dependency_set += manifest_dependencies
|
61
61
|
dependency_set += lockfile_dependencies
|
62
|
+
dependency_set += workspace_catalog_dependencies if enable_pnpm_workspace_catalog?
|
62
63
|
|
63
64
|
dependencies = Helpers.dependencies_with_all_versions_metadata(dependency_set)
|
64
65
|
|
@@ -93,6 +94,11 @@ module Dependabot
|
|
93
94
|
|
94
95
|
private
|
95
96
|
|
97
|
+
sig { returns(T.nilable(T::Boolean)) }
|
98
|
+
def enable_pnpm_workspace_catalog?
|
99
|
+
pnpm_workspace_yml && Dependabot::Experiments.enabled?(:enable_pnpm_workspace_catalog)
|
100
|
+
end
|
101
|
+
|
96
102
|
sig { returns(PackageManagerHelper) }
|
97
103
|
def package_manager_helper
|
98
104
|
@package_manager_helper ||= T.let(
|
@@ -168,6 +174,13 @@ module Dependabot
|
|
168
174
|
end, T.nilable(Dependabot::DependencyFile))
|
169
175
|
end
|
170
176
|
|
177
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
178
|
+
def pnpm_workspace_yml
|
179
|
+
@pnpm_workspace_yml ||= T.let(dependency_files.find do |f|
|
180
|
+
f.name.end_with?(PNPMPackageManager::PNPM_WS_YML_FILENAME)
|
181
|
+
end, T.nilable(Dependabot::DependencyFile))
|
182
|
+
end
|
183
|
+
|
171
184
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
172
185
|
def bun_lock
|
173
186
|
@bun_lock ||= T.let(dependency_files.find do |f|
|
@@ -225,6 +238,30 @@ module Dependabot
|
|
225
238
|
dependency_set
|
226
239
|
end
|
227
240
|
|
241
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
242
|
+
def workspace_catalog_dependencies
|
243
|
+
dependency_set = DependencySet.new
|
244
|
+
workspace_config = YAML.safe_load(T.must(pnpm_workspace_yml&.content), aliases: true)
|
245
|
+
|
246
|
+
workspace_config["catalog"]&.each do |name, version|
|
247
|
+
dep = build_dependency(
|
248
|
+
file: T.must(pnpm_workspace_yml), type: "dependencies", name: name, requirement: version
|
249
|
+
)
|
250
|
+
dependency_set << dep if dep
|
251
|
+
end
|
252
|
+
|
253
|
+
workspace_config["catalogs"]&.each do |_, group_depenencies|
|
254
|
+
group_depenencies.each do |name, version|
|
255
|
+
dep = build_dependency(
|
256
|
+
file: T.must(pnpm_workspace_yml), type: "dependencies", name: name, requirement: version
|
257
|
+
)
|
258
|
+
dependency_set << dep if dep
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
dependency_set
|
263
|
+
end
|
264
|
+
|
228
265
|
sig { returns(LockfileParser) }
|
229
266
|
def lockfile_parser
|
230
267
|
@lockfile_parser ||= T.let(LockfileParser.new(
|
@@ -37,8 +37,13 @@ module Dependabot
|
|
37
37
|
sig { returns(T::Array[Dependabot::Dependency]) }
|
38
38
|
attr_reader :dependencies
|
39
39
|
|
40
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
41
|
+
|
40
42
|
sig { returns(T.nilable(String)) }
|
41
43
|
def updated_package_json_content
|
44
|
+
# checks if we are updating single dependency in package.json
|
45
|
+
unique_deps_count = dependencies.map(&:name).to_a.uniq.compact.length
|
46
|
+
|
42
47
|
dependencies.reduce(package_json.content.dup) do |content, dep|
|
43
48
|
updated_requirements(dep)&.each do |new_req|
|
44
49
|
old_req = old_requirement(dep, new_req)
|
@@ -50,7 +55,25 @@ module Dependabot
|
|
50
55
|
new_req: new_req
|
51
56
|
)
|
52
57
|
|
53
|
-
|
58
|
+
if Dependabot::Experiments.enabled?(:avoid_duplicate_updates_package_json) &&
|
59
|
+
(content == new_content && unique_deps_count > 1)
|
60
|
+
|
61
|
+
# (we observed that) package.json does not always contains the same dependencies compared to
|
62
|
+
# "dependencies" list, for example, dependencies object can contain same name dependency "dep"=> "1.0.0"
|
63
|
+
# and "dev" => "1.0.1" while package.json can only contain "dep" => "1.0.0",the other dependency is
|
64
|
+
# not present in package.json so we don't have to update it, this is most likely (as observed)
|
65
|
+
# a transitive dependency which only needs update in lockfile, So we avoid throwing exception and let
|
66
|
+
# the update continue.
|
67
|
+
|
68
|
+
Dependabot.logger.info("experiment: avoid_duplicate_updates_package_json.
|
69
|
+
Updating package.json for #{dep.name} ")
|
70
|
+
|
71
|
+
raise "Expected content to change!"
|
72
|
+
end
|
73
|
+
|
74
|
+
if !Dependabot::Experiments.enabled?(:avoid_duplicate_updates_package_json) && (content == new_content)
|
75
|
+
raise "Expected content to change!"
|
76
|
+
end
|
54
77
|
|
55
78
|
content = new_content
|
56
79
|
end
|
@@ -69,7 +92,7 @@ module Dependabot
|
|
69
92
|
content
|
70
93
|
end
|
71
94
|
end
|
72
|
-
|
95
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
73
96
|
sig do
|
74
97
|
params(
|
75
98
|
dependency: Dependabot::Dependency,
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/npm_and_yarn/helpers"
|
5
|
+
require "dependabot/npm_and_yarn/update_checker/registry_finder"
|
6
|
+
require "dependabot/npm_and_yarn/registry_parser"
|
7
|
+
require "dependabot/shared_helpers"
|
8
|
+
|
9
|
+
class DependencyRequirement < T::Struct
|
10
|
+
const :file, String
|
11
|
+
const :requirement, String
|
12
|
+
const :groups, T::Array[String]
|
13
|
+
const :source, T.nilable(String)
|
14
|
+
end
|
15
|
+
|
16
|
+
module Dependabot
|
17
|
+
module NpmAndYarn
|
18
|
+
class FileUpdater
|
19
|
+
class PnpmWorkspaceUpdater
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
sig do
|
23
|
+
params(
|
24
|
+
workspace_file: Dependabot::DependencyFile,
|
25
|
+
dependencies: T::Array[Dependabot::Dependency]
|
26
|
+
) .void
|
27
|
+
end
|
28
|
+
def initialize(workspace_file:, dependencies:)
|
29
|
+
@dependencies = dependencies
|
30
|
+
@workspace_file = workspace_file
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { returns(Dependabot::DependencyFile) }
|
34
|
+
def updated_pnpm_workspace
|
35
|
+
updated_file = workspace_file.dup
|
36
|
+
updated_file.content = updated_pnpm_workspace_content
|
37
|
+
updated_file
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
sig { returns(Dependabot::DependencyFile) }
|
43
|
+
attr_reader :workspace_file
|
44
|
+
|
45
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
46
|
+
attr_reader :dependencies
|
47
|
+
|
48
|
+
sig { returns(T.nilable(String)) }
|
49
|
+
def updated_pnpm_workspace_content
|
50
|
+
content = workspace_file.content.dup
|
51
|
+
dependencies.each do |dependency|
|
52
|
+
content = update_dependency_versions(T.must(content), dependency)
|
53
|
+
end
|
54
|
+
|
55
|
+
content
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { params(content: String, dependency: Dependabot::Dependency).returns(String) }
|
59
|
+
def update_dependency_versions(content, dependency)
|
60
|
+
new_requirements(dependency).each do |requirement|
|
61
|
+
content = replace_version_in_content(
|
62
|
+
content: content,
|
63
|
+
dependency: dependency,
|
64
|
+
old_requirement: T.must(old_requirement(dependency, requirement)),
|
65
|
+
new_requirement: requirement
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
content
|
70
|
+
end
|
71
|
+
|
72
|
+
sig do
|
73
|
+
params(
|
74
|
+
content: String,
|
75
|
+
dependency: Dependabot::Dependency,
|
76
|
+
old_requirement: DependencyRequirement,
|
77
|
+
new_requirement: DependencyRequirement
|
78
|
+
).returns(String)
|
79
|
+
end
|
80
|
+
def replace_version_in_content(content:, dependency:, old_requirement:, new_requirement:)
|
81
|
+
old_version = old_requirement.requirement
|
82
|
+
new_version = new_requirement.requirement
|
83
|
+
|
84
|
+
pattern = build_replacement_pattern(
|
85
|
+
dependency_name: dependency.name,
|
86
|
+
version: old_version
|
87
|
+
)
|
88
|
+
|
89
|
+
replacement = build_replacement_string(
|
90
|
+
dependency_name: dependency.name,
|
91
|
+
version: new_version
|
92
|
+
)
|
93
|
+
|
94
|
+
content.gsub(pattern, replacement)
|
95
|
+
end
|
96
|
+
|
97
|
+
sig { params(dependency_name: String, version: String).returns(Regexp) }
|
98
|
+
def build_replacement_pattern(dependency_name:, version:)
|
99
|
+
/(["']?)#{dependency_name}\1:\s*(["']?)#{Regexp.escape(version)}\2/
|
100
|
+
end
|
101
|
+
|
102
|
+
sig { params(dependency_name: String, version: String).returns(String) }
|
103
|
+
def build_replacement_string(dependency_name:, version:)
|
104
|
+
"\\1#{dependency_name}\\1: \\2#{version}\\2"
|
105
|
+
end
|
106
|
+
|
107
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Array[DependencyRequirement]) }
|
108
|
+
def new_requirements(dependency)
|
109
|
+
dependency.requirements
|
110
|
+
.select { |r| r[:file] == workspace_file.name }
|
111
|
+
.map do |r|
|
112
|
+
DependencyRequirement.new(
|
113
|
+
file: r[:file],
|
114
|
+
requirement: r[:requirement],
|
115
|
+
groups: r[:groups],
|
116
|
+
source: r[:source]
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
sig do
|
122
|
+
params(dependency: Dependabot::Dependency,
|
123
|
+
new_requirement: DependencyRequirement).returns(T.nilable(DependencyRequirement))
|
124
|
+
end
|
125
|
+
def old_requirement(dependency, new_requirement)
|
126
|
+
matching_req = T.must(dependency.previous_requirements).find { |r| r[:groups] == new_requirement.groups }
|
127
|
+
|
128
|
+
return nil if matching_req.nil?
|
129
|
+
|
130
|
+
DependencyRequirement.new(
|
131
|
+
file: matching_req[:file],
|
132
|
+
requirement: matching_req[:requirement],
|
133
|
+
groups: matching_req[:groups],
|
134
|
+
source: matching_req[:source]
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -11,7 +11,7 @@ require "sorbet-runtime"
|
|
11
11
|
|
12
12
|
module Dependabot
|
13
13
|
module NpmAndYarn
|
14
|
-
class FileUpdater < Dependabot::FileUpdaters::Base
|
14
|
+
class FileUpdater < Dependabot::FileUpdaters::Base # rubocop:disable Metrics/ClassLength
|
15
15
|
extend T::Sig
|
16
16
|
|
17
17
|
require_relative "file_updater/package_json_updater"
|
@@ -19,6 +19,7 @@ module Dependabot
|
|
19
19
|
require_relative "file_updater/yarn_lockfile_updater"
|
20
20
|
require_relative "file_updater/pnpm_lockfile_updater"
|
21
21
|
require_relative "file_updater/bun_lockfile_updater"
|
22
|
+
require_relative "file_updater/pnpm_workspace_updater"
|
22
23
|
|
23
24
|
class NoChangeError < StandardError
|
24
25
|
extend T::Sig
|
@@ -43,6 +44,7 @@ module Dependabot
|
|
43
44
|
%r{^(?:.*/)?npm-shrinkwrap\.json$},
|
44
45
|
%r{^(?:.*/)?yarn\.lock$},
|
45
46
|
%r{^(?:.*/)?pnpm-lock\.yaml$},
|
47
|
+
%r{^(?:.*/)?pnpm-workspace\.yaml$},
|
46
48
|
%r{^(?:.*/)?\.yarn/.*}, # Matches any file within the .yarn/ directory
|
47
49
|
%r{^(?:.*/)?\.pnp\.(?:js|cjs)$} # Matches .pnp.js or .pnp.cjs files
|
48
50
|
]
|
@@ -54,6 +56,9 @@ module Dependabot
|
|
54
56
|
updated_files = T.let([], T::Array[DependencyFile])
|
55
57
|
|
56
58
|
updated_files += updated_manifest_files
|
59
|
+
if Dependabot::Experiments.enabled?(:enable_pnpm_workspace_catalog)
|
60
|
+
updated_files += updated_pnpm_workspace_files
|
61
|
+
end
|
57
62
|
updated_files += updated_lockfiles
|
58
63
|
|
59
64
|
if updated_files.none?
|
@@ -208,6 +213,15 @@ module Dependabot
|
|
208
213
|
)
|
209
214
|
end
|
210
215
|
|
216
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
217
|
+
def pnpm_workspace
|
218
|
+
@pnpm_workspace ||= T.let(
|
219
|
+
filtered_dependency_files
|
220
|
+
.select { |f| f.name.end_with?("pnpm-workspace.yaml") },
|
221
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
222
|
+
)
|
223
|
+
end
|
224
|
+
|
211
225
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
212
226
|
def bun_locks
|
213
227
|
@bun_locks ||= T.let(
|
@@ -270,6 +284,16 @@ module Dependabot
|
|
270
284
|
end
|
271
285
|
end
|
272
286
|
|
287
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
288
|
+
def updated_pnpm_workspace_files
|
289
|
+
pnpm_workspace.filter_map do |file|
|
290
|
+
updated_content = updated_pnpm_workspace_content(file)
|
291
|
+
next if updated_content == file.content
|
292
|
+
|
293
|
+
updated_file(file: file, content: T.must(updated_content))
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
273
297
|
# rubocop:disable Metrics/MethodLength
|
274
298
|
# rubocop:disable Metrics/PerceivedComplexity
|
275
299
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
@@ -407,6 +431,19 @@ module Dependabot
|
|
407
431
|
dependencies: dependencies
|
408
432
|
).updated_package_json.content
|
409
433
|
end
|
434
|
+
|
435
|
+
sig do
|
436
|
+
params(file: Dependabot::DependencyFile)
|
437
|
+
.returns(T.nilable(String))
|
438
|
+
end
|
439
|
+
def updated_pnpm_workspace_content(file)
|
440
|
+
@updated_pnpm_workspace_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
441
|
+
@updated_pnpm_workspace_content[file.name] ||=
|
442
|
+
PnpmWorkspaceUpdater.new(
|
443
|
+
workspace_file: file,
|
444
|
+
dependencies: dependencies
|
445
|
+
).updated_pnpm_workspace.content
|
446
|
+
end
|
410
447
|
end
|
411
448
|
end
|
412
449
|
end
|
@@ -38,8 +38,8 @@ module Dependabot
|
|
38
38
|
def initialize(detected_version: nil, raw_version: nil, requirement: nil)
|
39
39
|
super(
|
40
40
|
name: NAME,
|
41
|
-
detected_version: detected_version ? Version.new(detected_version) : nil,
|
42
|
-
version: raw_version ? Version.new(raw_version) : nil,
|
41
|
+
detected_version: detected_version && !detected_version.empty? ? Version.new(detected_version) : nil,
|
42
|
+
version: raw_version && !raw_version.empty? ? Version.new(raw_version) : nil,
|
43
43
|
deprecated_versions: DEPRECATED_VERSIONS,
|
44
44
|
supported_versions: SUPPORTED_VERSIONS,
|
45
45
|
requirement: requirement
|
@@ -48,22 +48,16 @@ module Dependabot
|
|
48
48
|
|
49
49
|
sig { override.returns(T::Boolean) }
|
50
50
|
def deprecated?
|
51
|
-
return false unless detected_version
|
52
|
-
|
53
|
-
return false if unsupported?
|
54
|
-
|
55
51
|
return false unless Dependabot::Experiments.enabled?(:npm_v6_deprecation_warning)
|
56
52
|
|
57
|
-
|
53
|
+
super
|
58
54
|
end
|
59
55
|
|
60
56
|
sig { override.returns(T::Boolean) }
|
61
57
|
def unsupported?
|
62
|
-
return false unless detected_version
|
63
|
-
|
64
58
|
return false unless Dependabot::Experiments.enabled?(:npm_v6_unsupported_error)
|
65
59
|
|
66
|
-
|
60
|
+
super
|
67
61
|
end
|
68
62
|
end
|
69
63
|
end
|
@@ -11,6 +11,7 @@ require "dependabot/npm_and_yarn/yarn_package_manager"
|
|
11
11
|
require "dependabot/npm_and_yarn/pnpm_package_manager"
|
12
12
|
require "dependabot/npm_and_yarn/bun_package_manager"
|
13
13
|
require "dependabot/npm_and_yarn/language"
|
14
|
+
require "dependabot/npm_and_yarn/constraint_helper"
|
14
15
|
|
15
16
|
module Dependabot
|
16
17
|
module NpmAndYarn
|
@@ -189,7 +190,7 @@ module Dependabot
|
|
189
190
|
end
|
190
191
|
|
191
192
|
sig { params(name: String).returns(T.nilable(Requirement)) }
|
192
|
-
def find_engine_constraints_as_requirement(name)
|
193
|
+
def find_engine_constraints_as_requirement(name) # rubocop:disable Metrics/PerceivedComplexity
|
193
194
|
Dependabot.logger.info("Processing engine constraints for #{name}")
|
194
195
|
|
195
196
|
return nil unless @engines.is_a?(Hash) && @engines[name]
|
@@ -197,19 +198,31 @@ module Dependabot
|
|
197
198
|
raw_constraint = @engines[name].to_s.strip
|
198
199
|
return nil if raw_constraint.empty?
|
199
200
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
"=#{constraint}"
|
209
|
-
else
|
210
|
-
Dependabot.logger.warn("Unrecognized constraint format for #{name}: #{constraint}")
|
211
|
-
constraint
|
201
|
+
if Dependabot::Experiments.enabled?(:enable_engine_version_detection)
|
202
|
+
constraints = ConstraintHelper.extract_constraints(raw_constraint)
|
203
|
+
|
204
|
+
# When constraints are invalid we return constraints array nil
|
205
|
+
if constraints.nil?
|
206
|
+
Dependabot.logger.warn(
|
207
|
+
"Unrecognized constraint format for #{name}: #{raw_constraint}"
|
208
|
+
)
|
212
209
|
end
|
210
|
+
else
|
211
|
+
raw_constraints = raw_constraint.split
|
212
|
+
constraints = raw_constraints.map do |constraint|
|
213
|
+
case constraint
|
214
|
+
when /^\d+$/
|
215
|
+
">=#{constraint}.0.0 <#{constraint.to_i + 1}.0.0"
|
216
|
+
when /^\d+\.\d+$/
|
217
|
+
">=#{constraint} <#{constraint.split('.').first.to_i + 1}.0.0"
|
218
|
+
when /^\d+\.\d+\.\d+$/
|
219
|
+
"=#{constraint}"
|
220
|
+
else
|
221
|
+
Dependabot.logger.warn("Unrecognized constraint format for #{name}: #{constraint}")
|
222
|
+
constraint
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
213
226
|
end
|
214
227
|
|
215
228
|
Dependabot.logger.info("Parsed constraints for #{name}: #{constraints.join(', ')}")
|
@@ -289,19 +302,24 @@ module Dependabot
|
|
289
302
|
|
290
303
|
sig { params(name: String).returns(T.nilable(String)) }
|
291
304
|
def detect_version(name)
|
292
|
-
#
|
305
|
+
# Prioritize version mentioned in "packageManager" instead of "engines"
|
293
306
|
if @manifest_package_manager&.start_with?("#{name}@")
|
294
307
|
detected_version = @manifest_package_manager.split("@").last.to_s
|
295
308
|
end
|
296
309
|
|
297
|
-
#
|
298
|
-
detected_version
|
310
|
+
# If "packageManager" has no version specified, check if we can extract "engines" information
|
311
|
+
detected_version ||= check_engine_version(name) if detected_version.to_s.empty?
|
299
312
|
|
300
|
-
#
|
301
|
-
|
302
|
-
detected_version = guessed_version(name) if !detected_version || detected_version.empty?
|
313
|
+
# If neither "packageManager" nor "engines" have versions, infer version from lockfileVersion
|
314
|
+
detected_version ||= guessed_version(name) if detected_version.to_s.empty?
|
303
315
|
|
304
|
-
|
316
|
+
# Strip and validate version format
|
317
|
+
detected_version_string = detected_version.to_s.strip
|
318
|
+
|
319
|
+
# Ensure detected_version is neither "0" nor invalid format
|
320
|
+
return if detected_version_string == "0" || !detected_version_string.match?(ConstraintHelper::VERSION_REGEX)
|
321
|
+
|
322
|
+
detected_version_string
|
305
323
|
end
|
306
324
|
|
307
325
|
sig { params(name: T.nilable(String)).returns(Ecosystem::VersionManager) }
|
@@ -332,7 +350,7 @@ module Dependabot
|
|
332
350
|
end
|
333
351
|
|
334
352
|
package_manager_class.new(
|
335
|
-
detected_version: detected_version
|
353
|
+
detected_version: detected_version,
|
336
354
|
raw_version: installed_version,
|
337
355
|
requirement: package_manager_requirement
|
338
356
|
)
|
@@ -434,7 +452,8 @@ module Dependabot
|
|
434
452
|
return if @package_json.nil?
|
435
453
|
|
436
454
|
version_selector = VersionSelector.new
|
437
|
-
|
455
|
+
|
456
|
+
engine_versions = version_selector.setup(@package_json, name, dependabot_versions(name))
|
438
457
|
|
439
458
|
return if engine_versions.empty?
|
440
459
|
|
@@ -442,6 +461,20 @@ module Dependabot
|
|
442
461
|
Dependabot.logger.info("Returned (#{MANIFEST_ENGINES_KEY}) info \"#{name}\" : \"#{version}\"")
|
443
462
|
version
|
444
463
|
end
|
464
|
+
|
465
|
+
sig { params(name: String).returns(T.nilable(T::Array[Dependabot::Version])) }
|
466
|
+
def dependabot_versions(name)
|
467
|
+
case name
|
468
|
+
when "npm"
|
469
|
+
NpmPackageManager::SUPPORTED_VERSIONS
|
470
|
+
when "yarn"
|
471
|
+
YarnPackageManager::SUPPORTED_VERSIONS
|
472
|
+
when "bun"
|
473
|
+
BunPackageManager::SUPPORTED_VERSIONS
|
474
|
+
when "pnpm"
|
475
|
+
PNPMPackageManager::SUPPORTED_VERSIONS
|
476
|
+
end
|
477
|
+
end
|
445
478
|
end
|
446
479
|
end
|
447
480
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/shared_helpers"
|
5
|
+
require "dependabot/npm_and_yarn/constraint_helper"
|
5
6
|
|
6
7
|
module Dependabot
|
7
8
|
module NpmAndYarn
|
@@ -13,18 +14,42 @@ module Dependabot
|
|
13
14
|
# such as "20.8.7", "8.1.2", "8.21.2",
|
14
15
|
NODE_ENGINE_SUPPORTED_REGEX = /^\d+(?:\.\d+)*$/
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
# Sets up engine versions from the given manifest JSON.
|
18
|
+
#
|
19
|
+
# @param manifest_json [Hash] The manifest JSON containing version information.
|
20
|
+
# @param name [String] The engine name to match.
|
21
|
+
# @return [Hash] A hash with selected versions, if found.
|
22
|
+
sig do
|
23
|
+
params(
|
24
|
+
manifest_json: T::Hash[String, T.untyped],
|
25
|
+
name: String,
|
26
|
+
dependabot_versions: T.nilable(T::Array[Dependabot::Version])
|
27
|
+
)
|
28
|
+
.returns(T::Hash[Symbol, T.untyped])
|
29
|
+
end
|
30
|
+
def setup(manifest_json, name, dependabot_versions = nil)
|
18
31
|
engine_versions = manifest_json["engines"]
|
19
32
|
|
33
|
+
# Return an empty hash if no engine versions are specified
|
20
34
|
return {} if engine_versions.nil?
|
21
35
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
36
|
+
versions = {}
|
37
|
+
|
38
|
+
if Dependabot::Experiments.enabled?(:enable_engine_version_detection)
|
39
|
+
engine_versions.each do |engine, value|
|
40
|
+
next unless engine.to_s.match(name)
|
41
|
+
|
42
|
+
versions[name] = ConstraintHelper.find_highest_version_from_constraint_expression(
|
43
|
+
value, dependabot_versions
|
44
|
+
)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
versions = engine_versions.select do |engine, value|
|
48
|
+
engine.to_s.match(name) && valid_extracted_version?(value)
|
49
|
+
end
|
50
|
+
end
|
26
51
|
|
27
|
-
|
52
|
+
versions
|
28
53
|
end
|
29
54
|
|
30
55
|
sig { params(version: String).returns(T::Boolean) }
|
@@ -146,6 +146,10 @@ module Dependabot
|
|
146
146
|
# if not package found with specified version
|
147
147
|
YARN_PACKAGE_NOT_FOUND = /MessageError: Couldn't find any versions for "(?<pkg>.*?)" that matches "(?<ver>.*?)"/
|
148
148
|
|
149
|
+
YN0001_DEPS_RESOLUTION_FAILED = T.let({
|
150
|
+
DEPS_INCORRECT_MET: /peer dependencies are incorrectly met/
|
151
|
+
}.freeze, T::Hash[String, Regexp])
|
152
|
+
|
149
153
|
YN0001_FILE_NOT_RESOLVED_CODES = T.let({
|
150
154
|
FIND_PACKAGE_LOCATION: /YN0001:(.*?)UsageError: Couldn't find the (?<pkg>.*) state file/,
|
151
155
|
NO_CANDIDATE_FOUND: /YN0001:(.*?)Error: (?<pkg>.*): No candidates found/,
|
@@ -165,6 +169,8 @@ module Dependabot
|
|
165
169
|
REQUIREMENT_NOT_PROVIDED: /(?<dep>.*)(.*?)doesn't provide (?<pkg>.*)(.*?), requested by (?<parent>.*)/
|
166
170
|
}.freeze, T::Hash[String, Regexp])
|
167
171
|
|
172
|
+
YN0086_DEPS_RESOLUTION_FAILED = /peer dependencies are incorrectly met/
|
173
|
+
|
168
174
|
# registry returns malformed response
|
169
175
|
REGISTRY_NOT_REACHABLE = /Received malformed response from registry for "(?<ver>.*)". The registry may be down./
|
170
176
|
|
@@ -227,6 +233,12 @@ module Dependabot
|
|
227
233
|
end
|
228
234
|
end
|
229
235
|
|
236
|
+
YN0001_DEPS_RESOLUTION_FAILED.each do |(_yn0001_key, yn0001_regex)|
|
237
|
+
if (msg = message.match(yn0001_regex))
|
238
|
+
return Dependabot::DependencyFileNotResolvable.new(msg)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
230
242
|
Dependabot::DependabotError.new(message)
|
231
243
|
}
|
232
244
|
},
|
@@ -351,6 +363,13 @@ module Dependabot
|
|
351
363
|
Dependabot::DependencyNotFound.new(message)
|
352
364
|
end
|
353
365
|
}
|
366
|
+
},
|
367
|
+
"YN0086" => {
|
368
|
+
message: "deps resolution failed",
|
369
|
+
handler: lambda { |message, _error, _params|
|
370
|
+
msg = message.match(YN0086_DEPS_RESOLUTION_FAILED)
|
371
|
+
Dependabot::DependencyFileNotResolvable.new(msg || message)
|
372
|
+
}
|
354
373
|
}
|
355
374
|
}.freeze, T::Hash[String, {
|
356
375
|
message: T.any(String, NilClass),
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-npm_and_yarn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.295.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.295.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.295.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -307,6 +307,7 @@ files:
|
|
307
307
|
- helpers/test/yarn/updater.test.js
|
308
308
|
- lib/dependabot/npm_and_yarn.rb
|
309
309
|
- lib/dependabot/npm_and_yarn/bun_package_manager.rb
|
310
|
+
- lib/dependabot/npm_and_yarn/constraint_helper.rb
|
310
311
|
- lib/dependabot/npm_and_yarn/dependency_files_filterer.rb
|
311
312
|
- lib/dependabot/npm_and_yarn/file_fetcher.rb
|
312
313
|
- lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb
|
@@ -323,6 +324,7 @@ files:
|
|
323
324
|
- lib/dependabot/npm_and_yarn/file_updater/package_json_preparer.rb
|
324
325
|
- lib/dependabot/npm_and_yarn/file_updater/package_json_updater.rb
|
325
326
|
- lib/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater.rb
|
327
|
+
- lib/dependabot/npm_and_yarn/file_updater/pnpm_workspace_updater.rb
|
326
328
|
- lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb
|
327
329
|
- lib/dependabot/npm_and_yarn/helpers.rb
|
328
330
|
- lib/dependabot/npm_and_yarn/language.rb
|
@@ -354,7 +356,7 @@ licenses:
|
|
354
356
|
- MIT
|
355
357
|
metadata:
|
356
358
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
357
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
359
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.295.0
|
358
360
|
post_install_message:
|
359
361
|
rdoc_options: []
|
360
362
|
require_paths:
|