dependabot-npm_and_yarn 0.217.0 → 0.218.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,6 +12,7 @@ module Dependabot
12
12
  require_relative "file_updater/package_json_updater"
13
13
  require_relative "file_updater/npm_lockfile_updater"
14
14
  require_relative "file_updater/yarn_lockfile_updater"
15
+ require_relative "file_updater/pnpm_lockfile_updater"
15
16
 
16
17
  class NoChangeError < StandardError
17
18
  def initialize(message:, error_context:)
@@ -29,7 +30,8 @@ module Dependabot
29
30
  /^package\.json$/,
30
31
  /^package-lock\.json$/,
31
32
  /^npm-shrinkwrap\.json$/,
32
- /^yarn\.lock$/
33
+ /^yarn\.lock$/,
34
+ /^pnpm-lock\.yaml$/
33
35
  ]
34
36
  end
35
37
 
@@ -145,6 +147,12 @@ module Dependabot
145
147
  select { |f| f.name.end_with?("yarn.lock") }
146
148
  end
147
149
 
150
+ def pnpm_locks
151
+ @pnpm_locks ||=
152
+ filtered_dependency_files.
153
+ select { |f| f.name.end_with?("pnpm-lock.yaml") }
154
+ end
155
+
148
156
  def shrinkwraps
149
157
  @shrinkwraps ||=
150
158
  filtered_dependency_files.
@@ -162,6 +170,10 @@ module Dependabot
162
170
  yarn_lock.content != updated_yarn_lock_content(yarn_lock)
163
171
  end
164
172
 
173
+ def pnpm_lock_changed?(pnpm_lock)
174
+ pnpm_lock.content != updated_pnpm_lock_content(pnpm_lock)
175
+ end
176
+
165
177
  def package_lock_changed?(package_lock)
166
178
  package_lock.content != updated_lockfile_content(package_lock)
167
179
  end
@@ -191,6 +203,15 @@ module Dependabot
191
203
  )
192
204
  end
193
205
 
206
+ pnpm_locks.each do |pnpm_lock|
207
+ next unless pnpm_lock_changed?(pnpm_lock)
208
+
209
+ updated_files << updated_file(
210
+ file: pnpm_lock,
211
+ content: updated_pnpm_lock_content(pnpm_lock)
212
+ )
213
+ end
214
+
194
215
  package_locks.each do |package_lock|
195
216
  next unless package_lock_changed?(package_lock)
196
217
 
@@ -218,6 +239,12 @@ module Dependabot
218
239
  yarn_lockfile_updater.updated_yarn_lock_content(yarn_lock)
219
240
  end
220
241
 
242
+ def updated_pnpm_lock_content(pnpm_lock)
243
+ @updated_pnpm_lock_content ||= {}
244
+ @updated_pnpm_lock_content[pnpm_lock.name] ||=
245
+ pnpm_lockfile_updater.updated_pnpm_lock_content(pnpm_lock)
246
+ end
247
+
221
248
  def yarn_lockfile_updater
222
249
  @yarn_lockfile_updater ||=
223
250
  YarnLockfileUpdater.new(
@@ -228,6 +255,16 @@ module Dependabot
228
255
  )
229
256
  end
230
257
 
258
+ def pnpm_lockfile_updater
259
+ @pnpm_lockfile_updater ||=
260
+ PnpmLockfileUpdater.new(
261
+ dependencies: dependencies,
262
+ dependency_files: dependency_files,
263
+ repo_contents_path: repo_contents_path,
264
+ credentials: credentials
265
+ )
266
+ end
267
+
231
268
  def updated_lockfile_content(file)
232
269
  @updated_lockfile_content ||= {}
233
270
  @updated_lockfile_content[file.name] ||=
@@ -40,10 +40,23 @@ module Dependabot
40
40
  end
41
41
 
42
42
  def self.yarn_major_version
43
+ @yarn_major_version ||= fetch_yarn_major_version
44
+ end
45
+
46
+ def self.pnpm_major_version
47
+ @pnpm_major_version ||= fetch_pnpm_major_version
48
+ end
49
+
50
+ def self.fetch_yarn_major_version
43
51
  output = SharedHelpers.run_shell_command("yarn --version")
44
52
  Version.new(output).major
45
53
  end
46
54
 
55
+ def self.fetch_pnpm_major_version
56
+ output = SharedHelpers.run_shell_command("pnpm --version")
57
+ Version.new(output).major
58
+ end
59
+
47
60
  def self.yarn_zero_install?
48
61
  File.exist?(".pnp.cjs")
