dependabot-nuget 0.291.0 → 0.293.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/.editorconfig +1 -0
- data/helpers/lib/NuGetUpdater/.gitignore +1 -0
- data/helpers/lib/NuGetUpdater/Directory.Build.props +1 -0
- data/helpers/lib/NuGetUpdater/Directory.Packages.props +2 -1
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Correlator.cs +197 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/DotNetPackageCorrelation.csproj +12 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs +68 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageSet.cs +11 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Release.cs +25 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/ReleasesFile.cs +9 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/RuntimePackages.cs +11 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Sdk.cs +13 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVerComparer.cs +16 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVersionConverter.cs +42 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/DotNetPackageCorrelation.Cli.csproj +16 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/Program.cs +32 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/CorrelatorTests.cs +99 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/DotNetPackageCorrelation.Test.csproj +18 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/EndToEndTests.cs +30 -0
- data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/RuntimePackagesTests.cs +206 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +6 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +19 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +5 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +17 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +8 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +128 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +8 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +9 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +19 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadRequirementException.cs +9 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +40 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +65 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +3 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +99 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EnsureDotNetPackageCorrelation.targets +25 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +15 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +5 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/MissingFileException.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +3 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +4 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/BadRequirement.cs +10 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +7 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotParseable.cs +15 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +25 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobRepoNotFound.cs +1 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +9 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/UnparseableFileException.cs +12 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +6 -30
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +12 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +0 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs +46 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +59 -30
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +15 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +15 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +60 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +20 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +56 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +108 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +16 -12
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +15 -28
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +5 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +84 -40
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +24 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/ExpectedUpdateOperationResult.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +25 -11
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +251 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +14 -8
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +154 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +71 -15
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +38 -20
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/ProjectHelperTests.cs +65 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +18 -1
- data/helpers/lib/NuGetUpdater/global.json +1 -1
- data/lib/dependabot/nuget/language.rb +21 -5
- data/lib/dependabot/nuget/native_helpers.rb +41 -14
- data/lib/dependabot/nuget/package_manager.rb +4 -4
- metadata +30 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +0 -11
@@ -7,6 +7,7 @@ using NuGet.Frameworks;
|
|
7
7
|
using NuGet.Versioning;
|
8
8
|
|
9
9
|
using NuGetUpdater.Core.Discover;
|
10
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
10
11
|
|
11
12
|
namespace NuGetUpdater.Core.Analyze;
|
12
13
|
|
@@ -16,6 +17,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
|
|
16
17
|
{
|
17
18
|
public const string AnalysisDirectoryName = "./.dependabot/analysis";
|
18
19
|
|
20
|
+
private readonly string _jobId;
|
19
21
|
private readonly ExperimentsManager _experimentsManager;
|
20
22
|
private readonly ILogger _logger;
|
21
23
|
|
@@ -25,8 +27,9 @@ public partial class AnalyzeWorker : IAnalyzeWorker
|
|
25
27
|
Converters = { new JsonStringEnumConverter(), new RequirementArrayConverter() },
|
26
28
|
};
|
27
29
|
|
28
|
-
public AnalyzeWorker(ExperimentsManager experimentsManager, ILogger logger)
|
30
|
+
public AnalyzeWorker(string jobId, ExperimentsManager experimentsManager, ILogger logger)
|
29
31
|
{
|
32
|
+
_jobId = jobId;
|
30
33
|
_experimentsManager = experimentsManager;
|
31
34
|
_logger = logger;
|
32
35
|
}
|
@@ -48,15 +51,11 @@ public partial class AnalyzeWorker : IAnalyzeWorker
|
|
48
51
|
{
|
49
52
|
analysisResult = await RunAsync(repoRoot, discovery, dependencyInfo);
|
50
53
|
}
|
51
|
-
catch (
|
52
|
-
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
|
54
|
+
catch (Exception ex)
|
53
55
|
{
|
54
|
-
var localPath = PathHelper.JoinPath(repoRoot, discovery.Path);
|
55
|
-
using var nugetContext = new NuGetContext(localPath);
|
56
56
|
analysisResult = new AnalysisResult
|
57
57
|
{
|
58
|
-
|
59
|
-
ErrorDetails = "(" + string.Join("|", nugetContext.PackageSources.Select(s => s.Source)) + ")",
|
58
|
+
Error = JobErrorBase.ErrorFromException(ex, _jobId, PathHelper.JoinPath(repoRoot, discovery.Path)),
|
60
59
|
UpdatedVersion = string.Empty,
|
61
60
|
CanUpdate = false,
|
62
61
|
UpdatedDependencies = [],
|
@@ -68,6 +67,8 @@ public partial class AnalyzeWorker : IAnalyzeWorker
|
|
68
67
|
|
69
68
|
public async Task<AnalysisResult> RunAsync(string repoRoot, WorkspaceDiscoveryResult discovery, DependencyInfo dependencyInfo)
|
70
69
|
{
|
70
|
+
MSBuildHelper.RegisterMSBuild(repoRoot, repoRoot);
|
71
|
+
|
71
72
|
var startingDirectory = PathHelper.JoinPath(repoRoot, discovery.Path);
|
72
73
|
|
73
74
|
_logger.Info($"Starting analysis of {dependencyInfo.Name}...");
|
@@ -412,6 +413,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
|
|
412
413
|
.SelectMany(p => p.TargetFrameworks)
|
413
414
|
.Select(NuGetFramework.Parse)
|
414
415
|
.Distinct()
|
416
|
+
.Select(f => f.GetShortFolderName())
|
415
417
|
.ToImmutableArray();
|
416
418
|
|
417
419
|
// When updating peer dependencies, we only need to consider top-level dependencies.
|
@@ -7,10 +7,10 @@ namespace NuGetUpdater.Core.Analyze;
|
|
7
7
|
|
8
8
|
internal static class DependencyFinder
|
9
9
|
{
|
10
|
-
public static async Task<ImmutableDictionary<
|
10
|
+
public static async Task<ImmutableDictionary<string, ImmutableArray<Dependency>>> GetDependenciesAsync(
|
11
11
|
string repoRoot,
|
12
12
|
string projectPath,
|
13
|
-
IEnumerable<
|
13
|
+
IEnumerable<string> frameworks,
|
14
14
|
ImmutableHashSet<string> packageIds,
|
15
15
|
NuGetVersion version,
|
16
16
|
NuGetContext nugetContext,
|
@@ -23,13 +23,13 @@ internal static class DependencyFinder
|
|
23
23
|
.Select(id => new Dependency(id, versionString, DependencyType.Unknown))
|
24
24
|
.ToImmutableArray();
|
25
25
|
|
26
|
-
var result = ImmutableDictionary.CreateBuilder<
|
26
|
+
var result = ImmutableDictionary.CreateBuilder<string, ImmutableArray<Dependency>>();
|
27
27
|
foreach (var framework in frameworks)
|
28
28
|
{
|
29
29
|
var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(
|
30
30
|
repoRoot,
|
31
31
|
projectPath,
|
32
|
-
framework
|
32
|
+
framework,
|
33
33
|
packages,
|
34
34
|
experimentsManager,
|
35
35
|
logger);
|
@@ -7,7 +7,7 @@ using NuGetUpdater.Core;
|
|
7
7
|
|
8
8
|
internal static class Extensions
|
9
9
|
{
|
10
|
-
public static ImmutableArray<Dependency> GetDependencies(this ImmutableDictionary<
|
10
|
+
public static ImmutableArray<Dependency> GetDependencies(this ImmutableDictionary<string, ImmutableArray<Dependency>> dependenciesByTfm)
|
11
11
|
{
|
12
12
|
Dictionary<string, Dependency> dependencies = [];
|
13
13
|
foreach (var (_framework, dependenciesForTfm) in dependenciesByTfm)
|
@@ -7,7 +7,25 @@ public class RequirementConverter : JsonConverter<Requirement>
|
|
7
7
|
{
|
8
8
|
public override Requirement? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
9
9
|
{
|
10
|
-
|
10
|
+
if (reader.TokenType != JsonTokenType.String)
|
11
|
+
{
|
12
|
+
throw new BadRequirementException($"Expected token type {nameof(JsonTokenType.String)}, but found {reader.TokenType}.");
|
13
|
+
}
|
14
|
+
|
15
|
+
var text = reader.GetString();
|
16
|
+
if (text is null)
|
17
|
+
{
|
18
|
+
throw new BadRequirementException("Unexpected null token.");
|
19
|
+
}
|
20
|
+
|
21
|
+
try
|
22
|
+
{
|
23
|
+
return Requirement.Parse(text);
|
24
|
+
}
|
25
|
+
catch
|
26
|
+
{
|
27
|
+
throw new BadRequirementException(text);
|
28
|
+
}
|
11
29
|
}
|
12
30
|
|
13
31
|
public override void Write(Utf8JsonWriter writer, Requirement value, JsonSerializerOptions options)
|
@@ -9,23 +9,49 @@ namespace NuGetUpdater.Core.Clone;
|
|
9
9
|
|
10
10
|
public class CloneWorker
|
11
11
|
{
|
12
|
+
private readonly string _jobId;
|
12
13
|
private readonly IApiHandler _apiHandler;
|
13
14
|
private readonly IGitCommandHandler _gitCommandHandler;
|
14
|
-
private readonly ILogger _logger;
|
15
15
|
|
16
|
-
public CloneWorker(IApiHandler apiHandler, IGitCommandHandler gitCommandHandler
|
16
|
+
public CloneWorker(string jobId, IApiHandler apiHandler, IGitCommandHandler gitCommandHandler)
|
17
17
|
{
|
18
|
+
_jobId = jobId;
|
18
19
|
_apiHandler = apiHandler;
|
19
20
|
_gitCommandHandler = gitCommandHandler;
|
20
|
-
_logger = logger;
|
21
21
|
}
|
22
22
|
|
23
23
|
// entrypoint for cli
|
24
24
|
public async Task<int> RunAsync(FileInfo jobFilePath, DirectoryInfo repoContentsPath)
|
25
25
|
{
|
26
26
|
var jobFileContent = await File.ReadAllTextAsync(jobFilePath.FullName);
|
27
|
-
|
28
|
-
|
27
|
+
|
28
|
+
// only a limited set of errors can occur here
|
29
|
+
JobFile? jobFile = null;
|
30
|
+
JobErrorBase? parseError = null;
|
31
|
+
try
|
32
|
+
{
|
33
|
+
jobFile = RunWorker.Deserialize(jobFileContent);
|
34
|
+
if (jobFile is null)
|
35
|
+
{
|
36
|
+
parseError = new UnknownError(new Exception("Job file could not be deserialized"), _jobId);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
catch (BadRequirementException ex)
|
40
|
+
{
|
41
|
+
parseError = new BadRequirement(ex.Message);
|
42
|
+
}
|
43
|
+
catch (Exception ex)
|
44
|
+
{
|
45
|
+
parseError = new UnknownError(ex, _jobId);
|
46
|
+
}
|
47
|
+
|
48
|
+
if (parseError is not null)
|
49
|
+
{
|
50
|
+
await ReportError(parseError);
|
51
|
+
return 1;
|
52
|
+
}
|
53
|
+
|
54
|
+
var result = await RunAsync(jobFile!.Job, repoContentsPath.FullName);
|
29
55
|
return result;
|
30
56
|
}
|
31
57
|
|
@@ -44,23 +70,29 @@ public class CloneWorker
|
|
44
70
|
catch (HttpRequestException ex)
|
45
71
|
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
|
46
72
|
{
|
73
|
+
// this is a _very_ specific case we want to handle before the common error handling kicks in
|
47
74
|
error = new JobRepoNotFound(ex.Message);
|
48
75
|
}
|
49
76
|
catch (Exception ex)
|
50
77
|
{
|
51
|
-
error =
|
78
|
+
error = JobErrorBase.ErrorFromException(ex, _jobId, repoContentsPath);
|
52
79
|
}
|
53
80
|
|
54
81
|
if (error is not null)
|
55
82
|
{
|
56
|
-
await
|
57
|
-
await _apiHandler.MarkAsProcessed(new("unknown"));
|
83
|
+
await ReportError(error);
|
58
84
|
return 1;
|
59
85
|
}
|
60
86
|
|
61
87
|
return 0;
|
62
88
|
}
|
63
89
|
|
90
|
+
private async Task ReportError(JobErrorBase error)
|
91
|
+
{
|
92
|
+
await _apiHandler.RecordUpdateJobError(error);
|
93
|
+
await _apiHandler.MarkAsProcessed(new("unknown"));
|
94
|
+
}
|
95
|
+
|
64
96
|
internal static CommandArguments[] GetAllCommandArgs(Job job, string repoContentsPath)
|
65
97
|
{
|
66
98
|
var commandArgs = new List<CommandArguments>()
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<Project>
|
2
2
|
<Import Project="DependencyDiscovery.props" />
|
3
3
|
|
4
|
-
<Target Name="_DiscoverDependencies" DependsOnTargets="GenerateBuildDependencyFile;ResolvePackageAssets">
|
4
|
+
<Target Name="_DiscoverDependencies" DependsOnTargets="ResolveAssemblyReferences;GenerateBuildDependencyFile;ResolvePackageAssets">
|
5
5
|
<!--
|
6
|
-
The
|
6
|
+
The targets ResolveAssemblyReferences and GenerateBuildDependencyFile are sufficient for projects targeting .NET Standard or .NET Core.
|
7
7
|
The target ResolvePackageAssets is necessary for projects targeting .NET Framework.
|
8
8
|
-->
|
9
9
|
</Target>
|
@@ -1,5 +1,4 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
-
using System.Net;
|
3
2
|
using System.Text.Json;
|
4
3
|
using System.Text.Json.Serialization;
|
5
4
|
|
@@ -8,7 +7,9 @@ using Microsoft.Build.Definition;
|
|
8
7
|
using Microsoft.Build.Evaluation;
|
9
8
|
using Microsoft.Build.Exceptions;
|
10
9
|
|
11
|
-
using
|
10
|
+
using NuGet.Frameworks;
|
11
|
+
|
12
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
12
13
|
using NuGetUpdater.Core.Utilities;
|
13
14
|
|
14
15
|
namespace NuGetUpdater.Core.Discover;
|
@@ -17,6 +18,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
17
18
|
{
|
18
19
|
public const string DiscoveryResultFileName = "./.dependabot/discovery.json";
|
19
20
|
|
21
|
+
private readonly string _jobId;
|
20
22
|
private readonly ExperimentsManager _experimentsManager;
|
21
23
|
private readonly ILogger _logger;
|
22
24
|
private readonly HashSet<string> _processedProjectPaths = new(StringComparer.Ordinal); private readonly HashSet<string> _restoredMSBuildSdks = new(StringComparer.OrdinalIgnoreCase);
|
@@ -27,8 +29,9 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
27
29
|
Converters = { new JsonStringEnumConverter() },
|
28
30
|
};
|
29
31
|
|
30
|
-
public DiscoveryWorker(ExperimentsManager experimentsManager, ILogger logger)
|
32
|
+
public DiscoveryWorker(string jobId, ExperimentsManager experimentsManager, ILogger logger)
|
31
33
|
{
|
34
|
+
_jobId = jobId;
|
32
35
|
_experimentsManager = experimentsManager;
|
33
36
|
_logger = logger;
|
34
37
|
}
|
@@ -46,13 +49,11 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
46
49
|
{
|
47
50
|
result = await RunAsync(repoRootPath, workspacePath);
|
48
51
|
}
|
49
|
-
catch (
|
50
|
-
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
|
52
|
+
catch (Exception ex)
|
51
53
|
{
|
52
54
|
result = new WorkspaceDiscoveryResult
|
53
55
|
{
|
54
|
-
|
55
|
-
ErrorDetails = "(" + string.Join("|", NuGetContext.GetPackageSourceUrls(PathHelper.JoinPath(repoRootPath, workspacePath))) + ")",
|
56
|
+
Error = JobErrorBase.ErrorFromException(ex, _jobId, PathHelper.JoinPath(repoRootPath, workspacePath)),
|
56
57
|
Path = workspacePath,
|
57
58
|
Projects = [],
|
58
59
|
};
|
@@ -111,8 +112,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
111
112
|
DotNetToolsJson = null,
|
112
113
|
GlobalJson = null,
|
113
114
|
Projects = projectResults.Where(p => p.IsSuccess).OrderBy(p => p.FilePath).ToImmutableArray(),
|
114
|
-
|
115
|
-
ErrorDetails = failedProjectResult.FilePath,
|
115
|
+
Error = failedProjectResult.Error,
|
116
116
|
IsSuccess = false,
|
117
117
|
};
|
118
118
|
|
@@ -180,8 +180,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
180
180
|
ImportedFiles = ImmutableArray<string>.Empty,
|
181
181
|
AdditionalFiles = ImmutableArray<string>.Empty,
|
182
182
|
IsSuccess = false,
|
183
|
-
|
184
|
-
ErrorDetails = "Failed to parse project file found at " + invalidProjectFile,
|
183
|
+
Error = new DependencyFileNotParseable(invalidProjectFile),
|
185
184
|
}];
|
186
185
|
}
|
187
186
|
if (projects.IsEmpty)
|
@@ -364,19 +363,62 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
364
363
|
}
|
365
364
|
}
|
366
365
|
|
367
|
-
if (
|
368
|
-
packagesConfigResult is not null &&
|
369
|
-
packagesConfigResult.Dependencies.Length > 0)
|
366
|
+
if (packagesConfigResult is not null)
|
370
367
|
{
|
371
|
-
//
|
372
|
-
results
|
368
|
+
// we might have to merge this dependency with some others
|
369
|
+
if (results.TryGetValue(relativeProjectPath, out var existingProjectDiscovery))
|
373
370
|
{
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
371
|
+
// merge SDK and packages.config results
|
372
|
+
var mergedDependencies = existingProjectDiscovery.Dependencies.Concat(packagesConfigResult.Dependencies)
|
373
|
+
.DistinctBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
|
374
|
+
.OrderBy(d => d.Name)
|
375
|
+
.ToImmutableArray();
|
376
|
+
var mergedTargetFrameworks = existingProjectDiscovery.TargetFrameworks.Concat(packagesConfigResult.TargetFrameworks)
|
377
|
+
.Select(t =>
|
378
|
+
{
|
379
|
+
try
|
380
|
+
{
|
381
|
+
var tfm = NuGetFramework.Parse(t);
|
382
|
+
return tfm.GetShortFolderName();
|
383
|
+
}
|
384
|
+
catch
|
385
|
+
{
|
386
|
+
return string.Empty;
|
387
|
+
}
|
388
|
+
})
|
389
|
+
.Where(tfm => !string.IsNullOrEmpty(tfm))
|
390
|
+
.Distinct()
|
391
|
+
.OrderBy(tfm => tfm)
|
392
|
+
.ToImmutableArray();
|
393
|
+
var mergedProperties = existingProjectDiscovery.Properties; // packages.config discovery doesn't produce properties
|
394
|
+
var mergedImportedFiles = existingProjectDiscovery.ImportedFiles; // packages.config discovery doesn't produce imported files
|
395
|
+
var mergedAdditionalFiles = existingProjectDiscovery.AdditionalFiles.Concat(packagesConfigResult.AdditionalFiles)
|
396
|
+
.Distinct(StringComparer.OrdinalIgnoreCase)
|
397
|
+
.OrderBy(f => f)
|
398
|
+
.ToImmutableArray();
|
399
|
+
var mergedResult = new ProjectDiscoveryResult()
|
400
|
+
{
|
401
|
+
FilePath = existingProjectDiscovery.FilePath,
|
402
|
+
Dependencies = mergedDependencies,
|
403
|
+
TargetFrameworks = mergedTargetFrameworks,
|
404
|
+
Properties = mergedProperties,
|
405
|
+
ImportedFiles = mergedImportedFiles,
|
406
|
+
AdditionalFiles = mergedAdditionalFiles,
|
407
|
+
};
|
408
|
+
results[relativeProjectPath] = mergedResult;
|
409
|
+
}
|
410
|
+
else
|
411
|
+
{
|
412
|
+
// add packages.config results
|
413
|
+
results[relativeProjectPath] = new ProjectDiscoveryResult()
|
414
|
+
{
|
415
|
+
FilePath = relativeProjectPath,
|
416
|
+
Dependencies = packagesConfigResult.Dependencies,
|
417
|
+
TargetFrameworks = packagesConfigResult.TargetFrameworks,
|
418
|
+
ImportedFiles = [], // no imported files resolved for packages.config scenarios
|
419
|
+
AdditionalFiles = packagesConfigResult.AdditionalFiles,
|
420
|
+
};
|
421
|
+
}
|
380
422
|
}
|
381
423
|
}
|
382
424
|
}
|
@@ -397,6 +439,6 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
397
439
|
}
|
398
440
|
|
399
441
|
var resultJson = JsonSerializer.Serialize(result, SerializerOptions);
|
400
|
-
await File.WriteAllTextAsync(
|
442
|
+
await File.WriteAllTextAsync(resultPath, resultJson);
|
401
443
|
}
|
402
444
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
-
|
2
|
+
|
3
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
3
4
|
|
4
5
|
namespace NuGetUpdater.Core.Discover;
|
5
6
|
|
@@ -8,8 +9,7 @@ public record ProjectDiscoveryResult : IDiscoveryResultWithDependencies
|
|
8
9
|
public required string FilePath { get; init; }
|
9
10
|
public required ImmutableArray<Dependency> Dependencies { get; init; }
|
10
11
|
public bool IsSuccess { get; init; } = true;
|
11
|
-
public
|
12
|
-
public ErrorType? ErrorType { get; init; }
|
12
|
+
public JobErrorBase? Error { get; init; } = null;
|
13
13
|
public ImmutableArray<Property> Properties { get; init; } = [];
|
14
14
|
public ImmutableArray<string> TargetFrameworks { get; init; } = [];
|
15
15
|
public ImmutableArray<string> ReferencedProjectPaths { get; init; } = [];
|
@@ -9,6 +9,8 @@ using NuGet.Versioning;
|
|
9
9
|
|
10
10
|
using NuGetUpdater.Core.Utilities;
|
11
11
|
|
12
|
+
using Semver;
|
13
|
+
|
12
14
|
using LoggerProperty = Microsoft.Build.Logging.StructuredLogger.Property;
|
13
15
|
|
14
16
|
namespace NuGetUpdater.Core.Discover;
|
@@ -72,6 +74,9 @@ internal static class SdkProjectDiscovery
|
|
72
74
|
Dictionary<string, HashSet<string>> topLevelPackagesPerProject = new(PathComparer.Instance);
|
73
75
|
// projectPath, packageNames
|
74
76
|
|
77
|
+
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesReplacedBySdkPerProject = new(PathComparer.Instance);
|
78
|
+
// projectPath tfm packageName, packageVersion
|
79
|
+
|
75
80
|
Dictionary<string, Dictionary<string, string>> resolvedProperties = new(PathComparer.Instance);
|
76
81
|
// projectPath propertyName, propertyValue
|
77
82
|
|
@@ -164,7 +169,7 @@ internal static class SdkProjectDiscovery
|
|
164
169
|
}
|
165
170
|
break;
|
166
171
|
case NamedNode namedNode when namedNode is AddItem or RemoveItem:
|
167
|
-
ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject);
|
172
|
+
ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, experimentsManager);
|
168
173
|
|
169
174
|
if (namedNode is AddItem addItem)
|
170
175
|
{
|
@@ -203,6 +208,70 @@ internal static class SdkProjectDiscovery
|
|
203
208
|
}
|
204
209
|
}
|
205
210
|
break;
|
211
|
+
case Target target when target.Name == "_HandlePackageFileConflicts":
|
212
|
+
// this only works if we've installed the exact SDK required
|
213
|
+
if (experimentsManager.InstallDotnetSdks)
|
214
|
+
{
|
215
|
+
var projectEvaluation = GetNearestProjectEvaluation(target);
|
216
|
+
if (projectEvaluation is null)
|
217
|
+
{
|
218
|
+
break;
|
219
|
+
}
|
220
|
+
|
221
|
+
var removedReferences = target.Children.OfType<RemoveItem>().FirstOrDefault(r => r.Name == "Reference");
|
222
|
+
var addedReferences = target.Children.OfType<AddItem>().FirstOrDefault(r => r.Name == "Reference");
|
223
|
+
if (removedReferences is null || addedReferences is null)
|
224
|
+
{
|
225
|
+
break;
|
226
|
+
}
|
227
|
+
|
228
|
+
foreach (var removedAssembly in removedReferences.Children.OfType<Item>())
|
229
|
+
{
|
230
|
+
var removedPackageName = GetChildMetadataValue(removedAssembly, "NuGetPackageId");
|
231
|
+
var removedFileName = Path.GetFileName(removedAssembly.Name);
|
232
|
+
if (removedPackageName is null || removedFileName is null)
|
233
|
+
{
|
234
|
+
continue;
|
235
|
+
}
|
236
|
+
|
237
|
+
var existingProjectPackagesByTfm = packagesPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(PathComparer.Instance));
|
238
|
+
var existingProjectPackages = existingProjectPackagesByTfm.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
|
239
|
+
if (!existingProjectPackages.ContainsKey(removedPackageName))
|
240
|
+
{
|
241
|
+
continue;
|
242
|
+
}
|
243
|
+
|
244
|
+
var correspondingAddedFile = addedReferences.Children.OfType<Item>()
|
245
|
+
.FirstOrDefault(i => removedFileName.Equals(Path.GetFileName(i.Name), StringComparison.OrdinalIgnoreCase));
|
246
|
+
if (correspondingAddedFile is null)
|
247
|
+
{
|
248
|
+
continue;
|
249
|
+
}
|
250
|
+
|
251
|
+
var runtimePackageName = GetChildMetadataValue(correspondingAddedFile, "NuGetPackageId");
|
252
|
+
var runtimePackageVersion = GetChildMetadataValue(correspondingAddedFile, "NuGetPackageVersion");
|
253
|
+
if (runtimePackageName is null ||
|
254
|
+
runtimePackageVersion is null ||
|
255
|
+
!SemVersion.TryParse(runtimePackageVersion, out var parsedRuntimePackageVersion))
|
256
|
+
{
|
257
|
+
continue;
|
258
|
+
}
|
259
|
+
|
260
|
+
var packageMapper = DotNetPackageCorrelationManager.GetPackageMapper();
|
261
|
+
var replacementPackageVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage(runtimePackageName, parsedRuntimePackageVersion, removedPackageName);
|
262
|
+
if (replacementPackageVersion is null)
|
263
|
+
{
|
264
|
+
continue;
|
265
|
+
}
|
266
|
+
|
267
|
+
var packagesPerThisProject = packagesReplacedBySdkPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(PathComparer.Instance));
|
268
|
+
var packagesPerTfm = packagesPerThisProject.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
|
269
|
+
packagesPerTfm[removedPackageName] = replacementPackageVersion.ToString();
|
270
|
+
var relativeProjectPath = Path.GetRelativePath(repoRootPath, projectEvaluation.ProjectFile).NormalizePathToUnix();
|
271
|
+
logger.Info($"Re-added SDK managed package [{removedPackageName}/{replacementPackageVersion}] to project [{relativeProjectPath}]");
|
272
|
+
}
|
273
|
+
}
|
274
|
+
break;
|
206
275
|
}
|
207
276
|
}, takeChildrenSnapshot: true);
|
208
277
|
}
|
@@ -228,6 +297,33 @@ internal static class SdkProjectDiscovery
|
|
228
297
|
{
|
229
298
|
// gather some project-level information
|
230
299
|
var packagesByTfm = packagesPerProject[projectPath];
|
300
|
+
if (packagesReplacedBySdkPerProject.TryGetValue(projectPath, out var packagesReplacedBySdk))
|
301
|
+
{
|
302
|
+
var consolidatedPackagesByTfm = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
303
|
+
|
304
|
+
// copy the first dictionary
|
305
|
+
foreach (var kvp in packagesByTfm)
|
306
|
+
{
|
307
|
+
var tfm = kvp.Key;
|
308
|
+
var packages = kvp.Value;
|
309
|
+
consolidatedPackagesByTfm[tfm] = packages;
|
310
|
+
}
|
311
|
+
|
312
|
+
// merge in the second
|
313
|
+
foreach (var kvp in packagesReplacedBySdk)
|
314
|
+
{
|
315
|
+
var tfm = kvp.Key;
|
316
|
+
var packages = kvp.Value;
|
317
|
+
var replacedPackages = consolidatedPackagesByTfm.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
|
318
|
+
foreach (var packagePair in packages)
|
319
|
+
{
|
320
|
+
replacedPackages[packagePair.Key] = packagePair.Value;
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
packagesByTfm = consolidatedPackagesByTfm;
|
325
|
+
}
|
326
|
+
|
231
327
|
var projectFullDirectory = Path.GetDirectoryName(projectPath)!;
|
232
328
|
var doc = XDocument.Load(projectPath);
|
233
329
|
var localPropertyDefinitionElements = doc.Root!.XPathSelectElements("/Project/PropertyGroup/*");
|
@@ -288,7 +384,8 @@ internal static class SdkProjectDiscovery
|
|
288
384
|
private static void ProcessResolvedPackageReference(
|
289
385
|
NamedNode node,
|
290
386
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject, // projectPath -> tfm -> (packageName, packageVersion)
|
291
|
-
Dictionary<string, HashSet<string>> topLevelPackagesPerProject
|
387
|
+
Dictionary<string, HashSet<string>> topLevelPackagesPerProject,
|
388
|
+
ExperimentsManager experimentsManager
|
292
389
|
)
|
293
390
|
{
|
294
391
|
var doRemoveOperation = node is RemoveItem;
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<Project>
|
2
|
+
|
3
|
+
<PropertyGroup>
|
4
|
+
<PackageCorrelationCliDirectory>$(MSBuildThisFileDirectory)..\DotNetPackageCorrelation.Cli</PackageCorrelationCliDirectory>
|
5
|
+
<DotNetCoreDirectory>$(MSBuildThisFileDirectory)..\..\dotnet-core</DotNetCoreDirectory>
|
6
|
+
<PackageCorrelationFile>$(MSBuildThisFileDirectory)dotnet-package-correlation.json</PackageCorrelationFile>
|
7
|
+
</PropertyGroup>
|
8
|
+
|
9
|
+
<ItemGroup>
|
10
|
+
<None Include="$(PackageCorrelationFile)" CopyToOutputDirectory="PreserveNewest" />
|
11
|
+
<_DotNetCoreFiles Include="$(DotNetCoreDirectory)\**\*" />
|
12
|
+
</ItemGroup>
|
13
|
+
|
14
|
+
<Target Name="BuildDotNetPackageCorrelationFile" Inputs="@(_DotNetCoreFiles)" Outputs="$(PackageCorrelationFile)" BeforeTargets="GetCopyToOutputDirectoryItems">
|
15
|
+
<Exec Command="dotnet run --core-location "$(DotNetCoreDirectory)" --output "$(PackageCorrelationFile)"" WorkingDirectory="$(PackageCorrelationCliDirectory)" />
|
16
|
+
</Target>
|
17
|
+
|
18
|
+
<Target Name="CleanDotNetPackageCorrelationFile" BeforeTargets="Clean">
|
19
|
+
<Delete Files="$(PackageCorrelationFile)" Condition="Exists('$(PackageCorrelationFile)')" />
|
20
|
+
</Target>
|
21
|
+
|
22
|
+
<Target Name="RebuildDotNetPackageCorrelationFile" BeforeTargets="Rebuild" DependsOnTargets="CleanDotNetPackageCorrelationFile">
|
23
|
+
</Target>
|
24
|
+
|
25
|
+
</Project>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
using System.Text.Json;
|
2
2
|
|
3
3
|
using NuGetUpdater.Core.Run;
|
4
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
4
5
|
|
5
6
|
namespace NuGetUpdater.Core;
|
6
7
|
|
@@ -30,19 +31,28 @@ public record ExperimentsManager
|
|
30
31
|
};
|
31
32
|
}
|
32
33
|
|
33
|
-
public static async Task<ExperimentsManager> FromJobFileAsync(string
|
34
|
+
public static async Task<(ExperimentsManager ExperimentsManager, JobErrorBase? Error)> FromJobFileAsync(string jobId, string jobFilePath)
|
34
35
|
{
|
35
|
-
var
|
36
|
+
var experimentsManager = new ExperimentsManager();
|
37
|
+
JobErrorBase? error = null;
|
38
|
+
var jobFileContent = string.Empty;
|
36
39
|
try
|
37
40
|
{
|
41
|
+
jobFileContent = await File.ReadAllTextAsync(jobFilePath);
|
38
42
|
var jobWrapper = RunWorker.Deserialize(jobFileContent);
|
39
|
-
|
43
|
+
experimentsManager = GetExperimentsManager(jobWrapper.Job.Experiments);
|
40
44
|
}
|
41
45
|
catch (JsonException ex)
|
42
46
|
{
|
43
|
-
|
44
|
-
|
47
|
+
// this is a very specific case where we want to log the JSON contents for easier debugging
|
48
|
+
error = JobErrorBase.ErrorFromException(new NotSupportedException($"Error deserializing job file contents: {jobFileContent}", ex), jobId, Environment.CurrentDirectory); // TODO
|
45
49
|
}
|
50
|
+
catch (Exception ex)
|
51
|
+
{
|
52
|
+
error = JobErrorBase.ErrorFromException(ex, jobId, Environment.CurrentDirectory); // TODO
|
53
|
+
}
|
54
|
+
|
55
|
+
return (experimentsManager, error);
|
46
56
|
}
|
47
57
|
|
48
58
|
private static bool IsEnabled(Dictionary<string, object>? experiments, string experimentName)
|
@@ -13,12 +13,16 @@ internal sealed class PackagesConfigBuildFile : XmlBuildFile
|
|
13
13
|
public PackagesConfigBuildFile(string basePath, string path, XmlDocumentSyntax contents)
|
14
14
|
: base(basePath, path, contents)
|
15
15
|
{
|
16
|
+
var invalidPackageElements = Packages.Where(p => p.GetAttribute("id") is null || p.GetAttribute("version") is null).ToList();
|
17
|
+
if (invalidPackageElements.Any())
|
18
|
+
{
|
19
|
+
throw new UnparseableFileException("`package` element missing `id` or `version` attributes", path);
|
20
|
+
}
|
16
21
|
}
|
17
22
|
|
18
23
|
public IEnumerable<IXmlElementSyntax> Packages => Contents.RootSyntax.GetElements("package", StringComparison.OrdinalIgnoreCase);
|
19
24
|
|
20
25
|
public IEnumerable<Dependency> GetDependencies() => Packages
|
21
|
-
.Where(p => p.GetAttribute("id") is not null && p.GetAttribute("version") is not null)
|
22
26
|
.Select(p => new Dependency(
|
23
27
|
p.GetAttributeValue("id", StringComparison.OrdinalIgnoreCase),
|
24
28
|
p.GetAttributeValue("version", StringComparison.OrdinalIgnoreCase),
|