dependabot-nuget 0.236.0 → 0.238.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 +72 -63
- 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/metadata_finder.rb +4 -1
- 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 +49 -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: 4d913e6e79cd35b4a00551789e2d9fbea3a71476b76a12f64adfc3427cf419b1
|
4
|
+
data.tar.gz: 103b3ed625f8a89133b2c53b6a49941826cf3663de6897e9baad943a78fa37c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c820263ed7825b8f7270693513d8ebab3ee73169318ca5113c75cad689887b5b42c1731fd6496e12033ba7a3ff0d3a59752b7bc512e64ae93baf4a520d485a83
|
7
|
+
data.tar.gz: 7ce94d621ef880f809124364816e19aa8cb1fe1975967d8eb907cf40b1573ff2c3fe4c8fad91631f5f545433813252736c3c1db17228366dea8950f0ab50ac9a
|
@@ -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
|
@@ -4,30 +4,38 @@
|
|
4
4
|
require "dependabot/file_fetchers"
|
5
5
|
require "dependabot/file_fetchers/base"
|
6
6
|
require "set"
|
7
|
+
require "sorbet-runtime"
|
7
8
|
|
8
9
|
module Dependabot
|
9
10
|
module Nuget
|
10
11
|
class FileFetcher < Dependabot::FileFetchers::Base
|
12
|
+
extend T::Sig
|
13
|
+
extend T::Helpers
|
14
|
+
|
11
15
|
require_relative "file_fetcher/import_paths_finder"
|
12
16
|
require_relative "file_fetcher/sln_project_paths_finder"
|
13
17
|
|
18
|
+
BUILD_FILE_NAMES = /^Directory\.Build\.(props|targets)$/i # Directory.Build.props, Directory.Build.targets
|
19
|
+
|
14
20
|
def self.required_files_in?(filenames)
|
15
21
|
return true if filenames.any? { |f| f.match?(/^packages\.config$/i) }
|
16
22
|
return true if filenames.any? { |f| f.end_with?(".sln") }
|
17
23
|
return true if filenames.any? { |f| f.match?("^src$") }
|
24
|
+
return true if filenames.any? { |f| f.end_with?(".proj") }
|
18
25
|
|
19
26
|
filenames.any? { |name| name.match?(%r{^[^/]*\.[a-z]{2}proj$}) }
|
20
27
|
end
|
21
28
|
|
22
29
|
def self.required_files_message
|
23
|
-
"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."
|
24
31
|
end
|
25
32
|
|
26
|
-
|
27
|
-
|
33
|
+
# rubocop:disable Metrics/AbcSize
|
34
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
28
35
|
def fetch_files
|
29
36
|
fetched_files = []
|
30
37
|
fetched_files += project_files
|
38
|
+
fetched_files += project_files.filter_map { |f| directory_packages_props_file_from_project_file(f) }
|
31
39
|
fetched_files += directory_build_files
|
32
40
|
fetched_files += imported_property_files
|
33
41
|
|
@@ -37,7 +45,10 @@ module Dependabot
|
|
37
45
|
fetched_files << dotnet_tools_json if dotnet_tools_json
|
38
46
|
fetched_files << packages_props if packages_props
|
39
47
|
|
40
|
-
|
48
|
+
# dedup files based on their absolute path
|
49
|
+
fetched_files = fetched_files.uniq do |fetched_file|
|
50
|
+
Pathname.new(File.join(fetched_file.directory, fetched_file.name)).cleanpath.to_path
|
51
|
+
end
|
41
52
|
|
42
53
|
if project_files.none? && packages_config_files.none?
|
43
54
|
raise @missing_sln_project_file_errors.first if @missing_sln_project_file_errors&.any?
|
@@ -50,15 +61,17 @@ module Dependabot
|
|
50
61
|
|
51
62
|
fetched_files
|
52
63
|
end
|
64
|
+
# rubocop:enable Metrics/AbcSize
|
65
|
+
|
66
|
+
private
|
53
67
|
|
54
68
|
def project_files
|
55
69
|
@project_files ||=
|
56
70
|
begin
|
57
71
|
project_files = []
|
58
|
-
project_files
|
59
|
-
project_files
|
60
|
-
project_files
|
61
|
-
project_files << directory_packages_props_file if directory_packages_props_file
|
72
|
+
project_files += csproj_file
|
73
|
+
project_files += vbproj_file
|
74
|
+
project_files += fsproj_file
|
62
75
|
|
63
76
|
project_files += sln_project_files
|
64
77
|
project_files
|
@@ -107,49 +120,37 @@ module Dependabot
|
|
107
120
|
@directory_build_files ||= fetch_directory_build_files
|
108
121
|
end
|
109
122
|
|
123
|
+
# rubocop:disable Metrics/AbcSize
|
110
124
|
def fetch_directory_build_files
|
111
|
-
|
125
|
+
attempted_dirs = []
|
112
126
|
directory_build_files = []
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
project_files.map { |f| File.dirname(f.name) }.uniq.
|
117
|
-
|
118
|
-
|
119
|
-
|
127
|
+
directory_path = Pathname.new(directory)
|
128
|
+
|
129
|
+
# find all build files (Directory.Build.props/.targets) relative to the given project file
|
130
|
+
project_files.map { |f| File.dirname(File.join(f.directory, f.name)) }.uniq.each do |dir|
|
131
|
+
# Simulate MSBuild walking up the directory structure looking for a file
|
132
|
+
possible_dirs = dir.split("/").map.with_index do |_, i|
|
133
|
+
candidate_dir = dir.split("/").first(i + 1).join("/")
|
134
|
+
candidate_dir = "/#{candidate_dir}" unless candidate_dir.start_with?("/")
|
135
|
+
candidate_dir
|
120
136
|
end.reverse
|
121
137
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
break if attempted_paths.include?(path)
|
133
|
-
|
134
|
-
attempted_paths << path
|
135
|
-
file = fetch_file_if_present(path)
|
136
|
-
directory_build_files << file if file
|
138
|
+
possible_dirs.each do |possible_dir|
|
139
|
+
break if attempted_dirs.include?(possible_dir)
|
140
|
+
|
141
|
+
attempted_dirs << possible_dir
|
142
|
+
relative_possible_dir = Pathname.new(possible_dir).relative_path_from(directory_path).to_s
|
143
|
+
build_files = repo_contents(dir: relative_possible_dir).select { |f| f.name.match?(BUILD_FILE_NAMES) }
|
144
|
+
directory_build_files += build_files.map do |file|
|
145
|
+
possible_file = File.join(relative_possible_dir, file.name).delete_prefix("/")
|
146
|
+
fetch_file_from_host(possible_file)
|
147
|
+
end
|
137
148
|
end
|
138
149
|
end
|
139
150
|
|
140
151
|
directory_build_files
|
141
152
|
end
|
142
|
-
|
143
|
-
def possible_build_file_paths(base)
|
144
|
-
[
|
145
|
-
Pathname.new(base + "/Directory.Build.props").cleanpath.to_path,
|
146
|
-
Pathname.new(base + "/Directory.build.props").cleanpath.to_path,
|
147
|
-
Pathname.new(base + "/Directory.Packages.props").cleanpath.to_path,
|
148
|
-
Pathname.new(base + "/Directory.packages.props").cleanpath.to_path,
|
149
|
-
Pathname.new(base + "/Directory.Build.targets").cleanpath.to_path,
|
150
|
-
Pathname.new(base + "/Directory.build.targets").cleanpath.to_path
|
151
|
-
]
|
152
|
-
end
|
153
|
+
# rubocop:enable Metrics/AbcSize
|
153
154
|
|
154
155
|
def sln_project_files
|
155
156
|
return [] unless sln_files
|
@@ -184,35 +185,42 @@ module Dependabot
|
|
184
185
|
end
|
185
186
|
|
186
187
|
def csproj_file
|
187
|
-
@csproj_file ||=
|
188
|
-
begin
|
189
|
-
file = repo_contents.find { |f| f.name.end_with?(".csproj") }
|
190
|
-
fetch_file_from_host(file.name) if file
|
191
|
-
end
|
188
|
+
@csproj_file ||= find_and_fetch_with_suffix(".csproj")
|
192
189
|
end
|
193
190
|
|
194
191
|
def vbproj_file
|
195
|
-
@vbproj_file ||=
|
196
|
-
begin
|
197
|
-
file = repo_contents.find { |f| f.name.end_with?(".vbproj") }
|
198
|
-
fetch_file_from_host(file.name) if file
|
199
|
-
end
|
192
|
+
@vbproj_file ||= find_and_fetch_with_suffix(".vbproj")
|
200
193
|
end
|
201
194
|
|
202
195
|
def fsproj_file
|
203
|
-
@fsproj_file ||=
|
204
|
-
begin
|
205
|
-
file = repo_contents.find { |f| f.name.end_with?(".fsproj") }
|
206
|
-
fetch_file_from_host(file.name) if file
|
207
|
-
end
|
196
|
+
@fsproj_file ||= find_and_fetch_with_suffix(".fsproj")
|
208
197
|
end
|
209
198
|
|
210
|
-
def
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
199
|
+
def directory_packages_props_file_from_project_file(project_file)
|
200
|
+
# walk up the tree from each project file stopping at the first `Directory.Packages.props` file found
|
201
|
+
# https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management#central-package-management-rules
|
202
|
+
|
203
|
+
found_directory_packages_props_file = nil
|
204
|
+
directory_path = Pathname.new(directory)
|
205
|
+
full_project_dir = File.dirname(File.join(project_file.directory, project_file.name))
|
206
|
+
full_project_dir.split("/").each.with_index do |_, i|
|
207
|
+
break if found_directory_packages_props_file
|
208
|
+
|
209
|
+
base = full_project_dir.split("/").first(i + 1).join("/")
|
210
|
+
candidate_file_path = Pathname.new(base + "/Directory.Packages.props").cleanpath.to_path
|
211
|
+
candidate_directory = Pathname.new(File.dirname(candidate_file_path))
|
212
|
+
relative_candidate_directory = candidate_directory.relative_path_from(directory_path)
|
213
|
+
candidate_file = repo_contents(dir: relative_candidate_directory).find do |f|
|
214
|
+
f.name.casecmp?("Directory.Packages.props")
|
215
215
|
end
|
216
|
+
found_directory_packages_props_file = fetch_file_from_host(candidate_file.name) if candidate_file
|
217
|
+
end
|
218
|
+
|
219
|
+
found_directory_packages_props_file
|
220
|
+
end
|
221
|
+
|
222
|
+
def find_and_fetch_with_suffix(suffix)
|
223
|
+
repo_contents.select { |f| f.name.end_with?(suffix) }.map { |f| fetch_file_from_host(f.name) }
|
216
224
|
end
|
217
225
|
|
218
226
|
def nuget_config_files
|
@@ -281,7 +289,8 @@ module Dependabot
|
|
281
289
|
def fetch_imported_property_files(file:, previously_fetched_files:)
|
282
290
|
paths =
|
283
291
|
ImportPathsFinder.new(project_file: file).import_paths +
|
284
|
-
ImportPathsFinder.new(project_file: file).project_reference_paths
|
292
|
+
ImportPathsFinder.new(project_file: file).project_reference_paths +
|
293
|
+
ImportPathsFinder.new(project_file: file).project_file_paths
|
285
294
|
|
286
295
|
paths.flat_map do |path|
|
287
296
|
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
|