dependabot-nuget 0.237.0 → 0.239.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 +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
|