dependabot-nuget 0.246.0 → 0.247.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +40 -6
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +27 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +18 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +110 -0
  6. data/lib/dependabot/nuget/cache_manager.rb +9 -3
  7. data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +15 -12
  8. data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +13 -3
  9. data/lib/dependabot/nuget/file_fetcher.rb +79 -31
  10. data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +10 -2
  11. data/lib/dependabot/nuget/file_parser/global_json_parser.rb +10 -2
  12. data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +11 -2
  13. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +140 -41
  14. data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
  15. data/lib/dependabot/nuget/file_parser.rb +3 -3
  16. data/lib/dependabot/nuget/file_updater/property_value_updater.rb +25 -8
  17. data/lib/dependabot/nuget/file_updater.rb +74 -38
  18. data/lib/dependabot/nuget/http_response_helpers.rb +6 -1
  19. data/lib/dependabot/nuget/metadata_finder.rb +27 -3
  20. data/lib/dependabot/nuget/nuget_client.rb +23 -0
  21. data/lib/dependabot/nuget/requirement.rb +4 -1
  22. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
  23. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +11 -13
  24. data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
  25. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
  26. data/lib/dependabot/nuget/update_checker/version_finder.rb +15 -6
  27. data/lib/dependabot/nuget/update_checker.rb +4 -4
  28. data/lib/dependabot/nuget/version.rb +7 -2
  29. metadata +19 -5
@@ -1,4 +1,4 @@
1
- # typed: false
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/file_fetchers"
@@ -18,6 +18,7 @@ module Dependabot
18
18
 
19
19
  BUILD_FILE_NAMES = /^Directory\.Build\.(props|targets)$/i # Directory.Build.props, Directory.Build.targets
20
20
 
21
+ sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
21
22
  def self.required_files_in?(filenames)
22
23
  return true if filenames.any? { |f| f.match?(/^packages\.config$/i) }
23
24
  return true if filenames.any? { |f| f.end_with?(".sln") }
@@ -27,12 +28,32 @@ module Dependabot
27
28
  filenames.any? { |name| name.match?(/\.[a-z]{2}proj$/) }
28
29
  end
29
30
 
31
+ sig { override.returns(String) }
30
32
  def self.required_files_message
31
33
  "Repo must contain a .proj file, .(cs|vb|fs)proj file, or a packages.config."
32
34
  end
33
35
 
36
+ sig do
37
+ params(
38
+ source: Dependabot::Source,
39
+ credentials: T::Array[Credential],
40
+ repo_contents_path: T.nilable(String),
41
+ options: T::Hash[String, String]
42
+ ).void
43
+ end
44
+ def initialize(source:, credentials:, repo_contents_path: nil, options: {})
45
+ super(source: source, credentials: credentials, repo_contents_path: repo_contents_path, options: options)
46
+
47
+ @sln_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
48
+ @sln_project_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
49
+ @project_files = T.let([], T::Array[Dependabot::DependencyFile])
50
+ @fetched_files = T.let({}, T::Hash[String, T::Array[Dependabot::DependencyFile]])
51
+ @nuget_config_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
52
+ @packages_config_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
53
+ end
54
+
34
55
  sig { override.returns(T::Array[DependencyFile]) }
35
- def fetch_files
56
+ def fetch_files # rubocop:disable Metrics/AbcSize
36
57
  fetched_files = []
37
58
  fetched_files += project_files
38
59
  fetched_files += directory_build_files
@@ -50,7 +71,7 @@ module Dependabot
50
71
  end
51
72
 
52
73
  if project_files.none? && packages_config_files.none?
53
- raise @missing_sln_project_file_errors.first if @missing_sln_project_file_errors&.any?
74
+ raise T.must(@missing_sln_project_file_errors.first) if @missing_sln_project_file_errors&.any?
54
75
 
55
76
  raise(
56
77
  Dependabot::DependencyFileNotFound,
@@ -63,8 +84,11 @@ module Dependabot
63
84
 
64
85
  private
65
86
 
87
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
66
88
  def project_files
67
- @project_files ||=
89
+ return @project_files if @project_files.any?
90
+
91
+ @project_files =
68
92
  begin
69
93
  project_files = []
70
94
  project_files += csproj_file
@@ -84,13 +108,14 @@ module Dependabot
84
108
  )
85
109
  end
86
110
 
111
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
87
112
  def packages_config_files
88
113
  return @packages_config_files if @packages_config_files
89
114
 
90
115
  candidate_paths =
91
116
  [*project_files.map { |f| File.dirname(f.name) }, "."].uniq
92
117
 
93
- @packages_config_files ||=
118
+ @packages_config_files =
94
119
  candidate_paths.filter_map do |dir|
95
120
  file = repo_contents(dir: dir)
96
121
  .find { |f| f.name.casecmp("packages.config").zero? }
