dependabot-nuget 0.265.0 → 0.266.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +6 -6
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalysisResult.cs +1 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +73 -77
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +21 -8
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +24 -8
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +33 -16
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +44 -25
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +1 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +8 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +2 -2
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +8 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +18 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +2 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs +5 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +62 -22
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +19 -1
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +6 -2
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +448 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/CompatibilityCheckerTests.cs +23 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +2 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +148 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +81 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +27 -7
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +32 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +87 -2
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +88 -0
  30. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -0
  31. data/lib/dependabot/nuget/file_fetcher.rb +1 -1
  32. data/lib/dependabot/nuget/metadata_finder.rb +160 -2
  33. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +3 -0
  34. data/lib/dependabot/nuget/native_helpers.rb +34 -3
  35. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +1 -0
  36. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +3 -0
  37. metadata +11 -7
@@ -12,16 +12,148 @@ module Dependabot
12
12
  class MetadataFinder < Dependabot::MetadataFinders::Base
13
13
  extend T::Sig
14
14
 
15
+ sig do
16
+ override
17
+ .params(
18
+ dependency: Dependabot::Dependency,
19
+ credentials: T::Array[Dependabot::Credential]
20
+ )
21
+ .void
22
+ end
23
+ def initialize(dependency:, credentials:)
24
+ @dependency_nuspec_file = T.let(nil, T.nilable(Nokogiri::XML::Document))
25
+
26
+ super
27
+ end
28
+
15
29
  private
16
30
 
17
31
  sig { override.returns(T.nilable(Dependabot::Source)) }
18
32
  def look_up_source
19
- source_url = dependency_source_url
20
- return Source.from_url(source_url) if source_url
33
+ return Source.from_url(dependency_source_url) if dependency_source_url
34
+
35
+ if dependency_nuspec_file
36
+ src_repo = look_up_source_in_nuspec(T.must(dependency_nuspec_file))
37
+ return src_repo if src_repo
38
+ end
39
+
40
+ # Fallback to getting source from the search result's projectUrl or licenseUrl.
41
+ # GitHub Packages doesn't support getting the `.nuspec`, switch to getting
42
+ # that instead once it is supported.
43
+ src_repo_from_project
44
+ rescue StandardError
45
+ # At this point in the process the PR is ready to be posted, we tried to gather commit
46
+ # and release notes, but have encountered an exception. So let's eat it since it's
47
+ # better to have a PR with no info than error out.
48
+ nil
49
+ end
50
+
51
+ sig { returns(T.nilable(Dependabot::Source)) }
52
+ def src_repo_from_project
53
+ source = dependency.requirements.find { |r| r.fetch(:source) }&.fetch(:source)
54
+ return unless source
55
+
56
+ # Query the service index e.g. https://nuget.pkg.github.com/ORG/index.json
57
+ response = Dependabot::RegistryClient.get(
58
+ url: source.fetch(:url),
59
+ headers: { **auth_header, "Accept" => "application/json" }
60
+ )
61
+ return unless response.status == 200
21
62
 
63
+ # Extract the query url e.g. https://nuget.pkg.github.com/ORG/query
64
+ search_base = extract_search_url(response.body)
65
+ return unless search_base
66
+
67
+ response = Dependabot::RegistryClient.get(
68
+ url: search_base + "?q=#{dependency.name.downcase}&prerelease=true&semVerLevel=2.0.0",
69
+ headers: { **auth_header, "Accept" => "application/json" }
70
+ )
71
+ return unless response.status == 200
72
+
73
+ # Find a projectUrl or licenseUrl that look like a source URL
74
+ extract_source_repo(response.body)
75
+ rescue JSON::ParserError
76
+ # Ignored, this is expected for some registries that don't handle these request.
77
+ end
78
+
79
+ sig { params(body: String).returns(T.nilable(String)) }
80
+ def extract_search_url(body)
81
+ JSON.parse(body)
82
+ .fetch("resources", [])
83
+ .find { |r| r.fetch("@type") == "SearchQueryService" }
84
+ &.fetch("@id")
85
+ end
86
+
87
+ sig { params(body: String).returns(T.nilable(Dependabot::Source)) }
88
+ def extract_source_repo(body)
89
+ JSON.parse(body).fetch("data", []).each do |search_result|
90
+ next unless search_result["id"].casecmp(dependency.name).zero?
91
+
92
+ if search_result.key?("projectUrl")
93
+ source = Source.from_url(search_result.fetch("projectUrl"))
94
+ return source if source
95
+ end
96
+ if search_result.key?("licenseUrl")
97
+ source = Source.from_url(search_result.fetch("licenseUrl"))
98
+ return source if source
99
+ end
100
+ end
101
+ # failed to find a source URL
22
102
  nil
23
103
  end
24
104
 
105
+ sig { params(nuspec: Nokogiri::XML::Document).returns(T.nilable(Dependabot::Source)) }
106
+ def look_up_source_in_nuspec(nuspec)
107
+ potential_source_urls = [
108
+ nuspec.at_css("package > metadata > repository")
109
+ &.attribute("url")&.value,
110
+ nuspec.at_css("package > metadata > repository > url")&.content,
111
+ nuspec.at_css("package > metadata > projectUrl")&.content,
112
+ nuspec.at_css("package > metadata > licenseUrl")&.content
113
+ ].compact
114
+
115
+ source_url = potential_source_urls.find { |url| Source.from_url(url) }
116
+ source_url ||= source_from_anywhere_in_nuspec(nuspec)
117
+
118
+ Source.from_url(source_url)
119
+ end
120
+
121
+ sig { params(nuspec: Nokogiri::XML::Document).returns(T.nilable(String)) }
122
+ def source_from_anywhere_in_nuspec(nuspec)
123
+ github_urls = []
124
+ nuspec.to_s.force_encoding(Encoding::UTF_8)
125
+ .scan(Source::SOURCE_REGEX) do
126
+ github_urls << Regexp.last_match.to_s
127
+ end
128
+
129
+ github_urls.find do |url|
130
+ repo = T.must(Source.from_url(url)).repo
131
+ repo.downcase.end_with?(dependency.name.downcase)
132
+ end
133
+ end
134
+
135
+ sig { returns(T.nilable(Nokogiri::XML::Document)) }
136
+ def dependency_nuspec_file
137
+ return @dependency_nuspec_file unless @dependency_nuspec_file.nil?
138
+
139
+ return if dependency_nuspec_url.nil?
140
+
141
+ response = Dependabot::RegistryClient.get(
142
+ url: T.must(dependency_nuspec_url),
143
+ headers: auth_header
144
+ )
145
+
146
+ @dependency_nuspec_file = Nokogiri::XML(response.body)
147
+ end
148
+
149
+ sig { returns(T.nilable(String)) }
150
+ def dependency_nuspec_url
151
+ source = dependency.requirements
152
+ .find { |r| r.fetch(:source) }&.fetch(:source)
153
+
154
+ source.fetch(:nuspec_url) if source&.key?(:nuspec_url)
155
+ end
156
+
25
157
  sig { returns(T.nilable(String)) }
26
158
  def dependency_source_url
27
159
  source = dependency.requirements
@@ -32,6 +164,32 @@ module Dependabot
32
164
 
33
165
  source.fetch("source_url")
34
166
  end
167
+
168
+ # rubocop:disable Metrics/PerceivedComplexity
169
+ sig { returns(T::Hash[String, String]) }
170
+ def auth_header
171
+ source = dependency.requirements
172
+ .find { |r| r.fetch(:source) }&.fetch(:source)
173
+ url = source&.fetch(:url, nil) || source&.fetch("url")
174
+
175
+ token = credentials
176
+ .select { |cred| cred["type"] == "nuget_feed" }
177
+ .find { |cred| cred["url"] == url }
178
+ &.fetch("token", nil)
179
+
180
+ return {} unless token
181
+
182
+ if token.include?(":")
183
+ encoded_token = Base64.encode64(token).delete("\n")
184
+ { "Authorization" => "Basic #{encoded_token}" }
185
+ elsif Base64.decode64(token).ascii_only? &&
186
+ Base64.decode64(token).include?(":")
187
+ { "Authorization" => "Basic #{token.delete("\n")}" }
188
+ else
189
+ { "Authorization" => "Bearer #{token}" }
190
+ end
191
+ end
192
+ # rubocop:enable Metrics/PerceivedComplexity
35
193
  end
36
194
  end
37
195
  end
@@ -4,6 +4,7 @@
4
4
  require "dependabot/nuget/native_discovery/native_dependency_file_discovery"
5
5
  require "dependabot/nuget/native_discovery/native_directory_packages_props_discovery"
6
6
  require "dependabot/nuget/native_discovery/native_project_discovery"
7
+ require "dependabot/nuget/native_helpers"
7
8
  require "sorbet-runtime"
8
9
 
9
10
  module Dependabot
@@ -13,6 +14,8 @@ module Dependabot
13
14
 
14
15
  sig { params(json: T::Hash[String, T.untyped]).returns(NativeWorkspaceDiscovery) }
15
16
  def self.from_json(json)
17
+ Dependabot::Nuget::NativeHelpers.ensure_no_errors(json)
18
+
16
19
  path = T.let(json.fetch("Path"), String)
17
20
  path = "/" + path unless path.start_with?("/")
18
21
  projects = T.let(json.fetch("Projects"), T::Array[T::Hash[String, T.untyped]]).filter_map do |project|
@@ -169,11 +169,12 @@ module Dependabot
169
169
  end
170
170
  end
171
171
 
172
+ # rubocop:disable Metrics/MethodLength
172
173
  sig do
173
174
  params(repo_root: String, proj_path: String, dependency: Dependency,
174
- is_transitive: T::Boolean).returns([String, String])
175
+ is_transitive: T::Boolean, result_output_path: String).returns([String, String])
175
176
  end
176
- def self.get_nuget_updater_tool_command(repo_root:, proj_path:, dependency:, is_transitive:)
177
+ def self.get_nuget_updater_tool_command(repo_root:, proj_path:, dependency:, is_transitive:, result_output_path:)
177
178
  exe_path = File.join(native_helpers_root, "NuGetUpdater", "NuGetUpdater.Cli")
178
179
  command_parts = [
179
180
  exe_path,
@@ -189,6 +190,8 @@ module Dependabot
189
190
  "--previous-version",
190
191
  dependency.previous_version,
191
192
  is_transitive ? "--transitive" : nil,
193
+ "--result-output-path",
194
+ result_output_path,
192
195
  "--verbose"
193
196
  ].compact
194
197
 
@@ -208,11 +211,19 @@ module Dependabot
208
211
  "--previous-version",
209
212
  "<previous-version>",
210
213
  is_transitive ? "--transitive" : nil,
214
+ "--result-output-path",
215
+ "<result-output-path>",
211
216
  "--verbose"
212
217
  ].compact.join(" ")
213
218
 
214
219
  [command, fingerprint]
215
220
  end
221
+ # rubocop:enable Metrics/MethodLength
222
+
223
+ sig { returns(String) }
224
+ def self.update_result_file_path
225
+ File.join(Dir.tmpdir, "update-result.json")
226
+ end
216
227
 
217
228
  sig do
218
229
  params(
@@ -225,13 +236,33 @@ module Dependabot
225
236
  end
226
237
  def self.run_nuget_updater_tool(repo_root:, proj_path:, dependency:, is_transitive:, credentials:)
227
238
  (command, fingerprint) = get_nuget_updater_tool_command(repo_root: repo_root, proj_path: proj_path,
228
- dependency: dependency, is_transitive: is_transitive)
239
+ dependency: dependency, is_transitive: is_transitive,
240
+ result_output_path: update_result_file_path)
229
241
 
230
242
  puts "running NuGet updater:\n" + command
231
243
 
232
244
  NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
233
245
  output = SharedHelpers.run_shell_command(command, allow_unsafe_shell_command: true, fingerprint: fingerprint)
234
246
  puts output
247
+
248
+ result_contents = File.read(update_result_file_path)
249
+ Dependabot.logger.info("update result: #{result_contents}")
250
+ result_json = T.let(JSON.parse(result_contents), T::Hash[String, T.untyped])
251
+ ensure_no_errors(result_json)
252
+ end
253
+ end
254
+
255
+ sig { params(json: T::Hash[String, T.untyped]).void }
256
+ def self.ensure_no_errors(json)
257
+ error_type = T.let(json.fetch("ErrorType", nil), T.nilable(String))
258
+ error_details = T.let(json.fetch("ErrorDetails", nil), T.nilable(String))
259
+ case error_type
260
+ when "None", nil
261
+ # no issue
262
+ when "AuthenticationFailure"
263
+ raise PrivateSourceAuthenticationFailure, error_details
264
+ else
265
+ raise "Unexpected error type from native tool: #{error_type}: #{error_details}"
235
266
  end
236
267
  end
237
268
  end
@@ -134,6 +134,7 @@ module Dependabot
134
134
  nil?
135
135
  end
136
136
 
137
+ Dependabot.logger.info("Writing dependency info: #{dependency_info}")
137
138
  File.write(dependency_file_path, dependency_info)
138
139
  end
139
140
 
@@ -68,6 +68,9 @@ module Dependabot
68
68
  add_credentials_to_nuget_config(credentials)
69
69
  begin
70
70
  yield
71
+ rescue DependabotError
72
+ # forward these
73
+ raise
71
74
  rescue StandardError => e
72
75
  log_message =
73
76
  <<~LOG_MESSAGE
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-nuget
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.265.0
4
+ version: 0.266.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-11 00:00:00.000000000 Z
11
+ date: 2024-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.265.0
19
+ version: 0.266.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.265.0
26
+ version: 0.266.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rubyzip
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -134,14 +134,14 @@ dependencies:
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: 1.63.2
137
+ version: 1.65.0
138
138
  type: :development
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: 1.63.2
144
+ version: 1.65.0
145
145
  - !ruby/object:Gem::Dependency
146
146
  name: rubocop-performance
147
147
  requirement: !ruby/object:Gem::Requirement
@@ -328,6 +328,7 @@ files:
328
328
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs
329
329
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs
330
330
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestExtensions.cs
331
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs
331
332
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs
332
333
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs
333
334
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs
@@ -369,6 +370,7 @@ files:
369
370
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs
370
371
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs
371
372
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs
373
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs
372
374
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/EvaluationResult.cs
373
375
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/EvaluationResultType.cs
374
376
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/BuildFile.cs
@@ -381,6 +383,7 @@ files:
381
383
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs
382
384
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs
383
385
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs
386
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs
384
387
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj
385
388
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Property.cs
386
389
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs
@@ -390,6 +393,7 @@ files:
390
393
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs
391
394
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs
392
395
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs
396
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs
393
397
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateResult.cs
394
398
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs
395
399
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs
@@ -456,7 +460,7 @@ licenses:
456
460
  - MIT
457
461
  metadata:
458
462
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
459
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.265.0
463
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.266.0
460
464
  post_install_message:
461
465
  rdoc_options: []
462
466
  require_paths: