dependabot-nuget 0.250.0 → 0.252.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Common.props +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Packages.props +26 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +35 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/NuGetUpdater.Cli.csproj +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +4 -7
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +251 -0
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/NuGetUpdater.Cli.Test.csproj +3 -3
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +56 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyType.cs +1 -1
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DirectoryPackagesPropsDiscovery.cs +69 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DirectoryPackagesPropsDiscoveryResult.cs +11 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +217 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscovery.cs +30 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscoveryResult.cs +10 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscovery.cs +30 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscoveryResult.cs +10 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/IDiscoveryResult.cs +14 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +29 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscoveryResult.cs +10 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +13 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +127 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +13 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EvaluationResult.cs +8 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EvaluationResultType.cs +9 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/BuildFile.cs +6 -8
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/DotNetToolsJsonBuildFile.cs +4 -7
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/GlobalJsonBuildFile.cs +24 -17
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +2 -2
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +8 -13
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +100 -19
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/XmlBuildFile.cs +2 -2
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +6 -6
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Property.cs +6 -0
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +23 -36
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +5 -10
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +16 -21
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +4 -19
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/HashSetExtensions.cs +14 -0
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ImmutableArrayExtensions.cs +18 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +0 -1
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +121 -67
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +27 -4
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +117 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +91 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +71 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +59 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +380 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +306 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +36 -0
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/DotNetToolsJsonBuildFileTests.cs +1 -2
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/GlobalJsonBuildFileTests.cs +2 -3
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/PackagesConfigBuildFileTests.cs +4 -6
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/ProjectBuildFileTests.cs +6 -5
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +4 -3
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +38 -6
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +12 -40
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +30 -0
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/AssertEx.cs +272 -0
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/DiffUtil.cs +266 -0
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +195 -152
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +7 -11
  63. data/lib/dependabot/nuget/discovery/dependency_details.rb +95 -0
  64. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +126 -0
  65. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +43 -0
  66. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +83 -0
  67. data/lib/dependabot/nuget/discovery/evaluation_details.rb +63 -0
  68. data/lib/dependabot/nuget/discovery/project_discovery.rb +71 -0
  69. data/lib/dependabot/nuget/discovery/property_details.rb +43 -0
  70. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +66 -0
  71. data/lib/dependabot/nuget/file_parser.rb +19 -128
  72. data/lib/dependabot/nuget/file_updater.rb +28 -60
  73. data/lib/dependabot/nuget/native_helpers.rb +55 -0
  74. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +3 -8
  75. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +1 -0
  76. data/lib/dependabot/nuget/update_checker/property_updater.rb +1 -0
  77. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +17 -152
  78. data/lib/dependabot/nuget/update_checker/version_finder.rb +1 -6
  79. data/lib/dependabot/nuget/update_checker.rb +4 -1
  80. metadata +43 -11
  81. data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +0 -71
  82. data/lib/dependabot/nuget/file_parser/global_json_parser.rb +0 -68
  83. data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +0 -92
  84. data/lib/dependabot/nuget/file_parser/project_file_parser.rb +0 -620
  85. data/lib/dependabot/nuget/file_parser/property_value_finder.rb +0 -225
  86. data/lib/dependabot/nuget/file_updater/property_value_updater.rb +0 -81
@@ -1,11 +1,11 @@
1
1
  # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
- require "nokogiri"
5
-
6
4
  require "dependabot/dependency"
7
5
  require "dependabot/file_parsers"
8
6
  require "dependabot/file_parsers/base"
7
+ require "dependabot/nuget/discovery/discovery_json_reader"
8
+ require "dependabot/nuget/native_helpers"
9
9
  require "sorbet-runtime"
10
10
 
11
11
  # For details on how dotnet handles version constraints, see:
@@ -16,106 +16,31 @@ module Dependabot
16
16
  extend T::Sig
17
17
 
18
18
  require "dependabot/file_parsers/base/dependency_set"
19
- require_relative "file_parser/project_file_parser"
20
- require_relative "file_parser/packages_config_parser"
21
- require_relative "file_parser/global_json_parser"
22
- require_relative "file_parser/dotnet_tools_json_parser"
23
-
24
- PACKAGE_CONF_DEPENDENCY_SELECTOR = "packages > packages"
25
19
 
26
20
  sig { override.returns(T::Array[Dependabot::Dependency]) }
27
21
  def parse