49
62
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ module NpmAndYarn
5
+ class PackageManager
6
+ def initialize(package_json)
7
+ @package_json = package_json
8
+ end
9
+
10
+ def locked_version(name)
11
+ locked = @package_json.fetch("packageManager", nil)
12
+ return unless locked
13
+
14
+ version_match = locked.match(/#{name}@(?<version>\d+.\d+.\d+)/)
15
+ version_match&.named_captures&.fetch("version", nil)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -50,7 +50,8 @@ module Dependabot
50
50
  file.name.end_with?(
51
51
  "package-lock.json",
52
52
  "yarn.lock",
53
- "npm-shrinkwrap.json"
53
+ "npm-shrinkwrap.json",
54
+ "pnpm-lock.yaml"
54
55
  )
55
56
  end
56
57
 
@@ -19,7 +19,7 @@ module Dependabot
19
19
  if Helpers.yarn_berry?(yarn_locks.first)
20
20
  File.write(".yarnrc.yml", yarnrc_yml_content) if yarnrc_yml_file
21
21
  else
22
- File.write(".npmrc", npmrc_content) unless Helpers.yarn_berry?(yarn_locks.first)
22
+ File.write(".npmrc", npmrc_content)
23
23
  File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
24
24
  end
25
25
 
@@ -42,12 +42,24 @@ module Dependabot
42
42
  select { |f| f.name.end_with?("yarn.lock") }
43
43
  end
44
44
 
45
+ def pnpm_locks
46
+ @pnpm_locks ||=
47
+ dependency_files.
48
+ select { |f| f.name.end_with?("pnpm-lock.yaml") }
49
+ end
50
+
45
51
  def root_yarn_lock
46
52
  @root_yarn_lock ||=
47
53
  dependency_files.
48
54
  find { |f| f.name == "yarn.lock" }
49
55
  end
50
56
 
57
+ def root_pnpm_lock
58
+ @root_pnpm_lock ||=
59
+ dependency_files.
60
+ find { |f| f.name == "pnpm-lock.yaml" }
61
+ end
62
+
51
63
  def shrinkwraps
52
64
  @shrinkwraps ||=
53
65
  dependency_files.
@@ -55,7 +67,7 @@ module Dependabot
55
67
  end
56
68
 
57
69
  def lockfiles
58
- [*package_locks, *shrinkwraps, *yarn_locks]
70
+ [*package_locks, *shrinkwraps, *yarn_locks, *pnpm_locks]
59
71
  end
60
72
 
61
73
  def package_files
@@ -74,6 +86,11 @@ module Dependabot
74
86
  File.write(f.name, prepared_yarn_lockfile_content(f.content))
75
87
  end
76
88
 
89
+ pnpm_locks.each do |f|
90
+ FileUtils.mkdir_p(Pathname.new(f.name).dirname)
91
+ File.write(f.name, f.content)
92
+ end
93
+
77
94
  [*package_locks, *shrinkwraps].each do |f|
78
95
  FileUtils.mkdir_p(Pathname.new(f.name).dirname)
79
96
  File.write(f.name, f.content)
@@ -15,7 +15,7 @@ module Dependabot
15
15
  class RequirementsUpdater
16
16
  VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/
17
17
  SEPARATOR = /(?<=[a-zA-Z0-9*])[\s|]+(?![\s|-])/
18
- ALLOWED_UPDATE_STRATEGIES = %i(widen_ranges bump_versions bump_versions_if_necessary).freeze
18
+ ALLOWED_UPDATE_STRATEGIES = %i(lockfile_only widen_ranges bump_versions bump_versions_if_necessary).freeze
19
19
 
20
20
  def initialize(requirements:, updated_source:, update_strategy:,
21
21
  latest_resolvable_version:)
@@ -32,6 +32,8 @@ module Dependabot
32
32
  end
33
33
 
34
34
  def updated_requirements
35
+ return requirements if update_strategy == :lockfile_only
36
+
35
37
  requirements.map do |req|
36
38
  req = req.merge(source: updated_source)
37
39
  next req unless latest_resolvable_version
@@ -4,8 +4,6 @@ require "dependabot/dependency"
4
4
  require "dependabot/errors"
5
5
  require "dependabot/logger"
6
6
  require "dependabot/npm_and_yarn/file_parser"
7
- require "dependabot/npm_and_yarn/file_updater/npmrc_builder"
8
- require "dependabot/npm_and_yarn/file_updater/package_json_preparer"
9
7
  require "dependabot/npm_and_yarn/helpers"
10
8
  require "dependabot/npm_and_yarn/native_helpers"
11
9
  require "dependabot/npm_and_yarn/sub_dependency_files_filterer"
@@ -65,6 +63,8 @@ module Dependabot
65
63
  run_yarn_berry_updater(path, lockfile_name)
66
64
  elsif lockfile.name.end_with?("yarn.lock")
67
65
  run_yarn_updater(path, lockfile_name)
66
+ elsif lockfile.name.end_with?("pnpm-lock.yaml")
67
+ run_pnpm_updater(path, lockfile_name)
68
68
  else
69
69
  run_npm_updater(path, lockfile_name, lockfile.content)
70
70
  end
@@ -74,9 +74,7 @@ module Dependabot
74
74
 
75
75
  def version_from_updated_lockfiles(updated_lockfiles)
76
76
  updated_files = dependency_files -
77
- dependency_files_builder.yarn_locks -
78
- dependency_files_builder.package_locks -
79
- dependency_files_builder.shrinkwraps +
77
+ dependency_files_builder.lockfiles +
80
78
  updated_lockfiles
81
79
 
82
80
  updated_version = NpmAndYarn::FileParser.new(
@@ -125,6 +123,18 @@ module Dependabot
125
123
  end
126
124
  end
127
125
 
126
+ def run_pnpm_updater(path, lockfile_name)
127
+ SharedHelpers.with_git_configured(credentials: credentials) do
128
+ Dir.chdir(path) do
129
+ SharedHelpers.run_shell_command(
130
+ "pnpm update #{dependency.name} --lockfile-only",
131
+ fingerprint: "pnpm update <dependency_name> --lockfile-only"
132
+ )
133
+ { lockfile_name => File.read(lockfile_name) }
134
+ end
135
+ end
136
+ end
137
+
128
138
  def run_npm_updater(path, lockfile_name, lockfile_content)
129
139
  SharedHelpers.with_git_configured(credentials: credentials) do
130
140
  Dir.chdir(path) do
@@ -26,7 +26,7 @@ module Dependabot
26
26
  "vue" => %w(vue vue-template-compiler)
27
27
  }.freeze
28
28
 
29
- # Error message from yarn add:
29
+ # Error message returned by `yarn add` (for Yarn classic):
30
30
  # " > @reach/router@1.2.1" has incorrect peer dependency "react@15.x || 16.x || 16.4.0-alpha.0911da3"
31
31
  # "workspace-aggregator-<random-string> > test > react-dom@15.6.2" has incorrect peer dependency "react@^15.6.2"
32
32
  # " > react-burger-menu@1.9.9" has unmet peer dependency "react@>=0.14.0 <16.0.0"
@@ -37,7 +37,7 @@ module Dependabot
37
37
  "(?<required_dep>[^"]+)"
