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
@@ -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.239.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-28 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.239.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.239.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
|
@@ -114,14 +134,14 @@ dependencies:
|
|
114
134
|
requirements:
|
115
135
|
- - "~>"
|
116
136
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.
|
137
|
+
version: 1.58.0
|
118
138
|
type: :development
|
119
139
|
prerelease: false
|
120
140
|
version_requirements: !ruby/object:Gem::Requirement
|
121
141
|
requirements:
|
122
142
|
- - "~>"
|
123
143
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.
|
144
|
+
version: 1.58.0
|
125
145
|
- !ruby/object:Gem::Dependency
|
126
146
|
name: rubocop-performance
|
127
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -215,6 +235,7 @@ extensions: []
|
|
215
235
|
extra_rdoc_files: []
|
216
236
|
files:
|
217
237
|
- lib/dependabot/nuget.rb
|
238
|
+
- lib/dependabot/nuget/cache_manager.rb
|
218
239
|
- lib/dependabot/nuget/file_fetcher.rb
|
219
240
|
- lib/dependabot/nuget/file_fetcher/import_paths_finder.rb
|
220
241
|
- lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb
|
@@ -225,15 +246,20 @@ files:
|
|
225
246
|
- lib/dependabot/nuget/file_parser/project_file_parser.rb
|
226
247
|
- lib/dependabot/nuget/file_parser/property_value_finder.rb
|
227
248
|
- lib/dependabot/nuget/file_updater.rb
|
228
|
-
- lib/dependabot/nuget/file_updater/packages_config_declaration_finder.rb
|
229
|
-
- lib/dependabot/nuget/file_updater/project_file_declaration_finder.rb
|
230
249
|
- lib/dependabot/nuget/file_updater/property_value_updater.rb
|
231
250
|
- lib/dependabot/nuget/metadata_finder.rb
|
251
|
+
- lib/dependabot/nuget/native_helpers.rb
|
232
252
|
- lib/dependabot/nuget/requirement.rb
|
233
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
|
234
258
|
- lib/dependabot/nuget/update_checker/property_updater.rb
|
235
259
|
- lib/dependabot/nuget/update_checker/repository_finder.rb
|
236
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
|
237
263
|
- lib/dependabot/nuget/update_checker/version_finder.rb
|
238
264
|
- lib/dependabot/nuget/version.rb
|
239
265
|
homepage: https://github.com/dependabot/dependabot-core
|
@@ -241,7 +267,7 @@ licenses:
|
|
241
267
|
- Nonstandard
|
242
268
|
metadata:
|
243
269
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
244
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
270
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.239.0
|
245
271
|
post_install_message:
|
246
272
|
rdoc_options: []
|
247
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
|