dependabot-nuget 0.242.1 → 0.243.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.editorconfig +37 -28
  3. data/helpers/lib/NuGetUpdater/.gitignore +1 -0
  4. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/AssemblyMetadataExtractor.cs +2 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +2 -2
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +178 -176
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +2 -1
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +1 -0
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +5 -4
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +1 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +10 -5
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +16 -12
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +18 -17
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +7 -7
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +13 -20
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +9 -3
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +32 -16
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +42 -22
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +32 -13
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +47 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs +55 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +12 -9
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +49 -42
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +16 -3
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +6 -6
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +11 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/ProjectBuildFileTests.cs +18 -9
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/CompatibilityCheckerFacts.cs +2 -2
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/FrameworkCompatibilityServiceFacts.cs +7 -7
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/SupportedFrameworkFacts.cs +1 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +9 -9
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorker.DirsProj.cs +81 -80
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +22 -9
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +140 -104
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +25 -25
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +8 -9
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +198 -22
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +401 -399
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/JsonHelperTests.cs +17 -15
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +111 -42
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterTests.cs +103 -87
  42. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +45 -19
  43. data/lib/dependabot/nuget/file_parser.rb +21 -3
  44. data/lib/dependabot/nuget/file_updater.rb +42 -6
  45. data/lib/dependabot/nuget/native_helpers.rb +27 -8
  46. data/lib/dependabot/nuget/nuget_client.rb +66 -23
  47. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +7 -3
  48. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +63 -59
  49. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +2 -2
  50. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +1 -1
  51. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +22 -17
  52. data/lib/dependabot/nuget/update_checker/repository_finder.rb +292 -270
  53. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +11 -13
  54. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +80 -82
  55. data/lib/dependabot/nuget/update_checker/version_finder.rb +3 -2
  56. data/lib/dependabot/nuget/version.rb +18 -7
  57. data/lib/dependabot/nuget.rb +0 -2
  58. metadata +7 -5
@@ -5,80 +5,84 @@ require "dependabot/update_checkers/base"
5
5
 
6
6
  module Dependabot
7
7
  module Nuget
8
- class UpdateChecker < Dependabot::UpdateCheckers::Base
9
- class CompatibilityChecker
10
- require_relative "nuspec_fetcher"
11
- require_relative "nupkg_fetcher"
12
- require_relative "tfm_finder"
13
- require_relative "tfm_comparer"
14
-
15
- def initialize(dependency_urls:, dependency:, tfm_finder:)
16
- @dependency_urls = dependency_urls
17
- @dependency = dependency
18
- @tfm_finder = tfm_finder
19
- end
8
+ class CompatibilityChecker
9
+ require_relative "nuspec_fetcher"
10
+ require_relative "nupkg_fetcher"
11
+ require_relative "tfm_finder"
12
+ require_relative "tfm_comparer"
13
+
14
+ def initialize(dependency_urls:, dependency:, tfm_finder:)
15
+ @dependency_urls = dependency_urls
16
+ @dependency = dependency
17
+ @tfm_finder = tfm_finder
18
+ end
20
19
 
21
- def compatible?(version)
22
- nuspec_xml = NuspecFetcher.fetch_nuspec(dependency_urls, dependency.name, version)
23
- return false unless nuspec_xml
20
+ def compatible?(version)
21
+ nuspec_xml = NuspecFetcher.fetch_nuspec(dependency_urls, dependency.name, version)
22
+ return false unless nuspec_xml
24
23
 
25
- # development dependencies are packages such as analyzers which need to be
26
- # compatible with the compiler not the project itself.
27
- return true if development_dependency?(nuspec_xml)
24
+ # development dependencies are packages such as analyzers which need to be compatible with the compiler not the
25
+ # project itself, but some packages that report themselves as development dependencies still contain target
26
+ # framework dependencies and should be checked for compatibility through the regular means
27
+ return true if pure_development_dependency?(nuspec_xml)
28
28
 
29
- package_tfms = parse_package_tfms(nuspec_xml)
30
- package_tfms = fetch_package_tfms(version) if package_tfms.empty?
31
- # nil is a special return value that indicates that the package is likely a development dependency
32
- return true if package_tfms.nil?
33
- return false if package_tfms.empty?
29
+ package_tfms = parse_package_tfms(nuspec_xml)
30
+ package_tfms = fetch_package_tfms(version) if package_tfms.empty?
31
+ # nil is a special return value that indicates that the package is likely a development dependency
32
+ return true if package_tfms.nil?
33
+ return false if package_tfms.empty?
34
34
 