28
- dependency_set = DependencySet.new
29
- dependency_set += project_file_dependencies
30
- dependency_set += packages_config_dependencies
31
- dependency_set += global_json_dependencies if global_json
32
- dependency_set += dotnet_tools_json_dependencies if dotnet_tools_json
33
-
34
- (dependencies, deps_with_unresolved_versions) = dependency_set.dependencies.partition do |d|
35
- # try to parse the version; don't care about result, just that it succeeded
36
- _ = Version.new(d.version)
37
- true
38
- rescue ArgumentError
39
- # version could not be parsed
40
- false
41
- end
22
+ workspace_path = project_files.first&.directory
23
+ return [] unless workspace_path
42
24
 
43
- deps_with_unresolved_versions.each do |d|
44
- Dependabot.logger.warn "Dependency '#{d.name}' excluded due to unparsable version: #{d.version}"
45
- end
46
-
47
- dependency_info = dependencies.map do |d|
48
- requirements_info = d.requirements.filter_map { |r| " file: #{r[:file]}, metadata: #{r[:metadata]}" }
49
- .join("\n")
50
- " name: #{d.name}, version: #{d.version}\n#{requirements_info}"
51
- end.join("\n")
25
+ # run discovery for the repo
26
+ NativeHelpers.run_nuget_discover_tool(repo_root: T.must(repo_contents_path),
27
+ workspace_path: workspace_path,
28
+ output_path: DiscoveryJsonReader.discovery_file_path,
29
+ credentials: credentials)
52
30
 
53
- if dependencies.length.positive?
54
- Dependabot.logger.info "The following dependencies were found:\n#{dependency_info}"
55
- end
56
-
57
- dependencies
31
+ discovered_dependencies.dependencies
58
32
  end
59
33
 
60
34
  private
61
35
 
62
36
  sig { returns(Dependabot::FileParsers::Base::DependencySet) }
63
- def project_file_dependencies
64
- dependency_set = DependencySet.new
65
-
66
- project_files.each do |project_file|
67
- tfms = project_file_parser.target_frameworks(project_file: project_file)
68
- unless tfms.any?
69
- Dependabot.logger.warn "Excluding project file '#{project_file.name}' due to unresolvable target framework"
70
- next
71
- end
72
-
73
- dependency_set += project_file_parser.dependency_set(project_file: project_file)
74
- end
75
-
76
- proj_files.each do |proj_file|
77
- dependency_set += project_file_parser.dependency_set(project_file: proj_file)
78
- end
79
-
80
- dependency_set
81
- end
82
-
83
- sig { returns(Dependabot::FileParsers::Base::DependencySet) }
84
- def packages_config_dependencies
85
- dependency_set = DependencySet.new
86
-
87
- packages_config_files.each do |file|
88
- parser = PackagesConfigParser.new(packages_config: file)
89
- dependency_set += parser.dependency_set
90
- end
91
-
92
- dependency_set
93
- end
94
-
95
- sig { returns(Dependabot::FileParsers::Base::DependencySet) }
96
- def global_json_dependencies
97
- return DependencySet.new unless global_json
37
+ def discovered_dependencies
38
+ discovery_json = DiscoveryJsonReader.discovery_json
39
+ return DependencySet.new unless discovery_json
98
40
 
99
- GlobalJsonParser.new(global_json: T.must(global_json)).dependency_set
100
- end
101
-
102
- sig { returns(Dependabot::FileParsers::Base::DependencySet) }
103
- def dotnet_tools_json_dependencies
104
- return DependencySet.new unless dotnet_tools_json
105
-
106
- DotNetToolsJsonParser.new(dotnet_tools_json: T.must(dotnet_tools_json)).dependency_set
107
- end
108
-
109
- sig { returns(Dependabot::Nuget::FileParser::ProjectFileParser) }
110
- def project_file_parser
111
- @project_file_parser ||= T.let(
112
- ProjectFileParser.new(
113
- dependency_files: dependency_files,
114
- credentials: credentials,
115
- repo_contents_path: @repo_contents_path
116
- ),
117
- T.nilable(Dependabot::Nuget::FileParser::ProjectFileParser)
118
- )
41
+ DiscoveryJsonReader.new(
42
+ discovery_json: discovery_json
43
+ ).dependency_set
119
44
  end
120
45
 
121
46
  sig { returns(T::Array[Dependabot::DependencyFile]) }
@@ -136,47 +61,13 @@ module Dependabot
136
61
  end
137
62
  end
138
63
 
139
- sig { returns(T::Array[Dependabot::DependencyFile]) }
140
- def packages_config_files
141
- dependency_files.select do |f|
142
- f.name.split("/").last&.casecmp("packages.config")&.zero?
143
- end
144
- end
145
-
146
- sig { returns(T::Array[Dependabot::DependencyFile]) }
147
- def project_import_files
148
- dependency_files -
149
- project_files -
150
- packages_config_files -
151
- nuget_configs -
152
- [global_json] -
153
- [dotnet_tools_json]
154
- end
155
-
156
- sig { returns(T::Array[Dependabot::DependencyFile]) }
157
- def nuget_configs
158
- dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
159
- end
160
-
161
- sig { returns(T.nilable(Dependabot::DependencyFile)) }
162
- def global_json
163
- dependency_files.find { |f| f.name.casecmp?("global.json") }
164
- end
165
-
166
- sig { returns(T.nilable(Dependabot::DependencyFile)) }
167
- def dotnet_tools_json
168
- dependency_files.find { |f| f.name.casecmp?(".config/dotnet-tools.json") }
169
- end
170
-
171
64
  sig { override.void }
172
65
  def check_required_files
173
- if project_files.any? || proj_files.any? || packages_config_files.any? || global_json || dotnet_tools_json
174
- return
175
- end
66
+ return if project_files.any? || proj_files.any?
176
67
 
177
68
  raise Dependabot::DependencyFileNotFound.new(
178
- "*.(cs|vb|fs)proj, *.proj, packages.config, global.json, dotnet-tools.json",
179
- "No project file, *.proj, packages.config, global.json, or dotnet-tools.json!"
69
+ "*.(cs|vb|fs)proj, *.proj",
70
+ "No project file or *.proj!"
180
71
  )
181
72
  end
182
73
  end
@@ -4,6 +4,9 @@
4
4
  require "dependabot/dependency_file"
5
5
  require "dependabot/file_updaters"
6
6
  require "dependabot/file_updaters/base"
7
+ require "dependabot/nuget/discovery/dependency_details"
8
+ require "dependabot/nuget/discovery/discovery_json_reader"
9
+ require "dependabot/nuget/discovery/workspace_discovery"
7
10
  require "dependabot/nuget/native_helpers"
8
11
  require "dependabot/shared_helpers"
9
12
  require "sorbet-runtime"
@@ -13,11 +16,6 @@ module Dependabot
13
16
  class FileUpdater < Dependabot::FileUpdaters::Base
14
17
  extend T::Sig
15
18
 
16
- require_relative "file_updater/property_value_updater"
17
- require_relative "file_parser/project_file_parser"
18
- require_relative "file_parser/dotnet_tools_json_parser"
19
- require_relative "file_parser/packages_config_parser"
20
-
21
19
  sig { override.returns(T::Array[Regexp]) }
22
20
  def self.updated_files_regex
23
21
  [
@@ -67,7 +65,7 @@ module Dependabot
67
65
  project_dependencies = project_dependencies(project_file)
68
66
  proj_path = dependency_file_path(project_file)
69
67
 
70
- next unless project_dependencies.any? { |dep| dep.name.casecmp(dependency.name)&.zero? }
68
+ next unless project_dependencies.any? { |dep| dep.name.casecmp?(dependency.name) }
71
69
 
72
70
  next unless repo_contents_path
73
71
 
@@ -76,7 +74,7 @@ module Dependabot
76
74
 
77
75
  checked_files.add(checked_key)
78
76
  # We need to check the downstream references even though we're already evaluated the file
79
- downstream_files = project_file_parser.downstream_file_references(project_file: project_file)
77
+ downstream_files = referenced_project_paths(project_file)
80
78
  downstream_files.each do |downstream_file|
81
79
  checked_files.add("#{downstream_file}-#{dependency.name}#{dependency.version}")
82
80
  end
@@ -87,8 +85,8 @@ module Dependabot
87
85
 
88
86
  sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
89
87
  def try_update_json(dependency)
90
- if dotnet_tools_json_dependencies.any? { |dep| dep.name.casecmp(dependency.name)&.zero? } ||
91
- global_json_dependencies.any? { |dep| dep.name.casecmp(dependency.name)&.zero? }
88
+ if dotnet_tools_json_dependencies.any? { |dep| dep.name.casecmp?(dependency.name) } ||
89
+ global_json_dependencies.any? { |dep| dep.name.casecmp?(dependency.name) }
92
90
 
93
91
  # We just need to feed the updater a project file, grab the first
94
92
  project_file = T.must(project_files.first)
@@ -128,58 +126,38 @@ module Dependabot
128
126
  @update_tooling_calls
129
127
  end
130
128
 
131
- sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[Dependabot::Dependency]) }
132
- def project_dependencies(project_file)
133
- # Collect all dependencies from the project file and associated packages.config
134
- dependencies = project_file_parser.dependency_set(project_file: project_file).dependencies
135
- packages_config = find_packages_config(project_file)
136
- return dependencies unless packages_config
129
+ sig { returns(T.nilable(WorkspaceDiscovery)) }
130
+ def workspace
131
+ @workspace ||= T.let(begin
132
+ discovery_json = DiscoveryJsonReader.discovery_json
133
+ if discovery_json
134
+ workspace = DiscoveryJsonReader.new(
135
+ discovery_json: discovery_json
136
+ ).workspace_discovery
137
+ end
137
138
 
138
- dependencies + FileParser::PackagesConfigParser.new(packages_config: packages_config)
139
- .dependency_set.dependencies
139
+ workspace
140
+ end, T.nilable(WorkspaceDiscovery))
140
141
  end
