dependabot-nuget 0.237.0 → 0.238.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.
- checksums.yaml +4 -4
- data/lib/dependabot/nuget/cache_manager.rb +22 -0
- data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +15 -0
- data/lib/dependabot/nuget/file_fetcher.rb +65 -61
- data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +2 -1
- data/lib/dependabot/nuget/file_parser/global_json_parser.rb +2 -1
- data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +22 -4
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +287 -15
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +24 -52
- data/lib/dependabot/nuget/file_parser.rb +4 -1
- data/lib/dependabot/nuget/file_updater.rb +123 -117
- data/lib/dependabot/nuget/native_helpers.rb +94 -0
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +85 -0
- data/lib/dependabot/nuget/update_checker/dependency_finder.rb +228 -0
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +114 -0
- data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +86 -0
- data/lib/dependabot/nuget/update_checker/property_updater.rb +30 -3
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +32 -10
- data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +31 -0
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +127 -0
- data/lib/dependabot/nuget/update_checker/version_finder.rb +47 -6
- data/lib/dependabot/nuget/update_checker.rb +42 -8
- data/lib/dependabot/nuget.rb +2 -0
- metadata +33 -7
- data/lib/dependabot/nuget/file_updater/packages_config_declaration_finder.rb +0 -70
- data/lib/dependabot/nuget/file_updater/project_file_declaration_finder.rb +0 -183
@@ -0,0 +1,228 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "nokogiri"
|
5
|
+
require "zip"
|
6
|
+
require "stringio"
|
7
|
+
require "dependabot/nuget/update_checker"
|
8
|
+
require "dependabot/nuget/version"
|
9
|
+
|
10
|
+
module Dependabot
|
11
|
+
module Nuget
|
12
|
+
class UpdateChecker
|
13
|
+
class DependencyFinder
|
14
|
+
require_relative "requirements_updater"
|
15
|
+
require_relative "nuspec_fetcher"
|
16
|
+
|
17
|
+
def self.transitive_dependencies_cache
|
18
|
+
CacheManager.cache("dependency_finder_transitive_dependencies")
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.updated_peer_dependencies_cache
|
22
|
+
CacheManager.cache("dependency_finder_updated_peer_dependencies")
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.fetch_dependencies_cache
|
26
|
+
CacheManager.cache("dependency_finder_fetch_dependencies")
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(dependency:, dependency_files:, credentials:)
|
30
|
+
@dependency = dependency
|
31
|
+
@dependency_files = dependency_files
|
32
|
+
@credentials = credentials
|
33
|
+
end
|
34
|
+
|
35
|
+
def transitive_dependencies
|
36
|
+
key = "#{dependency.name.downcase}::#{dependency.version}"
|
37
|
+
cache = DependencyFinder.transitive_dependencies_cache
|
38
|
+
|
39
|
+
cache[key] ||= fetch_transitive_dependencies(
|
40
|
+
@dependency.name,
|
41
|
+
@dependency.version
|
42
|
+
).map do |dependency_info|
|
43
|
+
package_name = dependency_info["packageName"]
|
44
|
+
target_version = dependency_info["version"]
|
45
|
+
|
46
|
+
Dependency.new(
|
47
|
+
name: package_name,
|
48
|
+
version: target_version.to_s,
|
49
|
+
requirements: [], # Empty requirements for transitive dependencies
|
50
|
+
package_manager: @dependency.package_manager
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
cache[key]
|
55
|
+
end
|
56
|
+
|
57
|
+
def updated_peer_dependencies
|
58
|
+
key = "#{dependency.name.downcase}::#{dependency.version}"
|
59
|
+
cache = DependencyFinder.updated_peer_dependencies_cache
|
60
|
+
|
61
|
+
cache[key] ||= fetch_transitive_dependencies(
|
62
|
+
@dependency.name,
|
63
|
+
@dependency.version
|
64
|
+
).filter_map do |dependency_info|
|
65
|
+
package_name = dependency_info["packageName"]
|
66
|
+
target_version = dependency_info["version"]
|
67
|
+
|
68
|
+
# Find the Dependency object for the peer dependency. We will not return
|
69
|
+
# dependencies that are not referenced from dependency files.
|
70
|
+
peer_dependency = top_level_dependencies.find { |d| d.name == package_name }
|
71
|
+
next unless peer_dependency
|
72
|
+
next unless target_version > peer_dependency.numeric_version
|
73
|
+
|
74
|
+
# Use version finder to determine the source details for the peer dependency.
|
75
|
+
target_version_details = version_finder(peer_dependency).versions.find do |v|
|
76
|
+
v.fetch(:version) == target_version
|
77
|
+
end
|
78
|
+
next unless target_version_details
|
79
|
+
|
80
|
+
Dependency.new(
|
81
|
+
name: peer_dependency.name,
|
82
|
+
version: target_version_details.fetch(:version).to_s,
|
83
|
+
requirements: updated_requirements(peer_dependency, target_version_details),
|
84
|
+
previous_version: peer_dependency.version,
|
85
|
+
previous_requirements: peer_dependency.requirements,
|
86
|
+
package_manager: peer_dependency.package_manager,
|
87
|
+
metadata: { information_only: true } # Instruct updater to not directly update this dependency
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
cache[key]
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
attr_reader :dependency, :dependency_files, :credentials
|
97
|
+
|
98
|
+
def updated_requirements(dep, target_version_details)
|
99
|
+
@updated_requirements ||= {}
|
100
|
+
@updated_requirements[dep.name] ||=
|
101
|
+
RequirementsUpdater.new(
|
102
|
+
requirements: dep.requirements,
|
103
|
+
latest_version: target_version_details.fetch(:version).to_s,
|
104
|
+
source_details: target_version_details
|
105
|
+
&.slice(:nuspec_url, :repo_url, :source_url)
|
106
|
+
).updated_requirements
|
107
|
+
end
|
108
|
+
|
109
|
+
def top_level_dependencies
|
110
|
+
@top_level_dependencies ||=
|
111
|
+
Nuget::FileParser.new(
|
112
|
+
dependency_files: dependency_files,
|
113
|
+
source: nil
|
114
|
+
).parse.select(&:top_level?)
|
115
|
+
end
|
116
|
+
|
117
|
+
def nuget_configs
|
118
|
+
@nuget_configs ||=
|
119
|
+
@dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
|
120
|
+
end
|
121
|
+
|
122
|
+
def dependency_urls
|
123
|
+
@dependency_urls ||=
|
124
|
+
UpdateChecker::RepositoryFinder.new(
|
125
|
+
dependency: @dependency,
|
126
|
+
credentials: @credentials,
|
127
|
+
config_files: nuget_configs
|
128
|
+
).dependency_urls
|
129
|
+
.select { |url| url.fetch(:repository_type) == "v3" }
|
130
|
+
end
|
131
|
+
|
132
|
+
def fetch_transitive_dependencies(package_id, package_version)
|
133
|
+
all_dependencies = {}
|
134
|
+
fetch_transitive_dependencies_impl(package_id, package_version, all_dependencies)
|
135
|
+
all_dependencies.map { |_, dependency_info| dependency_info }
|
136
|
+
end
|
137
|
+
|
138
|
+
def fetch_transitive_dependencies_impl(package_id, package_version, all_dependencies)
|
139
|
+
dependencies = fetch_dependencies(package_id, package_version)
|
140
|
+
return unless dependencies.any?
|
141
|
+
|
142
|
+
dependencies.each do |dependency|
|
143
|
+
next if dependency.nil?
|
144
|
+
|
145
|
+
dependency_id = dependency["packageName"]
|
146
|
+
dependency_version_range = dependency["versionRange"]
|
147
|
+
|
148
|
+
nuget_version_range_regex = /[\[(](\d+(\.\d+)*(-\w+(\.\d+)*)?)/
|
149
|
+
nuget_version_range_match_data = nuget_version_range_regex.match(dependency_version_range)
|
150
|
+
|
151
|
+
dependency_version = if nuget_version_range_match_data.nil?
|
152
|
+
dependency_version_range
|
153
|
+
else
|
154
|
+
nuget_version_range_match_data[1]
|
155
|
+
end
|
156
|
+
|
157
|
+
dependency["version"] = Version.new(dependency_version)
|
158
|
+
|
159
|
+
current_dependency = all_dependencies[dependency_id.downcase]
|
160
|
+
next unless current_dependency.nil? || current_dependency["version"] < dependency["version"]
|
161
|
+
|
162
|
+
all_dependencies[dependency_id.downcase] = dependency
|
163
|
+
fetch_transitive_dependencies_impl(dependency_id, dependency_version, all_dependencies)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def fetch_dependencies(package_id, package_version)
|
168
|
+
key = "#{package_id.downcase}::#{package_version}"
|
169
|
+
cache = DependencyFinder.fetch_dependencies_cache
|
170
|
+
|
171
|
+
cache[key] ||= begin
|
172
|
+
nuspec_xml = NuspecFetcher.fetch_nuspec(dependency_urls, package_id, package_version)
|
173
|
+
if nuspec_xml.nil?
|
174
|
+
[]
|
175
|
+
else
|
176
|
+
read_dependencies_from_nuspec(nuspec_xml)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
cache[key]
|
181
|
+
end
|
182
|
+
|
183
|
+
def read_dependencies_from_nuspec(nuspec_xml) # rubocop:disable Metrics/PerceivedComplexity
|
184
|
+
# we want to exclude development dependencies from the lookup
|
185
|
+
allowed_attributes = %w(all compile native runtime)
|
186
|
+
|
187
|
+
nuspec_xml_dependencies = nuspec_xml.xpath("//dependencies/child::node()/dependency").select do |dependency|
|
188
|
+
include_attr = dependency.attribute("include")
|
189
|
+
exclude_attr = dependency.attribute("exclude")
|
190
|
+
|
191
|
+
if include_attr.nil? && exclude_attr.nil?
|
192
|
+
true
|
193
|
+
elsif include_attr
|
194
|
+
include_values = include_attr.value.split(",").map(&:strip)
|
195
|
+
include_values.any? { |element1| allowed_attributes.any? { |element2| element1.casecmp?(element2) } }
|
196
|
+
else
|
197
|
+
exclude_values = exclude_attr.value.split(",").map(&:strip)
|
198
|
+
exclude_values.none? { |element1| allowed_attributes.any? { |element2| element1.casecmp?(element2) } }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
dependency_list = []
|
203
|
+
nuspec_xml_dependencies.each do |dependency|
|
204
|
+
next unless dependency.attribute("version")
|
205
|
+
|
206
|
+
dependency_list << {
|
207
|
+
"packageName" => dependency.attribute("id").value,
|
208
|
+
"versionRange" => dependency.attribute("version").value
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
dependency_list
|
213
|
+
end
|
214
|
+
|
215
|
+
def version_finder(dep)
|
216
|
+
VersionFinder.new(
|
217
|
+
dependency: dep,
|
218
|
+
dependency_files: dependency_files,
|
219
|
+
credentials: credentials,
|
220
|
+
ignored_versions: [],
|
221
|
+
raise_on_ignored: false,
|
222
|
+
security_advisories: []
|
223
|
+
)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "nokogiri"
|
5
|
+
require "zip"
|
6
|
+
require "stringio"
|
7
|
+
require "dependabot/nuget/update_checker"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module Nuget
|
11
|
+
class UpdateChecker
|
12
|
+
class NupkgFetcher
|
13
|
+
require_relative "repository_finder"
|
14
|
+
|
15
|
+
def self.fetch_nupkg_buffer(dependency_urls, package_id, package_version)
|
16
|
+
# check all repositories for the first one that has the nupkg
|
17
|
+
dependency_urls.reduce(nil) do |nupkg_buffer, repository_details|
|
18
|
+
nupkg_buffer || fetch_nupkg_buffer_from_repository(repository_details, package_id, package_version)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.fetch_nupkg_buffer_from_repository(repository_details, package_id, package_version)
|
23
|
+
return unless package_id && package_version && !package_version.empty?
|
24
|
+
|
25
|
+
feed_url = repository_details[:repository_url]
|
26
|
+
auth_header = repository_details[:auth_header]
|
27
|
+
|
28
|
+
azure_devops_match = try_match_azure_url(feed_url)
|
29
|
+
package_url = if azure_devops_match
|
30
|
+
get_azure_package_url(azure_devops_match, package_id, package_version)
|
31
|
+
elsif feed_url.include?("/v2")
|
32
|
+
get_nuget_v2_package_url(feed_url, package_id, package_version)
|
33
|
+
elsif feed_url.include?("/v3")
|
34
|
+
get_nuget_v3_package_url(feed_url, package_id, package_version)
|
35
|
+
else
|
36
|
+
raise Dependabot::DependencyFileNotResolvable, "Unexpected NuGet feed format: #{feed_url}"
|
37
|
+
end
|
38
|
+
|
39
|
+
fetch_stream(package_url, auth_header)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.try_match_azure_url(feed_url)
|
43
|
+
# if url is azure devops
|
44
|
+
azure_devops_regexs = [
|
45
|
+
%r{https://pkgs\.dev\.azure\.com/(?<organization>[^/]+)/(?<project>[^/]+)/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json},
|
46
|
+
%r{https://pkgs\.dev\.azure\.com/(?<organization>[^/]+)/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json(?<project>)},
|
47
|
+
%r{https://(?<organization>[^\.\/]+)\.pkgs\.visualstudio\.com/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json(?<project>)}
|
48
|
+
]
|
49
|
+
regex = azure_devops_regexs.find { |reg| reg.match(feed_url) }
|
50
|
+
return unless regex
|
51
|
+
|
52
|
+
regex.match(feed_url)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.get_azure_package_url(azure_devops_match, package_id, package_version)
|
56
|
+
organization = azure_devops_match[:organization]
|
57
|
+
project = azure_devops_match[:project]
|
58
|
+
feed_id = azure_devops_match[:feedId]
|
59
|
+
|
60
|
+
if project.empty?
|
61
|
+
"https://pkgs.dev.azure.com/#{organization}/_apis/packaging/feeds/#{feed_id}/nuget/packages/#{package_id}/versions/#{package_version}/content?sourceProtocolVersion=nuget&api-version=7.0-preview"
|
62
|
+
else
|
63
|
+
"https://pkgs.dev.azure.com/#{organization}/#{project}/_apis/packaging/feeds/#{feed_id}/nuget/packages/#{package_id}/versions/#{package_version}/content?sourceProtocolVersion=nuget&api-version=7.0-preview"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.get_nuget_v3_package_url(feed_url, package_id, package_version)
|
68
|
+
base_url = feed_url.gsub("/index.json", "-flatcontainer")
|
69
|
+
package_id_downcased = package_id.downcase
|
70
|
+
"#{base_url}/#{package_id_downcased}/#{package_version}/#{package_id_downcased}.#{package_version}.nupkg"
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.get_nuget_v2_package_url(feed_url, package_id, package_version)
|
74
|
+
base_url = feed_url
|
75
|
+
base_url += "/" unless base_url.end_with?("/")
|
76
|
+
package_id_downcased = package_id.downcase
|
77
|
+
"#{base_url}/package/#{package_id_downcased}/#{package_version}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.fetch_stream(stream_url, auth_header, max_redirects = 5)
|
81
|
+
current_url = stream_url
|
82
|
+
current_redirects = 0
|
83
|
+
|
84
|
+
loop do
|
85
|
+
connection = Excon.new(current_url, persistent: true)
|
86
|
+
|
87
|
+
package_data = StringIO.new
|
88
|
+
response_block = lambda do |chunk, _remaining_bytes, _total_bytes|
|
89
|
+
package_data.write(chunk)
|
90
|
+
end
|
91
|
+
|
92
|
+
response = connection.request(
|
93
|
+
method: :get,
|
94
|
+
headers: auth_header,
|
95
|
+
response_block: response_block
|
96
|
+
)
|
97
|
+
|
98
|
+
if response.status == 303
|
99
|
+
current_redirects += 1
|
100
|
+
return nil if current_redirects > max_redirects
|
101
|
+
|
102
|
+
current_url = response.headers["Location"]
|
103
|
+
elsif response.status == 200
|
104
|
+
package_data.rewind
|
105
|
+
return package_data
|
106
|
+
else
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "nokogiri"
|
5
|
+
require "zip"
|
6
|
+
require "stringio"
|
7
|
+
require "dependabot/nuget/update_checker"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module Nuget
|
11
|
+
class UpdateChecker
|
12
|
+
class NuspecFetcher
|
13
|
+
require_relative "nupkg_fetcher"
|
14
|
+
require_relative "repository_finder"
|
15
|
+
|
16
|
+
def self.fetch_nuspec(dependency_urls, package_id, package_version)
|
17
|
+
# check all repositories for the first one that has the nuspec
|
18
|
+
dependency_urls.reduce(nil) do |nuspec_xml, repository_details|
|
19
|
+
nuspec_xml || fetch_nuspec_from_repository(repository_details, package_id, package_version)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.fetch_nuspec_from_repository(repository_details, package_id, package_version)
|
24
|
+
return unless package_id && package_version && !package_version.empty?
|
25
|
+
|
26
|
+
feed_url = repository_details[:repository_url]
|
27
|
+
auth_header = repository_details[:auth_header]
|
28
|
+
|
29
|
+
nuspec_xml = nil
|
30
|
+
|
31
|
+
if azure_package_feed?(feed_url)
|
32
|
+
# this is an azure devops url we can extract the nuspec from the nupkg
|
33
|
+
package_data = NupkgFetcher.fetch_nupkg_buffer_from_repository(repository_details, package_id,
|
34
|
+
package_version)
|
35
|
+
return if package_data.nil?
|
36
|
+
|
37
|
+
nuspec_string = extract_nuspec(package_data, package_id)
|
38
|
+
nuspec_xml = Nokogiri::XML(nuspec_string)
|
39
|
+
else
|
40
|
+
# we can use the normal nuget apis to get the nuspec and list out the dependencies
|
41
|
+
base_url = feed_url.gsub("/index.json", "-flatcontainer")
|
42
|
+
package_id_downcased = package_id.downcase
|
43
|
+
nuspec_url = "#{base_url}/#{package_id_downcased}/#{package_version}/#{package_id_downcased}.nuspec"
|
44
|
+
|
45
|
+
nuspec_response = Dependabot::RegistryClient.get(
|
46
|
+
url: nuspec_url,
|
47
|
+
headers: auth_header
|
48
|
+
)
|
49
|
+
|
50
|
+
return unless nuspec_response.status == 200
|
51
|
+
|
52
|
+
nuspec_response_body = remove_wrapping_zero_width_chars(nuspec_response.body)
|
53
|
+
nuspec_xml = Nokogiri::XML(nuspec_response_body)
|
54
|
+
end
|
55
|
+
|
56
|
+
nuspec_xml.remove_namespaces!
|
57
|
+
nuspec_xml
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.azure_package_feed?(feed_url)
|
61
|
+
# if url is azure devops
|
62
|
+
azure_devops_regexs = [
|
63
|
+
%r{https://pkgs\.dev\.azure\.com/(?<organization>[^/]+)/(?<project>[^/]+)/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json},
|
64
|
+
%r{https://pkgs\.dev\.azure\.com/(?<organization>[^/]+)/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json(?<project>)},
|
65
|
+
%r{https://(?<organization>[^\.\/]+)\.pkgs\.visualstudio\.com/_packaging/(?<feedId>[^/]+)/nuget/v3/index\.json(?<project>)}
|
66
|
+
]
|
67
|
+
azure_devops_regexs.any? { |reg| reg.match(feed_url) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.extract_nuspec(zip_stream, package_id)
|
71
|
+
Zip::File.open_buffer(zip_stream) do |zip|
|
72
|
+
nuspec_entry = zip.find { |entry| entry.name == "#{package_id}.nuspec" }
|
73
|
+
return nuspec_entry.get_input_stream.read if nuspec_entry
|
74
|
+
end
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.remove_wrapping_zero_width_chars(string)
|
79
|
+
string.force_encoding("UTF-8").encode
|
80
|
+
.gsub(/\A[\u200B-\u200D\uFEFF]/, "")
|
81
|
+
.gsub(/[\u200B-\u200D\uFEFF]\Z/, "")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -10,6 +10,7 @@ module Dependabot
|
|
10
10
|
class PropertyUpdater
|
11
11
|
require_relative "version_finder"
|
12
12
|
require_relative "requirements_updater"
|
13
|
+
require_relative "dependency_finder"
|
13
14
|
|
14
15
|
def initialize(dependency:, dependency_files:, credentials:,
|
15
16
|
target_version_details:, ignored_versions:,
|
@@ -45,9 +46,15 @@ module Dependabot
|
|
45
46
|
def updated_dependencies
|
46
47
|
raise "Update not possible!" unless update_possible?
|
47
48
|
|
48
|
-
@updated_dependencies ||=
|
49
|
-
|
50
|
-
|
49
|
+
@updated_dependencies ||= begin
|
50
|
+
dependencies = {}
|
51
|
+
|
52
|
+
dependencies_using_property.each do |dep|
|
53
|
+
# Only keep one copy of each dependency, the one with the highest target version.
|
54
|
+
visited_dependency = dependencies[dep.name.downcase]
|
55
|
+
next unless visited_dependency.nil? || visited_dependency.numeric_version < target_version
|
56
|
+
|
57
|
+
updated_dependency = Dependency.new(
|
51
58
|
name: dep.name,
|
52
59
|
version: target_version.to_s,
|
53
60
|
requirements: updated_requirements(dep),
|
@@ -55,7 +62,13 @@ module Dependabot
|
|
55
62
|
previous_requirements: dep.requirements,
|
56
63
|
package_manager: dep.package_manager
|
57
64
|
)
|
65
|
+
dependencies[updated_dependency.name.downcase] = updated_dependency
|
66
|
+
# Add peer dependencies to the list of updated dependencies.
|
67
|
+
process_updated_peer_dependencies(updated_dependency, dependencies)
|
58
68
|
end
|
69
|
+
|
70
|
+
dependencies.map { |_, dependency| dependency }
|
71
|
+
end
|
59
72
|
end
|
60
73
|
|
61
74
|
private
|
@@ -63,6 +76,20 @@ module Dependabot
|
|
63
76
|
attr_reader :dependency, :dependency_files, :target_version,
|
64
77
|
:source_details, :credentials, :ignored_versions
|
65
78
|
|
79
|
+
def process_updated_peer_dependencies(dependency, dependencies)
|
80
|
+
DependencyFinder.new(
|
81
|
+
dependency: dependency,
|
82
|
+
dependency_files: dependency_files,
|
83
|
+
credentials: credentials
|
84
|
+
).updated_peer_dependencies.each do |peer_dependency|
|
85
|
+
# Only keep one copy of each dependency, the one with the highest target version.
|
86
|
+
visited_dependency = dependencies[peer_dependency.name.downcase]
|
87
|
+
next unless visited_dependency.nil? || visited_dependency.numeric_version < peer_dependency.numeric_version
|
88
|
+
|
89
|
+
dependencies[peer_dependency.name.downcase] = peer_dependency
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
66
93
|
def dependencies_using_property
|
67
94
|
@dependencies_using_property ||=
|
68
95
|
Nuget::FileParser.new(
|
@@ -24,6 +24,18 @@ module Dependabot
|
|
24
24
|
find_dependency_urls
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.get_default_repository_details(dependency_name)
|
28
|
+
{
|
29
|
+
repository_url: DEFAULT_REPOSITORY_URL,
|
30
|
+
versions_url: "https://api.nuget.org/v3-flatcontainer/" \
|
31
|
+
"#{dependency_name.downcase}/index.json",
|
32
|
+
search_url: "https://azuresearch-usnc.nuget.org/query" \
|
33
|
+
"?q=#{dependency_name.downcase}&prerelease=true&semVerLevel=2.0.0",
|
34
|
+
auth_header: {},
|
35
|
+
repository_type: "v3"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
27
39
|
private
|
28
40
|
|
29
41
|
attr_reader :dependency, :credentials, :config_files
|
@@ -85,9 +97,16 @@ module Dependabot
|
|
85
97
|
end
|
86
98
|
|
87
99
|
def search_url_from_v3_metadata(metadata)
|
100
|
+
# allowable values from here: https://learn.microsoft.com/en-us/nuget/api/search-query-service-resource#versioning
|
101
|
+
allowed_search_types = %w(
|
102
|
+
SearchQueryService
|
103
|
+
SearchQueryService/3.0.0-beta
|
104
|
+
SearchQueryService/3.0.0-rc
|
105
|
+
SearchQueryService/3.5.0
|
106
|
+
)
|
88
107
|
metadata
|
89
108
|
.fetch("resources", [])
|
90
|
-
.find { |r| r.fetch("@type") ==
|
109
|
+
.find { |r| allowed_search_types.find { |s| r.fetch("@type") == s } }
|
91
110
|
&.fetch("@id")
|
92
111
|
end
|
93
112
|
|
@@ -157,6 +176,8 @@ module Dependabot
|
|
157
176
|
base_sources = [{ url: DEFAULT_REPOSITORY_URL, key: "nuget.org" }]
|
158
177
|
|
159
178
|
sources = []
|
179
|
+
|
180
|
+
# regular package sources
|
160
181
|
doc.css("configuration > packageSources").children.each do |node|
|
161
182
|
if node.name == "clear"
|
162
183
|
sources.clear
|
@@ -167,6 +188,15 @@ module Dependabot
|
|
167
188
|
sources << { url: url, key: key }
|
168
189
|
end
|
169
190
|
end
|
191
|
+
|
192
|
+
# signed package sources
|
193
|
+
# https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file#trustedsigners-section
|
194
|
+
doc.xpath("/configuration/trustedSigners/repository").each do |node|
|
195
|
+
name = node.attribute("name")&.value&.strip
|
196
|
+
service_index = node.attribute("serviceIndex")&.value&.strip
|
197
|
+
sources << { url: service_index, key: name }
|
198
|
+
end
|
199
|
+
|
170
200
|
sources += base_sources # TODO: quirky overwrite behavior
|
171
201
|
disabled_sources = disabled_sources(doc)
|
172
202
|
sources.reject! do |s|
|
@@ -190,15 +220,7 @@ module Dependabot
|
|
190
220
|
# rubocop:enable Metrics/CyclomaticComplexity
|
191
221
|
|
192
222
|
def default_repository_details
|
193
|
-
|
194
|
-
repository_url: DEFAULT_REPOSITORY_URL,
|
195
|
-
versions_url: "https://api.nuget.org/v3-flatcontainer/" \
|
196
|
-
"#{dependency.name.downcase}/index.json",
|
197
|
-
search_url: "https://azuresearch-usnc.nuget.org/query" \
|
198
|
-
"?q=#{dependency.name.downcase}&prerelease=true&semVerLevel=2.0.0",
|
199
|
-
auth_header: {},
|
200
|
-
repository_type: "v3"
|
201
|
-
}
|
223
|
+
RepositoryFinder.get_default_repository_details(dependency.name)
|
202
224
|
end
|
203
225
|
|
204
226
|
# rubocop:disable Metrics/PerceivedComplexity
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/nuget/version"
|
5
|
+
require "dependabot/nuget/requirement"
|
6
|
+
require "dependabot/nuget/native_helpers"
|
7
|
+
require "dependabot/nuget/update_checker"
|
8
|
+
require "dependabot/shared_helpers"
|
9
|
+
|
10
|
+
module Dependabot
|
11
|
+
module Nuget
|
12
|
+
class UpdateChecker
|
13
|
+
class TfmComparer
|
14
|
+
def self.are_frameworks_compatible?(project_tfms, package_tfms)
|
15
|
+
return false if package_tfms.empty?
|
16
|
+
return false if project_tfms.empty?
|
17
|
+
|
18
|
+
key = "project_ftms:#{project_tfms.sort.join(',')}:package_tfms:#{package_tfms.sort.join(',')}".downcase
|
19
|
+
|
20
|
+
@cached_framework_check ||= {}
|
21
|
+
unless @cached_framework_check.key?(key)
|
22
|
+
@cached_framework_check[key] =
|
23
|
+
NativeHelpers.run_nuget_framework_check(project_tfms,
|
24
|
+
package_tfms)
|
25
|
+
end
|
26
|
+
@cached_framework_check[key]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|