38
38
  /x
39
39
 
40
- # Error message from yarn add:
40
+ # Error message returned by `yarn add` (for Yarn berry):
41
41
  # YN0060: │ eve-roster@workspace:. provides jest (p8d618) \
42
42
  # with version 29.3.0, which doesn't satisfy \
43
43
  # what ts-jest requests\n
@@ -46,7 +46,16 @@ module Dependabot
46
46
  YN0060:\s|\s.+\sprovides\s(?<required_dep>.+?)\s\((?<info_hash>\w+)\).+what\s(?<requiring_dep>.+?)\srequests
47
47
  /x
48
48
 
49
- # Error message from npm install:
49
+ # Error message returned by `pnpm update`:
50
+ # └─┬ react-dom 15.7.0
51
+ # └── ✕ unmet peer react@^15.7.0: found 16.3.1
52
+ PNPM_PEER_DEP_ERROR_REGEX =
53
+ /
54
+ ┬\s(?<requiring_dep>[^\n]+)\n
55
+ [^\n]*✕\sunmet\speer\s(?<required_dep>[^:]+):
56
+ /mx
57
+
58
+ # Error message returned by `npm install` (for NPM 6):
50
59
  # react-dom@15.2.0 requires a peer of react@^15.2.0 \
51
60
  # but none is installed. You must install peer dependencies yourself.
52
61
  NPM6_PEER_DEP_ERROR_REGEX =
@@ -56,7 +65,7 @@ module Dependabot
56
65
  (?<required_dep>.+?)\sbut\snone\sis\sinstalled.
57
66
  /x
58
67
 
59
- # Error message from npm install:
68
+ # Error message returned by `npm install` (for NPM 8):
60
69
  # npm ERR! Could not resolve dependency:
61
70
  # npm ERR! peer react@"^16.14.0" from react-dom@16.14.0
62
71
  #
@@ -320,8 +329,7 @@ module Dependabot
320
329
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
321
330
  dependency_files_builder.write_temporary_dependency_files
