dependabot-nuget 0.246.0 → 0.247.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/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +40 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +27 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +18 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +110 -0
- data/lib/dependabot/nuget/cache_manager.rb +9 -3
- data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +15 -12
- data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +13 -3
- data/lib/dependabot/nuget/file_fetcher.rb +79 -31
- data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +10 -2
- data/lib/dependabot/nuget/file_parser/global_json_parser.rb +10 -2
- data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +11 -2
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +140 -41
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
- data/lib/dependabot/nuget/file_parser.rb +3 -3
- data/lib/dependabot/nuget/file_updater/property_value_updater.rb +25 -8
- data/lib/dependabot/nuget/file_updater.rb +74 -38
- data/lib/dependabot/nuget/http_response_helpers.rb +6 -1
- data/lib/dependabot/nuget/metadata_finder.rb +27 -3
- data/lib/dependabot/nuget/nuget_client.rb +23 -0
- data/lib/dependabot/nuget/requirement.rb +4 -1
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +11 -13
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
- data/lib/dependabot/nuget/update_checker/version_finder.rb +15 -6
- data/lib/dependabot/nuget/update_checker.rb +4 -4
- data/lib/dependabot/nuget/version.rb +7 -2
- metadata +19 -5
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "nokogiri"
|
@@ -11,25 +11,38 @@ module Dependabot
|
|
11
11
|
module Nuget
|
12
12
|
class FileUpdater
|
13
13
|
class PropertyValueUpdater
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { params(dependency_files: T::Array[Dependabot::DependencyFile]).void }
|
14
17
|
def initialize(dependency_files:)
|
15
18
|
@dependency_files = dependency_files
|
16
19
|
end
|
17
20
|
|
21
|
+
sig do
|
22
|
+
params(property_name: String,
|
23
|
+
updated_value: String,
|
24
|
+
callsite_file: Dependabot::DependencyFile)
|
25
|
+
.returns(T::Array[Dependabot::DependencyFile])
|
26
|
+
end
|
18
27
|
def update_files_for_property_change(property_name:, updated_value:,
|
19
28
|
callsite_file:)
|
20
29
|
declaration_details =
|
21
|
-
property_value_finder
|
22
|
-
.property_details(
|
30
|
+
property_value_finder.property_details(
|
23
31
|
property_name: property_name,
|
24
32
|
callsite_file: callsite_file
|
25
33
|
)
|
34
|
+
throw "Unable to locate property details" unless declaration_details
|
26
35
|
|
36
|
+
declaration_filename = declaration_details.fetch(:file)
|
27
37
|
declaration_file = dependency_files.find do |f|
|
28
|
-
|
38
|
+
declaration_filename == f.name
|
29
39
|
end
|
40
|
+
throw "Unable to locate declaration file" unless declaration_file
|
41
|
+
|
42
|
+
content = T.must(declaration_file.content)
|
30
43
|
node = declaration_details.fetch(:node)
|
31
44
|
|
32
|
-
updated_content =
|
45
|
+
updated_content = content.sub(
|
33
46
|
%r{(<#{Regexp.quote(node.name)}(?:\s[^>]*)?>)
|
34
47
|
\s*#{Regexp.quote(node.content)}\s*
|
35
48
|
</#{Regexp.quote(node.name)}>}xm,
|
@@ -37,21 +50,25 @@ module Dependabot
|
|
37
50
|
)
|
38
51
|
|
39
52
|
files = dependency_files.dup
|
40
|
-
files
|
53
|
+
file_index = T.must(files.index(declaration_file))
|
54
|
+
files[file_index] =
|
41
55
|
update_file(file: declaration_file, content: updated_content)
|
42
56
|
files
|
43
57
|
end
|
44
58
|
|
45
59
|
private
|
46
60
|
|
61
|
+
sig { returns(T::Array[DependencyFile]) }
|
47
62
|
attr_reader :dependency_files
|
48
63
|
|
64
|
+
sig { returns(FileParser::PropertyValueFinder) }
|
49
65
|
def property_value_finder
|
50
66
|
@property_value_finder ||=
|
51
|
-
|
52
|
-
.new(dependency_files: dependency_files)
|
67
|
+
T.let(FileParser::PropertyValueFinder
|
68
|
+
.new(dependency_files: dependency_files), T.nilable(FileParser::PropertyValueFinder))
|
53
69
|
end
|
54
70
|
|
71
|
+
sig { params(file: DependencyFile, content: String).returns(DependencyFile) }
|
55
72
|
def update_file(file:, content:)
|
56
73
|
updated_file = file.dup
|
57
74
|
updated_file.content = content
|
@@ -1,10 +1,11 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/dependency_file"
|
5
5
|
require "dependabot/file_updaters"
|
6
6
|
require "dependabot/file_updaters/base"
|
7
7
|
require "dependabot/nuget/native_helpers"
|
8
|
+
require "dependabot/shared_helpers"
|
8
9
|
require "sorbet-runtime"
|
9
10
|
|
10
11
|
module Dependabot
|
@@ -17,6 +18,7 @@ module Dependabot
|
|
17
18
|
require_relative "file_parser/dotnet_tools_json_parser"
|
18
19
|
require_relative "file_parser/packages_config_parser"
|
19
20
|
|
21
|
+
sig { override.returns(T::Array[Regexp]) }
|
20
22
|
def self.updated_files_regex
|
21
23
|
[
|
22
24
|
%r{^[^/]*\.([a-z]{2})?proj$},
|
@@ -29,32 +31,33 @@ module Dependabot
|
|
29
31
|
]
|
30
32
|
end
|
31
33
|
|
34
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
32
35
|
def updated_dependency_files
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
base_dir = T.must(dependency_files.first).directory
|
37
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
38
|
+
dependencies.each do |dependency|
|
39
|
+
try_update_projects(dependency) || try_update_json(dependency)
|
40
|
+
end
|
41
|
+
updated_files = dependency_files.filter_map do |f|
|
42
|
+
updated_content = File.read(dependency_file_path(f))
|
43
|
+
next if updated_content == f.content
|
36
44
|
|
37
|
-
|
38
|
-
|
39
|
-
updated_content = File.read(dependency_file_path(f))
|
40
|
-
next if updated_content == f.content
|
45
|
+
normalized_content = normalize_content(f, updated_content)
|
46
|
+
next if normalized_content == f.content
|
41
47
|
|
42
|
-
|
43
|
-
next if normalized_content == f.content
|
48
|
+
next if only_deleted_lines?(f.content, normalized_content)
|
44
49
|
|
45
|
-
|
50
|
+
puts "The contents of file [#{f.name}] were updated."
|
46
51
|
|
47
|
-
|
52
|
+
updated_file(file: f, content: normalized_content)
|
53
|
+
end
|
54
|
+
updated_files
|
48
55
|
end
|
49
|
-
|
50
|
-
# reset repo files
|
51
|
-
SharedHelpers.reset_git_repo(T.cast(repo_contents_path, String)) if repo_contents_path
|
52
|
-
|
53
|
-
updated_files
|
54
56
|
end
|
55
57
|
|
56
58
|
private
|
57
59
|
|
60
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
|
58
61
|
def try_update_projects(dependency)
|
59
62
|
update_ran = T.let(false, T::Boolean)
|
60
63
|
checked_files = Set.new
|
@@ -64,7 +67,7 @@ module Dependabot
|
|
64
67
|
project_dependencies = project_dependencies(project_file)
|
65
68
|
proj_path = dependency_file_path(project_file)
|
66
69
|
|
67
|
-
next unless project_dependencies.any? { |dep| dep.name.casecmp(dependency.name)
|
70
|
+
next unless project_dependencies.any? { |dep| dep.name.casecmp(dependency.name)&.zero? }
|
68
71
|
|
69
72
|
next unless repo_contents_path
|
70
73
|
|
@@ -79,16 +82,16 @@ module Dependabot
|
|
79
82
|
end
|
80
83
|
update_ran = true
|
81
84
|
end
|
82
|
-
|
83
85
|
update_ran
|
84
86
|
end
|
85
87
|
|
88
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
|
86
89
|
def try_update_json(dependency)
|
87
|
-
if dotnet_tools_json_dependencies.any? { |dep| dep.name.casecmp(dependency.name)
|
88
|
-
global_json_dependencies.any? { |dep| dep.name.casecmp(dependency.name)
|
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? }
|
89
92
|
|
90
93
|
# We just need to feed the updater a project file, grab the first
|
91
|
-
project_file = project_files.first
|
94
|
+
project_file = T.must(project_files.first)
|
92
95
|
proj_path = dependency_file_path(project_file)
|
93
96
|
|
94
97
|
return false unless repo_contents_path
|
@@ -109,13 +112,14 @@ module Dependabot
|
|
109
112
|
# Tests need to track how many times we call the tooling updater to ensure we don't recurse needlessly
|
110
113
|
# Ideally we should find a way to not run this code in prod
|
111
114
|
# (or a better way to track calls made to NativeHelpers)
|
112
|
-
@update_tooling_calls ||= {}
|
115
|
+
@update_tooling_calls ||= T.let({}, T.nilable(T::Hash[String, Integer]))
|
113
116
|
key = proj_path + dependency.name
|
114
|
-
|
115
|
-
@update_tooling_calls[key]
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
@update_tooling_calls[key] =
|
118
|
+
if @update_tooling_calls[key]
|
119
|
+
T.must(@update_tooling_calls[key]) + 1
|
120
|
+
else
|
121
|
+
1
|
122
|
+
end
|
119
123
|
end
|
120
124
|
|
121
125
|
# Don't call this from outside tests, we're only checking that we aren't recursing needlessly
|
@@ -124,6 +128,7 @@ module Dependabot
|
|
124
128
|
@update_tooling_calls
|
125
129
|
end
|
126
130
|
|
131
|
+
sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[Dependabot::Dependency]) }
|
127
132
|
def project_dependencies(project_file)
|
128
133
|
# Collect all dependencies from the project file and associated packages.config
|
129
134
|
dependencies = project_file_parser.dependency_set(project_file: project_file).dependencies
|
@@ -134,38 +139,54 @@ module Dependabot
|
|
134
139
|
.dependency_set.dependencies
|
135
140
|
end
|
136
141
|
|
142
|
+
sig { params(project_file: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile)) }
|
137
143
|
def find_packages_config(project_file)
|
138
144
|
project_file_name = File.basename(project_file.name)
|
139
145
|
packages_config_path = project_file.name.gsub(project_file_name, "packages.config")
|
140
146
|
packages_config_files.find { |f| f.name == packages_config_path }
|
141
147
|
end
|
142
148
|
|
149
|
+
sig { returns(Dependabot::Nuget::FileParser::ProjectFileParser) }
|
143
150
|
def project_file_parser
|
144
151
|
@project_file_parser ||=
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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)
|
149
159
|
)
|
150
160
|
end
|
151
161
|
|
162
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
152
163
|
def global_json_dependencies
|
153
164
|
return [] unless global_json
|
154
165
|
|
155
166
|
@global_json_dependencies ||=
|
156
|
-
|
167
|
+
T.let(
|
168
|
+
FileParser::GlobalJsonParser.new(global_json: T.must(global_json)).dependency_set.dependencies,
|
169
|
+
T.nilable(T::Array[Dependabot::Dependency])
|
170
|
+
)
|
157
171
|
end
|
158
172
|
|
173
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
159
174
|
def dotnet_tools_json_dependencies
|
160
175
|
return [] unless dotnet_tools_json
|
161
176
|
|
162
177
|
@dotnet_tools_json_dependencies ||=
|
163
|
-
|
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
|
+
)
|
164
183
|
end
|
165
184
|
|
185
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
186
|
+
sig { params(dependency_file: Dependabot::DependencyFile, updated_content: String).returns(String) }
|
166
187
|
def normalize_content(dependency_file, updated_content)
|
167
188
|
# Fix up line endings
|
168
|
-
if dependency_file.content
|
189
|
+
if dependency_file.content&.include?("\r\n") && updated_content.match?(/(?<!\r)\n/)
|
169
190
|
# The original content contain windows style newlines.
|
170
191
|
# Ensure the updated content also uses windows style newlines.
|
171
192
|
updated_content = updated_content.gsub(/(?<!\r)\n/, "\r\n")
|
@@ -178,19 +199,21 @@ module Dependabot
|
|
178
199
|
end
|
179
200
|
|
180
201
|
# Fix up BOM
|
181
|
-
if !dependency_file.content
|
202
|
+
if !dependency_file.content&.start_with?("\uFEFF") && updated_content.start_with?("\uFEFF")
|
182
203
|
updated_content = updated_content.delete_prefix("\uFEFF")
|
183
204
|
puts "Removing BOM from [#{dependency_file.name}]."
|
184
|
-
elsif dependency_file.content
|
205
|
+
elsif dependency_file.content&.start_with?("\uFEFF") && !updated_content.start_with?("\uFEFF")
|
185
206
|
updated_content = "\uFEFF" + updated_content
|
186
207
|
puts "Adding BOM to [#{dependency_file.name}]."
|
187
208
|
end
|
188
209
|
|
189
210
|
updated_content
|
190
211
|
end
|
212
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
191
213
|
|
214
|
+
sig { params(dependency_file: Dependabot::DependencyFile).returns(String) }
|
192
215
|
def dependency_file_path(dependency_file)
|
193
|
-
if dependency_file.directory.start_with?(repo_contents_path)
|
216
|
+
if dependency_file.directory.start_with?(T.must(repo_contents_path))
|
194
217
|
File.join(dependency_file.directory, dependency_file.name)
|
195
218
|
else
|
196
219
|
file_directory = dependency_file.directory
|
@@ -199,29 +222,42 @@ module Dependabot
|
|
199
222
|
end
|
200
223
|
end
|
201
224
|
|
225
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
202
226
|
def project_files
|
203
227
|
dependency_files.select { |df| df.name.match?(/\.([a-z]{2})?proj$/) }
|
204
228
|
end
|
205
229
|
|
230
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
206
231
|
def packages_config_files
|
207
232
|
dependency_files.select do |f|
|
208
233
|
T.must(T.must(f.name.split("/").last).casecmp("packages.config")).zero?
|
209
234
|
end
|
210
235
|
end
|
211
236
|
|
237
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
212
238
|
def global_json
|
213
239
|
dependency_files.find { |f| T.must(f.name.casecmp("global.json")).zero? }
|
214
240
|
end
|
215
241
|
|
242
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
216
243
|
def dotnet_tools_json
|
217
244
|
dependency_files.find { |f| T.must(f.name.casecmp(".config/dotnet-tools.json")).zero? }
|
218
245
|
end
|
219
246
|
|
247
|
+
sig { override.void }
|
220
248
|
def check_required_files
|
221
249
|
return if project_files.any? || packages_config_files.any?
|
222
250
|
|
223
251
|
raise "No project file or packages.config!"
|
224
252
|
end
|
253
|
+
|
254
|
+
sig { params(original_content: T.nilable(String), updated_content: String).returns(T::Boolean) }
|
255
|
+
def only_deleted_lines?(original_content, updated_content)
|
256
|
+
original_lines = original_content&.lines || []
|
257
|
+
updated_lines = updated_content.lines
|
258
|
+
|
259
|
+
original_lines.count > updated_lines.count
|
260
|
+
end
|
225
261
|
end
|
226
262
|
end
|
227
263
|
end
|
@@ -1,9 +1,14 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
module Nuget
|
6
8
|
module HttpResponseHelpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(string: String).returns(String) }
|
7
12
|
def self.remove_wrapping_zero_width_chars(string)
|
8
13
|
string.force_encoding("UTF-8").encode
|
9
14
|
.gsub(/\A[\u200B-\u200D\uFEFF]/, "")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "nokogiri"
|
@@ -12,13 +12,28 @@ 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
|
|
31
|
+
sig { override.returns(T.nilable(Dependabot::Source)) }
|
17
32
|
def look_up_source
|
18
33
|
return Source.from_url(dependency_source_url) if dependency_source_url
|
19
34
|
|
20
35
|
if dependency_nuspec_file
|
21
|
-
src_repo = look_up_source_in_nuspec(dependency_nuspec_file)
|
36
|
+
src_repo = look_up_source_in_nuspec(T.must(dependency_nuspec_file))
|
22
37
|
return src_repo if src_repo
|
23
38
|
end
|
24
39
|
|
@@ -33,6 +48,7 @@ module Dependabot
|
|
33
48
|
nil
|
34
49
|
end
|
35
50
|
|
51
|
+
sig { returns(T.nilable(Dependabot::Source)) }
|
36
52
|
def src_repo_from_project
|
37
53
|
source = dependency.requirements.find { |r| r.fetch(:source) }&.fetch(:source)
|
38
54
|
return unless source
|
@@ -60,6 +76,7 @@ module Dependabot
|
|
60
76
|
# Ignored, this is expected for some registries that don't handle these request.
|
61
77
|
end
|
62
78
|
|
79
|
+
sig { params(body: String).returns(T.nilable(String)) }
|
63
80
|
def extract_search_url(body)
|
64
81
|
JSON.parse(body)
|
65
82
|
.fetch("resources", [])
|
@@ -67,6 +84,7 @@ module Dependabot
|
|
67
84
|
&.fetch("@id")
|
68
85
|
end
|
69
86
|
|
87
|
+
sig { params(body: String).returns(T.nilable(Dependabot::Source)) }
|
70
88
|
def extract_source_repo(body)
|
71
89
|
JSON.parse(body).fetch("data", []).each do |search_result|
|
72
90
|
next unless search_result["id"].casecmp(dependency.name).zero?
|
@@ -84,6 +102,7 @@ module Dependabot
|
|
84
102
|
nil
|
85
103
|
end
|
86
104
|
|
105
|
+
sig { params(nuspec: Nokogiri::XML::Document).returns(T.nilable(Dependabot::Source)) }
|
87
106
|
def look_up_source_in_nuspec(nuspec)
|
88
107
|
potential_source_urls = [
|
89
108
|
nuspec.at_css("package > metadata > repository")
|
@@ -99,6 +118,7 @@ module Dependabot
|
|
99
118
|
Source.from_url(source_url)
|
100
119
|
end
|
101
120
|
|
121
|
+
sig { params(nuspec: Nokogiri::XML::Document).returns(T.nilable(String)) }
|
102
122
|
def source_from_anywhere_in_nuspec(nuspec)
|
103
123
|
github_urls = []
|
104
124
|
nuspec.to_s.force_encoding(Encoding::UTF_8)
|
@@ -112,19 +132,21 @@ module Dependabot
|
|
112
132
|
end
|
113
133
|
end
|
114
134
|
|
135
|
+
sig { returns(T.nilable(Nokogiri::XML::Document)) }
|
115
136
|
def dependency_nuspec_file
|
116
137
|
return @dependency_nuspec_file unless @dependency_nuspec_file.nil?
|
117
138
|
|
118
139
|
return if dependency_nuspec_url.nil?
|
119
140
|
|
120
141
|
response = Dependabot::RegistryClient.get(
|
121
|
-
url: dependency_nuspec_url,
|
142
|
+
url: T.must(dependency_nuspec_url),
|
122
143
|
headers: auth_header
|
123
144
|
)
|
124
145
|
|
125
146
|
@dependency_nuspec_file = Nokogiri::XML(response.body)
|
126
147
|
end
|
127
148
|
|
149
|
+
sig { returns(T.nilable(String)) }
|
128
150
|
def dependency_nuspec_url
|
129
151
|
source = dependency.requirements
|
130
152
|
.find { |r| r.fetch(:source) }&.fetch(:source)
|
@@ -132,6 +154,7 @@ module Dependabot
|
|
132
154
|
source.fetch(:nuspec_url) if source&.key?(:nuspec_url)
|
133
155
|
end
|
134
156
|
|
157
|
+
sig { returns(T.nilable(String)) }
|
135
158
|
def dependency_source_url
|
136
159
|
source = dependency.requirements
|
137
160
|
.find { |r| r.fetch(:source) }&.fetch(:source)
|
@@ -143,6 +166,7 @@ module Dependabot
|
|
143
166
|
end
|
144
167
|
|
145
168
|
# rubocop:disable Metrics/PerceivedComplexity
|
169
|
+
sig { returns(T::Hash[String, String]) }
|
146
170
|
def auth_header
|
147
171
|
source = dependency.requirements
|
148
172
|
.find { |r| r.fetch(:source) }&.fetch(:source)
|
@@ -21,11 +21,33 @@ module Dependabot
|
|
21
21
|
get_package_versions_v3(dependency_name, repository_details)
|
22
22
|
elsif repository_type == "v2"
|
23
23
|
get_package_versions_v2(dependency_name, repository_details)
|
24
|
+
elsif repository_type == "local"
|
25
|
+
get_package_versions_local(dependency_name, repository_details)
|
24
26
|
else
|
25
27
|
raise "Unknown repository type: #{repository_type}"
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
31
|
+
sig do
|
32
|
+
params(dependency_name: String, repository_details: T::Hash[Symbol, String])
|
33
|
+
.returns(T.nilable(T::Set[String]))
|
34
|
+
end
|
35
|
+
private_class_method def self.get_package_versions_local(dependency_name, repository_details)
|
36
|
+
url = repository_details.fetch(:base_url)
|
37
|
+
raise "Local repo #{url} doesn't exist or isn't a directory" unless File.exist?(url) && File.directory?(url)
|
38
|
+
|
39
|
+
package_dir = File.join(url, dependency_name)
|
40
|
+
|
41
|
+
versions = Set.new
|
42
|
+
return versions unless File.exist?(package_dir) && File.directory?(package_dir)
|
43
|
+
|
44
|
+
Dir.each_child(package_dir) do |child|
|
45
|
+
versions.add(child) if File.directory?(File.join(package_dir, child))
|
46
|
+
end
|
47
|
+
|
48
|
+
versions
|
49
|
+
end
|
50
|
+
|
29
51
|
sig do
|
30
52
|
params(dependency_name: String, repository_details: T::Hash[Symbol, String])
|
31
53
|
.returns(T.nilable(T::Set[String]))
|
@@ -133,6 +155,7 @@ module Dependabot
|
|
133
155
|
&.find { |d| d.fetch("id").casecmp(dependency_name.downcase).zero? }
|
134
156
|
&.fetch("versions")
|
135
157
|
&.map { |d| d.fetch("version") }
|
158
|
+
&.to_set
|
136
159
|
end
|
137
160
|
|
138
161
|
sig do
|
@@ -60,6 +60,7 @@ module Dependabot
|
|
60
60
|
convert_wildcard_req(req_string)
|
61
61
|
end
|
62
62
|
|
63
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
63
64
|
def convert_dotnet_range_to_ruby_range(req_string)
|
64
65
|
lower_b, upper_b = req_string.split(",").map(&:strip).map do |bound|
|
65
66
|
next convert_range_wildcard_req(bound) if bound.include?("*")
|
@@ -75,7 +76,8 @@ module Dependabot
|
|
75
76
|
end
|
76
77
|
|
77
78
|
upper_b =
|
78
|
-
if
|
79
|
+
if !upper_b then nil
|
80
|
+
elsif [")", "]"].include?(upper_b) then nil
|
79
81
|
elsif upper_b.end_with?(")") then "< #{upper_b.sub(/\s*\)/, '')}"
|
80
82
|
else
|
81
83
|
"<= #{upper_b.sub(/\s*\]/, '').strip}"
|
@@ -83,6 +85,7 @@ module Dependabot
|
|
83
85
|
|
84
86
|
[lower_b, upper_b].compact
|
85
87
|
end
|
88
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
86
89
|
|
87
90
|
def convert_range_wildcard_req(req_string)
|
88
91
|
range_end = req_string[-1]
|
@@ -66,23 +66,34 @@ module Dependabot
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def fetch_package_tfms(dependency_version)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
69
|
+
cache = CacheManager.cache("compatibility_checker_tfms_cache")
|
70
|
+
key = "#{dependency.name}::#{dependency_version}"
|
71
|
+
|
72
|
+
cache[key] ||= begin
|
73
|
+
nupkg_buffer = NupkgFetcher.fetch_nupkg_buffer(dependency_urls, dependency.name, dependency_version)
|
74
|
+
return [] unless nupkg_buffer
|
75
|
+
|
76
|
+
# Parse tfms from the folders beneath the lib folder
|
77
|
+
folder_name = "lib/"
|
78
|
+
tfms = Set.new
|
79
|
+
Zip::File.open_buffer(nupkg_buffer) do |zip|
|
80
|
+
lib_file_entries = zip.select { |entry| entry.name.start_with?(folder_name) }
|
81
|
+
# If there is no lib folder in this package, assume it is a development dependency
|
82
|
+
return nil if lib_file_entries.empty?
|
83
|
+
|
84
|
+
lib_file_entries.each do |entry|
|
85
|
+
_, tfm = entry.name.split("/").first(2)
|
86
|
+
|
87
|
+
# some zip compressors create empty directory entries (in this case `lib/`) which can cause the string
|
88
|
+
# split to return `nil`, so we have to explicitly guard against that
|
89
|
+
tfms << tfm if tfm
|
90
|
+
end
|
83
91
|
end
|
92
|
+
|
93
|
+
tfms.to_a
|
84
94
|
end
|
85
|
-
|
95
|
+
|
96
|
+
cache[key]
|
86
97
|
end
|
87
98
|
end
|
88
99
|
end
|
@@ -111,25 +111,19 @@ module Dependabot
|
|
111
111
|
current_redirects = 0
|
112
112
|
|
113
113
|
loop do
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
package_data.write(chunk)
|
119
|
-
end
|
120
|
-
|
121
|
-
response = connection.request(
|
122
|
-
method: :get,
|
114
|
+
# Directly download the stream without any additional settings _except_ for `omit_default_port: true` which
|
115
|
+
# is necessary to not break the URL signing that some NuGet feeds use.
|
116
|
+
response = Excon.get(
|
117
|
+
current_url,
|
123
118
|
headers: auth_header,
|
124
|
-
|
119
|
+
omit_default_port: true
|
125
120
|
)
|
126
121
|
|
127
122
|
# redirect the HTTP response as appropriate based on documentation here:
|
128
123
|
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
|
129
124
|
case response.status
|
130
125
|
when 200
|
131
|
-
|
132
|
-
return package_data
|
126
|
+
return response.body
|
133
127
|
when 301, 302, 303, 307, 308
|
134
128
|
current_redirects += 1
|
135
129
|
return nil if current_redirects > max_redirects
|
@@ -142,10 +136,14 @@ module Dependabot
|
|
142
136
|
end
|
143
137
|
|
144
138
|
def self.fetch_url(url, repository_details)
|
139
|
+
fetch_url_with_auth(url, repository_details.fetch(:auth_header))
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.fetch_url_with_auth(url, auth_header)
|
145
143
|
cache = CacheManager.cache("nupkg_fetcher_cache")
|
146
144
|
cache[url] ||= Dependabot::RegistryClient.get(
|
147
145
|
url: url,
|
148
|
-
headers:
|
146
|
+
headers: auth_header
|
149
147
|
)
|
150
148
|
|
151
149
|
cache[url]
|