141
142
 
142
- sig { params(project_file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
143
- def find_packages_config(project_file)
144
- project_file_name = File.basename(project_file.name)
145
- packages_config_path = project_file.name.gsub(project_file_name, "packages.config")
146
- packages_config_files.find { |f| f.name == packages_config_path }
143
+ sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[String]) }
144
+ def referenced_project_paths(project_file)
145
+ workspace&.projects&.find { |p| p.file_path == project_file.name }&.referenced_project_paths || []
147
146
  end
148
147
 
149
- sig { returns(Dependabot::Nuget::FileParser::ProjectFileParser) }
150
- def project_file_parser
151
- @project_file_parser ||=
152
- T.let(
153
- FileParser::ProjectFileParser.new(
154
- dependency_files: dependency_files,
155
- credentials: credentials,
156
- repo_contents_path: repo_contents_path
157
- ),
158
- T.nilable(Dependabot::Nuget::FileParser::ProjectFileParser)
159
- )
148
+ sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[DependencyDetails]) }
149
+ def project_dependencies(project_file)
150
+ workspace&.projects&.find { |p| p.file_path == project_file.name }&.dependencies || []
160
151
  end
161
152
 
162
- sig { returns(T::Array[Dependabot::Dependency]) }
153
+ sig { returns(T::Array[DependencyDetails]) }
163
154
  def global_json_dependencies
164
- return [] unless global_json
165
-
166
- @global_json_dependencies ||=
167
- T.let(
168
- FileParser::GlobalJsonParser.new(global_json: T.must(global_json)).dependency_set.dependencies,
169
- T.nilable(T::Array[Dependabot::Dependency])
170
- )
155
+ workspace&.global_json&.dependencies || []
171
156
  end
172
157
 
173
- sig { returns(T::Array[Dependabot::Dependency]) }
158
+ sig { returns(T::Array[DependencyDetails]) }
174
159
  def dotnet_tools_json_dependencies
175
- return [] unless dotnet_tools_json
176
-
177
- @dotnet_tools_json_dependencies ||=
178
- T.let(
179
- FileParser::DotNetToolsJsonParser.new(dotnet_tools_json: T.must(dotnet_tools_json))
180
- .dependency_set.dependencies,
181
- T.nilable(T::Array[Dependabot::Dependency])
182
- )
160
+ workspace&.dotnet_tools_json&.dependencies || []
183
161
  end
184
162
 
185
163
  # rubocop:disable Metrics/PerceivedComplexity
@@ -234,16 +212,6 @@ module Dependabot
234
212
  end
235
213
  end
236
214
 
237
- sig { returns(T.nilable(Dependabot::DependencyFile)) }
238
- def global_json
239
- dependency_files.find { |f| T.must(f.name.casecmp("global.json")).zero? }
240
- end
241
-
242
- sig { returns(T.nilable(Dependabot::DependencyFile)) }
243
- def dotnet_tools_json
244
- dependency_files.find { |f| T.must(f.name.casecmp(".config/dotnet-tools.json")).zero? }
245
- end
246
-
247
215
  sig { override.void }
248
216
  def check_required_files
