dependabot-nuget 0.378.0 → 0.379.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 +105 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +4 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +71 -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: 6a30c97332d1fca64d5fc48d192b55802ef9000a40aa3eb283b165c347ebe64d
|
|
4
|
+
data.tar.gz: 63eaedf049ff81f75b1e4c0cdc2ea42601d0a01a3f68fab8b8577ecc8f2d4b8e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5522815d4c5abbe6cd19a15a62eddd9d81b25e15b4facbe4ca6ffd01246ece451ce6887f3289bbd30139877a565c5edc56eb20c187ae69e69d2970911ef0a614
|
|
7
|
+
data.tar.gz: 052c7aa235636a4eab5db8b7149ee81ee7cc576e8dff81049e3345ca4774ac68ad04f30b23c8d0bbbf29d5bdc9cd27b8b1f50bcdd3a27e59f8778f387bec0060
|
|
@@ -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);
|
|
@@ -284,6 +313,24 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
284
313
|
}
|
|
285
314
|
|
|
286
315
|
var result = expandedProjects.OrderBy(p => p).ToImmutableArray();
|
|
316
|
+
|
|
317
|
+
// pre-filter projects that are in submodules to avoid unnecessary restore operations
|
|
318
|
+
var submodulePaths = GetSubmodulePaths(repoRootPath);
|
|
319
|
+
if (submodulePaths.Length > 0)
|
|
320
|
+
{
|
|
321
|
+
result = [.. result.Where(p =>
|
|
322
|
+
{
|
|
323
|
+
var relativePath = Path.GetRelativePath(repoRootPath, p).NormalizePathToUnix();
|
|
324
|
+
if (IsPathInSubmodule(relativePath, submodulePaths))
|
|
325
|
+
{
|
|
326
|
+
logger.Info($" Excluding project [{relativePath}] because it is in a submodule.");
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return true;
|
|
331
|
+
})];
|
|
332
|
+
}
|
|
333
|
+
|
|
287
334
|
return result;
|
|
288
335
|
}
|
|
289
336
|
|
|
@@ -344,13 +391,20 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
344
391
|
try
|
|
345
392
|
{
|
|
346
393
|
// get all packages.config results first
|
|
347
|
-
var expandedProjects = await ExpandEntryPointsIntoProjectsAsync(normalizedProjectPaths, _experimentsManager, _logger);
|
|
394
|
+
var expandedProjects = await ExpandEntryPointsIntoProjectsAsync(normalizedProjectPaths, _experimentsManager, _logger, repoRootPath);
|
|
348
395
|
foreach (var expandedProject in expandedProjects)
|
|
349
396
|
{
|
|
350
397
|
var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, expandedProject, _logger);
|
|
351
398
|
if (packagesConfigResult is not null)
|
|
352
399
|
{
|
|
353
400
|
var relativeProjectPath = Path.GetRelativePath(workspacePath, expandedProject).NormalizePathToUnix();
|
|
401
|
+
var dependencyGraph = packagesConfigResult.Dependencies
|
|
402
|
+
.Where(d => !string.IsNullOrEmpty(d.Version))
|
|
403
|
+
.OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
|
|
404
|
+
.ToImmutableDictionary(
|
|
405
|
+
d => $"{d.Name}/{d.Version}",
|
|
406
|
+
_ => ImmutableArray<string>.Empty,
|
|
407
|
+
StringComparer.OrdinalIgnoreCase);
|
|
354
408
|
results[relativeProjectPath] = new ProjectDiscoveryResult()
|
|
355
409
|
{
|
|
356
410
|
FilePath = relativeProjectPath,
|
|
@@ -358,6 +412,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
358
412
|
TargetFrameworks = packagesConfigResult.TargetFrameworks,
|
|
359
413
|
ImportedFiles = [], // no imported files resolved for packages.config scenarios
|
|
360
414
|
AdditionalFiles = packagesConfigResult.AdditionalFiles,
|
|
415
|
+
DependencyGraph = dependencyGraph,
|
|
361
416
|
};
|
|
362
417
|
}
|
|
363
418
|
}
|
|
@@ -472,10 +527,57 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
|
472
527
|
AdditionalFiles = mergedAdditionalFiles,
|
|
473
528
|
PackageManagementKind = (PackageManagementKind)Math.Max((int)result1.PackageManagementKind, (int)result2.PackageManagementKind),
|
|
474
529
|
PackageManagementSpecialFileRelativePath = result1.PackageManagementSpecialFileRelativePath ?? result2.PackageManagementSpecialFileRelativePath,
|
|
530
|
+
HasNoWarnNU1701 = result1.HasNoWarnNU1701 || result2.HasNoWarnNU1701,
|
|
531
|
+
DependencyGraph = MergeDependencyGraphs(result1.DependencyGraph, result2.DependencyGraph),
|
|
475
532
|
};
|
|
476
533
|
return mergedResult;
|
|
477
534
|
}
|
|
478
535
|
|
|
536
|
+
internal ImmutableArray<ProjectDiscoveryResult> FilterProjectsInSubmodules(
|
|
537
|
+
ImmutableArray<ProjectDiscoveryResult> projectResults,
|
|
538
|
+
string workspacePath,
|
|
539
|
+
ImmutableArray<string> submodulePaths)
|
|
540
|
+
{
|
|
541
|
+
var filtered = new List<ProjectDiscoveryResult>();
|
|
542
|
+
foreach (var project in projectResults)
|
|
543
|
+
{
|
|
544
|
+
var fullRelativePath = PathHelper.JoinPath(workspacePath, project.FilePath).NormalizePathToUnix();
|
|
545
|
+
if (IsPathInSubmodule(fullRelativePath, submodulePaths))
|
|
546
|
+
{
|
|
547
|
+
_logger.Info($" Excluding project [{project.FilePath}] because it is in a submodule.");
|
|
548
|
+
}
|
|
549
|
+
else
|
|
550
|
+
{
|
|
551
|
+
filtered.Add(project);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return [.. filtered];
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private static ImmutableDictionary<string, ImmutableArray<string>> MergeDependencyGraphs(
|
|
559
|
+
ImmutableDictionary<string, ImmutableArray<string>> graph1,
|
|
560
|
+
ImmutableDictionary<string, ImmutableArray<string>> graph2)
|
|
561
|
+
{
|
|
562
|
+
var merged = graph1.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase);
|
|
563
|
+
foreach (var kvp in graph2)
|
|
564
|
+
{
|
|
565
|
+
if (merged.TryGetValue(kvp.Key, out var existing))
|
|
566
|
+
{
|
|
567
|
+
merged[kvp.Key] = existing
|
|
568
|
+
.Union(kvp.Value, StringComparer.OrdinalIgnoreCase)
|
|
569
|
+
.OrderBy(n => n, StringComparer.OrdinalIgnoreCase)
|
|
570
|
+
.ToImmutableArray();
|
|
571
|
+
}
|
|
572
|
+
else
|
|
573
|
+
{
|
|
574
|
+
merged[kvp.Key] = kvp.Value;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return merged.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase);
|
|
579
|
+
}
|
|
580
|
+
|
|
479
581
|
internal static async Task WriteResultsAsync(string repoRootPath, string outputPath, WorkspaceDiscoveryResult result)
|
|
480
582
|
{
|
|
481
583
|
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
|
}
|