@@ -99,6 +124,7 @@ module Dependabot
99
124
  end
100
125
 
101
126
  # rubocop:disable Metrics/PerceivedComplexity
127
+ sig { returns(T.nilable(T::Array[T.untyped])) }
102
128
  def sln_file_names
103
129
  sln_files = repo_contents.select { |f| f.name.end_with?(".sln") }
104
130
  src_dir = repo_contents.any? { |f| f.name == "src" && f.type == "dir" }
@@ -117,13 +143,15 @@ module Dependabot
117
143
  end
118
144
  # rubocop:enable Metrics/PerceivedComplexity
119
145
 
146
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
120
147
  def directory_build_files
121
- @directory_build_files ||= fetch_directory_build_files
148
+ @directory_build_files ||= T.let(fetch_directory_build_files, T.nilable(T::Array[Dependabot::DependencyFile]))
122
149
  end
123
150
 
151
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
124
152
  def fetch_directory_build_files
125
- attempted_dirs = []
126
- directory_build_files = []
153
+ attempted_dirs = T.let([], T::Array[Pathname])
154
+ directory_build_files = T.let([], T::Array[Dependabot::DependencyFile])
127
155
  directory_path = Pathname.new(directory)
128
156
 
129
157
  # find all build files (Directory.Build.props/.targets) relative to the given project file
@@ -145,12 +173,13 @@ module Dependabot
145
173
  directory_build_files
146
174
  end
147
175
 
176
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
148
177
  def sln_project_files
149
178
  return [] unless sln_files
150
179
 
151
180
  @sln_project_files ||=
152
181
  begin
153
- paths = sln_files.flat_map do |sln_file|
182
+ paths = T.must(sln_files).flat_map do |sln_file|
154
183
  SlnProjectPathsFinder
155
184
  .new(sln_file: sln_file)
156
185
  .project_paths
@@ -159,7 +188,7 @@ module Dependabot
159
188
  paths.filter_map do |path|
160
189
  fetch_file_from_host(path)
161
190
  rescue Dependabot::DependencyFileNotFound => e
162
- @missing_sln_project_file_errors ||= []
191
+ @missing_sln_project_file_errors ||= T.let([], T.nilable(T::Array[Dependabot::DependencyFileNotFound]))
163
192
  @missing_sln_project_file_errors << e
164
193
  # Don't worry about missing files too much for now (at least
165
194
  # until we start resolving properties)
@@ -168,35 +197,42 @@ module Dependabot
168
197
  end
169
198
  end
170
199
 
200
+ sig { returns(T.nilable(T::Array[Dependabot::DependencyFile])) }
171
201
  def sln_files
172
202
  return unless sln_file_names
173
203
 
174
204
  @sln_files ||=
175
205
  sln_file_names
176
- .map { |sln_file_name| fetch_file_from_host(sln_file_name) }
177
- .select { |file| file.content.valid_encoding? }
206
+ &.map { |sln_file_name| fetch_file_from_host(sln_file_name) }
207
+ &.select { |file| file.content&.valid_encoding? }
178
208
  end
179
209
 
210
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
180
211
  def csproj_file
181
- @csproj_file ||= find_and_fetch_with_suffix(".csproj")
212
+ @csproj_file ||= T.let(find_and_fetch_with_suffix(".csproj"), T.nilable(T::Array[Dependabot::DependencyFile]))
182
213
  end
183
214
 
215
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
184
216
  def vbproj_file
185
- @vbproj_file ||= find_and_fetch_with_suffix(".vbproj")
217
+ @vbproj_file ||= T.let(find_and_fetch_with_suffix(".vbproj"), T.nilable(T::Array[Dependabot::DependencyFile]))
186
218
  end
187
219
 
220
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
188
221
  def fsproj_file
189
- @fsproj_file ||= find_and_fetch_with_suffix(".fsproj")
222
+ @fsproj_file ||= T.let(find_and_fetch_with_suffix(".fsproj"), T.nilable(T::Array[Dependabot::DependencyFile]))
190
223
  end
191
224
 
225
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
192
226
  def proj_files
193
- @proj_files ||= find_and_fetch_with_suffix(".proj")
227
+ @proj_files ||= T.let(find_and_fetch_with_suffix(".proj"), T.nilable(T::Array[Dependabot::DependencyFile]))
194
228
  end
195
229
 
230
+ sig { params(suffix: String).returns(T::Array[Dependabot::DependencyFile]) }
196
231
  def find_and_fetch_with_suffix(suffix)
197
232
  repo_contents.select { |f| f.name.end_with?(suffix) }.map { |f| fetch_file_from_host(f.name) }
198
233
  end
199
234
 
235
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
200
236
  def nuget_config_files
201
237
  return @nuget_config_files if @nuget_config_files