249
217
  return if project_files.any? || packages_config_files.any?
@@ -55,6 +55,61 @@ module Dependabot
55
55
  false
56
56
  end
57
57
 
58
+ sig do
59
+ params(repo_root: String, workspace_path: String, output_path: String).returns([String, String])
60
+ end
61
+ def self.get_nuget_discover_tool_command(repo_root:, workspace_path:, output_path:)
62
+ exe_path = File.join(native_helpers_root, "NuGetUpdater", "NuGetUpdater.Cli")
63
+ command_parts = [
64
+ exe_path,
65
+ "discover",
66
+ "--repo-root",
67
+ repo_root,
68
+ "--workspace",
69
+ workspace_path,
70
+ "--output",
71
+ output_path,
72
+ "--verbose"
73
+ ].compact
74
+
75
+ command = Shellwords.join(command_parts)
76
+
77
+ fingerprint = [
78
+ exe_path,
79
+ "discover",
80
+ "--repo-root",
81
+ "<repo-root>",
82
+ "--workspace",
83
+ "<path-to-workspace>",
84
+ "--output",
85
+ "<path-to-output>",
86
+ "--verbose"
87
+ ].compact.join(" ")
88
+
89
+ [command, fingerprint]
90
+ end
91
+
92
+ sig do
93
+ params(
94
+ repo_root: String,
95
+ workspace_path: String,
96
+ output_path: String,
97
+ credentials: T::Array[Dependabot::Credential]
98
+ ).void
99
+ end
100
+ def self.run_nuget_discover_tool(repo_root:, workspace_path:, output_path:, credentials:)
101
+ (command, fingerprint) = get_nuget_discover_tool_command(repo_root: repo_root,
102
+ workspace_path: workspace_path,
103
+ output_path: output_path)
104
+
105
+ puts "running NuGet discovery:\n" + command
106
+
107
+ NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
108
+ output = SharedHelpers.run_shell_command(command, allow_unsafe_shell_command: true, fingerprint: fingerprint)
109
+ puts output
110
+ end
111
+ end
112
+
58
113
  sig do
59
114
  params(repo_root: String, proj_path: String, dependency: Dependency,
60
115
  is_transitive: T::Boolean).returns([String, String])
@@ -18,14 +18,12 @@ module Dependabot
18
18
  sig do
19
19
  params(
20
20
  dependency_urls: T::Array[T::Hash[Symbol, String]],
21
- dependency: Dependabot::Dependency,
22
- tfm_finder: Dependabot::Nuget::TfmFinder
21
+ dependency: Dependabot::Dependency
23
22
  ).void
24
23
  end
25
- def initialize(dependency_urls:, dependency:, tfm_finder:)
24
+ def initialize(dependency_urls:, dependency:)
26
25
  @dependency_urls = dependency_urls
27
26
  @dependency = dependency
28
- @tfm_finder = tfm_finder
29
27
  end
30
28
 
31
29
  sig { params(version: String).returns(T::Boolean) }
@@ -57,9 +55,6 @@ module Dependabot
57
55
  sig { returns(Dependabot::Dependency) }
58
56
  attr_reader :dependency
59
57
 
60
- sig { returns(Dependabot::Nuget::TfmFinder) }
61
- attr_reader :tfm_finder
62
-
63
58
  sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Boolean) }
64
59
  def pure_development_dependency?(nuspec_xml)
65
60
  contents = nuspec_xml.at_xpath("package/metadata/developmentDependency")&.content&.strip
@@ -82,7 +77,7 @@ module Dependabot
82
77
 
83
78
  sig { returns(T.nilable(T::Array[String])) }
84
79
  def project_tfms
85
- @project_tfms ||= T.let(tfm_finder.frameworks(dependency), T.nilable(T::Array[String]))
80
+ @project_tfms ||= T.let(TfmFinder.frameworks(dependency), T.nilable(T::Array[String]))
86
81
  end
87
82
 
88
83
  sig { params(dependency_version: String).returns(T.nilable(T::Array[String])) }