35
- return false if project_tfms.nil? || project_tfms.empty?
35
+ return false if project_tfms.nil? || project_tfms.empty?
36
36
 
37
- TfmComparer.are_frameworks_compatible?(project_tfms, package_tfms)
38
- end
37
+ TfmComparer.are_frameworks_compatible?(project_tfms, package_tfms)
38
+ end
39
39
 
40
- private
40
+ private
41
41
 
42
- attr_reader :dependency_urls, :dependency, :tfm_finder
42
+ attr_reader :dependency_urls, :dependency, :tfm_finder
43
43
 
44
- def development_dependency?(nuspec_xml)
45
- contents = nuspec_xml.at_xpath("package/metadata/developmentDependency")&.content&.strip
46
- return false unless contents
44
+ def pure_development_dependency?(nuspec_xml)
45
+ contents = nuspec_xml.at_xpath("package/metadata/developmentDependency")&.content&.strip
46
+ return false unless contents # no `developmentDependency` element
47
47
 
48
- contents.casecmp("true").zero?
49
- end
48
+ self_reports_as_development_dependency = contents.casecmp?("true")
49
+ return false unless self_reports_as_development_dependency
50
50
 
51
- def parse_package_tfms(nuspec_xml)
52
- nuspec_xml.xpath("//dependencies/group").map do |group|
53
- group.attribute("targetFramework")
54
- end
55
- end
51
+ # even though a package self-reports as a development dependency, it might not be if it has dependency groups
52
+ # with a target framework
53
+ dependency_groups_with_target_framework =
54
+ nuspec_xml.at_xpath("/package/metadata/dependencies/group[@targetFramework]")
55
+ dependency_groups_with_target_framework.to_a.empty?
56
+ end
57
+
58
+ def parse_package_tfms(nuspec_xml)
59
+ nuspec_xml.xpath("//dependencies/group").filter_map { |group| group.attribute("targetFramework") }
60
+ end
56
61
 
57
- def project_tfms
58
- return @project_tfms if defined?(@project_tfms)
62
+ def project_tfms
63
+ return @project_tfms if defined?(@project_tfms)
59
64
 
60
- @project_tfms = tfm_finder.frameworks(dependency)
61
- end
65
+ @project_tfms = tfm_finder.frameworks(dependency)
66
+ end
62
67
 
63
- def fetch_package_tfms(dependency_version)
64
- nupkg_buffer = NupkgFetcher.fetch_nupkg_buffer(dependency_urls, dependency.name, dependency_version)
65
- return [] unless nupkg_buffer
66
-
67
- # Parse tfms from the folders beneath the lib folder
68
- folder_name = "lib/"
69
- tfms = Set.new
70
- Zip::File.open_buffer(nupkg_buffer) do |zip|
71
- lib_file_entries = zip.select { |entry| entry.name.start_with?(folder_name) }
72
- # If there is no lib folder in this package, assume it is a development dependency
73
- return nil if lib_file_entries.empty?
74
-
75
- lib_file_entries.each do |entry|
76
- _, tfm = entry.name.split("/").first(2)
77
- tfms << tfm
78
- end
68
+ def fetch_package_tfms(dependency_version)
69
+ nupkg_buffer = NupkgFetcher.fetch_nupkg_buffer(dependency_urls, dependency.name, dependency_version)
70
+ return [] unless nupkg_buffer
71
+
72
+ # Parse tfms from the folders beneath the lib folder
73
+ folder_name = "lib/"
74
+ tfms = Set.new
75
+ Zip::File.open_buffer(nupkg_buffer) do |zip|
76
+ lib_file_entries = zip.select { |entry| entry.name.start_with?(folder_name) }
77
+ # If there is no lib folder in this package, assume it is a development dependency
78
+ return nil if lib_file_entries.empty?
79
+
80
+ lib_file_entries.each do |entry|
81
+ _, tfm = entry.name.split("/").first(2)
82
+ tfms << tfm
79
83
  end
80
- tfms.to_a
81
84
  end
85
+ tfms.to_a
82
86
  end
83
87
  end
84
88
  end
@@ -121,12 +121,12 @@ module Dependabot
121
121
 
122
122
  def dependency_urls
123
123
  @dependency_urls ||=