202
238
 
@@ -206,8 +242,15 @@ module Dependabot
206
242
  @nuget_config_files
207
243
  end
208
244
 
245
+ sig do
246
+ params(
247
+ project_file: Dependabot::DependencyFile,
248
+ expected_file_name: String
249
+ )
250
+ .returns(T.nilable(Dependabot::DependencyFile))
251
+ end
209
252
  def named_file_up_tree_from_project_file(project_file, expected_file_name)
210
- found_expected_file = nil
253
+ found_expected_file = T.let(nil, T.nilable(Dependabot::DependencyFile))
211
254
  directory_path = Pathname.new(directory)
212
255
  full_project_dir = Pathname.new(project_file.directory).join(project_file.name).dirname
213
256
  full_project_dir.ascend.each do |base|
@@ -228,26 +271,25 @@ module Dependabot
228
271
  found_expected_file
229
272
  end
230
273
 
274
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
231
275
  def global_json
232
- return @global_json if defined?(@global_json)
233
-
234
- @global_json = fetch_file_if_present("global.json")
276
+ @global_json ||= T.let(fetch_file_if_present("global.json"), T.nilable(Dependabot::DependencyFile))
235
277
  end
236
278
 
279
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
237
280
  def dotnet_tools_json
238
- return @dotnet_tools_json if defined?(@dotnet_tools_json)
239
-
240
- @dotnet_tools_json = fetch_file_if_present(".config/dotnet-tools.json")
281
+ @dotnet_tools_json ||= T.let(fetch_file_if_present(".config/dotnet-tools.json"),
282
+ T.nilable(Dependabot::DependencyFile))
241
283
  end
242
284
 
285
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
243
286
  def packages_props
244
- return @packages_props if defined?(@packages_props)
245
-
246
- @packages_props = fetch_file_if_present("Packages.props")
287
+ @packages_props ||= T.let(fetch_file_if_present("Packages.props"), T.nilable(Dependabot::DependencyFile))
247
288
  end
248
289
 
290
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
249
291
  def imported_property_files
250
- imported_property_files = []
292
+ imported_property_files = T.let([], T::Array[Dependabot::DependencyFile])
251
293
 
252
294
  files = [*project_files, *directory_build_files]
253
295
 
@@ -263,18 +305,24 @@ module Dependabot
263
305
  imported_property_files
264
306
  end
265
307
 
308
+ sig do
309
+ params(
310
+ file: Dependabot::DependencyFile,
311
+ previously_fetched_files: T::Array[Dependabot::DependencyFile]
312
+ )
313
+ .returns(T::Array[Dependabot::DependencyFile])
314
+ end
266
315
  def fetch_imported_property_files(file:, previously_fetched_files:)
267
316
  file_id = file.directory + "/" + file.name
268
- @fetched_files ||= {}
269
317
  if @fetched_files[file_id]
270
- @fetched_files[file_id]
318
+ T.must(@fetched_files[file_id])
271
319
  else
272
320
  paths =
273
321
  ImportPathsFinder.new(project_file: file).import_paths +
274
322
  ImportPathsFinder.new(project_file: file).project_reference_paths +
275
323
  ImportPathsFinder.new(project_file: file).project_file_paths
276
324
 
277
- paths.flat_map do |path|
325
+ paths.filter_map do |path|
278
326
  next if previously_fetched_files.map(&:name).include?(path)
279
327
  next if file.name == path
280
328
  next if path.include?("$(")
@@ -290,7 +338,7 @@ module Dependabot
290
338
  # Don't worry about missing files too much for now (at least
291
339
  # until we start resolving properties)
292
340
  nil
293
- end.compact
341
+ end.flatten
294
342
  end
295
343
  end
296
344
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
@@ -12,12 +12,17 @@ module Dependabot
12
12
  module Nuget
13
13
  class FileParser
14
14
  class DotNetToolsJsonParser
15
+ extend T::Sig
16
+
15
17
  require "dependabot/file_parsers/base/dependency_set"
16
18
 
19
+ sig { params(dotnet_tools_json: Dependabot::DependencyFile).void }
17
20
  def initialize(dotnet_tools_json:)
18
21
  @dotnet_tools_json = dotnet_tools_json
22
+ @parsed_dotnet_tools_json = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
19
23
  end
20
24
 
25
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
21
26
  def dependency_set
22
27
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
23
28
 
@@ -48,11 +53,14 @@ module Dependabot
48
53
 
49
54
  private
50
55
 
56
+ sig { returns(Dependabot::DependencyFile) }
51
57
  attr_reader :dotnet_tools_json
52
58
 
59
+ sig { returns(T::Hash[String, T.untyped]) }
53
60
  def parsed_dotnet_tools_json
54
61
  # Remove BOM if present as JSON should be UTF-8