322
331
 
323
- filtered_package_files.flat_map do |file|
324
- path = Pathname.new(file.name).dirname
332
+ paths_requiring_update_check.flat_map do |path|
325
333
  run_checker(path: path, version: version)
326
334
  end.compact
327
335
  end
@@ -332,24 +340,30 @@ module Dependabot
332
340
  []
333
341
  end
334
342
 
335
- def handle_peer_dependency_errors(error)
343
+ def handle_peer_dependency_errors(message)
336
344
  errors = []
337
- if error.message.match?(NPM6_PEER_DEP_ERROR_REGEX)
338
- error.message.scan(NPM6_PEER_DEP_ERROR_REGEX) do
345
+ if message.match?(NPM6_PEER_DEP_ERROR_REGEX)
346
+ message.scan(NPM6_PEER_DEP_ERROR_REGEX) do
339
347
  errors << Regexp.last_match.named_captures
340
348
  end
341
- elsif error.message.match?(NPM8_PEER_DEP_ERROR_REGEX)
342
- error.message.scan(NPM8_PEER_DEP_ERROR_REGEX) do
349
+ elsif message.match?(NPM8_PEER_DEP_ERROR_REGEX)
350
+ message.scan(NPM8_PEER_DEP_ERROR_REGEX) do
343
351
  errors << Regexp.last_match.named_captures
344
352
  end
345
- elsif error.message.match?(YARN_PEER_DEP_ERROR_REGEX)
346
- error.message.scan(YARN_PEER_DEP_ERROR_REGEX) do
353
+ elsif message.match?(YARN_PEER_DEP_ERROR_REGEX)
354
+ message.scan(YARN_PEER_DEP_ERROR_REGEX) do
347
355
  errors << Regexp.last_match.named_captures
348
356
  end
349
- elsif error.message.match?(YARN_BERRY_PEER_DEP_ERROR_REGEX)
350
- error.message.scan(YARN_BERRY_PEER_DEP_ERROR_REGEX) do
357
+ elsif message.match?(YARN_BERRY_PEER_DEP_ERROR_REGEX)
358
+ message.scan(YARN_BERRY_PEER_DEP_ERROR_REGEX) do
351
359
  errors << Regexp.last_match.named_captures
352
360
  end
361
+ elsif message.match?(PNPM_PEER_DEP_ERROR_REGEX)
362
+ message.scan(PNPM_PEER_DEP_ERROR_REGEX) do
363
+ captures = Regexp.last_match.named_captures
364
+ captures["requiring_dep"].tr!(" ", "@")
365
+ errors << captures
366
+ end
353
367
  else
354
368
  raise
355
369
  end
@@ -483,20 +497,24 @@ module Dependabot
483
497
  end
484
498
 
485
499
  def run_checker(path:, version:)
486
- # If there are both yarn lockfiles and npm lockfiles only run the
487
- # yarn updater
488
500
  yarn_lockfiles = lockfiles_for_path(lockfiles: dependency_files_builder.yarn_locks, path: path)
489
501
  return run_yarn_checker(path: path, version: version, lockfile: yarn_lockfiles.first) if yarn_lockfiles.any?
490
502
 
503
+ pnpm_lockfiles = lockfiles_for_path(lockfiles: dependency_files_builder.pnpm_locks, path: path)
504
+ return run_pnpm_checker(path: path, version: version) if pnpm_lockfiles.any?
505
+
491
506
  npm_lockfiles = lockfiles_for_path(lockfiles: dependency_files_builder.package_locks, path: path)
492
507
  return run_npm_checker(path: path, version: version) if npm_lockfiles.any?
493
508
 
494
509
  root_yarn_lock = dependency_files_builder.root_yarn_lock
495
510
  return run_yarn_checker(path: path, version: version, lockfile: root_yarn_lock) if root_yarn_lock
496
511
 
512
+ root_pnpm_lock = dependency_files_builder.root_pnpm_lock
513
+ return run_pnpm_checker(path: path, version: version) if root_pnpm_lock
514
+
497
515
  run_npm_checker(path: path, version: version)
498
516
  rescue SharedHelpers::HelperSubprocessFailed => e
499
- handle_peer_dependency_errors(e)
517
+ handle_peer_dependency_errors(e.message)
500
518
  end
501
519
 
502
520
  def run_yarn_checker(path:, version:, lockfile:)
@@ -505,6 +523,23 @@ module Dependabot
505
523
  run_yarn_classic_checker(path: path, version: version)
506
524
  end
507
525
 