@@ -156,6 +156,7 @@ module Dependabot
156
156
  T.let(
157
157
  Nuget::FileParser.new(
158
158
  dependency_files: dependency_files,
159
+ repo_contents_path: repo_contents_path,
159
160
  source: nil
160
161
  ).parse.select(&:top_level?),
161
162
  T.nilable(T::Array[Dependabot::Dependency])
@@ -154,6 +154,7 @@ module Dependabot
154
154
  T.let(
155
155
  Nuget::FileParser.new(
156
156
  dependency_files: dependency_files,
157
+ repo_contents_path: repo_contents_path,
157
158
  source: nil
158
159
  ).parse.select do |dep|
159
160
  dep.requirements.any? do |r|
@@ -1,164 +1,29 @@
1
1
  # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
- require "excon"
5
- require "nokogiri"
6
- require "sorbet-runtime"
7
-
8
- require "dependabot/update_checkers/base"
9
- require "dependabot/nuget/version"
10
- require "dependabot/nuget/requirement"
11
- require "dependabot/nuget/native_helpers"
12
- require "dependabot/shared_helpers"
4
+ require "dependabot/nuget/discovery/discovery_json_reader"
13
5
 
14
6
  module Dependabot
15
7
  module Nuget
16
8
  class TfmFinder
17
9
  extend T::Sig
18
10
 
19
- require "dependabot/nuget/file_parser/packages_config_parser"
20
- require "dependabot/nuget/file_parser/project_file_parser"
21
-
22
- sig do
23
- params(
24
- dependency_files: T::Array[Dependabot::DependencyFile],
25
- credentials: T::Array[Dependabot::Credential],
26
- repo_contents_path: T.nilable(String)
27
- ).void
28
- end
29
- def initialize(dependency_files:, credentials:, repo_contents_path:)
30
- @dependency_files = dependency_files
31
- @credentials = credentials
32
- @repo_contents_path = repo_contents_path
33
- end
34
-
35
- sig { params(dependency: Dependabot::Dependency).returns(T::Array[String]) }
36
- def frameworks(dependency)
37
- tfms = Set.new
38
- tfms += project_file_tfms(dependency)
39
- tfms += project_import_file_tfms
40
- tfms.to_a
41
- end
42
-
43
- private
44
-
45
- sig { returns(T::Array[Dependabot::DependencyFile]) }
46
- attr_reader :dependency_files
47
-
48
- sig { returns(T::Array[Dependabot::Credential]) }
49
- attr_reader :credentials
50
-
51
- sig { returns(T.nilable(String)) }
52
- attr_reader :repo_contents_path
53
-
54
- sig { params(dependency: Dependabot::Dependency).returns(T::Array[String]) }
55
- def project_file_tfms(dependency)
56
- project_files_with_dependency(dependency).flat_map do |file|
57
- project_file_parser.target_frameworks(project_file: file)
58
- end
59
- end
60
-
61
- sig { params(dependency: Dependabot::Dependency).returns(T::Array[Dependabot::DependencyFile]) }
62
- def project_files_with_dependency(dependency)
63
- project_files.select do |file|
64
- packages_config_contains_dependency?(file, dependency) ||
65
- project_file_contains_dependency?(file, dependency)
66
- end
67
- end
68
-
69
- sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
70
- def packages_config_contains_dependency?(file, dependency)
71
- config_file = find_packages_config_file(file)
72
- return false unless config_file
73
-
74
- config_parser = FileParser::PackagesConfigParser.new(packages_config: config_file)
75
- config_parser.dependency_set.dependencies.any? do |d|
76
- d.name.casecmp(dependency.name)&.zero?
77
- end
78
- end
79
-
80
- sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
81
- def project_file_contains_dependency?(file, dependency)
82
- project_file_parser.dependency_set(project_file: file).dependencies.any? do |d|
83
- d.name.casecmp(dependency.name)&.zero?
84
- end
85
- end
86
-
87
- sig { params(file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
88
- def find_packages_config_file(file)
89
- return file if file.name.end_with?("packages.config")
90
-
91
- filename = File.basename(file.name)
92
- search_path = file.name.sub(filename, "packages.config")
93
-
94
- dependency_files.find { |f| f.name.casecmp(search_path)&.zero? }
95
- end
96
-
97
- sig { returns(T::Array[String]) }
98
- def project_import_file_tfms
99
- @project_import_file_tfms ||=
100
- T.let(
101
- project_import_files.flat_map do |file|
102
- project_file_parser.target_frameworks(project_file: file)
103
- end,
104
- T.nilable(T::Array[String])
105
- )
106
- end
107
-
108
- sig { returns(FileParser::ProjectFileParser) }
109
- def project_file_parser
110
- @project_file_parser ||=
111
- T.let(
112
- FileParser::ProjectFileParser.new(
113
- dependency_files: dependency_files,
114
- credentials: credentials,
115
- repo_contents_path: repo_contents_path
116
- ),
117
- T.nilable(FileParser::ProjectFileParser)
118
- )
119
- end
120
-
121
- sig { returns(T::Array[Dependabot::DependencyFile]) }
122
- def project_files
123
- projfile = /\.[a-z]{2}proj$/
124
- packageprops = /[Dd]irectory.[Pp]ackages.props/
125
-
126
- dependency_files.select do |df|
127
- df.name.match?(projfile) ||
128
- df.name.match?(packageprops)
129
- end
130
- end
131
-
132
- sig { returns(T::Array[Dependabot::DependencyFile]) }
133
- def packages_config_files
134
- dependency_files.select do |f|
135
- f.name.split("/").last&.casecmp("packages.config")&.zero?
136
- end
137
- end
138
-
139
- sig { returns(T::Array[Dependabot::DependencyFile]) }
140
- def project_import_files
141
- dependency_files -
142
- project_files -
143
- packages_config_files -
144
- nuget_configs -
145
- [global_json] -
146
- [dotnet_tools_json]
147
- end
148
-
149
- sig { returns(T::Array[Dependabot::DependencyFile]) }
150
- def nuget_configs
151
- dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
152
- end
153
-
154
- sig { returns(T.nilable(Dependabot::DependencyFile)) }
155
- def global_json
156
- dependency_files.find { |f| f.name.casecmp("global.json")&.zero? }
157
- end
158
-
159
- sig { returns(T.nilable(Dependabot::DependencyFile)) }
160
- def dotnet_tools_json
161
- dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")&.zero? }
11
+ sig { params(dependency: Dependency).returns(T::Array[String]) }
12
+ def self.frameworks(dependency)
13
+ discovery_json = DiscoveryJsonReader.discovery_json
14
+ return [] unless discovery_json
15
+
16
+ workspace = DiscoveryJsonReader.new(
17
+ discovery_json: discovery_json
18
+ ).workspace_discovery
19
+ return [] unless workspace
20
+
21
+ workspace.projects.select do |project|
22
+ all_dependencies = project.dependencies + project.referenced_project_paths.flat_map do |ref|
23
+ workspace.projects.find { |p| p.file_path == ref }&.dependencies || []
24
+ end
25
+ all_dependencies.any? { |d| d.name.casecmp?(dependency.name) }
26
+ end.flat_map(&:target_frameworks).uniq
162
27
  end
163
28
  end
164
29
  end
@@ -160,12 +160,7 @@ module Dependabot
160
160
  T.let(
161
161
  CompatibilityChecker.new(
162
162
  dependency_urls: dependency_urls,
163
- dependency: dependency,
164
- tfm_finder: TfmFinder.new(
165
- dependency_files: dependency_files,
166
- credentials: credentials,
167
- repo_contents_path: repo_contents_path
168
- )
163
+ dependency: dependency
169
164
  ),
170
165
  T.nilable(Dependabot::Nuget::CompatibilityChecker)
171
166
  )
@@ -16,6 +16,8 @@ module Dependabot
16
16
  require_relative "update_checker/requirements_updater"
17
17
  require_relative "update_checker/dependency_finder"
18
18
 
19
+ PROPERTY_REGEX = /\$\((?<property>.*?)\)/
20
+
19
21
  sig { override.returns(T.nilable(String)) }
20
22
  def latest_version
21
23
  # No need to find latest version for transitive dependencies unless they have a vulnerability.
@@ -81,7 +83,7 @@ module Dependabot
81
83
  # that property couldn't be found, and the requirement therefore
82
84
  # cannot be unlocked (since we can't update that property)
83
85
  dependency.requirements.none? do |req|
84
- req.fetch(:requirement)&.match?(Nuget::FileParser::PropertyValueFinder::PROPERTY_REGEX)
86
+ req.fetch(:requirement)&.match?(PROPERTY_REGEX)
85
87
  end
86
88
  end
87
89
 
@@ -219,6 +221,7 @@ module Dependabot
219
221
  T.let(
220
222
  Nuget::FileParser.new(
221
223
  dependency_files: dependency_files,
224
+ repo_contents_path: repo_contents_path,
222
225
  source: nil
223
226
  ).parse.select do |dep|
224
227
  dep.requirements.any? { |req| req.dig(:metadata, :property_name) }