55
- @parsed_dotnet_tools_json ||= JSON.parse(dotnet_tools_json.content.delete_prefix("\uFEFF"))
62
+ content = T.must(dotnet_tools_json.content)
63
+ @parsed_dotnet_tools_json ||= JSON.parse(content.delete_prefix("\uFEFF"))
56
64
  rescue JSON::ParserError
57
65
  raise Dependabot::DependencyFileNotParseable, dotnet_tools_json.path
58
66
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
@@ -12,12 +12,17 @@ module Dependabot
12
12
  module Nuget
13
13
  class FileParser
14
14
  class GlobalJsonParser
15
+ extend T::Sig
16
+
15
17
  require "dependabot/file_parsers/base/dependency_set"
16
18
 
19
+ sig { params(global_json: Dependabot::DependencyFile).void }
17
20
  def initialize(global_json:)
18
21
  @global_json = global_json
22
+ @parsed_global_json = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
19
23
  end
20
24
 
25
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
21
26
  def dependency_set
22
27
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
23
28
 
@@ -45,11 +50,14 @@ module Dependabot
45
50
 
46
51
  private
47
52
 
53
+ sig { returns(Dependabot::DependencyFile) }
48
54
  attr_reader :global_json
49
55
 
56
+ sig { returns(T::Hash[String, T.untyped]) }
50
57
  def parsed_global_json
51
58
  # Remove BOM if present as JSON should be UTF-8
52
- @parsed_global_json ||= JSON.parse(global_json.content.delete_prefix("\uFEFF"))
59
+ content = T.must(global_json.content)
60
+ @parsed_global_json ||= JSON.parse(content.delete_prefix("\uFEFF"))
53
61
  rescue JSON::ParserError
54
62
  raise Dependabot::DependencyFileNotParseable, global_json.path
55
63
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "nokogiri"
@@ -13,18 +13,22 @@ module Dependabot
13
13
  module Nuget
14
14
  class FileParser
15
15
  class PackagesConfigParser
16
+ extend T::Sig
16
17
  require "dependabot/file_parsers/base/dependency_set"
17
18
 
18
19
  DEPENDENCY_SELECTOR = "packages > package"
19
20
 
21
+ sig { returns(T::Hash[String, Dependabot::FileParsers::Base::DependencySet]) }
20
22
  def self.dependency_set_cache
21
23
  CacheManager.cache("packages_config_dependency_set")
22
24
  end
23
25
 
26
+ sig { params(packages_config: Dependabot::DependencyFile).void }
24
27
  def initialize(packages_config:)
25
28
  @packages_config = packages_config
26
29
  end
27
30
 
31
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
28
32
  def dependency_set
29
33
  key = "#{packages_config.name.downcase}::#{packages_config.content.hash}"
30
34
  cache = PackagesConfigParser.dependency_set_cache
@@ -34,8 +38,10 @@ module Dependabot
34
38
 
35
39
  private
36
40
 
41
+ sig { returns(Dependabot::DependencyFile) }
37
42
  attr_reader :packages_config
38
43
 
44
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
39
45
  def parse_dependencies
40
46
  dependency_set = Dependabot::FileParsers::Base::DependencySet.new
41
47
 
@@ -44,7 +50,7 @@ module Dependabot
44
50
  doc.css(DEPENDENCY_SELECTOR).each do |dependency_node|
45
51
  dependency_set <<
46
52
  Dependency.new(
47
- name: dependency_name(dependency_node),
53
+ name: T.must(dependency_name(dependency_node)),
48
54
  version: dependency_version(dependency_node),
49
55
  package_manager: "nuget",
50
56
  requirements: [{
@@ -59,11 +65,13 @@ module Dependabot
59
65
  dependency_set
60
66
  end
61
67
 
68
+ sig { params(dependency_node: Nokogiri::XML::Node).returns(T.nilable(String)) }
62
69
  def dependency_name(dependency_node)
63
70
  dependency_node.attribute("id")&.value&.strip ||
64
71
  dependency_node.at_xpath("./id")&.content&.strip
65
72
  end
66
73
 
74
+ sig { params(dependency_node: Nokogiri::XML::Node).returns(T.nilable(String)) }
67
75
  def dependency_version(dependency_node)
68
76
  # Ranges and wildcards aren't allowed in a packages.config - the
69
77
  # specified requirement is always an exact version.
@@ -71,6 +79,7 @@ module Dependabot
71
79
  dependency_node.at_xpath("./version")&.content&.strip
72
80
  end
73
81
 
82
+ sig { params(dependency_node: Nokogiri::XML::Node).returns(String) }
74
83
  def dependency_type(dependency_node)
75
84
  val = dependency_node.attribute("developmentDependency")&.value&.strip ||
76
85
  dependency_node.at_xpath("./developmentDependency")&.content&.strip