dependabot-nuget 0.80.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 +7 -0
- data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +49 -0
- data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +53 -0
- data/lib/dependabot/nuget/file_fetcher.rb +216 -0
- data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +63 -0
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +154 -0
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +129 -0
- data/lib/dependabot/nuget/file_parser.rb +86 -0
- data/lib/dependabot/nuget/file_updater/packages_config_declaration_finder.rb +67 -0
- data/lib/dependabot/nuget/file_updater/project_file_declaration_finder.rb +76 -0
- data/lib/dependabot/nuget/file_updater/property_value_updater.rb +62 -0
- data/lib/dependabot/nuget/file_updater.rb +152 -0
- data/lib/dependabot/nuget/metadata_finder.rb +117 -0
- data/lib/dependabot/nuget/requirement.rb +90 -0
- data/lib/dependabot/nuget/update_checker/property_updater.rb +95 -0
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +230 -0
- data/lib/dependabot/nuget/update_checker/requirements_updater.rb +79 -0
- data/lib/dependabot/nuget/update_checker/version_finder.rb +229 -0
- data/lib/dependabot/nuget/update_checker.rb +128 -0
- data/lib/dependabot/nuget/version.rb +23 -0
- data/lib/dependabot/nuget.rb +10 -0
- metadata +190 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/nuget/file_parser"
|
4
|
+
require "dependabot/nuget/update_checker"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Nuget
|
8
|
+
class UpdateChecker
|
9
|
+
class PropertyUpdater
|
10
|
+
require_relative "version_finder"
|
11
|
+
require_relative "requirements_updater"
|
12
|
+
|
13
|
+
def initialize(dependency:, dependency_files:, credentials:,
|
14
|
+
target_version_details:, ignored_versions:)
|
15
|
+
@dependency = dependency
|
16
|
+
@dependency_files = dependency_files
|
17
|
+
@credentials = credentials
|
18
|
+
@ignored_versions = ignored_versions
|
19
|
+
@target_version = target_version_details&.fetch(:version)
|
20
|
+
@source_details = target_version_details&.
|
21
|
+
slice(:nuspec_url, :repo_url, :source_url)
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_possible?
|
25
|
+
return false unless target_version
|
26
|
+
|
27
|
+
@update_possible ||=
|
28
|
+
dependencies_using_property.all? do |dep|
|
29
|
+
versions = VersionFinder.new(
|
30
|
+
dependency: dep,
|
31
|
+
dependency_files: dependency_files,
|
32
|
+
credentials: credentials,
|
33
|
+
ignored_versions: ignored_versions
|
34
|
+
).versions.map { |v| v.fetch(:version) }
|
35
|
+
|
36
|
+
versions.include?(target_version) || versions.none?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def updated_dependencies
|
41
|
+
raise "Update not possible!" unless update_possible?
|
42
|
+
|
43
|
+
@updated_dependencies ||=
|
44
|
+
dependencies_using_property.map do |dep|
|
45
|
+
Dependency.new(
|
46
|
+
name: dep.name,
|
47
|
+
version: target_version.to_s,
|
48
|
+
requirements: updated_requirements(dep),
|
49
|
+
previous_version: dep.version,
|
50
|
+
previous_requirements: dep.requirements,
|
51
|
+
package_manager: dep.package_manager
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :dependency, :dependency_files, :target_version,
|
59
|
+
:source_details, :credentials, :ignored_versions
|
60
|
+
|
61
|
+
def dependencies_using_property
|
62
|
+
@dependencies_using_property ||=
|
63
|
+
Nuget::FileParser.new(
|
64
|
+
dependency_files: dependency_files,
|
65
|
+
source: nil
|
66
|
+
).parse.select do |dep|
|
67
|
+
dep.requirements.any? do |r|
|
68
|
+
r.dig(:metadata, :property_name) == property_name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def property_name
|
74
|
+
@property_name ||= dependency.requirements.
|
75
|
+
find { |r| r.dig(:metadata, :property_name) }&.
|
76
|
+
dig(:metadata, :property_name)
|
77
|
+
|
78
|
+
raise "No requirement with a property name!" unless @property_name
|
79
|
+
|
80
|
+
@property_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def updated_requirements(dep)
|
84
|
+
@updated_requirements ||= {}
|
85
|
+
@updated_requirements[dep.name] ||=
|
86
|
+
RequirementsUpdater.new(
|
87
|
+
requirements: dep.requirements,
|
88
|
+
latest_version: target_version,
|
89
|
+
source_details: source_details
|
90
|
+
).updated_requirements
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "excon"
|
4
|
+
require "nokogiri"
|
5
|
+
require "dependabot/errors"
|
6
|
+
require "dependabot/nuget/update_checker"
|
7
|
+
require "dependabot/shared_helpers"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module Nuget
|
11
|
+
class UpdateChecker
|
12
|
+
class RepositoryFinder
|
13
|
+
DEFAULT_REPOSITORY_URL = "https://api.nuget.org/v3/index.json"
|
14
|
+
|
15
|
+
def initialize(dependency:, credentials:, config_file: nil)
|
16
|
+
@dependency = dependency
|
17
|
+
@credentials = credentials
|
18
|
+
@config_file = config_file
|
19
|
+
end
|
20
|
+
|
21
|
+
def dependency_urls
|
22
|
+
find_dependency_urls
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :dependency, :credentials, :config_file
|
28
|
+
|
29
|
+
def find_dependency_urls
|
30
|
+
@find_dependency_urls ||=
|
31
|
+
known_repositories.flat_map do |details|
|
32
|
+
if details.fetch(:url) == DEFAULT_REPOSITORY_URL
|
33
|
+
# Save a request for the default URL, since we already how
|
34
|
+
# it addresses packages
|
35
|
+
next default_repository_details
|
36
|
+
end
|
37
|
+
|
38
|
+
build_url_for_details(details)
|
39
|
+
end.compact.uniq
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_url_for_details(repo_details)
|
43
|
+
response = get_repo_metadata(repo_details)
|
44
|
+
check_repo_reponse(response, repo_details)
|
45
|
+
return unless response.status == 200
|
46
|
+
|
47
|
+
base_url = base_url_from_v3_metadata(JSON.parse(response.body))
|
48
|
+
search_url = search_url_from_v3_metadata(JSON.parse(response.body))
|
49
|
+
|
50
|
+
details = {
|
51
|
+
repository_url: repo_details.fetch(:url),
|
52
|
+
auth_header: auth_header_for_token(repo_details.fetch(:token)),
|
53
|
+
repository_type: "v3"
|
54
|
+
}
|
55
|
+
if base_url
|
56
|
+
details[:versions_url] =
|
57
|
+
File.join(base_url, dependency.name.downcase, "index.json")
|
58
|
+
end
|
59
|
+
if search_url
|
60
|
+
details[:search_url] =
|
61
|
+
search_url + "?q=#{dependency.name.downcase}&prerelease=true"
|
62
|
+
end
|
63
|
+
details
|
64
|
+
rescue JSON::ParserError
|
65
|
+
build_v2_url(response, repo_details)
|
66
|
+
rescue Excon::Error::Timeout, Excon::Error::Socket
|
67
|
+
handle_timeout(repo_metadata_url: repo_details.fetch(:url))
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_repo_metadata(repo_details)
|
71
|
+
Excon.get(
|
72
|
+
repo_details.fetch(:url),
|
73
|
+
headers: auth_header_for_token(repo_details.fetch(:token)),
|
74
|
+
idempotent: true,
|
75
|
+
**SharedHelpers.excon_defaults
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def base_url_from_v3_metadata(metadata)
|
80
|
+
metadata.
|
81
|
+
fetch("resources", []).
|
82
|
+
find { |r| r.fetch("@type") == "PackageBaseAddress/3.0.0" }&.
|
83
|
+
fetch("@id")
|
84
|
+
end
|
85
|
+
|
86
|
+
def search_url_from_v3_metadata(metadata)
|
87
|
+
metadata.
|
88
|
+
fetch("resources", []).
|
89
|
+
find { |r| r.fetch("@type") == "SearchQueryService" }&.
|
90
|
+
fetch("@id")
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_v2_url(response, repo_details)
|
94
|
+
doc = Nokogiri::XML(response.body)
|
95
|
+
doc.remove_namespaces!
|
96
|
+
base_url = doc.at_xpath("service")&.attributes&.fetch("base")&.value
|
97
|
+
return unless base_url
|
98
|
+
|
99
|
+
{
|
100
|
+
repository_url: base_url,
|
101
|
+
versions_url: File.join(
|
102
|
+
base_url,
|
103
|
+
"FindPackagesById()?id='#{dependency.name}'"
|
104
|
+
),
|
105
|
+
auth_header: auth_header_for_token(repo_details.fetch(:token)),
|
106
|
+
repository_type: "v2"
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
def check_repo_reponse(response, details)
|
111
|
+
return unless [401, 402, 403].include?(response.status)
|
112
|
+
raise if details.fetch(:url) == DEFAULT_REPOSITORY_URL
|
113
|
+
|
114
|
+
raise PrivateSourceAuthenticationFailure, details.fetch(:url)
|
115
|
+
end
|
116
|
+
|
117
|
+
def handle_timeout(repo_metadata_url:)
|
118
|
+
raise if repo_metadata_url == DEFAULT_REPOSITORY_URL
|
119
|
+
|
120
|
+
raise PrivateSourceTimedOut, repo_metadata_url
|
121
|
+
end
|
122
|
+
|
123
|
+
def known_repositories
|
124
|
+
return @known_repositories if @known_repositories
|
125
|
+
|
126
|
+
@known_repositories = []
|
127
|
+
@known_repositories += credential_repositories
|
128
|
+
@known_repositories += config_file_repositories
|
129
|
+
|
130
|
+
if @known_repositories.empty?
|
131
|
+
@known_repositories << { url: DEFAULT_REPOSITORY_URL, token: nil }
|
132
|
+
end
|
133
|
+
|
134
|
+
@known_repositories.uniq
|
135
|
+
end
|
136
|
+
|
137
|
+
def credential_repositories
|
138
|
+
@credential_repositories ||=
|
139
|
+
credentials.
|
140
|
+
select { |cred| cred["type"] == "nuget_feed" }.
|
141
|
+
map { |c| { url: c.fetch("url"), token: c["token"] } }
|
142
|
+
end
|
143
|
+
|
144
|
+
def config_file_repositories
|
145
|
+
return [] unless config_file
|
146
|
+
|
147
|
+
doc = Nokogiri::XML(config_file.content)
|
148
|
+
doc.remove_namespaces!
|
149
|
+
sources =
|
150
|
+
doc.css("configuration > packageSources > add").map do |node|
|
151
|
+
{
|
152
|
+
key:
|
153
|
+
node.attribute("key")&.value&.strip ||
|
154
|
+
node.at_xpath("./key")&.content&.strip,
|
155
|
+
url:
|
156
|
+
node.attribute("value")&.value&.strip ||
|
157
|
+
node.at_xpath("./value")&.content&.strip
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
sources.reject! do |s|
|
162
|
+
known_urls = credential_repositories.map { |cr| cr.fetch(:url) }
|
163
|
+
known_urls.include?(s.fetch(:url))
|
164
|
+
end
|
165
|
+
|
166
|
+
add_config_file_credentials(sources: sources, doc: doc)
|
167
|
+
sources.each { |details| details.delete(:key) }
|
168
|
+
|
169
|
+
sources
|
170
|
+
end
|
171
|
+
|
172
|
+
def default_repository_details
|
173
|
+
{
|
174
|
+
repository_url: DEFAULT_REPOSITORY_URL,
|
175
|
+
versions_url: "https://api.nuget.org/v3-flatcontainer/"\
|
176
|
+
"#{dependency.name.downcase}/index.json",
|
177
|
+
search_url: "https://api-v2v3search-0.nuget.org/query"\
|
178
|
+
"?q=#{dependency.name.downcase}&prerelease=true",
|
179
|
+
auth_header: {},
|
180
|
+
repository_type: "v3"
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_config_file_credentials(sources:, doc:)
|
185
|
+
sources.each do |source_details|
|
186
|
+
key = source_details.fetch(:key)
|
187
|
+
next source_details[:token] = nil unless key
|
188
|
+
next source_details[:token] = nil if key.match?(/^\d/)
|
189
|
+
|
190
|
+
tag = key.gsub(" ", "_x0020_")
|
191
|
+
creds_nodes = doc.css("configuration > packageSourceCredentials "\
|
192
|
+
"> #{tag} > add")
|
193
|
+
|
194
|
+
username =
|
195
|
+
creds_nodes.
|
196
|
+
find { |n| n.attribute("key")&.value == "Username" }&.
|
197
|
+
attribute("value")&.value
|
198
|
+
password =
|
199
|
+
creds_nodes.
|
200
|
+
find { |n| n.attribute("key")&.value == "ClearTextPassword" }&.
|
201
|
+
attribute("value")&.value
|
202
|
+
|
203
|
+
# Note: We have to look for plain text passwords, as we have no
|
204
|
+
# way of decrypting encrypted passwords. For the same reason we
|
205
|
+
# don't fetch API keys from the nuget.config at all.
|
206
|
+
next source_details[:token] = nil unless username && password
|
207
|
+
|
208
|
+
source_details[:token] = "#{username}:#{password}"
|
209
|
+
end
|
210
|
+
|
211
|
+
sources
|
212
|
+
end
|
213
|
+
|
214
|
+
def auth_header_for_token(token)
|
215
|
+
return {} unless token
|
216
|
+
|
217
|
+
if token.include?(":")
|
218
|
+
encoded_token = Base64.encode64(token).delete("\n")
|
219
|
+
{ "Authorization" => "Basic #{encoded_token}" }
|
220
|
+
elsif Base64.decode64(token).ascii_only? &&
|
221
|
+
Base64.decode64(token).include?(":")
|
222
|
+
{ "Authorization" => "Basic #{token.delete("\n")}" }
|
223
|
+
else
|
224
|
+
{ "Authorization" => "Bearer #{token}" }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#######################################################################
|
4
|
+
# For more details on Dotnet version constraints, see: #
|
5
|
+
# https://docs.microsoft.com/en-us/nuget/reference/package-versioning #
|
6
|
+
#######################################################################
|
7
|
+
|
8
|
+
require "dependabot/nuget/update_checker"
|
9
|
+
require "dependabot/nuget/version"
|
10
|
+
|
11
|
+
module Dependabot
|
12
|
+
module Nuget
|
13
|
+
class UpdateChecker
|
14
|
+
class RequirementsUpdater
|
15
|
+
VERSION_REGEX = /[0-9a-zA-Z]+(?:\.[a-zA-Z0-9\-]+)*/.freeze
|
16
|
+
|
17
|
+
def initialize(requirements:, latest_version:, source_details:)
|
18
|
+
@requirements = requirements
|
19
|
+
@source_details = source_details
|
20
|
+
return unless latest_version
|
21
|
+
|
22
|
+
@latest_version = version_class.new(latest_version)
|
23
|
+
end
|
24
|
+
|
25
|
+
def updated_requirements
|
26
|
+
return requirements unless latest_version
|
27
|
+
|
28
|
+
# Note: Order is important here. The FileUpdater needs the updated
|
29
|
+
# requirement at index `i` to correspond to the previous requirement
|
30
|
+
# at the same index.
|
31
|
+
requirements.map do |req|
|
32
|
+
next req if req.fetch(:requirement).nil?
|
33
|
+
next req if req.fetch(:requirement).include?(",")
|
34
|
+
|
35
|
+
new_req =
|
36
|
+
if req.fetch(:requirement).include?("*")
|
37
|
+
update_wildcard_requirement(req.fetch(:requirement))
|
38
|
+
else
|
39
|
+
# Since range requirements are excluded by the line above we
|
40
|
+
# can just do a `gsub` on anything that looks like a version
|
41
|
+
req[:requirement].gsub(VERSION_REGEX, latest_version.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
next req if new_req == req.fetch(:requirement)
|
45
|
+
|
46
|
+
req.merge(requirement: new_req, source: updated_source)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :requirements, :latest_version, :source_details
|
53
|
+
|
54
|
+
def version_class
|
55
|
+
Nuget::Version
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_wildcard_requirement(req_string)
|
59
|
+
precision = req_string.split("*").first.split(/\.|\-/).count
|
60
|
+
wilcard_section = req_string.partition(/(?=[.\-]\*)/).last
|
61
|
+
|
62
|
+
version_parts = latest_version.segments.first(precision)
|
63
|
+
version = version_parts.join(".")
|
64
|
+
|
65
|
+
version + wilcard_section
|
66
|
+
end
|
67
|
+
|
68
|
+
def updated_source
|
69
|
+
{
|
70
|
+
type: "nuget_repo",
|
71
|
+
url: source_details.fetch(:repo_url),
|
72
|
+
nuspec_url: source_details.fetch(:nuspec_url),
|
73
|
+
source_url: source_details.fetch(:source_url)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "excon"
|
4
|
+
require "nokogiri"
|
5
|
+
|
6
|
+
require "dependabot/nuget/version"
|
7
|
+
require "dependabot/nuget/requirement"
|
8
|
+
require "dependabot/nuget/update_checker"
|
9
|
+
require "dependabot/shared_helpers"
|
10
|
+
|
11
|
+
module Dependabot
|
12
|
+
module Nuget
|
13
|
+
class UpdateChecker
|
14
|
+
class VersionFinder
|
15
|
+
require_relative "repository_finder"
|
16
|
+
|
17
|
+
def initialize(dependency:, dependency_files:, credentials:,
|
18
|
+
ignored_versions: [])
|
19
|
+
@dependency = dependency
|
20
|
+
@dependency_files = dependency_files
|
21
|
+
@credentials = credentials
|
22
|
+
@ignored_versions = ignored_versions
|
23
|
+
end
|
24
|
+
|
25
|
+
def latest_version_details
|
26
|
+
@latest_version_details ||=
|
27
|
+
begin
|
28
|
+
tmp_versions = versions
|
29
|
+
unless wants_prerelease?
|
30
|
+
tmp_versions.reject! { |d| d.fetch(:version).prerelease? }
|
31
|
+
end
|
32
|
+
tmp_versions.reject! do |hash|
|
33
|
+
ignore_reqs.any? { |r| r.satisfied_by?(hash.fetch(:version)) }
|
34
|
+
end
|
35
|
+
tmp_versions.max_by { |hash| hash.fetch(:version) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def versions
|
40
|
+
available_v3_versions + available_v2_versions
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :dependency, :dependency_files, :credentials,
|
44
|
+
:ignored_versions
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def available_v3_versions
|
49
|
+
v3_nuget_listings.flat_map do |listing|
|
50
|
+
listing.
|
51
|
+
fetch("versions", []).
|
52
|
+
map do |v|
|
53
|
+
nuspec_url =
|
54
|
+
listing.fetch("listing_details").
|
55
|
+
fetch(:versions_url).
|
56
|
+
gsub(/index\.json$/, "#{v}/#{sanitized_name}.nuspec")
|
57
|
+
|
58
|
+
{
|
59
|
+
version: version_class.new(v),
|
60
|
+
nuspec_url: nuspec_url,
|
61
|
+
source_url: nil,
|
62
|
+
repo_url:
|
63
|
+
listing.fetch("listing_details").fetch(:repository_url)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def available_v2_versions
|
70
|
+
v2_nuget_listings.flat_map do |listing|
|
71
|
+
body = listing.fetch("xml_body", [])
|
72
|
+
doc = Nokogiri::XML(body)
|
73
|
+
doc.remove_namespaces!
|
74
|
+
|
75
|
+
doc.xpath("/feed/entry").map do |entry|
|
76
|
+
listed = entry.at_xpath("./properties/Listed")&.content&.strip
|
77
|
+
next if listed&.casecmp("false")&.zero?
|
78
|
+
|
79
|
+
entry_details = dependency_details_from_v2_entry(entry)
|
80
|
+
entry_details.merge(
|
81
|
+
repo_url: listing.fetch("listing_details").
|
82
|
+
fetch(:repository_url)
|
83
|
+
)
|
84
|
+
end.compact
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def dependency_details_from_v2_entry(entry)
|
89
|
+
version = entry.at_xpath("./properties/Version").content.strip
|
90
|
+
source_urls = []
|
91
|
+
[
|
92
|
+
entry.at_xpath("./properties/ProjectUrl").content,
|
93
|
+
entry.at_xpath("./properties/ReleaseNotes").content
|
94
|
+
].join(" ").scan(Source::SOURCE_REGEX) do
|
95
|
+
source_urls << Regexp.last_match.to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
source_url = source_urls.find { |url| Source.from_url(url) }
|
99
|
+
source_url = Source.from_url(source_url)&.url if source_url
|
100
|
+
|
101
|
+
{
|
102
|
+
version: version_class.new(version),
|
103
|
+
nuspec_url: nil,
|
104
|
+
source_url: source_url
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def wants_prerelease?
|
109
|
+
if dependency.version &&
|
110
|
+
version_class.correct?(dependency.version) &&
|
111
|
+
version_class.new(dependency.version).prerelease?
|
112
|
+
return true
|
113
|
+
end
|
114
|
+
|
115
|
+
dependency.requirements.any? do |req|
|
116
|
+
reqs = (req.fetch(:requirement) || "").split(",").map(&:strip)
|
117
|
+
reqs.any? { |r| r.include?("-") }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def v3_nuget_listings
|
122
|
+
return @v3_nuget_listings unless @v3_nuget_listings.nil?
|
123
|
+
|
124
|
+
dependency_urls.
|
125
|
+
select { |details| details.fetch(:repository_type) == "v3" }.
|
126
|
+
map do |url_details|
|
127
|
+
versions = versions_for_v3_repository(url_details)
|
128
|
+
next unless versions
|
129
|
+
|
130
|
+
{ "versions" => versions, "listing_details" => url_details }
|
131
|
+
end.compact
|
132
|
+
end
|
133
|
+
|
134
|
+
def v2_nuget_listings
|
135
|
+
return @v2_nuget_listings unless @v2_nuget_listings.nil?
|
136
|
+
|
137
|
+
dependency_urls.
|
138
|
+
select { |details| details.fetch(:repository_type) == "v2" }.
|
139
|
+
map do |url_details|
|
140
|
+
response = Excon.get(
|
141
|
+
url_details[:versions_url],
|
142
|
+
headers: url_details[:auth_header],
|
143
|
+
idempotent: true,
|
144
|
+
**excon_defaults
|
145
|
+
)
|
146
|
+
next unless response.status == 200
|
147
|
+
|
148
|
+
{
|
149
|
+
"xml_body" => response.body,
|
150
|
+
"listing_details" => url_details
|
151
|
+
}
|
152
|
+
end.compact
|
153
|
+
end
|
154
|
+
|
155
|
+
def versions_for_v3_repository(repository_details)
|
156
|
+
# If we have a search URL we use it (since it will exclude unlisted
|
157
|
+
# versions)
|
158
|
+
if repository_details[:search_url]
|
159
|
+
response = Excon.get(
|
160
|
+
repository_details[:search_url],
|
161
|
+
headers: repository_details[:auth_header],
|
162
|
+
idempotent: true,
|
163
|
+
**excon_defaults
|
164
|
+
)
|
165
|
+
return unless response.status == 200
|
166
|
+
|
167
|
+
JSON.parse(response.body).fetch("data").
|
168
|
+
find { |d| d.fetch("id").casecmp(sanitized_name).zero? }&.
|
169
|
+
fetch("versions")&.
|
170
|
+
map { |d| d.fetch("version") }
|
171
|
+
# Otherwise, use the versions URL
|
172
|
+
elsif repository_details[:versions_url]
|
173
|
+
response = Excon.get(
|
174
|
+
repository_details[:versions_url],
|
175
|
+
headers: repository_details[:auth_header],
|
176
|
+
idempotent: true,
|
177
|
+
**excon_defaults
|
178
|
+
)
|
179
|
+
return unless response.status == 200
|
180
|
+
|
181
|
+
JSON.parse(response.body).fetch("versions")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def dependency_urls
|
186
|
+
@dependency_urls ||=
|
187
|
+
RepositoryFinder.new(
|
188
|
+
dependency: dependency,
|
189
|
+
credentials: credentials,
|
190
|
+
config_file: nuget_config
|
191
|
+
).dependency_urls
|
192
|
+
end
|
193
|
+
|
194
|
+
def ignore_reqs
|
195
|
+
ignored_versions.map { |req| requirement_class.new(req.split(",")) }
|
196
|
+
end
|
197
|
+
|
198
|
+
def nuget_config
|
199
|
+
@nuget_config ||=
|
200
|
+
dependency_files.find { |f| f.name.casecmp("nuget.config").zero? }
|
201
|
+
end
|
202
|
+
|
203
|
+
def sanitized_name
|
204
|
+
dependency.name.downcase
|
205
|
+
end
|
206
|
+
|
207
|
+
def version_class
|
208
|
+
Nuget::Version
|
209
|
+
end
|
210
|
+
|
211
|
+
def requirement_class
|
212
|
+
Nuget::Requirement
|
213
|
+
end
|
214
|
+
|
215
|
+
def excon_defaults
|
216
|
+
# For large JSON files we sometimes need a little longer than for
|
217
|
+
# other languages. For example, see:
|
218
|
+
# https://dotnet.myget.org/F/aspnetcore-dev/api/v3/query?
|
219
|
+
# q=microsoft.aspnetcore.mvc&prerelease=true
|
220
|
+
SharedHelpers.excon_defaults.merge(
|
221
|
+
connect_timeout: 10,
|
222
|
+
write_timeout: 10,
|
223
|
+
read_timeout: 10
|
224
|
+
)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|