4
4
5
5
require "dependabot/npm_and_yarn/file_updater"
6
6
require "dependabot/npm_and_yarn/file_parser"
7
+ require "dependabot/npm_and_yarn/helpers"
7
8
require "dependabot/npm_and_yarn/update_checker/registry_finder"
8
9
require "dependabot/npm_and_yarn/native_helpers"
9
10
require "dependabot/shared_helpers"
10
11
require "dependabot/errors"
12
+ require "dependabot/experiments"
11
13
12
14
# rubocop:disable Metrics/ClassLength
13
15
module Dependabot
@@ -17,9 +19,10 @@ class YarnLockfileUpdater
17
19
require_relative "npmrc_builder"
18
20
require_relative "package_json_updater"
19
21
20
- def initialize ( dependencies :, dependency_files :, credentials :)
22
+ def initialize ( dependencies :, dependency_files :, repo_contents_path : , credentials :)
21
23
@dependencies = dependencies
22
24
@dependency_files = dependency_files
25
+ @repo_contents_path = repo_contents_path
23
26
@credentials = credentials
24
27
end
25
28
@@ -35,7 +38,7 @@ def updated_yarn_lock_content(yarn_lock)
35
38
36
39
private
37
40
38
- attr_reader :dependencies , :dependency_files , :credentials
41
+ attr_reader :dependencies , :dependency_files , :repo_contents_path , : credentials
39
42
40
43
UNREACHABLE_GIT = /ls-remote --tags --heads (?<url>.*)/ . freeze
41
44
TIMEOUT_FETCHING_PACKAGE =
@@ -51,21 +54,22 @@ def sub_dependencies
51
54
end
52
55
53
56
def updated_yarn_lock ( yarn_lock )
54
- SharedHelpers . in_a_temporary_directory do
57
+ base_dir = dependency_files . first . directory
58
+ SharedHelpers . in_a_temporary_repo_directory ( base_dir , repo_contents_path ) do
55
59
write_temporary_dependency_files
56
60
lockfile_name = Pathname . new ( yarn_lock . name ) . basename . to_s
57
61
path = Pathname . new ( yarn_lock . name ) . dirname . to_s
58
62
updated_files = run_current_yarn_update (
59
63
path : path ,
60
- lockfile_name : lockfile_name
64
+ yarn_lock : yarn_lock
61
65
)
62
66
updated_files . fetch ( lockfile_name )
63
67
end
64
68
rescue SharedHelpers ::HelperSubprocessFailed => e
65
69
handle_yarn_lock_updater_error ( e , yarn_lock )
66
70
end
67
71
68
- def run_current_yarn_update ( path :, lockfile_name :)
72
+ def run_current_yarn_update ( path :, yarn_lock :)
69
73
top_level_dependency_updates = top_level_dependencies . map do |d |
70
74
{
71
75
name : d . name ,
@@ -76,12 +80,12 @@ def run_current_yarn_update(path:, lockfile_name:)
76
80
77
81
run_yarn_updater (
78
82
path : path ,
79
- lockfile_name : lockfile_name ,
83
+ yarn_lock : yarn_lock ,
80
84
top_level_dependency_updates : top_level_dependency_updates
81
85
)
82
86
end
83
87
84
- def run_previous_yarn_update ( path :, lockfile_name :)
88
+ def run_previous_yarn_update ( path :, yarn_lock :)
85
89
previous_top_level_dependencies = top_level_dependencies . map do |d |
86
90
{
87
91
name : d . name ,
@@ -94,22 +98,29 @@ def run_previous_yarn_update(path:, lockfile_name:)
94
98
95
99
run_yarn_updater (
96
100
path : path ,
97
- lockfile_name : lockfile_name ,
101
+ yarn_lock : yarn_lock ,
98
102
top_level_dependency_updates : previous_top_level_dependencies
99
103
)
100
104
end
101
105
102
106
# rubocop:disable Metrics/PerceivedComplexity
103
- def run_yarn_updater ( path :, lockfile_name :,
104
- top_level_dependency_updates :)
107
+ def run_yarn_updater ( path :, yarn_lock :, top_level_dependency_updates :)
105
108
SharedHelpers . with_git_configured ( credentials : credentials ) do
106
109
Dir . chdir ( path ) do
107
110
if top_level_dependency_updates . any?
108
- run_yarn_top_level_updater (
109
- top_level_dependency_updates : top_level_dependency_updates
110
- )
111
+ if yarn_berry? ( yarn_lock )
112
+ run_yarn_berry_top_level_updater ( top_level_dependency_updates : top_level_dependency_updates ,
113
+ yarn_lock : yarn_lock )
114
+ else
115
+
116
+ run_yarn_top_level_updater (
117
+ top_level_dependency_updates : top_level_dependency_updates
118
+ )
119
+ end
120
+ elsif yarn_berry? ( yarn_lock )
121
+ run_yarn_berry_subdependency_updater ( yarn_lock : yarn_lock )
111
122
else
112
- run_yarn_subdependency_updater ( lockfile_name : lockfile_name )
123
+ run_yarn_subdependency_updater ( yarn_lock : yarn_lock )
113
124
end
114
125
end
115
126
end
@@ -133,6 +144,40 @@ def run_yarn_updater(path:, lockfile_name:,
133
144
134
145
# rubocop:enable Metrics/PerceivedComplexity
135
146
147
+ def yarn_berry? ( yarn_lock )
148
+ return false unless Experiments . enabled? ( :yarn_berry )
149
+
150
+ yaml = YAML . safe_load ( yarn_lock . content )
151
+ yaml . key? ( "__metadata" )
152
+ rescue StandardError
153
+ false
154
+ end
155
+
156
+ def run_yarn_berry_top_level_updater ( top_level_dependency_updates :, yarn_lock :)
157
+ updates = top_level_dependency_updates . collect do |dep |
158
+ # when there are multiple requirements, we're dealing with a
159
+ # workspace-like setup, where there are multiple package.json files
160
+ # that pull in the same dependency. It appears that these are always
161
+ # updated to a single new version, so we just pick the first one.
162
+ "#{ dep [ :name ] } @#{ dep [ :requirements ] . first [ :requirement ] } "
163
+ end
164
+ command = "yarn add #{ updates . join ( ' ' ) } "
165
+ Helpers . run_yarn_commands ( command )
166
+ { yarn_lock . name => File . read ( yarn_lock . name ) }
167
+ end
168
+
169
+ def run_yarn_berry_subdependency_updater ( yarn_lock :)
170
+ dep = sub_dependencies . first
171
+ update = "#{ dep . name } @#{ dep . version } "
172
+
173
+ Helpers . run_yarn_commands (
174
+ "yarn add #{ update } " ,
175
+ "yarn dedupe #{ dep . name } " ,
176
+ "yarn remove #{ dep . name } "
177
+ )
178
+ { yarn_lock . name => File . read ( yarn_lock . name ) }
179
+ end
180
+
136
181
def run_yarn_top_level_updater ( top_level_dependency_updates :)
137
182
SharedHelpers . run_helper_subprocess (
138
183
command : NativeHelpers . helper_path ,
@@ -144,7 +189,8 @@ def run_yarn_top_level_updater(top_level_dependency_updates:)
144
189
)
145
190
end
146
191
147
- def run_yarn_subdependency_updater ( lockfile_name :)
192
+ def run_yarn_subdependency_updater ( yarn_lock :)
193
+ lockfile_name = Pathname . new ( yarn_lock . name ) . basename . to_s
148
194
SharedHelpers . run_helper_subprocess (
149
195
command : NativeHelpers . helper_path ,
150
196
function : "yarn:updateSubdependency" ,
@@ -259,12 +305,11 @@ def resolvable_before_update?(yarn_lock)
259
305
260
306
@resolvable_before_update [ yarn_lock . name ] =
261
307
begin
262
- SharedHelpers . in_a_temporary_directory do
308
+ base_dir = dependency_files . first . directory
309
+ SharedHelpers . in_a_temporary_repo_directory ( base_dir , repo_contents_path ) do
263
310
write_temporary_dependency_files ( update_package_json : false )
264
- lockfile_name = Pathname . new ( yarn_lock . name ) . basename . to_s
265
311
path = Pathname . new ( yarn_lock . name ) . dirname . to_s
266
- run_previous_yarn_update ( path : path ,
267
- lockfile_name : lockfile_name )
312
+ run_previous_yarn_update ( path : path , yarn_lock : yarn_lock )
268
313
end
269
314
270
315
true
0 commit comments