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
@@ -0,0 +1,127 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "excon"
|
5
|
+
require "nokogiri"
|
6
|
+
|
7
|
+
require "dependabot/nuget/version"
|
8
|
+
require "dependabot/nuget/requirement"
|
9
|
+
require "dependabot/nuget/native_helpers"
|
10
|
+
require "dependabot/nuget/update_checker"
|
11
|
+
require "dependabot/shared_helpers"
|
12
|
+
|
13
|
+
module Dependabot
|
14
|
+
module Nuget
|
15
|
+
class UpdateChecker
|
16
|
+
class TfmFinder
|
17
|
+
require "dependabot/nuget/file_parser/packages_config_parser"
|
18
|
+
require "dependabot/nuget/file_parser/project_file_parser"
|
19
|
+
|
20
|
+
def initialize(dependency_files:, credentials:)
|
21
|
+
@dependency_files = dependency_files
|
22
|
+
@credentials = credentials
|
23
|
+
end
|
24
|
+
|
25
|
+
def frameworks(dependency)
|
26
|
+
tfms = Set.new
|
27
|
+
tfms += project_file_tfms(dependency)
|
28
|
+
tfms += project_import_file_tfms
|
29
|
+
tfms.to_a
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :dependency_files, :credentials
|
35
|
+
|
36
|
+
def project_file_tfms(dependency)
|
37
|
+
project_files_with_dependency(dependency).flat_map do |file|
|
38
|
+
project_file_parser.target_frameworks(project_file: file)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def project_files_with_dependency(dependency)
|
43
|
+
project_files.select do |file|
|
44
|
+
packages_config_contains_dependency?(file, dependency) ||
|
45
|
+
project_file_contains_dependency?(file, dependency)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def packages_config_contains_dependency?(file, dependency)
|
50
|
+
config_file = find_packages_config_file(file)
|
51
|
+
return false unless config_file
|
52
|
+
|
53
|
+
config_parser = FileParser::PackagesConfigParser.new(packages_config: config_file)
|
54
|
+
config_parser.dependency_set.dependencies.any? do |d|
|
55
|
+
d.name.casecmp(dependency.name).zero?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def project_file_contains_dependency?(file, dependency)
|
60
|
+
project_file_parser.dependency_set(project_file: file).dependencies.any? do |d|
|
61
|
+
d.name.casecmp(dependency.name).zero?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_packages_config_file(file)
|
66
|
+
return file if file.name.end_with?("packages.config")
|
67
|
+
|
68
|
+
filename = File.basename(file.name)
|
69
|
+
search_path = file.name.sub(filename, "packages.config")
|
70
|
+
|
71
|
+
dependency_files.find { |f| f.name.casecmp(search_path).zero? }
|
72
|
+
end
|
73
|
+
|
74
|
+
def project_import_file_tfms
|
75
|
+
@project_import_file_tfms ||= project_import_files.flat_map do |file|
|
76
|
+
project_file_parser.target_frameworks(project_file: file)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def project_file_parser
|
81
|
+
@project_file_parser ||=
|
82
|
+
FileParser::ProjectFileParser.new(
|
83
|
+
dependency_files: dependency_files,
|
84
|
+
credentials: credentials
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def project_files
|
89
|
+
projfile = /\.[a-z]{2}proj$/
|
90
|
+
packageprops = /[Dd]irectory.[Pp]ackages.props/
|
91
|
+
|
92
|
+
dependency_files.select do |df|
|
93
|
+
df.name.match?(projfile) ||
|
94
|
+
df.name.match?(packageprops)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def packages_config_files
|
99
|
+
dependency_files.select do |f|
|
100
|
+
f.name.split("/").last.casecmp("packages.config").zero?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def project_import_files
|
105
|
+
dependency_files -
|
106
|
+
project_files -
|
107
|
+
packages_config_files -
|
108
|
+
nuget_configs -
|
109
|
+
[global_json] -
|
110
|
+
[dotnet_tools_json]
|
111
|
+
end
|
112
|
+
|
113
|
+
def nuget_configs
|
114
|
+
dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def global_json
|
118
|
+
dependency_files.find { |f| f.name.casecmp("global.json").zero? }
|
119
|
+
end
|
120
|
+
|
121
|
+
def dotnet_tools_json
|
122
|
+
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json").zero? }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -1,19 +1,16 @@
|
|
1
1
|
# typed: false
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "excon"
|
5
|
-
require "nokogiri"
|
6
|
-
|
7
4
|
require "dependabot/nuget/version"
|
8
5
|
require "dependabot/nuget/requirement"
|
9
6
|
require "dependabot/update_checkers/version_filters"
|
10
7
|
require "dependabot/nuget/update_checker"
|
11
|
-
require "dependabot/shared_helpers"
|
12
8
|
|
13
9
|
module Dependabot
|
14
10
|
module Nuget
|
15
11
|
class UpdateChecker
|
16
12
|
class VersionFinder
|
13
|
+
require_relative "compatibility_checker"
|
17
14
|
require_relative "repository_finder"
|
18
15
|
|
19
16
|
NUGET_RANGE_REGEX = /[\(\[].*,.*[\)\]]/
|
@@ -35,7 +32,8 @@ module Dependabot
|
|
35
32
|
possible_versions = versions
|
36
33
|
possible_versions = filter_prereleases(possible_versions)
|
37
34
|
possible_versions = filter_ignored_versions(possible_versions)
|
38
|
-
|
35
|
+
|
36
|
+
find_highest_compatible_version(possible_versions)
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -50,7 +48,7 @@ module Dependabot
|
|
50
48
|
possible_versions = filter_ignored_versions(possible_versions)
|
51
49
|
possible_versions = filter_lower_versions(possible_versions)
|
52
50
|
|
53
|
-
possible_versions
|
51
|
+
find_lowest_compatible_version(possible_versions)
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
@@ -63,6 +61,49 @@ module Dependabot
|
|
63
61
|
|
64
62
|
private
|
65
63
|
|
64
|
+
def find_highest_compatible_version(possible_versions)
|
65
|
+
# sorted versions descending
|
66
|
+
sorted_versions = possible_versions.sort_by { |v| v.fetch(:version) }.reverse
|
67
|
+
find_compatible_version(sorted_versions)
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_lowest_compatible_version(possible_versions)
|
71
|
+
# sorted versions ascending
|
72
|
+
sorted_versions = possible_versions.sort_by { |v| v.fetch(:version) }
|
73
|
+
find_compatible_version(sorted_versions)
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_compatible_version(sorted_versions)
|
77
|
+
# By checking the first version separately, we can avoid additional network requests
|
78
|
+
first_version = sorted_versions.first
|
79
|
+
return unless first_version
|
80
|
+
# If the current package version is incompatible, then we don't enforce compatibility.
|
81
|
+
# It could appear incompatible because they are ignoring NU1701 or the package is poorly authored.
|
82
|
+
return first_version unless version_compatible?(dependency.version)
|
83
|
+
return first_version if version_compatible?(first_version.fetch(:version))
|
84
|
+
|
85
|
+
sorted_versions.bsearch { |v| version_compatible?(v.fetch(:version)) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def version_compatible?(version)
|
89
|
+
str_version_compatible?(version.to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
def str_version_compatible?(version)
|
93
|
+
compatibility_checker.compatible?(version)
|
94
|
+
end
|
95
|
+
|
96
|
+
def compatibility_checker
|
97
|
+
@compatibility_checker ||= CompatibilityChecker.new(
|
98
|
+
dependency_urls: dependency_urls,
|
99
|
+
dependency: dependency,
|
100
|
+
tfm_finder: TfmFinder.new(
|
101
|
+
dependency_files: dependency_files,
|
102
|
+
credentials: credentials
|
103
|
+
)
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
66
107
|
def filter_prereleases(possible_versions)
|
67
108
|
possible_versions.reject do |d|
|
68
109
|
version = d.fetch(:version)
|
@@ -11,16 +11,19 @@ module Dependabot
|
|
11
11
|
require_relative "update_checker/version_finder"
|
12
12
|
require_relative "update_checker/property_updater"
|
13
13
|
require_relative "update_checker/requirements_updater"
|
14
|
+
require_relative "update_checker/dependency_finder"
|
14
15
|
|
15
16
|
def latest_version
|
17
|
+
# No need to find latest version for transitive dependencies unless they have a vulnerability.
|
18
|
+
return dependency.version if !dependency.top_level? && !vulnerable?
|
19
|
+
|
16
20
|
@latest_version = latest_version_details&.fetch(:version)
|
17
21
|
end
|
18
22
|
|
19
23
|
def latest_resolvable_version
|
20
|
-
#
|
21
|
-
return nil
|
22
|
-
|
23
|
-
latest_version
|
24
|
+
# We always want a full unlock since any package update could update peer dependencies as well.
|
25
|
+
# To force a full unlock instead of an own unlock, we return nil.
|
26
|
+
nil
|
24
27
|
end
|
25
28
|
|
26
29
|
def lowest_security_fix_version
|
@@ -41,13 +44,16 @@ module Dependabot
|
|
41
44
|
def updated_requirements
|
42
45
|
RequirementsUpdater.new(
|
43
46
|
requirements: dependency.requirements,
|
44
|
-
latest_version:
|
45
|
-
source_details:
|
47
|
+
latest_version: preferred_resolvable_version_details.fetch(:version)&.to_s,
|
48
|
+
source_details: preferred_resolvable_version_details
|
46
49
|
&.slice(:nuspec_url, :repo_url, :source_url)
|
47
50
|
).updated_requirements
|
48
51
|
end
|
49
52
|
|
50
53
|
def up_to_date?
|
54
|
+
# No need to update transitive dependencies unless they have a vulnerability.
|
55
|
+
return true if !dependency.top_level? && !vulnerable?
|
56
|
+
|
51
57
|
# If any requirements have an uninterpolated property in them then
|
52
58
|
# that property couldn't be found, and we assume that the dependency
|
53
59
|
# is up-to-date
|
@@ -68,14 +74,42 @@ module Dependabot
|
|
68
74
|
|
69
75
|
private
|
70
76
|
|
77
|
+
def preferred_resolvable_version_details
|
78
|
+
# If this dependency is vulnerable, prefer trying to update to the
|
79
|
+
# lowest_resolvable_security_fix_version. Otherwise update all the way
|
80
|
+
# to the latest_resolvable_version.
|
81
|
+
return lowest_security_fix_version_details if vulnerable?
|
82
|
+
|
83
|
+
latest_version_details
|
84
|
+
end
|
85
|
+
|
71
86
|
def latest_version_resolvable_with_full_unlock?
|
72
|
-
|
87
|
+
# We always want a full unlock since any package update could update peer dependencies as well.
|
88
|
+
return true unless version_comes_from_multi_dependency_property?
|
73
89
|
|
74
90
|
property_updater.update_possible?
|
75
91
|
end
|
76
92
|
|
77
93
|
def updated_dependencies_after_full_unlock
|
78
|
-
property_updater.updated_dependencies
|
94
|
+
return property_updater.updated_dependencies if version_comes_from_multi_dependency_property?
|
95
|
+
|
96
|
+
puts "Finding updated dependencies for #{dependency.name}."
|
97
|
+
|
98
|
+
updated_dependency = Dependency.new(
|
99
|
+
name: dependency.name,
|
100
|
+
version: latest_version&.to_s,
|
101
|
+
requirements: updated_requirements,
|
102
|
+
previous_version: dependency.version,
|
103
|
+
previous_requirements: dependency.requirements,
|
104
|
+
package_manager: dependency.package_manager
|
105
|
+
)
|
106
|
+
updated_dependencies = [updated_dependency]
|
107
|
+
updated_dependencies += DependencyFinder.new(
|
108
|
+
dependency: updated_dependency,
|
109
|
+
dependency_files: dependency_files,
|
110
|
+
credentials: credentials
|
111
|
+
).updated_peer_dependencies
|
112
|
+
updated_dependencies
|
79
113
|
end
|
80
114
|
|
81
115
|
def preferred_version_details
|
data/lib/dependabot/nuget.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-nuget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.238.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,34 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.238.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.238.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubyzip
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.3.2
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3.0'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.3.2
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: debug
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,20 +114,34 @@ dependencies:
|
|
94
114
|
- - "~>"
|
95
115
|
- !ruby/object:Gem::Version
|
96
116
|
version: '1.3'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: rspec-sorbet
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 1.9.2
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 1.9.2
|
97
131
|
- !ruby/object:Gem::Dependency
|
98
132
|
name: rubocop
|
99
133
|
requirement: !ruby/object:Gem::Requirement
|
100
134
|
requirements:
|
101
135
|
- - "~>"
|
102
136
|
- !ruby/object:Gem::Version
|
103
|
-
version: 1.
|
137
|
+
version: 1.57.2
|
104
138
|
type: :development
|
105
139
|
prerelease: false
|
106
140
|
version_requirements: !ruby/object:Gem::Requirement
|
107
141
|
requirements:
|
108
142
|
- - "~>"
|
109
143
|
- !ruby/object:Gem::Version
|
110
|
-
version: 1.
|
144
|
+
version: 1.57.2
|
111
145
|
- !ruby/object:Gem::Dependency
|
112
146
|
name: rubocop-performance
|
113
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,6 +235,7 @@ extensions: []
|
|
201
235
|
extra_rdoc_files: []
|
202
236
|
files:
|
203
237
|
- lib/dependabot/nuget.rb
|
238
|
+
- lib/dependabot/nuget/cache_manager.rb
|
204
239
|
- lib/dependabot/nuget/file_fetcher.rb
|
205
240
|
- lib/dependabot/nuget/file_fetcher/import_paths_finder.rb
|
206
241
|
- lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb
|
@@ -211,15 +246,20 @@ files:
|
|
211
246
|
- lib/dependabot/nuget/file_parser/project_file_parser.rb
|
212
247
|
- lib/dependabot/nuget/file_parser/property_value_finder.rb
|
213
248
|
- lib/dependabot/nuget/file_updater.rb
|
214
|
-
- lib/dependabot/nuget/file_updater/packages_config_declaration_finder.rb
|
215
|
-
- lib/dependabot/nuget/file_updater/project_file_declaration_finder.rb
|
216
249
|
- lib/dependabot/nuget/file_updater/property_value_updater.rb
|
217
250
|
- lib/dependabot/nuget/metadata_finder.rb
|
251
|
+
- lib/dependabot/nuget/native_helpers.rb
|
218
252
|
- lib/dependabot/nuget/requirement.rb
|
219
253
|
- lib/dependabot/nuget/update_checker.rb
|
254
|
+
- lib/dependabot/nuget/update_checker/compatibility_checker.rb
|
255
|
+
- lib/dependabot/nuget/update_checker/dependency_finder.rb
|
256
|
+
- lib/dependabot/nuget/update_checker/nupkg_fetcher.rb
|
257
|
+
- lib/dependabot/nuget/update_checker/nuspec_fetcher.rb
|
220
258
|
- lib/dependabot/nuget/update_checker/property_updater.rb
|
221
259
|
- lib/dependabot/nuget/update_checker/repository_finder.rb
|
222
260
|
- lib/dependabot/nuget/update_checker/requirements_updater.rb
|
261
|
+
- lib/dependabot/nuget/update_checker/tfm_comparer.rb
|
262
|
+
- lib/dependabot/nuget/update_checker/tfm_finder.rb
|
223
263
|
- lib/dependabot/nuget/update_checker/version_finder.rb
|
224
264
|
- lib/dependabot/nuget/version.rb
|
225
265
|
homepage: https://github.com/dependabot/dependabot-core
|
@@ -227,7 +267,7 @@ licenses:
|
|
227
267
|
- Nonstandard
|
228
268
|
metadata:
|
229
269
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
230
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
270
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.238.0
|
231
271
|
post_install_message:
|
232
272
|
rdoc_options: []
|
233
273
|
require_paths:
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "nokogiri"
|
5
|
-
require "dependabot/nuget/file_updater"
|
6
|
-
|
7
|
-
module Dependabot
|
8
|
-
module Nuget
|
9
|
-
class FileUpdater
|
10
|
-
class PackagesConfigDeclarationFinder
|
11
|
-
DECLARATION_REGEX =
|
12
|
-
%r{<package\s[^>]*?/>|
|
13
|
-
<package\s[^>]*?[^/]>.*?</package>}mx
|
14
|
-
|
15
|
-
attr_reader :dependency_name, :declaring_requirement,
|
16
|
-
:packages_config
|
17
|
-
|
18
|
-
def initialize(dependency_name:, packages_config:,
|
19
|
-
declaring_requirement:)
|
20
|
-
@dependency_name = dependency_name
|
21
|
-
@packages_config = packages_config
|
22
|
-
@declaring_requirement = declaring_requirement
|
23
|
-
|
24
|
-
if declaring_requirement[:file].split("/").last
|
25
|
-
.casecmp("packages.config").zero?
|
26
|
-
return
|
27
|
-
end
|
28
|
-
|
29
|
-
raise "Requirement not from packages.config!"
|
30
|
-
end
|
31
|
-
|
32
|
-
def declaration_strings
|
33
|
-
@declaration_strings ||= fetch_declaration_strings
|
34
|
-
end
|
35
|
-
|
36
|
-
def declaration_nodes
|
37
|
-
declaration_strings.map do |declaration_string|
|
38
|
-
Nokogiri::XML(declaration_string)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
45
|
-
def fetch_declaration_strings
|
46
|
-
deep_find_declarations(packages_config.content).select do |nd|
|
47
|
-
node = Nokogiri::XML(nd)
|
48
|
-
node.remove_namespaces!
|
49
|
-
node = node.at_xpath("/package")
|
50
|
-
|
51
|
-
node_name = node.attribute("id")&.value&.strip ||
|
52
|
-
node.at_xpath("./id")&.content&.strip
|
53
|
-
next false unless node_name&.downcase == dependency_name&.downcase
|
54
|
-
|
55
|
-
node_requirement = node.attribute("version")&.value&.strip ||
|
56
|
-
node.at_xpath("./version")&.content&.strip
|
57
|
-
node_requirement == declaring_requirement.fetch(:requirement)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
61
|
-
|
62
|
-
def deep_find_declarations(string)
|
63
|
-
string.scan(DECLARATION_REGEX).flat_map do |matching_node|
|
64
|
-
[matching_node, *deep_find_declarations(matching_node[0..-2])]
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,183 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "nokogiri"
|
5
|
-
require "dependabot/nuget/file_updater"
|
6
|
-
|
7
|
-
module Dependabot
|
8
|
-
module Nuget
|
9
|
-
class FileUpdater
|
10
|
-
class ProjectFileDeclarationFinder
|
11
|
-
DECLARATION_REGEX =
|
12
|
-
%r{
|
13
|
-
<PackageReference [^>]*?/>|
|
14
|
-
<PackageReference [^>]*?[^/]>.*?</PackageReference>|
|
15
|
-
<GlobalPackageReference [^>]*?/>|
|
16
|
-
<GlobalPackageReference [^>]*?[^/]>.*?</GlobalPackageReference>|
|
17
|
-
<PackageVersion [^>]*?/>|
|
18
|
-
<PackageVersion [^>]*?[^/]>.*?</PackageVersion>|
|
19
|
-
<Dependency [^>]*?/>|
|
20
|
-
<Dependency [^>]*?[^/]>.*?</Dependency>|
|
21
|
-
<DevelopmentDependency [^>]*?/>|
|
22
|
-
<DevelopmentDependency [^>]*?[^/]>.*?</DevelopmentDependency>
|
23
|
-
}mx
|
24
|
-
SDK_IMPORT_REGEX =
|
25
|
-
/ <Import [^>]*?Sdk="[^"]*?"[^>]*?Version="[^"]*?"[^>]*?>
|
26
|
-
| <Import [^>]*?Version="[^"]*?"[^>]*?Sdk="[^"]*?"[^>]*?>
|
27
|
-
/mx
|
28
|
-
SDK_PROJECT_REGEX =
|
29
|
-
/ <Project [^>]*?Sdk="[^"]*?"[^>]*?>
|
30
|
-
/mx
|
31
|
-
SDK_SDK_REGEX =
|
32
|
-
/ <Sdk [^>]*?Name="[^"]*?"[^>]*?Version="[^"]*?"[^>]*?>
|
33
|
-
| <Sdk [^>]*?Version="[^"]*?"[^>]*?Name="[^"]*?"[^>]*?>
|
34
|
-
/mx
|
35
|
-
|
36
|
-
attr_reader :dependency_name, :declaring_requirement,
|
37
|
-
:dependency_files
|
38
|
-
|
39
|
-
def initialize(dependency_name:, dependency_files:,
|
40
|
-
declaring_requirement:)
|
41
|
-
@dependency_name = dependency_name
|
42
|
-
@dependency_files = dependency_files
|
43
|
-
@declaring_requirement = declaring_requirement
|
44
|
-
end
|
45
|
-
|
46
|
-
def declaration_strings
|
47
|
-
@declaration_strings ||= fetch_declaration_strings
|
48
|
-
@declaration_strings += fetch_sdk_strings
|
49
|
-
end
|
50
|
-
|
51
|
-
def declaration_nodes
|
52
|
-
declaration_strings.map do |declaration_string|
|
53
|
-
Nokogiri::XML(declaration_string)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def get_element_from_node(node)
|
60
|
-
node.at_xpath("/PackageReference") ||
|
61
|
-
node.at_xpath("/GlobalPackageReference") ||
|
62
|
-
node.at_xpath("/PackageVersion") ||
|
63
|
-
node.at_xpath("/Dependency") ||
|
64
|
-
node.at_xpath("/DevelopmentDependency")
|
65
|
-
end
|
66
|
-
|
67
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
68
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
69
|
-
def fetch_declaration_strings
|
70
|
-
deep_find_declarations(declaring_file.content).select do |nd|
|
71
|
-
node = Nokogiri::XML(nd)
|
72
|
-
node.remove_namespaces!
|
73
|
-
node = get_element_from_node(node)
|
74
|
-
|
75
|
-
node_name = node.attribute("Include")&.value&.strip ||
|
76
|
-
node.at_xpath("./Include")&.content&.strip ||
|
77
|
-
node.attribute("Update")&.value&.strip ||
|
78
|
-
node.at_xpath("./Update")&.content&.strip
|
79
|
-
next false unless node_name&.downcase == dependency_name&.downcase
|
80
|
-
|
81
|
-
node_requirement = get_node_version_value(node)
|
82
|
-
node_requirement == declaring_requirement.fetch(:requirement)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
86
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
87
|
-
|
88
|
-
def fetch_sdk_strings
|
89
|
-
sdk_project_strings + sdk_sdk_strings + sdk_import_strings
|
90
|
-
end
|
91
|
-
|
92
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
93
|
-
def get_node_version_value(node)
|
94
|
-
attribute = "Version"
|
95
|
-
node.attribute(attribute)&.value&.strip ||
|
96
|
-
node.at_xpath("./#{attribute}")&.content&.strip ||
|
97
|
-
node.attribute(attribute.downcase)&.value&.strip ||
|
98
|
-
node.at_xpath("./#{attribute.downcase}")&.content&.strip
|
99
|
-
end
|
100
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
101
|
-
|
102
|
-
def deep_find_declarations(string)
|
103
|
-
string.scan(DECLARATION_REGEX).flat_map do |matching_node|
|
104
|
-
[matching_node, *deep_find_declarations(matching_node[0..-2])]
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def declaring_file
|
109
|
-
filename = declaring_requirement.fetch(:file)
|
110
|
-
declaring_file = dependency_files.find { |f| f.name == filename }
|
111
|
-
return declaring_file if declaring_file
|
112
|
-
|
113
|
-
raise "No file found with name #{filename}!"
|
114
|
-
end
|
115
|
-
|
116
|
-
def sdk_import_strings
|
117
|
-
sdk_strings(SDK_IMPORT_REGEX, "Import", "Sdk", "Version")
|
118
|
-
end
|
119
|
-
|
120
|
-
def parse_element(string, name)
|
121
|
-
xml = string
|
122
|
-
xml += "</#{name}>" unless string.end_with?("/>")
|
123
|
-
node = Nokogiri::XML(xml)
|
124
|
-
node.remove_namespaces!
|
125
|
-
node.at_xpath("/#{name}")
|
126
|
-
end
|
127
|
-
|
128
|
-
def get_attribute_value_nocase(element, name)
|
129
|
-
value = element.attribute(name)&.value ||
|
130
|
-
element.attribute(name.downcase)&.value ||
|
131
|
-
element.attribute(name.upcase)&.value
|
132
|
-
value&.strip
|
133
|
-
end
|
134
|
-
|
135
|
-
def desired_sdk_reference?(sdk_reference, dep_name, dep_version)
|
136
|
-
parts = sdk_reference.split("/")
|
137
|
-
parts.length == 2 && parts[0]&.downcase == dep_name && parts[1] == dep_version
|
138
|
-
end
|
139
|
-
|
140
|
-
def sdk_project_strings
|
141
|
-
dep_name = dependency_name&.downcase
|
142
|
-
dep_version = declaring_requirement.fetch(:requirement)
|
143
|
-
strings = []
|
144
|
-
declaring_file.content.scan(SDK_PROJECT_REGEX).each do |string|
|
145
|
-
element = parse_element(string, "Project")
|
146
|
-
next unless element
|
147
|
-
|
148
|
-
sdk_references = get_attribute_value_nocase(element, "Sdk")
|
149
|
-
next unless sdk_references&.include?("/")
|
150
|
-
|
151
|
-
sdk_references.split(";").each do |sdk_reference|
|
152
|
-
strings << sdk_reference if desired_sdk_reference?(sdk_reference, dep_name, dep_version)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
strings.uniq
|
156
|
-
end
|
157
|
-
|
158
|
-
def sdk_sdk_strings
|
159
|
-
sdk_strings(SDK_SDK_REGEX, "Sdk", "Name", "Version")
|
160
|
-
end
|
161
|
-
|
162
|
-
def sdk_strings(regex, element_name, name_attribute, version_attribute)
|
163
|
-
dep_name = dependency_name&.downcase
|
164
|
-
dep_version = declaring_requirement.fetch(:requirement)
|
165
|
-
strings = []
|
166
|
-
declaring_file.content.scan(regex).each do |string|
|
167
|
-
element = parse_element(string, element_name)
|
168
|
-
next unless element
|
169
|
-
|
170
|
-
node_name = get_attribute_value_nocase(element, name_attribute)&.downcase
|
171
|
-
next unless node_name == dep_name
|
172
|
-
|
173
|
-
node_version = get_attribute_value_nocase(element, version_attribute)
|
174
|
-
next unless node_version == dep_version
|
175
|
-
|
176
|
-
strings << string
|
177
|
-
end
|
178
|
-
strings
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|