Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into vNext
Browse files Browse the repository at this point in the history
# Conflicts:
#	Doki.sln
  • Loading branch information
DavidVollmers committed Jun 22, 2024
2 parents e0d2d06 + 5010f7b commit 1a6998a
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 33 deletions.
7 changes: 7 additions & 0 deletions Doki.sln
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.Tests.Common", "tests\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.Extensions", "src\Doki.Extensions\Doki.Extensions.csproj", "{9961F717-DBD7-4987-9E68-2B85BB5830B4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.CommandLine.Tests", "tests\Doki.CommandLine.Tests\Doki.CommandLine.Tests.csproj", "{67B12AF1-2696-411F-ADFD-B5C32A43BA71}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -54,6 +56,7 @@ Global
{6CCD9EE6-B3FC-485F-9155-553165141B20} = {8C7B5305-B599-4F08-B28B-DD9F1715DD51}
{0293D689-DFDC-4A78-80D8-BFC11DB0A175} = {08041208-BE3D-4BE8-9AF7-806B73985275}
{0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC} = {8C7B5305-B599-4F08-B28B-DD9F1715DD51}
{67B12AF1-2696-411F-ADFD-B5C32A43BA71} = {8C7B5305-B599-4F08-B28B-DD9F1715DD51}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6F31B87A-2BD3-4FB4-8C08-7E059A338D4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -116,5 +119,9 @@ Global
{9961F717-DBD7-4987-9E68-2B85BB5830B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9961F717-DBD7-4987-9E68-2B85BB5830B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9961F717-DBD7-4987-9E68-2B85BB5830B4}.Release|Any CPU.Build.0 = Release|Any CPU
{67B12AF1-2696-411F-ADFD-B5C32A43BA71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67B12AF1-2696-411F-ADFD-B5C32A43BA71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67B12AF1-2696-411F-ADFD-B5C32A43BA71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67B12AF1-2696-411F-ADFD-B5C32A43BA71}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion src/Doki.CommandLine/Commands/GenerateCommand.Outputs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal partial class GenerateCommand

var nugetFolder = Path.Combine(workingDirectory.FullName, ".doki", "nuget");

using var nugetLoader = new NuGetLoader(output.From);
using var nugetLoader = new NuGetLoader(_logger, output.From);

var assemblyPath =
await nugetLoader.LoadPackageAsync(output.Type, nugetFolder, allowPreview, cancellationToken);
Expand Down
10 changes: 7 additions & 3 deletions src/Doki.CommandLine/Doki.CommandLine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@
<PackageOutputPath>..\..\nuget</PackageOutputPath>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Doki.CommandLine.Tests"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
<PackageReference Include="NuGet.Resolver" Version="6.9.1" />
<PackageReference Include="NuGet.Resolver" Version="6.9.1"/>
<PackageReference Include="Spectre.Console" Version="0.48.0"/>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Doki.Output.Extensions\Doki.Output.Extensions.csproj" />
<ProjectReference Include="..\Doki.Output.Extensions\Doki.Output.Extensions.csproj"/>
<ProjectReference Include="..\Doki\Doki.csproj"/>
</ItemGroup>

Expand Down
87 changes: 58 additions & 29 deletions src/Doki.CommandLine/NuGet/NuGetLoader.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyModel;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.Packaging;
Expand All @@ -16,12 +15,13 @@ internal class NuGetLoader : IDisposable
{
private readonly SourceCacheContext _cacheContext = new();

//TODO use console logger (ASCII)
private readonly ILogger _logger = NullLogger.Instance;
private readonly NuGetLogger _logger;
private readonly SourceRepositoryProvider _sourceRepositoryProvider;

public NuGetLoader(string? source = null)
public NuGetLoader(Microsoft.Extensions.Logging.ILogger logger, string? source = null)
{
_logger = new NuGetLogger(logger);

var sources = new List<PackageSource>
{
new("https://api.nuget.org/v3/index.json")
Expand All @@ -39,19 +39,27 @@ public async Task<string> LoadPackageAsync(string packageId, string destinationD
{
ArgumentNullException.ThrowIfNull(packageId, nameof(packageId));

var packageIdentity = await GetPackageIdentityAsync(packageId, allowPreview, cancellationToken);
var currentVersion = NuGetVersion.Parse(typeof(DocumentationObject).Assembly.GetName().Version!.ToString());

var packageIdentities = await GetPackageIdentitiesAsync(packageId, allowPreview, cancellationToken);
foreach (var packageIdentity in packageIdentities)
{
var dependencyInfos = new HashSet<SourcePackageDependencyInfo>();
var result = await ScanPackagesAsync(currentVersion, packageIdentity, dependencyInfos, cancellationToken);
if (!result) continue;

var dependencyInfos = new HashSet<SourcePackageDependencyInfo>();
await CollectPackagesAsync(packageIdentity, dependencyInfos, cancellationToken);
var packagesToInstall = GetPackagesToInstall(packageId, dependencyInfos.ToArray());

var packagesToInstall = GetPackagesToInstall(packageId, dependencyInfos.ToArray());
var settings = Settings.LoadDefaultSettings(destinationDirectory);

var settings = Settings.LoadDefaultSettings(destinationDirectory);
await InstallPackagesAsync(packagesToInstall, destinationDirectory, settings, cancellationToken);

await InstallPackagesAsync(packagesToInstall, destinationDirectory, settings, cancellationToken);
return Path.Combine(destinationDirectory, $"{packageIdentity.Id}.{packageIdentity.Version}", "lib",
"net8.0",
$"{packageIdentity.Id}.dll");
}

return Path.Combine(destinationDirectory, $"{packageIdentity.Id}.{packageIdentity.Version}", "lib", "net8.0",
$"{packageIdentity.Id}.dll");
throw new InvalidOperationException($"No applicable package '{packageId}' found for version: {currentVersion}");
}

private async Task InstallPackagesAsync(IEnumerable<SourcePackageDependencyInfo> packagesToInstall,
Expand Down Expand Up @@ -90,9 +98,9 @@ private IEnumerable<SourcePackageDependencyInfo> GetPackagesToInstall(string pac
var resolverContext = new PackageResolverContext(
DependencyBehavior.Lowest,
new[] { packageId },
Enumerable.Empty<string>(),
Enumerable.Empty<PackageReference>(),
Enumerable.Empty<PackageIdentity>(),
[],
[],
[],
dependencyInfos,
_sourceRepositoryProvider.GetRepositories().Select(s => s.PackageSource),
_logger);
Expand All @@ -103,10 +111,10 @@ private IEnumerable<SourcePackageDependencyInfo> GetPackagesToInstall(string pac
.Select(p => dependencyInfos.Single(x => PackageIdentityComparer.Default.Equals(x, p)));
}

private async Task CollectPackagesAsync(PackageIdentity package, ICollection<SourcePackageDependencyInfo> packages,
CancellationToken cancellationToken)
private async Task<bool> ScanPackagesAsync(NuGetVersion currentVersion, PackageIdentity package,
ICollection<SourcePackageDependencyInfo> packages, CancellationToken cancellationToken)
{
if (packages.Contains(package)) return;
if (packages.Contains(package)) return true;

foreach (var repository in _sourceRepositoryProvider.GetRepositories())
{
Expand All @@ -120,24 +128,45 @@ private async Task CollectPackagesAsync(PackageIdentity package, ICollection<Sou
cancellationToken);
if (dependencyInfo == null) continue;

var dependencies = new List<PackageDependency>();
foreach (var dependency in dependencyInfo.Dependencies)
{
if (dependency.Id is "Doki" or "Doki.Abstractions" or "Doki.Output.Extensions"
or "Doki.Output.Abstractions")
{
if (dependency.VersionRange.Satisfies(currentVersion)) continue;

_logger.LogDebug(
$"Doki dependency '{dependency.Id}' version '{dependency.VersionRange}' does not satisfy current version '{currentVersion}'");

return false;
}

if (!IsDependencyProvided(dependency)) dependencies.Add(dependency);
}

var filteredSourceDependency = new SourcePackageDependencyInfo(
dependencyInfo.Id,
dependencyInfo.Version,
dependencyInfo.Dependencies.Where(d => !IsDependencyProvided(d)),
dependencies,
dependencyInfo.Listed,
dependencyInfo.Source);

packages.Add(filteredSourceDependency);

foreach (var dependency in filteredSourceDependency.Dependencies)
foreach (var dependency in dependencies)
{
await CollectPackagesAsync(new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion),
packages, cancellationToken);
var result = await ScanPackagesAsync(currentVersion,
new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion), packages,
cancellationToken);
if (!result) return false;
}
}

return true;
}

private async Task<PackageIdentity> GetPackageIdentityAsync(string packageId, bool allowPreview,
private async Task<IEnumerable<PackageIdentity>> GetPackageIdentitiesAsync(string packageId, bool allowPreview,
CancellationToken cancellationToken)
{
foreach (var repository in _sourceRepositoryProvider.GetRepositories())
Expand All @@ -147,26 +176,26 @@ private async Task<PackageIdentity> GetPackageIdentityAsync(string packageId, bo
var versions = await packages.GetAllVersionsAsync(packageId, _cacheContext, _logger,
cancellationToken);

var latestVersion = versions.Where(v => allowPreview || !v.IsPrerelease).MaxBy(v => v);
if (latestVersion == null) continue;
var allowedVersions = versions.Where(v => allowPreview || !v.IsPrerelease).ToArray();

return new PackageIdentity(packageId, latestVersion);
if (allowedVersions.Length != 0)
return allowedVersions
.OrderByDescending(v => v)
.Select(v => new PackageIdentity(packageId, v));
}

throw new InvalidOperationException($"Package '{packageId}' not found.");
}

private static bool IsDependencyProvided(PackageDependency dependency)
{
if (dependency.Id is "Doki" or "Doki.Abstractions" or "Doki.Output.Abstractions") return true;

var runtimeLibrary = DependencyContext.Default!.RuntimeLibraries.FirstOrDefault(r => r.Name == dependency.Id);

if (runtimeLibrary == null) return false;

var parsedLibVersion = NuGetVersion.Parse(runtimeLibrary.Version);

return parsedLibVersion.IsPrerelease || dependency.VersionRange.Satisfies(parsedLibVersion);
return dependency.VersionRange.Satisfies(parsedLibVersion);
}

public void Dispose()
Expand Down
85 changes: 85 additions & 0 deletions src/Doki.CommandLine/NuGet/NuGetLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Microsoft.Extensions.Logging;
using NuGet.Common;
using ILogger = NuGet.Common.ILogger;
using LogLevel = NuGet.Common.LogLevel;

namespace Doki.CommandLine.NuGet;

internal class NuGetLogger(Microsoft.Extensions.Logging.ILogger logger) : ILogger
{
public void LogDebug(string data)
{
logger.LogDebug(data);
}

public void LogVerbose(string data)
{
logger.LogTrace(data);
}

public void LogInformation(string data)
{
logger.LogInformation(data);
}

public void LogMinimal(string data)
{
logger.LogInformation(data);
}

public void LogWarning(string data)
{
logger.LogWarning(data);
}

public void LogError(string data)
{
logger.LogError(data);
}

public void LogInformationSummary(string data)
{
logger.LogInformation(data);
}

public void Log(LogLevel level, string data)
{
switch (level)
{
case LogLevel.Debug:
LogDebug(data);
break;
case LogLevel.Verbose:
LogVerbose(data);
break;
case LogLevel.Information:
LogInformation(data);
break;
case LogLevel.Minimal:
LogMinimal(data);
break;
case LogLevel.Warning:
LogWarning(data);
break;
case LogLevel.Error:
LogError(data);
break;
}
}

public Task LogAsync(LogLevel level, string data)
{
Log(level, data);
return Task.CompletedTask;
}

public void Log(ILogMessage message)
{
Log(message.Level, message.Message);
}

public Task LogAsync(ILogMessage message)
{
return LogAsync(message.Level, message.Message);
}
}
30 changes: 30 additions & 0 deletions tests/Doki.CommandLine.Tests/Doki.CommandLine.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Doki.CommandLine\Doki.CommandLine.csproj" />
<ProjectReference Include="..\Doki.Tests.Common\Doki.Tests.Common.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions tests/Doki.CommandLine.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
27 changes: 27 additions & 0 deletions tests/Doki.CommandLine.Tests/NuGetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Doki.CommandLine.NuGet;
using Doki.Tests.Common;
using Xunit.Abstractions;

namespace Doki.CommandLine.Tests;

public class NuGetTests(ITestOutputHelper testOutputHelper)
{
[Fact]
public async Task NuGetLoader_TestAsync()
{
const string packageId = "Doki.Output.Json";
var tmpDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

var logger = new TestOutputLogger(testOutputHelper);

using var loader = new NuGetLoader(logger);

await loader.LoadPackageAsync(packageId, tmpDirectory);

Assert.False(logger.HadError);

var dllPath = Path.Combine(tmpDirectory, $"{packageId}.1.0.0", "lib", "net8.0", $"{packageId}.dll");

Assert.True(File.Exists(dllPath));
}
}

0 comments on commit 1a6998a

Please sign in to comment.