526
+ def run_pnpm_checker(path:, version:)
527
+ SharedHelpers.with_git_configured(credentials: credentials) do
528
+ Dir.chdir(path) do
529
+ output = SharedHelpers.run_shell_command(
530
+ "pnpm update #{dependency.name}@#{version} --lockfile-only",
531
+ fingerprint: "pnpm update <dependency_name>@<version> --lockfile-only"
532
+ )
533
+ if PNPM_PEER_DEP_ERROR_REGEX.match?(output)
534
+ raise SharedHelpers::HelperSubprocessFailed.new(
535
+ message: output,
536
+ error_context: {}
537
+ )
538
+ end
539
+ end
540
+ end
541
+ end
542
+
508
543
  def run_yarn_berry_checker(path:, version:)
509
544
  # This method mimics calling a native helper in order to comply with the caller's expectations
510
545
  # Specifically we add the dependency at the specified updated version
@@ -612,12 +647,12 @@ module Dependabot
612
647
  ).parse.select(&:top_level?)
613
648
  end
614
649
 
615
- def filtered_package_files
616
- @filtered_package_files ||=
650
+ def paths_requiring_update_check
651
+ @paths_requiring_update_check ||=
617
652
  DependencyFilesFilterer.new(
618
653
  dependency_files: dependency_files,
619
654
  updated_dependencies: [dependency]
620
- ).package_files_requiring_update
655
+ ).paths_requiring_update_check
621
656
  end
622
657
 
623
658
  def dependency_files_builder
@@ -102,6 +102,10 @@ module Dependabot
102
102
  ).updated_requirements
103
103
  end
104
104
 
105
+ def requirements_unlocked_or_can_be?
106
+ requirements_update_strategy != :lockfile_only
107
+ end
108
+
105
109
  def requirements_update_strategy
106
110
  # If passed in as an option (in the base class) honour that option
107
111
  return @requirements_update_strategy.to_sym if @requirements_update_strategy
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.217.0
4
+ version: 0.218.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-24 00:00:00.000000000 Z
11
+ date: 2023-05-22 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.217.0
19
+ version: 0.218.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.217.0
26
+ version: 0.218.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -227,6 +227,8 @@ files:
227
227
  - helpers/lib/npm6/remove-dependencies-from-lockfile.js
228
228
  - helpers/lib/npm6/subdependency-updater.js
229
229
  - helpers/lib/npm6/updater.js
230
+ - helpers/lib/pnpm/index.js
231
+ - helpers/lib/pnpm/lockfile-parser.js
230
232
  - helpers/lib/yarn/conflicting-dependency-parser.js
231
233
  - helpers/lib/yarn/fix-duplicates.js
232
234
  - helpers/lib/yarn/helpers.js
@@ -274,16 +276,19 @@ files:
274
276
  - lib/dependabot/npm_and_yarn/file_parser.rb
275
277
  - lib/dependabot/npm_and_yarn/file_parser/json_lock.rb
276
278
  - lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb
279
+ - lib/dependabot/npm_and_yarn/file_parser/pnpm_lock.rb
277
280
  - lib/dependabot/npm_and_yarn/file_parser/yarn_lock.rb
278
281
  - lib/dependabot/npm_and_yarn/file_updater.rb
279
282
  - lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb
280
283
  - lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb
281
284
  - lib/dependabot/npm_and_yarn/file_updater/package_json_preparer.rb
282
285
  - lib/dependabot/npm_and_yarn/file_updater/package_json_updater.rb
286
+ - lib/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater.rb
283
287
  - lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb
284
288
  - lib/dependabot/npm_and_yarn/helpers.rb
285
289
  - lib/dependabot/npm_and_yarn/metadata_finder.rb
286
290
  - lib/dependabot/npm_and_yarn/native_helpers.rb
291
+ - lib/dependabot/npm_and_yarn/package_manager.rb
287
292
  - lib/dependabot/npm_and_yarn/package_name.rb
288
293
  - lib/dependabot/npm_and_yarn/requirement.rb
289
294
  - lib/dependabot/npm_and_yarn/sub_dependency_files_filterer.rb
@@ -302,8 +307,8 @@ homepage: https://github.com/dependabot/dependabot-core
302
307
  licenses:
303
308
  - Nonstandard
304
309
  metadata:
305
- issue_tracker_uri: https://github.com/dependabot/dependabot-core/issues
306
- changelog_uri: https://github.com/dependabot/dependabot-core/blob/main/CHANGELOG.md
310
+ bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
311
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.218.0
307
312
  post_install_message:
308
313
  rdoc_options: []
309
314
  require_paths: