dependabot-nuget 0.262.0 → 0.264.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +37 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +3 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +169 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +79 -67
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.FrameworkCheck.cs +0 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +10 -11
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalysisResult.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +441 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +177 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +47 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyInfo.cs +12 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +36 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +128 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Requirement.cs +105 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/SecurityVulnerability.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/SecurityVulnerabilityExtensions.cs +36 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +179 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionResult.cs +54 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +5 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +0 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +0 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +0 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +0 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +0 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +6 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +0 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs +0 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/HashSetExtensions.cs +0 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +0 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/Logger.cs +0 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +7 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +0 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +0 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +0 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +90 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +304 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/CompatibilityCheckerTests.cs +145 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/ExpectedAnalysisResult.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs +69 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/SecurityVulnerabilityExtensionsTests.cs +78 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +193 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +1 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +102 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +8 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -7
- data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +63 -0
- data/lib/dependabot/nuget/analysis/dependency_analysis.rb +63 -0
- data/lib/dependabot/nuget/file_fetcher.rb +7 -6
- data/lib/dependabot/nuget/file_parser.rb +28 -21
- data/lib/dependabot/nuget/file_updater.rb +22 -25
- data/lib/dependabot/nuget/metadata_finder.rb +2 -160
- data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +102 -0
- data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +129 -0
- data/lib/dependabot/nuget/native_discovery/native_directory_packages_props_discovery.rb +44 -0
- data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +174 -0
- data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +63 -0
- data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +82 -0
- data/lib/dependabot/nuget/native_discovery/native_property_details.rb +43 -0
- data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +68 -0
- data/lib/dependabot/nuget/native_helpers.rb +59 -0
- data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +105 -0
- data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +200 -0
- data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +3 -2
- data/lib/dependabot/nuget/update_checker.rb +48 -1
- metadata +39 -5
@@ -246,12 +246,13 @@ public abstract class UpdateWorkerTestBase : TestBase
|
|
246
246
|
}
|
247
247
|
|
248
248
|
// ensure only the test feed is used
|
249
|
+
string relativeLocalFeedPath = Path.GetRelativePath(temporaryDirectory, localFeedPath);
|
249
250
|
await File.WriteAllTextAsync(Path.Join(temporaryDirectory, "NuGet.Config"), $"""
|
250
251
|
<?xml version="1.0" encoding="utf-8"?>
|
251
252
|
<configuration>
|
252
253
|
<packageSources>
|
253
254
|
<clear />
|
254
|
-
<add key="local-feed" value="{
|
255
|
+
<add key="local-feed" value="{relativeLocalFeedPath}" />
|
255
256
|
</packageSources>
|
256
257
|
</configuration>
|
257
258
|
"""
|
@@ -383,7 +383,7 @@ public class MSBuildHelperTests : TestBase
|
|
383
383
|
}
|
384
384
|
|
385
385
|
[Fact]
|
386
|
-
public async Task
|
386
|
+
public async Task LocalPackageSourcesAreHonored()
|
387
387
|
{
|
388
388
|
var nugetPackagesDirectory = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
|
389
389
|
var nugetHttpCacheDirectory = Environment.GetEnvironmentVariable("NUGET_HTTP_CACHE_PATH");
|
@@ -399,20 +399,21 @@ public class MSBuildHelperTests : TestBase
|
|
399
399
|
Environment.SetEnvironmentVariable("NUGET_HTTP_CACHE_PATH", tempNuGetHttpCacheDirectory);
|
400
400
|
|
401
401
|
// create two local package sources with different packages available in each
|
402
|
-
string localSource1 = Path.Combine(temp.DirectoryPath, "
|
402
|
+
string localSource1 = Path.Combine(temp.DirectoryPath, "local", "source1");
|
403
403
|
Directory.CreateDirectory(localSource1);
|
404
|
-
string localSource2 = Path.Combine(temp.DirectoryPath, "
|
404
|
+
string localSource2 = Path.Combine(temp.DirectoryPath, "local", "source2");
|
405
405
|
Directory.CreateDirectory(localSource2);
|
406
406
|
|
407
|
-
// `Package.A` will only live in `
|
408
|
-
// available in `
|
407
|
+
// `Package.A` will only live in `local\source1` and uses Windows-style directory separators and will have
|
408
|
+
// a dependency on `Package.B` which is only available in `local/source2` and uses Unix-style directory
|
409
|
+
// separators.
|
409
410
|
MockNuGetPackage.CreateSimplePackage("Package.A", "1.0.0", "net8.0", [(null, [("Package.B", "2.0.0")])]).WriteToDirectory(localSource1);
|
410
411
|
MockNuGetPackage.CreateSimplePackage("Package.B", "2.0.0", "net8.0").WriteToDirectory(localSource2);
|
411
412
|
await File.WriteAllTextAsync(Path.Join(temp.DirectoryPath, "NuGet.Config"), """
|
412
413
|
<configuration>
|
413
414
|
<packageSources>
|
414
|
-
<add key="localSource1" value="
|
415
|
-
<add key="localSource2" value="
|
415
|
+
<add key="localSource1" value="local\source1" />
|
416
|
+
<add key="localSource2" value="local/source2" />
|
416
417
|
</packageSources>
|
417
418
|
</configuration>
|
418
419
|
""");
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/dependency"
|
5
|
+
require "dependabot/nuget/analysis/dependency_analysis"
|
6
|
+
require "dependabot/nuget/native_discovery/native_discovery_json_reader"
|
7
|
+
require "json"
|
8
|
+
require "sorbet-runtime"
|
9
|
+
|
10
|
+
module Dependabot
|
11
|
+
module Nuget
|
12
|
+
class AnalysisJsonReader
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
sig { returns(String) }
|
16
|
+
def self.temp_directory
|
17
|
+
File.join(NativeDiscoveryJsonReader.temp_directory, "analysis")
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { params(dependency_name: String).returns(String) }
|
21
|
+
def self.analysis_file_path(dependency_name:)
|
22
|
+
File.join(temp_directory, "#{dependency_name}.json")
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { params(dependency_name: String).returns(T.nilable(DependencyFile)) }
|
26
|
+
def self.analysis_json(dependency_name:)
|
27
|
+
file_path = analysis_file_path(dependency_name: dependency_name)
|
28
|
+
return unless File.exist?(file_path)
|
29
|
+
|
30
|
+
DependencyFile.new(
|
31
|
+
name: Pathname.new(file_path).cleanpath.to_path,
|
32
|
+
directory: temp_directory,
|
33
|
+
type: "file",
|
34
|
+
content: File.read(file_path)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
sig { params(analysis_json: DependencyFile).void }
|
39
|
+
def initialize(analysis_json:)
|
40
|
+
@analysis_json = analysis_json
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { returns(DependencyAnalysis) }
|
44
|
+
def dependency_analysis
|
45
|
+
@dependency_analysis ||= T.let(begin
|
46
|
+
raise Dependabot::DependencyFileNotParseable, analysis_json.path unless analysis_json.content
|
47
|
+
|
48
|
+
Dependabot.logger.info("#{File.basename(analysis_json.path)} analysis content: #{analysis_json.content}")
|
49
|
+
|
50
|
+
parsed_json = T.let(JSON.parse(T.must(analysis_json.content)), T::Hash[String, T.untyped])
|
51
|
+
DependencyAnalysis.from_json(parsed_json)
|
52
|
+
end, T.nilable(DependencyAnalysis))
|
53
|
+
rescue JSON::ParserError
|
54
|
+
raise Dependabot::DependencyFileNotParseable, analysis_json.path
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
sig { returns(DependencyFile) }
|
60
|
+
attr_reader :analysis_json
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/nuget/version"
|
5
|
+
require "sorbet-runtime"
|
6
|
+
|
7
|
+
module Dependabot
|
8
|
+
module Nuget
|
9
|
+
class DependencyAnalysis
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(json: T::Hash[String, T.untyped]).returns(DependencyAnalysis) }
|
13
|
+
def self.from_json(json)
|
14
|
+
updated_version = T.let(json.fetch("UpdatedVersion"), String)
|
15
|
+
can_update = T.let(json.fetch("CanUpdate"), T::Boolean)
|
16
|
+
version_comes_from_multi_dependency_property = T.let(json.fetch("VersionComesFromMultiDependencyProperty"),
|
17
|
+
T::Boolean)
|
18
|
+
updated_dependencies = T.let(json.fetch("UpdatedDependencies"),
|
19
|
+
T::Array[T::Hash[String, T.untyped]]).map do |dep|
|
20
|
+
NativeDependencyDetails.from_json(dep)
|
21
|
+
end
|
22
|
+
|
23
|
+
DependencyAnalysis.new(
|
24
|
+
updated_version: updated_version,
|
25
|
+
can_update: can_update,
|
26
|
+
version_comes_from_multi_dependency_property: version_comes_from_multi_dependency_property,
|
27
|
+
updated_dependencies: updated_dependencies
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
sig do
|
32
|
+
params(updated_version: String,
|
33
|
+
can_update: T::Boolean,
|
34
|
+
version_comes_from_multi_dependency_property: T::Boolean,
|
35
|
+
updated_dependencies: T::Array[NativeDependencyDetails]).void
|
36
|
+
end
|
37
|
+
def initialize(updated_version:, can_update:, version_comes_from_multi_dependency_property:,
|
38
|
+
updated_dependencies:)
|
39
|
+
@updated_version = updated_version
|
40
|
+
@can_update = can_update
|
41
|
+
@version_comes_from_multi_dependency_property = version_comes_from_multi_dependency_property
|
42
|
+
@updated_dependencies = updated_dependencies
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { returns(String) }
|
46
|
+
attr_reader :updated_version
|
47
|
+
|
48
|
+
sig { returns(T::Boolean) }
|
49
|
+
attr_reader :can_update
|
50
|
+
|
51
|
+
sig { returns(T::Boolean) }
|
52
|
+
attr_reader :version_comes_from_multi_dependency_property
|
53
|
+
|
54
|
+
sig { returns(T::Array[NativeDependencyDetails]) }
|
55
|
+
attr_reader :updated_dependencies
|
56
|
+
|
57
|
+
sig { returns(Dependabot::Nuget::Version) }
|
58
|
+
def numeric_updated_version
|
59
|
+
@numeric_updated_version ||= T.let(Version.new(updated_version), T.nilable(Dependabot::Nuget::Version))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -34,12 +34,13 @@ module Dependabot
|
|
34
34
|
end
|
35
35
|
|
36
36
|
sig do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
override
|
38
|
+
.params(
|
39
|
+
source: Dependabot::Source,
|
40
|
+
credentials: T::Array[Credential],
|
41
|
+
repo_contents_path: T.nilable(String),
|
42
|
+
options: T::Hash[String, String]
|
43
|
+
).void
|
43
44
|
end
|
44
45
|
def initialize(source:, credentials:, repo_contents_path: nil, options: {})
|
45
46
|
super(source: source, credentials: credentials, repo_contents_path: repo_contents_path, options: options)
|
@@ -4,7 +4,7 @@
|
|
4
4
|
require "dependabot/dependency"
|
5
5
|
require "dependabot/file_parsers"
|
6
6
|
require "dependabot/file_parsers/base"
|
7
|
-
require "dependabot/nuget/
|
7
|
+
require "dependabot/nuget/native_discovery/native_discovery_json_reader"
|
8
8
|
require "dependabot/nuget/native_helpers"
|
9
9
|
require "sorbet-runtime"
|
10
10
|
|
@@ -18,39 +18,46 @@ module Dependabot
|
|
18
18
|
require "dependabot/file_parsers/base/dependency_set"
|
19
19
|
require_relative "cache_manager"
|
20
20
|
|
21
|
+
sig { returns(T::Hash[String, T::Array[Dependabot::Dependency]]) }
|
22
|
+
def self.file_dependency_cache
|
23
|
+
T.let(CacheManager.cache("file_parser.parse"), T::Hash[String, T::Array[Dependabot::Dependency]])
|
24
|
+
end
|
25
|
+
|
21
26
|
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
22
27
|
def parse
|
23
28
|
return [] unless repo_contents_path
|
24
29
|
|
25
|
-
|
26
|
-
|
27
|
-
key
|
28
|
-
cache[key] ||= begin
|
30
|
+
key = NativeDiscoveryJsonReader.create_cache_key(dependency_files)
|
31
|
+
workspace_path = source&.directory || "/"
|
32
|
+
self.class.file_dependency_cache[key] ||= begin
|
29
33
|
# run discovery for the repo
|
34
|
+
discovery_json_path = NativeDiscoveryJsonReader.create_discovery_file_path_from_dependency_files(
|
35
|
+
dependency_files
|
36
|
+
)
|
30
37
|
NativeHelpers.run_nuget_discover_tool(repo_root: T.must(repo_contents_path),
|
31
|
-
workspace_path:
|
32
|
-
output_path:
|
38
|
+
workspace_path: workspace_path,
|
39
|
+
output_path: discovery_json_path,
|
33
40
|
credentials: credentials)
|
34
|
-
discovered_dependencies.dependencies
|
35
|
-
end
|
36
41
|
|
37
|
-
|
38
|
-
|
42
|
+
discovery_json = NativeDiscoveryJsonReader.discovery_json_from_path(discovery_json_path)
|
43
|
+
return [] unless discovery_json
|
39
44
|
|
40
|
-
|
45
|
+
Dependabot.logger.info("Discovery JSON content: #{discovery_json.content}")
|
46
|
+
discovery_json_reader = NativeDiscoveryJsonReader.new(
|
47
|
+
discovery_json: discovery_json
|
48
|
+
)
|
41
49
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
Dependabot.logger.info("Discovery JSON content: #{discovery_json.content}")
|
50
|
+
# cache discovery results
|
51
|
+
NativeDiscoveryJsonReader.set_discovery_from_dependency_files(dependency_files: dependency_files,
|
52
|
+
discovery: discovery_json_reader)
|
53
|
+
discovery_json_reader.dependency_set.dependencies
|
54
|
+
end
|
48
55
|
|
49
|
-
|
50
|
-
discovery_json: discovery_json
|
51
|
-
).dependency_set
|
56
|
+
T.must(self.class.file_dependency_cache[key])
|
52
57
|
end
|
53
58
|
|
59
|
+
private
|
60
|
+
|
54
61
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
55
62
|
def proj_files
|
56
63
|
projfile = /\.proj$/
|
@@ -4,9 +4,9 @@
|
|
4
4
|
require "dependabot/dependency_file"
|
5
5
|
require "dependabot/file_updaters"
|
6
6
|
require "dependabot/file_updaters/base"
|
7
|
-
require "dependabot/nuget/
|
8
|
-
require "dependabot/nuget/
|
9
|
-
require "dependabot/nuget/
|
7
|
+
require "dependabot/nuget/native_discovery/native_dependency_details"
|
8
|
+
require "dependabot/nuget/native_discovery/native_discovery_json_reader"
|
9
|
+
require "dependabot/nuget/native_discovery/native_workspace_discovery"
|
10
10
|
require "dependabot/nuget/native_helpers"
|
11
11
|
require "dependabot/shared_helpers"
|
12
12
|
require "sorbet-runtime"
|
@@ -31,7 +31,7 @@ module Dependabot
|
|
31
31
|
|
32
32
|
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
33
33
|
def updated_dependency_files
|
34
|
-
base_dir =
|
34
|
+
base_dir = "/"
|
35
35
|
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
36
36
|
dependencies.each do |dependency|
|
37
37
|
try_update_projects(dependency) || try_update_json(dependency)
|
@@ -111,7 +111,7 @@ module Dependabot
|
|
111
111
|
# Ideally we should find a way to not run this code in prod
|
112
112
|
# (or a better way to track calls made to NativeHelpers)
|
113
113
|
@update_tooling_calls ||= T.let({}, T.nilable(T::Hash[String, Integer]))
|
114
|
-
key = proj_path
|
114
|
+
key = "#{proj_path.delete_prefix(T.must(repo_contents_path))}+#{dependency.name}"
|
115
115
|
@update_tooling_calls[key] =
|
116
116
|
if @update_tooling_calls[key]
|
117
117
|
T.must(@update_tooling_calls[key]) + 1
|
@@ -126,18 +126,10 @@ module Dependabot
|
|
126
126
|
@update_tooling_calls
|
127
127
|
end
|
128
128
|
|
129
|
-
sig { returns(T.nilable(
|
129
|
+
sig { returns(T.nilable(NativeWorkspaceDiscovery)) }
|
130
130
|
def workspace
|
131
|
-
|
132
|
-
|
133
|
-
if discovery_json
|
134
|
-
workspace = DiscoveryJsonReader.new(
|
135
|
-
discovery_json: discovery_json
|
136
|
-
).workspace_discovery
|
137
|
-
end
|
138
|
-
|
139
|
-
workspace
|
140
|
-
end, T.nilable(WorkspaceDiscovery))
|
131
|
+
discovery_json_reader = NativeDiscoveryJsonReader.get_discovery_from_dependency_files(dependency_files)
|
132
|
+
discovery_json_reader.workspace_discovery
|
141
133
|
end
|
142
134
|
|
143
135
|
sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
@@ -145,17 +137,20 @@ module Dependabot
|
|
145
137
|
workspace&.projects&.find { |p| p.file_path == project_file.name }&.referenced_project_paths || []
|
146
138
|
end
|
147
139
|
|
148
|
-
sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[
|
140
|
+
sig { params(project_file: Dependabot::DependencyFile).returns(T::Array[NativeDependencyDetails]) }
|
149
141
|
def project_dependencies(project_file)
|
150
|
-
workspace&.projects&.find
|
142
|
+
workspace&.projects&.find do |p|
|
143
|
+
full_project_file_path = File.join(project_file.directory, project_file.name)
|
144
|
+
p.file_path == full_project_file_path
|
145
|
+
end&.dependencies || []
|
151
146
|
end
|
152
147
|
|
153
|
-
sig { returns(T::Array[
|
148
|
+
sig { returns(T::Array[NativeDependencyDetails]) }
|
154
149
|
def global_json_dependencies
|
155
150
|
workspace&.global_json&.dependencies || []
|
156
151
|
end
|
157
152
|
|
158
|
-
sig { returns(T::Array[
|
153
|
+
sig { returns(T::Array[NativeDependencyDetails]) }
|
159
154
|
def dotnet_tools_json_dependencies
|
160
155
|
workspace&.dotnet_tools_json&.dependencies || []
|
161
156
|
end
|
@@ -164,13 +159,15 @@ module Dependabot
|
|
164
159
|
sig { params(dependency_file: Dependabot::DependencyFile, updated_content: String).returns(String) }
|
165
160
|
def normalize_content(dependency_file, updated_content)
|
166
161
|
# Fix up line endings
|
167
|
-
if dependency_file.content&.include?("\r\n")
|
162
|
+
if dependency_file.content&.include?("\r\n")
|
168
163
|
# The original content contain windows style newlines.
|
169
|
-
|
170
|
-
|
171
|
-
|
164
|
+
if updated_content.match?(/(?<!\r)\n/)
|
165
|
+
# Ensure the updated content also uses windows style newlines.
|
166
|
+
updated_content = updated_content.gsub(/(?<!\r)\n/, "\r\n")
|
167
|
+
puts "Fixing mismatched Windows line endings for [#{dependency_file.name}]."
|
168
|
+
end
|
172
169
|
elsif updated_content.include?("\r\n")
|
173
|
-
# The original content does not contain windows style newlines.
|
170
|
+
# The original content does not contain windows style newlines, but the updated content does.
|
174
171
|
# Ensure the updated content uses unix style newlines.
|
175
172
|
updated_content = updated_content.gsub("\r\n", "\n")
|
176
173
|
puts "Fixing mismatched Unix line endings for [#{dependency_file.name}]."
|
@@ -12,148 +12,16 @@ module Dependabot
|
|
12
12
|
class MetadataFinder < Dependabot::MetadataFinders::Base
|
13
13
|
extend T::Sig
|
14
14
|
|
15
|
-
sig do
|
16
|
-
override
|
17
|
-
.params(
|
18
|
-
dependency: Dependabot::Dependency,
|
19
|
-
credentials: T::Array[Dependabot::Credential]
|
20
|
-
)
|
21
|
-
.void
|
22
|
-
end
|
23
|
-
def initialize(dependency:, credentials:)
|
24
|
-
@dependency_nuspec_file = T.let(nil, T.nilable(Nokogiri::XML::Document))
|
25
|
-
|
26
|
-
super
|
27
|
-
end
|
28
|
-
|
29
15
|
private
|
30
16
|
|
31
17
|
sig { override.returns(T.nilable(Dependabot::Source)) }
|
32
18
|
def look_up_source
|
33
|
-
|
34
|
-
|
35
|
-
if dependency_nuspec_file
|
36
|
-
src_repo = look_up_source_in_nuspec(T.must(dependency_nuspec_file))
|
37
|
-
return src_repo if src_repo
|
38
|
-
end
|
39
|
-
|
40
|
-
# Fallback to getting source from the search result's projectUrl or licenseUrl.
|
41
|
-
# GitHub Packages doesn't support getting the `.nuspec`, switch to getting
|
42
|
-
# that instead once it is supported.
|
43
|
-
src_repo_from_project
|
44
|
-
rescue StandardError
|
45
|
-
# At this point in the process the PR is ready to be posted, we tried to gather commit
|
46
|
-
# and release notes, but have encountered an exception. So let's eat it since it's
|
47
|
-
# better to have a PR with no info than error out.
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
|
51
|
-
sig { returns(T.nilable(Dependabot::Source)) }
|
52
|
-
def src_repo_from_project
|
53
|
-
source = dependency.requirements.find { |r| r.fetch(:source) }&.fetch(:source)
|
54
|
-
return unless source
|
55
|
-
|
56
|
-
# Query the service index e.g. https://nuget.pkg.github.com/ORG/index.json
|
57
|
-
response = Dependabot::RegistryClient.get(
|
58
|
-
url: source.fetch(:url),
|
59
|
-
headers: { **auth_header, "Accept" => "application/json" }
|
60
|
-
)
|
61
|
-
return unless response.status == 200
|
19
|
+
source_url = dependency_source_url
|
20
|
+
return Source.from_url(source_url) if source_url
|
62
21
|
|
63
|
-
# Extract the query url e.g. https://nuget.pkg.github.com/ORG/query
|
64
|
-
search_base = extract_search_url(response.body)
|
65
|
-
return unless search_base
|
66
|
-
|
67
|
-
response = Dependabot::RegistryClient.get(
|
68
|
-
url: search_base + "?q=#{dependency.name.downcase}&prerelease=true&semVerLevel=2.0.0",
|
69
|
-
headers: { **auth_header, "Accept" => "application/json" }
|
70
|
-
)
|
71
|
-
return unless response.status == 200
|
72
|
-
|
73
|
-
# Find a projectUrl or licenseUrl that look like a source URL
|
74
|
-
extract_source_repo(response.body)
|
75
|
-
rescue JSON::ParserError
|
76
|
-
# Ignored, this is expected for some registries that don't handle these request.
|
77
|
-
end
|
78
|
-
|
79
|
-
sig { params(body: String).returns(T.nilable(String)) }
|
80
|
-
def extract_search_url(body)
|
81
|
-
JSON.parse(body)
|
82
|
-
.fetch("resources", [])
|
83
|
-
.find { |r| r.fetch("@type") == "SearchQueryService" }
|
84
|
-
&.fetch("@id")
|
85
|
-
end
|
86
|
-
|
87
|
-
sig { params(body: String).returns(T.nilable(Dependabot::Source)) }
|
88
|
-
def extract_source_repo(body)
|
89
|
-
JSON.parse(body).fetch("data", []).each do |search_result|
|
90
|
-
next unless search_result["id"].casecmp(dependency.name).zero?
|
91
|
-
|
92
|
-
if search_result.key?("projectUrl")
|
93
|
-
source = Source.from_url(search_result.fetch("projectUrl"))
|
94
|
-
return source if source
|
95
|
-
end
|
96
|
-
if search_result.key?("licenseUrl")
|
97
|
-
source = Source.from_url(search_result.fetch("licenseUrl"))
|
98
|
-
return source if source
|
99
|
-
end
|
100
|
-
end
|
101
|
-
# failed to find a source URL
|
102
22
|
nil
|
103
23
|
end
|
104
24
|
|
105
|
-
sig { params(nuspec: Nokogiri::XML::Document).returns(T.nilable(Dependabot::Source)) }
|
106
|
-
def look_up_source_in_nuspec(nuspec)
|
107
|
-
potential_source_urls = [
|
108
|
-
nuspec.at_css("package > metadata > repository")
|
109
|
-
&.attribute("url")&.value,
|
110
|
-
nuspec.at_css("package > metadata > repository > url")&.content,
|
111
|
-
nuspec.at_css("package > metadata > projectUrl")&.content,
|
112
|
-
nuspec.at_css("package > metadata > licenseUrl")&.content
|
113
|
-
].compact
|
114
|
-
|
115
|
-
source_url = potential_source_urls.find { |url| Source.from_url(url) }
|
116
|
-
source_url ||= source_from_anywhere_in_nuspec(nuspec)
|
117
|
-
|
118
|
-
Source.from_url(source_url)
|
119
|
-
end
|
120
|
-
|
121
|
-
sig { params(nuspec: Nokogiri::XML::Document).returns(T.nilable(String)) }
|
122
|
-
def source_from_anywhere_in_nuspec(nuspec)
|
123
|
-
github_urls = []
|
124
|
-
nuspec.to_s.force_encoding(Encoding::UTF_8)
|
125
|
-
.scan(Source::SOURCE_REGEX) do
|
126
|
-
github_urls << Regexp.last_match.to_s
|
127
|
-
end
|
128
|
-
|
129
|
-
github_urls.find do |url|
|
130
|
-
repo = T.must(Source.from_url(url)).repo
|
131
|
-
repo.downcase.end_with?(dependency.name.downcase)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
sig { returns(T.nilable(Nokogiri::XML::Document)) }
|
136
|
-
def dependency_nuspec_file
|
137
|
-
return @dependency_nuspec_file unless @dependency_nuspec_file.nil?
|
138
|
-
|
139
|
-
return if dependency_nuspec_url.nil?
|
140
|
-
|
141
|
-
response = Dependabot::RegistryClient.get(
|
142
|
-
url: T.must(dependency_nuspec_url),
|
143
|
-
headers: auth_header
|
144
|
-
)
|
145
|
-
|
146
|
-
@dependency_nuspec_file = Nokogiri::XML(response.body)
|
147
|
-
end
|
148
|
-
|
149
|
-
sig { returns(T.nilable(String)) }
|
150
|
-
def dependency_nuspec_url
|
151
|
-
source = dependency.requirements
|
152
|
-
.find { |r| r.fetch(:source) }&.fetch(:source)
|
153
|
-
|
154
|
-
source.fetch(:nuspec_url) if source&.key?(:nuspec_url)
|
155
|
-
end
|
156
|
-
|
157
25
|
sig { returns(T.nilable(String)) }
|
158
26
|
def dependency_source_url
|
159
27
|
source = dependency.requirements
|
@@ -164,32 +32,6 @@ module Dependabot
|
|
164
32
|
|
165
33
|
source.fetch("source_url")
|
166
34
|
end
|
167
|
-
|
168
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
169
|
-
sig { returns(T::Hash[String, String]) }
|
170
|
-
def auth_header
|
171
|
-
source = dependency.requirements
|
172
|
-
.find { |r| r.fetch(:source) }&.fetch(:source)
|
173
|
-
url = source&.fetch(:url, nil) || source&.fetch("url")
|
174
|
-
|
175
|
-
token = credentials
|
176
|
-
.select { |cred| cred["type"] == "nuget_feed" }
|
177
|
-
.find { |cred| cred["url"] == url }
|
178
|
-
&.fetch("token", nil)
|
179
|
-
|
180
|
-
return {} unless token
|
181
|
-
|
182
|
-
if token.include?(":")
|
183
|
-
encoded_token = Base64.encode64(token).delete("\n")
|
184
|
-
{ "Authorization" => "Basic #{encoded_token}" }
|
185
|
-
elsif Base64.decode64(token).ascii_only? &&
|
186
|
-
Base64.decode64(token).include?(":")
|
187
|
-
{ "Authorization" => "Basic #{token.delete("\n")}" }
|
188
|
-
else
|
189
|
-
{ "Authorization" => "Bearer #{token}" }
|
190
|
-
end
|
191
|
-
end
|
192
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
193
35
|
end
|
194
36
|
end
|
195
37
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/nuget/native_discovery/native_evaluation_details"
|
5
|
+
require "sorbet-runtime"
|
6
|
+
|
7
|
+
module Dependabot
|
8
|
+
module Nuget
|
9
|
+
class NativeDependencyDetails
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(json: T::Hash[String, T.untyped]).returns(NativeDependencyDetails) }
|
13
|
+
def self.from_json(json)
|
14
|
+
name = T.let(json.fetch("Name"), String)
|
15
|
+
version = T.let(json.fetch("Version"), T.nilable(String))
|
16
|
+
type = T.let(json.fetch("Type"), String)
|
17
|
+
evaluation = NativeEvaluationDetails
|
18
|
+
.from_json(T.let(json.fetch("EvaluationResult"), T.nilable(T::Hash[String, T.untyped])))
|
19
|
+
target_frameworks = T.let(json.fetch("TargetFrameworks"), T.nilable(T::Array[String]))
|
20
|
+
is_dev_dependency = T.let(json.fetch("IsDevDependency"), T::Boolean)
|
21
|
+
is_direct = T.let(json.fetch("IsDirect"), T::Boolean)
|
22
|
+
is_transitive = T.let(json.fetch("IsTransitive"), T::Boolean)
|
23
|
+
is_override = T.let(json.fetch("IsOverride"), T::Boolean)
|
24
|
+
is_update = T.let(json.fetch("IsUpdate"), T::Boolean)
|
25
|
+
info_url = T.let(json.fetch("InfoUrl"), T.nilable(String))
|
26
|
+
|
27
|
+
NativeDependencyDetails.new(name: name,
|
28
|
+
version: version,
|
29
|
+
type: type,
|
30
|
+
evaluation: evaluation,
|
31
|
+
target_frameworks: target_frameworks,
|
32
|
+
is_dev_dependency: is_dev_dependency,
|
33
|
+
is_direct: is_direct,
|
34
|
+
is_transitive: is_transitive,
|
35
|
+
is_override: is_override,
|
36
|
+
is_update: is_update,
|
37
|
+
info_url: info_url)
|
38
|
+
end
|
39
|
+
|
40
|
+
sig do
|
41
|
+
params(name: String,
|
42
|
+
version: T.nilable(String),
|
43
|
+
type: String,
|
44
|
+
evaluation: T.nilable(NativeEvaluationDetails),
|
45
|
+
target_frameworks: T.nilable(T::Array[String]),
|
46
|
+
is_dev_dependency: T::Boolean,
|
47
|
+
is_direct: T::Boolean,
|
48
|
+
is_transitive: T::Boolean,
|
49
|
+
is_override: T::Boolean,
|
50
|
+
is_update: T::Boolean,
|
51
|
+
info_url: T.nilable(String)).void
|
52
|
+
end
|
53
|
+
def initialize(name:, version:, type:, evaluation:, target_frameworks:, is_dev_dependency:, is_direct:,
|
54
|
+
is_transitive:, is_override:, is_update:, info_url:)
|
55
|
+
@name = name
|
56
|
+
@version = version
|
57
|
+
@type = type
|
58
|
+
@evaluation = evaluation
|
59
|
+
@target_frameworks = target_frameworks
|
60
|
+
@is_dev_dependency = is_dev_dependency
|
61
|
+
@is_direct = is_direct
|
62
|
+
@is_transitive = is_transitive
|
63
|
+
@is_override = is_override
|
64
|
+
@is_update = is_update
|
65
|
+
@info_url = info_url
|
66
|
+
end
|
67
|
+
|
68
|
+
sig { returns(String) }
|
69
|
+
attr_reader :name
|
70
|
+
|
71
|
+
sig { returns(T.nilable(String)) }
|
72
|
+
attr_reader :version
|
73
|
+
|
74
|
+
sig { returns(String) }
|
75
|
+
attr_reader :type
|
76
|
+
|
77
|
+
sig { returns(T.nilable(NativeEvaluationDetails)) }
|
78
|
+
attr_reader :evaluation
|
79
|
+
|
80
|
+
sig { returns(T.nilable(T::Array[String])) }
|
81
|
+
attr_reader :target_frameworks
|
82
|
+
|
83
|
+
sig { returns(T::Boolean) }
|
84
|
+
attr_reader :is_dev_dependency
|
85
|
+
|
86
|
+
sig { returns(T::Boolean) }
|
87
|
+
attr_reader :is_direct
|
88
|
+
|
89
|
+
sig { returns(T::Boolean) }
|
90
|
+
attr_reader :is_transitive
|
91
|
+
|
92
|
+
sig { returns(T::Boolean) }
|
93
|
+
attr_reader :is_override
|
94
|
+
|
95
|
+
sig { returns(T::Boolean) }
|
96
|
+
attr_reader :is_update
|
97
|
+
|
98
|
+
sig { returns(T.nilable(String)) }
|
99
|
+
attr_reader :info_url
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|