dependabot-nuget 0.237.0 → 0.239.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +61 -64
- 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/requirement.rb +5 -1
- 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 +119 -0
- data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +83 -0
- data/lib/dependabot/nuget/update_checker/property_updater.rb +30 -3
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +36 -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 +35 -9
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bf5bdfc4f365f5d04c30193e1b44e7a82ddb5816461148e4172cabc1a86bea3
|
4
|
+
data.tar.gz: 0ca7483d0fdc3d3a7dc2af7c8f475e7b02a121d4a8a5c58f495ff857d32c3dad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0bd5448386d99ac0fa6a27ea6f5ec137885cd8a208762602d69284c1cdea6bb9e521a885b9a50cb42eb45b2de9ec760def87940c15023a910f7f9455a612fc9
|
7
|
+
data.tar.gz: 4096cfc58f861ec2f4f5d7a4022fd06f001d0f4421130bf0708e877a93ce2e12e722259d60b29f8303375d2e3cdc5f595e396cb4273d5c1e03a34e23a3a154ec
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/file_fetchers"
|
5
|
+
require "dependabot/file_fetchers/base"
|
6
|
+
require "set"
|
7
|
+
|
8
|
+
module Dependabot
|
9
|
+
module Nuget
|
10
|
+
class CacheManager
|
11
|
+
def self.caching_disabled?
|
12
|
+
ENV["DEPENDABOT_NUGET_CACHE_DISABLED"] == "true"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.cache(name)
|
16
|
+
@cache ||= {}
|
17
|
+
@cache[name] ||= {}
|
18
|
+
@cache[name]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -39,6 +39,21 @@ module Dependabot
|
|
39
39
|
nodes.compact
|
40
40
|
end
|
41
41
|
|
42
|
+
def project_file_paths
|
43
|
+
doc = Nokogiri::XML(project_file.content)
|
44
|
+
doc.remove_namespaces!
|
45
|
+
nodes = doc.xpath("/Project/ItemGroup/ProjectFile").map do |node|
|
46
|
+
attribute = node.attribute("Include")
|
47
|
+
next unless attribute
|
48
|
+
|
49
|
+
path = attribute.value.strip.tr("\\", "/")
|
50
|
+
path = File.join(current_dir, path) unless current_dir.nil?
|
51
|
+
Pathname.new(path).cleanpath.to_path
|
52
|
+
end
|
53
|
+
|
54
|
+
nodes.compact
|
55
|
+
end
|
56
|
+
|
42
57
|
private
|
43
58
|
|
44
59
|
attr_reader :project_file
|
@@ -15,16 +15,19 @@ module Dependabot
|
|
15
15
|
require_relative "file_fetcher/import_paths_finder"
|
16
16
|
require_relative "file_fetcher/sln_project_paths_finder"
|
17
17
|
|
18
|
+
BUILD_FILE_NAMES = /^Directory\.Build\.(props|targets)$/i # Directory.Build.props, Directory.Build.targets
|
19
|
+
|
18
20
|
def self.required_files_in?(filenames)
|
19
21
|
return true if filenames.any? { |f| f.match?(/^packages\.config$/i) }
|
20
22
|
return true if filenames.any? { |f| f.end_with?(".sln") }
|
21
23
|
return true if filenames.any? { |f| f.match?("^src$") }
|
24
|
+
return true if filenames.any? { |f| f.end_with?(".proj") }
|
22
25
|
|
23
26
|
filenames.any? { |name| name.match?(%r{^[^/]*\.[a-z]{2}proj$}) }
|
24
27
|
end
|
25
28
|
|
26
29
|
def self.required_files_message
|
27
|
-
"Repo must contain a .(cs|vb|fs)proj file or a packages.config."
|
30
|
+
"Repo must contain a .proj file, .(cs|vb|fs)proj file, or a packages.config."
|
28
31
|
end
|
29
32
|
|
30
33
|
sig { override.returns(T::Array[DependencyFile]) }
|
@@ -40,7 +43,10 @@ module Dependabot
|
|
40
43
|
fetched_files << dotnet_tools_json if dotnet_tools_json
|
41
44
|
fetched_files << packages_props if packages_props
|
42
45
|
|
43
|
-
|
46
|
+
# dedup files based on their absolute path
|
47
|
+
fetched_files = fetched_files.uniq do |fetched_file|
|
48
|
+
Pathname.new(fetched_file.directory).join(fetched_file.name).cleanpath.to_path
|
49
|
+
end
|
44
50
|
|
45
51
|
if project_files.none? && packages_config_files.none?
|
46
52
|
raise @missing_sln_project_file_errors.first if @missing_sln_project_file_errors&.any?
|
@@ -60,12 +66,12 @@ module Dependabot
|
|
60
66
|
@project_files ||=
|
61
67
|
begin
|
62
68
|
project_files = []
|
63
|
-
project_files
|
64
|
-
project_files
|
65
|
-
project_files
|
66
|
-
project_files << directory_packages_props_file if directory_packages_props_file
|
67
|
-
|
69
|
+
project_files += csproj_file
|
70
|
+
project_files += vbproj_file
|
71
|
+
project_files += fsproj_file
|
68
72
|
project_files += sln_project_files
|
73
|
+
project_files += proj_files
|
74
|
+
project_files += project_files.filter_map { |f| directory_packages_props_file_from_project_file(f) }
|
69
75
|
project_files
|
70
76
|
end
|
71
77
|
rescue Octokit::NotFound, Gitlab::Error::NotFound
|
@@ -113,49 +119,29 @@ module Dependabot
|
|
113
119
|
end
|
114
120
|
|
115
121
|
def fetch_directory_build_files
|
116
|
-
|
122
|
+
attempted_dirs = []
|
117
123
|
directory_build_files = []
|
118
|
-
|
119
|
-
|
120
|
-
#
|
121
|
-
project_files.map { |f|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
"Directory.build.targets"
|
134
|
-
]
|
135
|
-
|
136
|
-
possible_paths.each do |path|
|
137
|
-
break if attempted_paths.include?(path)
|
138
|
-
|
139
|
-
attempted_paths << path
|
140
|
-
file = fetch_file_if_present(path)
|
141
|
-
directory_build_files << file if file
|
124
|
+
directory_path = Pathname.new(directory)
|
125
|
+
|
126
|
+
# find all build files (Directory.Build.props/.targets) relative to the given project file
|
127
|
+
project_files.map { |f| Pathname.new(f.directory).join(f.name).dirname }.uniq.each do |dir|
|
128
|
+
# Simulate MSBuild walking up the directory structure looking for a file
|
129
|
+
dir.descend.each do |possible_dir|
|
130
|
+
break if attempted_dirs.include?(possible_dir)
|
131
|
+
|
132
|
+
attempted_dirs << possible_dir
|
133
|
+
relative_possible_dir = Pathname.new(possible_dir).relative_path_from(directory_path).to_s
|
134
|
+
build_files = repo_contents(dir: relative_possible_dir).select { |f| f.name.match?(BUILD_FILE_NAMES) }
|
135
|
+
directory_build_files += build_files.map do |file|
|
136
|
+
possible_file = File.join(relative_possible_dir, file.name).delete_prefix("/")
|
137
|
+
fetch_file_from_host(possible_file)
|
138
|
+
end
|
142
139
|
end
|
143
140
|
end
|
144
141
|
|
145
142
|
directory_build_files
|
146
143
|
end
|
147
144
|
|
148
|
-
def possible_build_file_paths(base)
|
149
|
-
[
|
150
|
-
Pathname.new(base + "/Directory.Build.props").cleanpath.to_path,
|
151
|
-
Pathname.new(base + "/Directory.build.props").cleanpath.to_path,
|
152
|
-
Pathname.new(base + "/Directory.Packages.props").cleanpath.to_path,
|
153
|
-
Pathname.new(base + "/Directory.packages.props").cleanpath.to_path,
|
154
|
-
Pathname.new(base + "/Directory.Build.targets").cleanpath.to_path,
|
155
|
-
Pathname.new(base + "/Directory.build.targets").cleanpath.to_path
|
156
|
-
]
|
157
|
-
end
|
158
|
-
|
159
145
|
def sln_project_files
|
160
146
|
return [] unless sln_files
|
161
147
|
|
@@ -189,35 +175,45 @@ module Dependabot
|
|
189
175
|
end
|
190
176
|
|
191
177
|
def csproj_file
|
192
|
-
@csproj_file ||=
|
193
|
-
begin
|
194
|
-
file = repo_contents.find { |f| f.name.end_with?(".csproj") }
|
195
|
-
fetch_file_from_host(file.name) if file
|
196
|
-
end
|
178
|
+
@csproj_file ||= find_and_fetch_with_suffix(".csproj")
|
197
179
|
end
|
198
180
|
|
199
181
|
def vbproj_file
|
200
|
-
@vbproj_file ||=
|
201
|
-
begin
|
202
|
-
file = repo_contents.find { |f| f.name.end_with?(".vbproj") }
|
203
|
-
fetch_file_from_host(file.name) if file
|
204
|
-
end
|
182
|
+
@vbproj_file ||= find_and_fetch_with_suffix(".vbproj")
|
205
183
|
end
|
206
184
|
|
207
185
|
def fsproj_file
|
208
|
-
@fsproj_file ||=
|
209
|
-
begin
|
210
|
-
file = repo_contents.find { |f| f.name.end_with?(".fsproj") }
|
211
|
-
fetch_file_from_host(file.name) if file
|
212
|
-
end
|
186
|
+
@fsproj_file ||= find_and_fetch_with_suffix(".fsproj")
|
213
187
|
end
|
214
188
|
|
215
|
-
def
|
216
|
-
@
|
217
|
-
|
218
|
-
|
219
|
-
|
189
|
+
def proj_files
|
190
|
+
@proj_files ||= find_and_fetch_with_suffix(".proj")
|
191
|
+
end
|
192
|
+
|
193
|
+
def directory_packages_props_file_from_project_file(project_file)
|
194
|
+
# walk up the tree from each project file stopping at the first `Directory.Packages.props` file found
|
195
|
+
# https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management#central-package-management-rules
|
196
|
+
|
197
|
+
found_directory_packages_props_file = nil
|
198
|
+
directory_path = Pathname.new(directory)
|
199
|
+
full_project_dir = Pathname.new(project_file.directory).join(project_file.name).dirname
|
200
|
+
full_project_dir.ascend.each do |base|
|
201
|
+
break if found_directory_packages_props_file
|
202
|
+
|
203
|
+
candidate_file_path = Pathname.new(base).join("Directory.Packages.props").cleanpath.to_path
|
204
|
+
candidate_directory = Pathname.new(File.dirname(candidate_file_path))
|
205
|
+
relative_candidate_directory = candidate_directory.relative_path_from(directory_path)
|
206
|
+
candidate_file = repo_contents(dir: relative_candidate_directory).find do |f|
|
207
|
+
f.name.casecmp?("Directory.Packages.props")
|
220
208
|
end
|
209
|
+
found_directory_packages_props_file = fetch_file_from_host(candidate_file.name) if candidate_file
|
210
|
+
end
|
211
|
+
|
212
|
+
found_directory_packages_props_file
|
213
|
+
end
|
214
|
+
|
215
|
+
def find_and_fetch_with_suffix(suffix)
|
216
|
+
repo_contents.select { |f| f.name.end_with?(suffix) }.map { |f| fetch_file_from_host(f.name) }
|
221
217
|
end
|
222
218
|
|
223
219
|
def nuget_config_files
|
@@ -286,7 +282,8 @@ module Dependabot
|
|
286
282
|
def fetch_imported_property_files(file:, previously_fetched_files:)
|
287
283
|
paths =
|
288
284
|
ImportPathsFinder.new(project_file: file).import_paths +
|
289
|
-
ImportPathsFinder.new(project_file: file).project_reference_paths
|
285
|
+
ImportPathsFinder.new(project_file: file).project_reference_paths +
|
286
|
+
ImportPathsFinder.new(project_file: file).project_file_paths
|
290
287
|
|
291
288
|
paths.flat_map do |path|
|
292
289
|
next if previously_fetched_files.map(&:name).include?(path)
|
@@ -51,7 +51,8 @@ module Dependabot
|
|
51
51
|
attr_reader :dotnet_tools_json
|
52
52
|
|
53
53
|
def parsed_dotnet_tools_json
|
54
|
-
|
54
|
+
# Remove BOM if present as JSON should be UTF-8
|
55
|
+
@parsed_dotnet_tools_json ||= JSON.parse(dotnet_tools_json.content.delete_prefix("\uFEFF"))
|
55
56
|
rescue JSON::ParserError
|
56
57
|
raise Dependabot::DependencyFileNotParseable, dotnet_tools_json.path
|
57
58
|
end
|
@@ -48,7 +48,8 @@ module Dependabot
|
|
48
48
|
attr_reader :global_json
|
49
49
|
|
50
50
|
def parsed_global_json
|
51
|
-
|
51
|
+
# Remove BOM if present as JSON should be UTF-8
|
52
|
+
@parsed_global_json ||= JSON.parse(global_json.content.delete_prefix("\uFEFF"))
|
52
53
|
rescue JSON::ParserError
|
53
54
|
raise Dependabot::DependencyFileNotParseable, global_json.path
|
54
55
|
end
|
@@ -5,6 +5,7 @@ require "nokogiri"
|
|
5
5
|
|
6
6
|
require "dependabot/dependency"
|
7
7
|
require "dependabot/nuget/file_parser"
|
8
|
+
require "dependabot/nuget/cache_manager"
|
8
9
|
|
9
10
|
# For details on packages.config files see:
|
10
11
|
# https://docs.microsoft.com/en-us/nuget/reference/packages-config
|
@@ -16,11 +17,32 @@ module Dependabot
|
|
16
17
|
|
17
18
|
DEPENDENCY_SELECTOR = "packages > package"
|
18
19
|
|
20
|
+
def self.dependency_set_cache
|
21
|
+
CacheManager.cache("packages_config_dependency_set")
|
22
|
+
end
|
23
|
+
|
19
24
|
def initialize(packages_config:)
|
20
25
|
@packages_config = packages_config
|
21
26
|
end
|
22
27
|
|
23
28
|
def dependency_set
|
29
|
+
return parse_dependencies if CacheManager.caching_disabled?
|
30
|
+
|
31
|
+
key = "#{packages_config.name.downcase}::#{packages_config.content.hash}"
|
32
|
+
cache = PackagesConfigParser.dependency_set_cache
|
33
|
+
|
34
|
+
cache[key] ||= parse_dependencies
|
35
|
+
|
36
|
+
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
37
|
+
dependency_set += cache[key]
|
38
|
+
dependency_set
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :packages_config
|
44
|
+
|
45
|
+
def parse_dependencies
|
24
46
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
25
47
|
|
26
48
|
doc = Nokogiri::XML(packages_config.content)
|
@@ -43,10 +65,6 @@ module Dependabot
|
|
43
65
|
dependency_set
|
44
66
|
end
|
45
67
|
|
46
|
-
private
|
47
|
-
|
48
|
-
attr_reader :packages_config
|
49
|
-
|
50
68
|
def dependency_name(dependency_node)
|
51
69
|
dependency_node.attribute("id")&.value&.strip ||
|
52
70
|
dependency_node.at_xpath("./id")&.content&.strip
|