dependabot-nuget 0.246.0 → 0.247.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.
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