dependabot-nuget 0.378.0 → 0.380.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/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +8 -17
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/GraphCommand.cs +47 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +12 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/SharedOptions.cs +17 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTestHelper.cs +84 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Graph.cs +65 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +2 -60
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +5 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +115 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +4 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +76 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Graph/GraphWorker.cs +256 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IGraphWorker.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CreateDependencySubmission.cs +92 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +74 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +8 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/FileWriterWorker.cs +2 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +29 -49
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/GitSubmoduleParser.cs +62 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +34 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +334 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +56 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Graph/GraphWorkerTests.cs +465 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.InsecureHttpFeed.cs +292 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +35 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +305 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +100 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +82 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/BindingRedirectsTests.cs +52 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +73 -39
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/GitSubmoduleParserTests.cs +135 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -0
- metadata +16 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d1e2d0fc0dbdb9ca5edd820796d80e93ff3508a43ec0a21e25be1ca9a296dcd
|
|
4
|
+
data.tar.gz: 77f9feddc3e1f6a42a72770aef37989b081b19c4d721b4131b0c575a1cc27ae6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ac9681cf7e3f0e093c8bda609e7706b37bc5d8c2299c8dd8f0be92d16c9c1e8960b9fa1b82f293e4b0ba0a25c40709614fa5cff5c3e1d354aa2b079f611e88a
|
|
7
|
+
data.tar.gz: bbcf9c27baa289052da2eb6fcfeab8cd2610cfc855eb466a3f3857476a23726969cca288157462174a9bdc616444a061db54813ecbefe5c0d8343dbfcdbcea3e
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
<PackageVersion Include="OpenTelemetry" Version="1.15.3" />
|
|
28
28
|
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
|
|
29
29
|
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
|
|
30
|
+
<PackageVersion Include="packageurl-dotnet" Version="2.0.0" />
|
|
30
31
|
<PackageVersion Include="Semver" Version="3.0.0" />
|
|
31
32
|
<PackageVersion Include="System.CommandLine" Version="2.0.3" />
|
|
32
33
|
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.3" />
|
|
@@ -8,33 +8,24 @@ namespace NuGetUpdater.Cli.Commands;
|
|
|
8
8
|
|
|
9
9
|
internal static class CloneCommand
|
|
10
10
|
{
|
|
11
|
-
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
|
|
12
|
-
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
|
|
13
|
-
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
|
|
14
|
-
{
|
|
15
|
-
Required = true,
|
|
16
|
-
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
|
|
17
|
-
};
|
|
18
|
-
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
|
|
19
|
-
|
|
20
11
|
internal static Command GetCommand(Action<int> setExitCode)
|
|
21
12
|
{
|
|
22
13
|
var command = new Command("clone", "Clones a repository in preparation for a dependabot job.")
|
|
23
14
|
{
|
|
24
|
-
JobPathOption,
|
|
25
|
-
RepoContentsPathOption,
|
|
26
|
-
ApiUrlOption,
|
|
27
|
-
JobIdOption,
|
|
15
|
+
SharedOptions.JobPathOption,
|
|
16
|
+
SharedOptions.RepoContentsPathOption,
|
|
17
|
+
SharedOptions.ApiUrlOption,
|
|
18
|
+
SharedOptions.JobIdOption,
|
|
28
19
|
};
|
|
29
20
|
|
|
30
21
|
command.TreatUnmatchedTokensAsErrors = true;
|
|
31
22
|
|
|
32
23
|
command.SetAction(async (parseResult, cancellationToken) =>
|
|
33
24
|
{
|
|
34
|
-
var jobPath = parseResult.GetValue(JobPathOption);
|
|
35
|
-
var repoContentsPath = parseResult.GetValue(RepoContentsPathOption);
|
|
36
|
-
var apiUrl = parseResult.GetValue(ApiUrlOption);
|
|
37
|
-
var jobId = parseResult.GetValue(JobIdOption);
|
|
25
|
+
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
|
|
26
|
+
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
|
|
27
|
+
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
|
|
28
|
+
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
|
|
38
29
|
|
|
39
30
|
var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
|
|
40
31
|
var logger = new OpenTelemetryLogger();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
using System.CommandLine;
|
|
2
|
+
|
|
3
|
+
using NuGetUpdater.Core;
|
|
4
|
+
using NuGetUpdater.Core.Discover;
|
|
5
|
+
using NuGetUpdater.Core.Graph;
|
|
6
|
+
using NuGetUpdater.Core.Run;
|
|
7
|
+
|
|
8
|
+
namespace NuGetUpdater.Cli.Commands;
|
|
9
|
+
|
|
10
|
+
internal static class GraphCommand
|
|
11
|
+
{
|
|
12
|
+
internal static Command GetCommand(Action<int> setExitCode)
|
|
13
|
+
{
|
|
14
|
+
Command command = new("graph", "Generates a dependency graph for a repository.")
|
|
15
|
+
{
|
|
16
|
+
SharedOptions.JobPathOption,
|
|
17
|
+
SharedOptions.RepoContentsPathOption,
|
|
18
|
+
SharedOptions.CaseInsensitiveRepoContentsPathOption,
|
|
19
|
+
SharedOptions.ApiUrlOption,
|
|
20
|
+
SharedOptions.JobIdOption,
|
|
21
|
+
SharedOptions.BaseCommitShaOption
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
command.TreatUnmatchedTokensAsErrors = true;
|
|
25
|
+
|
|
26
|
+
command.SetAction(async (parseResult, cancellationToken) =>
|
|
27
|
+
{
|
|
28
|
+
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
|
|
29
|
+
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
|
|
30
|
+
var caseInsensitiveRepoContentsPath = parseResult.GetValue(SharedOptions.CaseInsensitiveRepoContentsPathOption);
|
|
31
|
+
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
|
|
32
|
+
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
|
|
33
|
+
var baseCommitSha = parseResult.GetValue(SharedOptions.BaseCommitShaOption);
|
|
34
|
+
|
|
35
|
+
var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
|
|
36
|
+
var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId!, jobPath!.FullName);
|
|
37
|
+
var logger = new OpenTelemetryLogger();
|
|
38
|
+
var discoverWorker = new DiscoveryWorker(jobId!, experimentsManager, logger);
|
|
39
|
+
var worker = new GraphWorker(jobId!, apiHandler, discoverWorker, logger);
|
|
40
|
+
var result = await worker.RunAsync(jobPath!, repoContentsPath!, caseInsensitiveRepoContentsPath, baseCommitSha!);
|
|
41
|
+
setExitCode(result);
|
|
42
|
+
return 0;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return command;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -9,39 +9,28 @@ namespace NuGetUpdater.Cli.Commands;
|
|
|
9
9
|
|
|
10
10
|
internal static class RunCommand
|
|
11
11
|
{
|
|
12
|
-
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
|
|
13
|
-
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
|
|
14
|
-
internal static readonly Option<DirectoryInfo?> CaseInsensitiveRepoContentsPathOption = new("--case-insensitive-repo-contents-path") { Required = false };
|
|
15
|
-
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
|
|
16
|
-
{
|
|
17
|
-
Required = true,
|
|
18
|
-
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
|
|
19
|
-
};
|
|
20
|
-
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
|
|
21
|
-
internal static readonly Option<string> BaseCommitShaOption = new("--base-commit-sha") { Required = true };
|
|
22
|
-
|
|
23
12
|
internal static Command GetCommand(Action<int> setExitCode)
|
|
24
13
|
{
|
|
25
14
|
Command command = new("run", "Runs a full dependabot job.")
|
|
26
15
|
{
|
|
27
|
-
JobPathOption,
|
|
28
|
-
RepoContentsPathOption,
|
|
29
|
-
CaseInsensitiveRepoContentsPathOption,
|
|
30
|
-
ApiUrlOption,
|
|
31
|
-
JobIdOption,
|
|
32
|
-
BaseCommitShaOption
|
|
16
|
+
SharedOptions.JobPathOption,
|
|
17
|
+
SharedOptions.RepoContentsPathOption,
|
|
18
|
+
SharedOptions.CaseInsensitiveRepoContentsPathOption,
|
|
19
|
+
SharedOptions.ApiUrlOption,
|
|
20
|
+
SharedOptions.JobIdOption,
|
|
21
|
+
SharedOptions.BaseCommitShaOption
|
|
33
22
|
};
|
|
34
23
|
|
|
35
24
|
command.TreatUnmatchedTokensAsErrors = true;
|
|
36
25
|
|
|
37
26
|
command.SetAction(async (parseResult, cancellationToken) =>
|
|
38
27
|
{
|
|
39
|
-
var jobPath = parseResult.GetValue(JobPathOption);
|
|
40
|
-
var repoContentsPath = parseResult.GetValue(RepoContentsPathOption);
|
|
41
|
-
var caseInsensitiveRepoContentsPath = parseResult.GetValue(CaseInsensitiveRepoContentsPathOption);
|
|
42
|
-
var apiUrl = parseResult.GetValue(ApiUrlOption);
|
|
43
|
-
var jobId = parseResult.GetValue(JobIdOption);
|
|
44
|
-
var baseCommitSha = parseResult.GetValue(BaseCommitShaOption);
|
|
28
|
+
var jobPath = parseResult.GetValue(SharedOptions.JobPathOption);
|
|
29
|
+
var repoContentsPath = parseResult.GetValue(SharedOptions.RepoContentsPathOption);
|
|
30
|
+
var caseInsensitiveRepoContentsPath = parseResult.GetValue(SharedOptions.CaseInsensitiveRepoContentsPathOption);
|
|
31
|
+
var apiUrl = parseResult.GetValue(SharedOptions.ApiUrlOption);
|
|
32
|
+
var jobId = parseResult.GetValue(SharedOptions.JobIdOption);
|
|
33
|
+
var baseCommitSha = parseResult.GetValue(SharedOptions.BaseCommitShaOption);
|
|
45
34
|
|
|
46
35
|
var apiHandler = new HttpApiHandler(apiUrl!.ToString(), jobId!);
|
|
47
36
|
var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId!, jobPath!.FullName);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
using System.CommandLine;
|
|
2
|
+
|
|
3
|
+
namespace NuGetUpdater.Cli.Commands;
|
|
4
|
+
|
|
5
|
+
internal static class SharedOptions
|
|
6
|
+
{
|
|
7
|
+
internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { Required = true };
|
|
8
|
+
internal static readonly Option<DirectoryInfo> RepoContentsPathOption = new("--repo-contents-path") { Required = true };
|
|
9
|
+
internal static readonly Option<DirectoryInfo?> CaseInsensitiveRepoContentsPathOption = new("--case-insensitive-repo-contents-path") { Required = false };
|
|
10
|
+
internal static readonly Option<Uri> ApiUrlOption = new("--api-url")
|
|
11
|
+
{
|
|
12
|
+
Required = true,
|
|
13
|
+
CustomParser = (argumentResult) => Uri.TryCreate(argumentResult.Tokens.Single().Value, UriKind.Absolute, out var uri) ? uri : throw new ArgumentException("Invalid API URL format.")
|
|
14
|
+
};
|
|
15
|
+
internal static readonly Option<string> JobIdOption = new("--job-id") { Required = true };
|
|
16
|
+
internal static readonly Option<string> BaseCommitShaOption = new("--base-commit-sha") { Required = true };
|
|
17
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
using System.Text;
|
|
2
|
+
using System.Text.Json;
|
|
3
|
+
|
|
4
|
+
using NuGetUpdater.Core.Run;
|
|
5
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
|
6
|
+
using NuGetUpdater.Core.Test;
|
|
7
|
+
using NuGetUpdater.Core.Test.Update;
|
|
8
|
+
|
|
9
|
+
using Xunit;
|
|
10
|
+
|
|
11
|
+
namespace NuGetUpdater.Cli.Test;
|
|
12
|
+
|
|
13
|
+
using TestFile = (string Path, string Content);
|
|
14
|
+
|
|
15
|
+
internal static class EntryPointTestHelper
|
|
16
|
+
{
|
|
17
|
+
internal static async Task RunAsync(
|
|
18
|
+
string commandName,
|
|
19
|
+
TestFile[] files,
|
|
20
|
+
Job job,
|
|
21
|
+
string[] expectedUrls,
|
|
22
|
+
MockNuGetPackage[]? packages = null,
|
|
23
|
+
string? repoContentsPath = null,
|
|
24
|
+
int expectedExitCode = 0)
|
|
25
|
+
{
|
|
26
|
+
using var tempDirectory = new TemporaryDirectory();
|
|
27
|
+
|
|
28
|
+
// write test files
|
|
29
|
+
foreach (var testFile in files)
|
|
30
|
+
{
|
|
31
|
+
var fullPath = Path.Join(tempDirectory.DirectoryPath, testFile.Path);
|
|
32
|
+
var directory = Path.GetDirectoryName(fullPath)!;
|
|
33
|
+
Directory.CreateDirectory(directory);
|
|
34
|
+
await File.WriteAllTextAsync(fullPath, testFile.Content);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// write job file
|
|
38
|
+
var jobPath = Path.Combine(tempDirectory.DirectoryPath, "job.json");
|
|
39
|
+
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(new { Job = job }, RunWorker.SerializerOptions));
|
|
40
|
+
|
|
41
|
+
// save packages
|
|
42
|
+
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);
|
|
43
|
+
|
|
44
|
+
var actualUrls = new List<string>();
|
|
45
|
+
using var http = TestHttpServer.CreateTestStringServer((method, url) =>
|
|
46
|
+
{
|
|
47
|
+
actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
|
|
48
|
+
return (200, "ok");
|
|
49
|
+
});
|
|
50
|
+
var args = new List<string>()
|
|
51
|
+
{
|
|
52
|
+
commandName,
|
|
53
|
+
"--job-path",
|
|
54
|
+
jobPath,
|
|
55
|
+
"--repo-contents-path",
|
|
56
|
+
repoContentsPath ?? tempDirectory.DirectoryPath,
|
|
57
|
+
"--api-url",
|
|
58
|
+
http.BaseUrl,
|
|
59
|
+
"--job-id",
|
|
60
|
+
"TEST-ID",
|
|
61
|
+
"--base-commit-sha",
|
|
62
|
+
"BASE-COMMIT-SHA"
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
var output = new StringBuilder();
|
|
66
|
+
// redirect stdout
|
|
67
|
+
var originalOut = Console.Out;
|
|
68
|
+
Console.SetOut(new StringWriter(output));
|
|
69
|
+
int result = -1;
|
|
70
|
+
try
|
|
71
|
+
{
|
|
72
|
+
result = await Program.Main(args.ToArray());
|
|
73
|
+
}
|
|
74
|
+
catch
|
|
75
|
+
{
|
|
76
|
+
// restore stdout
|
|
77
|
+
Console.SetOut(originalOut);
|
|
78
|
+
throw;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Assert.True(result == expectedExitCode, $"Expected exit code {expectedExitCode} but got {result}.\nSTDOUT:\n" + output.ToString());
|
|
82
|
+
Assert.Equal(expectedUrls, actualUrls);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
|
2
|
+
using NuGetUpdater.Core.Test;
|
|
3
|
+
|
|
4
|
+
using Xunit;
|
|
5
|
+
|
|
6
|
+
namespace NuGetUpdater.Cli.Test;
|
|
7
|
+
|
|
8
|
+
using TestFile = (string Path, string Content);
|
|
9
|
+
|
|
10
|
+
public partial class EntryPointTests
|
|
11
|
+
{
|
|
12
|
+
public class Graph
|
|
13
|
+
{
|
|
14
|
+
[Fact]
|
|
15
|
+
public async Task Graph_Simple()
|
|
16
|
+
{
|
|
17
|
+
// verify we can pass command line arguments for graph command
|
|
18
|
+
await RunAsync(
|
|
19
|
+
packages:
|
|
20
|
+
[
|
|
21
|
+
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
|
|
22
|
+
],
|
|
23
|
+
files:
|
|
24
|
+
[
|
|
25
|
+
("Directory.Build.props", "<Project />"),
|
|
26
|
+
("Directory.Build.targets", "<Project />"),
|
|
27
|
+
("Directory.Packages.props", """
|
|
28
|
+
<Project>
|
|
29
|
+
<PropertyGroup>
|
|
30
|
+
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
|
31
|
+
</PropertyGroup>
|
|
32
|
+
</Project>
|
|
33
|
+
"""),
|
|
34
|
+
("src/project.csproj", """
|
|
35
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
36
|
+
<PropertyGroup>
|
|
37
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
38
|
+
</PropertyGroup>
|
|
39
|
+
<ItemGroup>
|
|
40
|
+
<PackageReference Include="Some.Package" Version="1.0.0" />
|
|
41
|
+
</ItemGroup>
|
|
42
|
+
</Project>
|
|
43
|
+
""")
|
|
44
|
+
],
|
|
45
|
+
job: new Job()
|
|
46
|
+
{
|
|
47
|
+
Source = new()
|
|
48
|
+
{
|
|
49
|
+
Provider = "github",
|
|
50
|
+
Repo = "test",
|
|
51
|
+
Directory = "src",
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
expectedUrls:
|
|
55
|
+
[
|
|
56
|
+
"POST /update_jobs/TEST-ID/create_dependency_submission",
|
|
57
|
+
"PATCH /update_jobs/TEST-ID/mark_as_processed",
|
|
58
|
+
]
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private static Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
|
|
63
|
+
=> EntryPointTestHelper.RunAsync("graph", files, job, expectedUrls, packages, repoContentsPath, expectedExitCode);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -143,65 +143,7 @@ public partial class EntryPointTests
|
|
|
143
143
|
);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
private static
|
|
147
|
-
|
|
148
|
-
using var tempDirectory = new TemporaryDirectory();
|
|
149
|
-
|
|
150
|
-
// write test files
|
|
151
|
-
foreach (var testFile in files)
|
|
152
|
-
{
|
|
153
|
-
var fullPath = Path.Join(tempDirectory.DirectoryPath, testFile.Path);
|
|
154
|
-
var directory = Path.GetDirectoryName(fullPath)!;
|
|
155
|
-
Directory.CreateDirectory(directory);
|
|
156
|
-
await File.WriteAllTextAsync(fullPath, testFile.Content);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// write job file
|
|
160
|
-
var jobPath = Path.Combine(tempDirectory.DirectoryPath, "job.json");
|
|
161
|
-
await File.WriteAllTextAsync(jobPath, JsonSerializer.Serialize(new { Job = job }, RunWorker.SerializerOptions));
|
|
162
|
-
|
|
163
|
-
// save packages
|
|
164
|
-
await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);
|
|
165
|
-
|
|
166
|
-
var actualUrls = new List<string>();
|
|
167
|
-
using var http = TestHttpServer.CreateTestStringServer((method, url) =>
|
|
168
|
-
{
|
|
169
|
-
actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
|
|
170
|
-
return (200, "ok");
|
|
171
|
-
});
|
|
172
|
-
var args = new List<string>()
|
|
173
|
-
{
|
|
174
|
-
"run",
|
|
175
|
-
"--job-path",
|
|
176
|
-
jobPath,
|
|
177
|
-
"--repo-contents-path",
|
|
178
|
-
repoContentsPath ?? tempDirectory.DirectoryPath,
|
|
179
|
-
"--api-url",
|
|
180
|
-
http.BaseUrl,
|
|
181
|
-
"--job-id",
|
|
182
|
-
"TEST-ID",
|
|
183
|
-
"--base-commit-sha",
|
|
184
|
-
"BASE-COMMIT-SHA"
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
var output = new StringBuilder();
|
|
188
|
-
// redirect stdout
|
|
189
|
-
var originalOut = Console.Out;
|
|
190
|
-
Console.SetOut(new StringWriter(output));
|
|
191
|
-
int result = -1;
|
|
192
|
-
try
|
|
193
|
-
{
|
|
194
|
-
result = await Program.Main(args.ToArray());
|
|
195
|
-
}
|
|
196
|
-
catch
|
|
197
|
-
{
|
|
198
|
-
// restore stdout
|
|
199
|
-
Console.SetOut(originalOut);
|
|
200
|
-
throw;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
Assert.True(result == expectedExitCode, $"Expected exit code {expectedExitCode} but got {result}.\nSTDOUT:\n" + output.ToString());
|
|
204
|
-
Assert.Equal(expectedUrls, actualUrls);
|
|
205
|
-
}
|
|
146
|
+
private static Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null, int expectedExitCode = 0)
|
|
147
|
+
=> EntryPointTestHelper.RunAsync("run", files, job, expectedUrls, packages, repoContentsPath, expectedExitCode);
|
|
206
148
|
}
|
|
207
149
|
}
|
|
@@ -172,15 +172,13 @@ internal static class CompatibilityChecker
|
|
|
172
172
|
|
|
173
173
|
foreach (var source in sources)
|
|
174
174
|
{
|
|
175
|
-
|
|
176
|
-
var feed = await sourceRepository.GetResourceAsync<FindPackageByIdResource>();
|
|
177
|
-
if (feed is null)
|
|
178
|
-
{
|
|
179
|
-
throw new NotSupportedException($"Failed to get FindPackageByIdResource for {source.SourceUri}");
|
|
180
|
-
}
|
|
181
|
-
|
|
175
|
+
FindPackageByIdResource feed;
|
|
182
176
|
try
|
|
183
177
|
{
|
|
178
|
+
var sourceRepository = Repository.Factory.GetCoreV3(source);
|
|
179
|
+
feed = await sourceRepository.GetResourceAsync<FindPackageByIdResource>()
|
|
180
|
+
?? throw new NotSupportedException($"Failed to get FindPackageByIdResource for {source.SourceUri}");
|
|
181
|
+
|
|
184
182
|
// a non-compliant v2 API returning 404 can cause this to throw
|
|
185
183
|
var exists = await feed.DoesPackageExistAsync(
|
|
186
184
|
package.Id,
|
|
@@ -16,6 +16,8 @@ using NuGetUpdater.Core.Run.ApiModel;
|
|
|
16
16
|
using NuGetUpdater.Core.Updater;
|
|
17
17
|
using NuGetUpdater.Core.Utilities;
|
|
18
18
|
|
|
19
|
+
using static NuGetUpdater.Core.Utilities.GitSubmoduleParser;
|
|
20
|
+
|
|
19
21
|
namespace NuGetUpdater.Core.Discover;
|
|
20
22
|
|
|
21
23
|
public partial class DiscoveryWorker : IDiscoveryWorker
|
|
@@ -110,6 +112,33 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
110
112
|
var repoRoot = new DirectoryInfo(repoRootPath);
|
|
111
113
|
projectResults = [.. projectResults.Where(p => PathHelper.IsFileUnderDirectory(repoRoot, new FileInfo(Path.Join(workspacePath, p.FilePath))))];
|
|
112
114
|
|
|
115
|
+
// filter out projects that are in submodules
|
|
116
|
+
var submodulePaths = GetSubmodulePaths(repoRootPath);
|
|
117
|
+
if (submodulePaths.Length > 0)
|
|
118
|
+
{
|
|
119
|
+
projectResults = FilterProjectsInSubmodules(projectResults, initialWorkspacePath, submodulePaths);
|
|
120
|
+
|
|
121
|
+
if (dotNetToolsJsonDiscovery is not null)
|
|
122
|
+
{
|
|
123
|
+
var fullRelativePath = PathHelper.JoinPath(initialWorkspacePath, dotNetToolsJsonDiscovery.FilePath).NormalizePathToUnix();
|
|
124
|
+
if (IsPathInSubmodule(fullRelativePath, submodulePaths))
|
|
125
|
+
{
|
|
126
|
+
_logger.Info($" Excluding file [{dotNetToolsJsonDiscovery.FilePath}] because it is in a submodule.");
|
|
127
|
+
dotNetToolsJsonDiscovery = null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (globalJsonDiscovery is not null)
|
|
132
|
+
{
|
|
133
|
+
var fullRelativePath = PathHelper.JoinPath(initialWorkspacePath, globalJsonDiscovery.FilePath).NormalizePathToUnix();
|
|
134
|
+
if (IsPathInSubmodule(fullRelativePath, submodulePaths))
|
|
135
|
+
{
|
|
136
|
+
_logger.Info($" Excluding file [{globalJsonDiscovery.FilePath}] because it is in a submodule.");
|
|
137
|
+
globalJsonDiscovery = null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
113
142
|
// if any projectResults are not successful, return a failed result
|
|
114
143
|
if (projectResults.Any(p => p.IsSuccess == false))
|
|
115
144
|
{
|
|
@@ -175,7 +204,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
175
204
|
ImmutableArray<string> projects;
|
|
176
205
|
try
|
|
177
206
|
{
|
|
178
|
-
projects = await ExpandEntryPointsIntoProjectsAsync(entryPoints, _experimentsManager, _logger);
|
|
207
|
+
projects = await ExpandEntryPointsIntoProjectsAsync(entryPoints, _experimentsManager, _logger, repoRootPath);
|
|
179
208
|
}
|
|
180
209
|
catch (InvalidProjectFileException e)
|
|
181
210
|
{
|
|
@@ -223,7 +252,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
223
252
|
.ToImmutableArray();
|
|
224
253
|
}
|
|
225
254
|
|
|
226
|
-
internal async static Task<ImmutableArray<string>> ExpandEntryPointsIntoProjectsAsync(IEnumerable<string> entryPoints, ExperimentsManager experimentsManager, ILogger logger)
|
|
255
|
+
internal async static Task<ImmutableArray<string>> ExpandEntryPointsIntoProjectsAsync(IEnumerable<string> entryPoints, ExperimentsManager experimentsManager, ILogger logger, string repoRootPath)
|
|
227
256
|
{
|
|
228
257
|
HashSet<string> expandedProjects = new(PathComparer.Instance);
|
|
229
258
|
HashSet<string> seenProjects = new(PathComparer.Instance);
|
|
@@ -247,7 +276,16 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
247
276
|
else if (extension == ".slnx")
|
|
248
277
|
{
|
|
249
278
|
logger.Info($" Expanding solution: {candidateEntryPoint}:");
|
|
250
|
-
SolutionModel solution
|
|
279
|
+
SolutionModel solution;
|
|
280
|
+
try
|
|
281
|
+
{
|
|
282
|
+
solution = await SolutionSerializers.SlnXml.OpenAsync(candidateEntryPoint, CancellationToken.None);
|
|
283
|
+
}
|
|
284
|
+
catch (SolutionException ex)
|
|
285
|
+
{
|
|
286
|
+
throw new UnparseableFileException(ex.Message, candidateEntryPoint);
|
|
287
|
+
}
|
|
288
|
+
|
|
251
289
|
string solutionPath = Path.GetDirectoryName(candidateEntryPoint) ?? string.Empty;
|
|
252
290
|
|
|
253
291
|
foreach (SolutionProjectModel project in solution.SolutionProjects)
|
|
@@ -284,6 +322,24 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
284
322
|
}
|
|
285
323
|
|
|
286
324
|
var result = expandedProjects.OrderBy(p => p).ToImmutableArray();
|
|
325
|
+
|
|
326
|
+
// pre-filter projects that are in submodules to avoid unnecessary restore operations
|
|
327
|
+
var submodulePaths = GetSubmodulePaths(repoRootPath);
|
|
328
|
+
if (submodulePaths.Length > 0)
|
|
329
|
+
{
|
|
330
|
+
result = [.. result.Where(p =>
|
|
331
|
+
{
|
|
332
|
+
var relativePath = Path.GetRelativePath(repoRootPath, p).NormalizePathToUnix();
|
|
333
|
+
if (IsPathInSubmodule(relativePath, submodulePaths))
|
|
334
|
+
{
|
|
335
|
+
logger.Info($" Excluding project [{relativePath}] because it is in a submodule.");
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return true;
|
|
340
|
+
})];
|
|
341
|
+
}
|
|
342
|
+
|
|
287
343
|
return result;
|
|
288
344
|
}
|
|
289
345
|
|
|
@@ -344,13 +400,20 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
344
400
|
try
|
|
345
401
|
{
|
|
346
402
|
// get all packages.config results first
|
|
347
|
-
var expandedProjects = await ExpandEntryPointsIntoProjectsAsync(normalizedProjectPaths, _experimentsManager, _logger);
|
|
403
|
+
var expandedProjects = await ExpandEntryPointsIntoProjectsAsync(normalizedProjectPaths, _experimentsManager, _logger, repoRootPath);
|
|
348
404
|
foreach (var expandedProject in expandedProjects)
|
|
349
405
|
{
|
|
350
406
|
var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, expandedProject, _logger);
|
|
351
407
|
if (packagesConfigResult is not null)
|
|
352
408
|
{
|
|
353
409
|
var relativeProjectPath = Path.GetRelativePath(workspacePath, expandedProject).NormalizePathToUnix();
|
|
410
|
+
var dependencyGraph = packagesConfigResult.Dependencies
|
|
411
|
+
.Where(d => !string.IsNullOrEmpty(d.Version))
|
|
412
|
+
.OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
|
|
413
|
+
.ToImmutableDictionary(
|
|
414
|
+
d => $"{d.Name}/{d.Version}",
|
|
415
|
+
_ => ImmutableArray<string>.Empty,
|
|
416
|
+
StringComparer.OrdinalIgnoreCase);
|
|
354
417
|
results[relativeProjectPath] = new ProjectDiscoveryResult()
|
|
355
418
|
{
|
|
356
419
|
FilePath = relativeProjectPath,
|
|
@@ -358,6 +421,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
358
421
|
TargetFrameworks = packagesConfigResult.TargetFrameworks,
|
|
359
422
|
ImportedFiles = [], // no imported files resolved for packages.config scenarios
|
|
360
423
|
AdditionalFiles = packagesConfigResult.AdditionalFiles,
|
|
424
|
+
DependencyGraph = dependencyGraph,
|
|
361
425
|
};
|
|
362
426
|
}
|
|
363
427
|
}
|
|
@@ -472,10 +536,57 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
472
536
|
AdditionalFiles = mergedAdditionalFiles,
|
|
473
537
|
PackageManagementKind = (PackageManagementKind)Math.Max((int)result1.PackageManagementKind, (int)result2.PackageManagementKind),
|
|
474
538
|
PackageManagementSpecialFileRelativePath = result1.PackageManagementSpecialFileRelativePath ?? result2.PackageManagementSpecialFileRelativePath,
|
|
539
|
+
HasNoWarnNU1701 = result1.HasNoWarnNU1701 || result2.HasNoWarnNU1701,
|
|
540
|
+
DependencyGraph = MergeDependencyGraphs(result1.DependencyGraph, result2.DependencyGraph),
|
|
475
541
|
};
|
|
476
542
|
return mergedResult;
|
|
477
543
|
}
|
|
478
544
|
|
|
545
|
+
internal ImmutableArray<ProjectDiscoveryResult> FilterProjectsInSubmodules(
|
|
546
|
+
ImmutableArray<ProjectDiscoveryResult> projectResults,
|
|
547
|
+
string workspacePath,
|
|
548
|
+
ImmutableArray<string> submodulePaths)
|
|
549
|
+
{
|
|
550
|
+
var filtered = new List<ProjectDiscoveryResult>();
|
|
551
|
+
foreach (var project in projectResults)
|
|
552
|
+
{
|
|
553
|
+
var fullRelativePath = PathHelper.JoinPath(workspacePath, project.FilePath).NormalizePathToUnix();
|
|
554
|
+
if (IsPathInSubmodule(fullRelativePath, submodulePaths))
|
|
555
|
+
{
|
|
556
|
+
_logger.Info($" Excluding project [{project.FilePath}] because it is in a submodule.");
|
|
557
|
+
}
|
|
558
|
+
else
|
|
559
|
+
{
|
|
560
|
+
filtered.Add(project);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return [.. filtered];
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private static ImmutableDictionary<string, ImmutableArray<string>> MergeDependencyGraphs(
|
|
568
|
+
ImmutableDictionary<string, ImmutableArray<string>> graph1,
|
|
569
|
+
ImmutableDictionary<string, ImmutableArray<string>> graph2)
|
|
570
|
+
{
|
|
571
|
+
var merged = graph1.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase);
|
|
572
|
+
foreach (var kvp in graph2)
|
|
573
|
+
{
|
|
574
|
+
if (merged.TryGetValue(kvp.Key, out var existing))
|
|
575
|
+
{
|
|
576
|
+
merged[kvp.Key] = existing
|
|
577
|
+
.Union(kvp.Value, StringComparer.OrdinalIgnoreCase)
|
|
578
|
+
.OrderBy(n => n, StringComparer.OrdinalIgnoreCase)
|
|
579
|
+
.ToImmutableArray();
|
|
580
|
+
}
|
|
581
|
+
else
|
|
582
|
+
{
|
|
583
|
+
merged[kvp.Key] = kvp.Value;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return merged.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase);
|
|
588
|
+
}
|
|
589
|
+
|
|
479
590
|
internal static async Task WriteResultsAsync(string repoRootPath, string outputPath, WorkspaceDiscoveryResult result)
|
|
480
591
|
{
|
|
481
592
|
var resultPath = Path.IsPathRooted(outputPath)
|
|
@@ -17,4 +17,8 @@ public record ProjectDiscoveryResult : IDiscoveryResultWithDependencies
|
|
|
17
17
|
public required ImmutableArray<string> AdditionalFiles { get; init; }
|
|
18
18
|
public required ImmutableArray<Dependency> Dependencies { get; init; }
|
|
19
19
|
public bool HasNoWarnNU1701 { get; init; } = false;
|
|
20
|
+
/// <summary>
|
|
21
|
+
/// Maps each package (keyed as "Name/Version") to its direct dependency package names, as extracted from project.assets.json.
|
|
22
|
+
/// </summary>
|
|
23
|
+
public ImmutableDictionary<string, ImmutableArray<string>> DependencyGraph { get; init; } = ImmutableDictionary<string, ImmutableArray<string>>.Empty;
|
|
20
24
|
}
|