124
- UpdateChecker::RepositoryFinder.new(
124
+ RepositoryFinder.new(
125
125
  dependency: @dependency,
126
126
  credentials: @credentials,
127
127
  config_files: nuget_configs
128
128
  ).dependency_urls
129
- .select { |url| url.fetch(:repository_type) == "v3" }
129
+ .select { |url| url.fetch(:repository_type) == "v3" }
130
130
  end
131
131
 
132
132
  def fetch_transitive_dependencies(package_id, package_version)
@@ -73,7 +73,7 @@ module Dependabot
73
73
  response_block: response_block
74
74
  )
75
75
 
76
- if response.status == 303
76
+ if response.status == 303 || response.status == 307
77
77
  current_redirects += 1
78
78
  return nil if current_redirects > max_redirects
79
79
 
@@ -26,17 +26,9 @@ module Dependabot
26
26
 
27
27
  nuspec_xml = nil
28
28
 
29
- if azure_package_feed?(feed_url)
30
- # this is an azure devops url we can extract the nuspec from the nupkg
31
- package_data = NupkgFetcher.fetch_nupkg_buffer_from_repository(repository_details, package_id,
32
- package_version)
33
- return if package_data.nil?
34
-
35
- nuspec_string = extract_nuspec(package_data, package_id)
36
- nuspec_xml = Nokogiri::XML(nuspec_string)
37
- else
29
+ if feed_supports_nuspec_download?(feed_url)
38
30
  # we can use the normal nuget apis to get the nuspec and list out the dependencies
39
- base_url = feed_url.gsub("/index.json", "-flatcontainer")
31
+ base_url = repository_details[:base_url].delete_suffix("/")
40
32
  package_id_downcased = package_id.downcase
41
33
  nuspec_url = "#{base_url}/#{package_id_downcased}/#{package_version}/#{package_id_downcased}.nuspec"
42
34
 
@@ -47,22 +39,32 @@ module Dependabot
47
39
 
48
40
  return unless nuspec_response.status == 200
49
41
 
50
- nuspec_response_body = remove_wrapping_zero_width_chars(nuspec_response.body)
42
+ nuspec_response_body = remove_invalid_characters(nuspec_response.body)
51
43
  nuspec_xml = Nokogiri::XML(nuspec_response_body)
44
+ else
45
+ # no guarantee we can directly query the .nuspec; fall back to extracting it from the .nupkg
46
+ package_data = NupkgFetcher.fetch_nupkg_buffer_from_repository(repository_details, package_id,
47
+ package_version)
48
+ return if package_data.nil?
49
+
50
+ nuspec_string = extract_nuspec(package_data, package_id)
51
+ nuspec_xml = Nokogiri::XML(nuspec_string)
52
52
  end
53
53
 
54
54
  nuspec_xml.remove_namespaces!
55
55
  nuspec_xml
56
56
  end
57
57
 
58
- def self.azure_package_feed?(feed_url)
59
- # if url is azure devops
60
- azure_devops_regexs = [
58
+ def self.feed_supports_nuspec_download?(feed_url)
59
+ feed_regexs = [
60
+ # nuget
61
+ %r{https://api\.nuget\.org/v3/index\.json},
62
+ # azure devops
61
63
  %r{https://pkgs\.dev\.azure\.com/(?<organization>[^/]+)/(?<project>[^/]+)/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json},
62
64
  %r{https://pkgs\.dev\.azure\.com/(?<organization>[^/]+)/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json(?<project>)},
63
65
  %r{https://(?<organization>[^\.\/]+)\.pkgs\.visualstudio\.com/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json(?<project>)}
64
66
  ]
65
- azure_devops_regexs.any? { |reg| reg.match(feed_url) }
67
+ feed_regexs.any? { |reg| reg.match(feed_url) }
66
68
  end
67
69
 
68
70
  def self.extract_nuspec(zip_stream, package_id)
@@ -73,8 +75,11 @@ module Dependabot
73
75
  nil
74
76
  end
75
77
 
76
- def self.remove_wrapping_zero_width_chars(string)
77
- string.force_encoding("UTF-8").encode
78
+ def self.remove_invalid_characters(string)
79
+ string.dup
80
+ .force_encoding(Encoding::UTF_8)
81
+ .encode
82
+ .scrub("")
78
83
  .gsub(/\A[\u200B-\u200D\uFEFF]/, "")
79
84
  .gsub(/[\u200B-\u200D\uFEFF]\Z/, "")
80
85
  end