Skip to content

Commit

Permalink
Add package metadata page
Browse files Browse the repository at this point in the history
  • Loading branch information
joelverhagen committed Dec 21, 2023
1 parent 2a46827 commit bff5f8c
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 37 deletions.
113 changes: 90 additions & 23 deletions src/Knapcode.NuGetTools.Logic.Direct/VersionedToolsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ public async Task<IEnumerable<string>> GetAvailableVersionsAsync(CancellationTok
}
}

public async Task<IReadOnlyList<NuGetPackage>?> GetPackagesAsync(string version, CancellationToken token)
{
var matchingVersion = await GetMatchingVersionAsync(version);

if (matchingVersion == null)
{
return null;
}

var context = await InitializeAndGetContextAsync(matchingVersion);

return context.Packages;
}

public async Task<IFrameworkPrecedenceService?> GetFrameworkPrecedenceServiceAsync(string version, CancellationToken token)
{
var matchingVersion = await GetMatchingVersionAsync(version);
Expand Down Expand Up @@ -189,11 +203,16 @@ public Task<string> GetLatestVersionAsync(CancellationToken token)
return matchedVersion;
}

private async Task<INuGetLogic> InitializeAndGetLogicAsync(NuGetVersion version)
private async Task<VersionContext> InitializeAndGetContextAsync(NuGetVersion version)
{
await _versionSet.Value;

var context = await GetContextAsync(_settings.GlobalPackagesFolder, version);
return await GetContextAsync(_settings.GlobalPackagesFolder, version);
}

private async Task<INuGetLogic> InitializeAndGetLogicAsync(NuGetVersion version)
{
var context = await InitializeAndGetContextAsync(version);

return context.Logic!;
}
Expand Down Expand Up @@ -237,7 +256,7 @@ private static VersionContext InitializeContext(string packagesFolder, NuGetVers
name: $"NuGet {version.ToNormalizedString()}",
isCollectible: false);

var logicAssembly = release switch
(var logicAssembly, var assemblyInfo) = release switch
{
NuGetRelease.Version2x => GetV2Implementation(packagesFolder, version, assemblyLoadContext),
NuGetRelease.Version3x => GetV3Implementation(packagesFolder, version, assemblyLoadContext),
Expand All @@ -247,38 +266,82 @@ private static VersionContext InitializeContext(string packagesFolder, NuGetVers
var logicType = logicAssembly
.ExportedTypes
.First(t => t.IsClass && !t.IsAbstract && t.IsAssignableTo(typeof(INuGetLogic)));
var logic = (INuGetLogic)Activator.CreateInstance(logicType)!;

context.Logic = (INuGetLogic)Activator.CreateInstance(logicType)!;
context.Packages = GetPackageInfo(assemblyInfo, logic);
context.Logic = logic;
context.AssemblyLoadContext = assemblyLoadContext;

return context;
}

private static Assembly GetV2Implementation(string packagesFolder, NuGetVersion version, AssemblyLoadContext context)
private static List<NuGetPackage> GetPackageInfo(List<(PackageIdentity Identity, string Path, Assembly Assembly)> assemblyInfo, INuGetLogic logic)
{
var coreIdentity = new PackageIdentity(Constants.CoreId, version);
var assemblies = LoadPackageAssemblies(packagesFolder, coreIdentity, context);
var assemblyNameToInfo = assemblyInfo.ToDictionary(p => p.Assembly.FullName!);

var identityGroups = assemblyInfo.GroupBy(x => x.Identity);
var packages = new Dictionary<PackageIdentity, List<NuGetAssembly>>();

foreach (var assemblyName in logic.AssemblyNames)
{
if (!assemblyNameToInfo.TryGetValue(assemblyName, out var info))
{
throw new InvalidOperationException($"Could not find a relative path for assembly '{assemblyName}'.");
}

if (!packages.TryGetValue(info.Identity, out var assemblies))
{
assemblies = new List<NuGetAssembly>();
packages.Add(info.Identity, assemblies);
}

assemblies.Add(NuGetAssembly.FromAssembly(
info.Path,
info.Assembly));
}

var logicAssembly = RewriteProxyReferences(context, NuGetLogic2xModule, assemblies);
return logicAssembly;
return packages
.Select(p => new NuGetPackage(p.Key.Id, p.Key.Version.ToNormalizedString(), p.Value))
.ToList();
}

private static Assembly GetV3Implementation(string packagesFolder, NuGetVersion version, AssemblyLoadContext context)
private static (Assembly Logic, List<(PackageIdentity Identity, string Path, Assembly Assembly)> Assemblies) GetV2Implementation(
string packagesFolder,
NuGetVersion version,
AssemblyLoadContext context)
{
var versioningIdentity = new PackageIdentity(Constants.VersioningId, version);
var assemblies = LoadPackageAssemblies(packagesFolder, versioningIdentity, context);
var assemblies = LoadPackageAssemblies(
packagesFolder,
new PackageIdentity(Constants.CoreId, version),
context).ToList();

var frameworksIdentity = new PackageIdentity(Constants.FrameworksId, version);
assemblies.AddRange(LoadPackageAssemblies(packagesFolder, frameworksIdentity, context));
var logicAssembly = RewriteProxyReferences(context, NuGetLogic2xModule, assemblies.Select(x => x.Assembly));
return (logicAssembly, assemblies);
}

var logicAssembly = RewriteProxyReferences(context, NuGetLogic3xModule, assemblies);
return logicAssembly;
private static (Assembly Logic, List<(PackageIdentity Identity, string Path, Assembly Assembly)> Assemblies) GetV3Implementation(
string packagesFolder,
NuGetVersion version,
AssemblyLoadContext context)
{
var assemblies = LoadPackageAssemblies(
packagesFolder,
new PackageIdentity(Constants.VersioningId, version),
context).ToList();

assemblies.AddRange(LoadPackageAssemblies(
packagesFolder,
new PackageIdentity(Constants.FrameworksId, version),
context));

var logicAssembly = RewriteProxyReferences(context, NuGetLogic3xModule, assemblies.Select(x => x.Assembly));
return (logicAssembly, assemblies);
}

private static Assembly RewriteProxyReferences(
AssemblyLoadContext context,
Lazy<ModuleDefinition> lazyBaseModule,
List<Assembly> newReferences)
IEnumerable<Assembly> newReferences)
{
using var moduleStream = new MemoryStream();

Expand All @@ -304,7 +367,10 @@ private static Assembly RewriteProxyReferences(
return context.LoadFromStream(moduleStream);
}

private static List<Assembly> LoadPackageAssemblies(string packagesFolder, PackageIdentity packageIdentity, AssemblyLoadContext context)
private static IEnumerable<(PackageIdentity Identity, string Path, Assembly Assembly)> LoadPackageAssemblies(
string packagesFolder,
PackageIdentity packageIdentity,
AssemblyLoadContext context)
{
var pathResolver = new VersionFolderPathResolver(packagesFolder);
var hashPath = pathResolver.GetHashPath(packageIdentity.Id, packageIdentity.Version);
Expand All @@ -322,7 +388,7 @@ private static List<Assembly> LoadPackageAssemblies(string packagesFolder, Packa
{
if (TryLoadWithFramework(context, framework, installPath, packageReader, out var assemblies))
{
return assemblies;
return assemblies.Select(x => (packageIdentity, x.Path, x.Assembly));
}
}

Expand All @@ -338,7 +404,7 @@ private static bool TryLoadWithFramework(
NuGetFramework framework,
string installPath,
PackageFolderReader packageReader,
[NotNullWhen(true)] out List<Assembly>? assemblies)
[NotNullWhen(true)] out List<(string Path, Assembly Assembly)>? pathToAssembly)
{
var conventions = new ManagedCodeConventions(null);
var criteria = conventions.Criteria.ForFramework(framework);
Expand All @@ -358,17 +424,17 @@ private static bool TryLoadWithFramework(

if (runtimeGroup is null)
{
assemblies = null;
pathToAssembly = null;
return false;
}

assemblies = new List<Assembly>();
pathToAssembly = new List<(string Path, Assembly Assembly)>();
foreach (var asset in runtimeGroup.Items)
{
var absolutePath = Path.Combine(
installPath,
asset.Path.Replace(AssetDirectorySeparator, Path.DirectorySeparatorChar));
assemblies.Add(context.LoadFromAssemblyPath(absolutePath));
pathToAssembly.Add((asset.Path, context.LoadFromAssemblyPath(absolutePath)));
}

return true;
Expand All @@ -378,6 +444,7 @@ private class VersionContext
{
public SemaphoreSlim Lock { get; } = new SemaphoreSlim(initialCount: 1);
public AssemblyLoadContext? AssemblyLoadContext { get; set; }
public IReadOnlyList<NuGetPackage>? Packages { get; set; }
public INuGetLogic? Logic { get; set; }
}
}
7 changes: 5 additions & 2 deletions src/Knapcode.NuGetTools.Logic.NuGet2x/NuGetLogic2x.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ public NuGetLogic2x()
Framework = new FrameworkLogic2x();
Version = new VersionLogic2x();
VersionRange = new VersionRangeLogic2x();
Assemblies = new[] { NuGetAssembly.FromType<SemanticVersion>() };
AssemblyNames = new[]
{
typeof(VersionUtility).Assembly.FullName ?? throw new NotSupportedException(),
};
}

public IFrameworkLogic Framework { get; }
public IVersionLogic Version { get; }
public IVersionRangeLogic VersionRange { get; }
public IReadOnlyList<NuGetAssembly> Assemblies { get; }
public IReadOnlyList<string> AssemblyNames { get; }
}
8 changes: 4 additions & 4 deletions src/Knapcode.NuGetTools.Logic.NuGet3x/NuGetLogic3x.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ public NuGetLogic3x()
Framework = new FrameworkLogic3x();
Version = new VersionLogic3x();
VersionRange = new VersionRangeLogic3x();
Assemblies = new[]
AssemblyNames = new[]
{
NuGetAssembly.FromType<NuGetFramework>(),
NuGetAssembly.FromType<NuGetVersion>(),
typeof(NuGetFramework).Assembly.FullName ?? throw new NotSupportedException(),
typeof(NuGetVersion).Assembly.FullName ?? throw new NotSupportedException(),
};
}

public IFrameworkLogic Framework { get; }
public IVersionLogic Version { get; }
public IVersionRangeLogic VersionRange { get; }
public IReadOnlyList<NuGetAssembly> Assemblies { get; }
public IReadOnlyList<string> AssemblyNames { get; }
}
14 changes: 14 additions & 0 deletions src/Knapcode.NuGetTools.Logic/DirectToolsFactory.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Knapcode.NuGetTools.Logic.Wrappers;

namespace Knapcode.NuGetTools.Logic;

public class DirectToolsFactory : IToolsFactory
Expand Down Expand Up @@ -73,4 +75,16 @@ public Task<string> GetLatestVersionAsync(CancellationToken token)
{
return Task.FromResult(_version);
}

public Task<IReadOnlyList<NuGetPackage>?> GetPackagesAsync(string version, CancellationToken token)
{
IReadOnlyList<NuGetPackage>? output = null;

if (version == _version)
{
output = new List<NuGetPackage>();
}

return Task.FromResult(output);
}
}
4 changes: 4 additions & 0 deletions src/Knapcode.NuGetTools.Logic/IToolsFactory.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Knapcode.NuGetTools.Logic.Wrappers;

namespace Knapcode.NuGetTools.Logic;

public interface IToolsFactory
Expand All @@ -17,4 +19,6 @@ public interface IToolsFactory
Task<IFrameworkPrecedenceService?> GetFrameworkPrecedenceServiceAsync(string version, CancellationToken token);

Task<IFrameworkList> GetFrameworkListAsync(CancellationToken token);

Task<IReadOnlyList<NuGetPackage>?> GetPackagesAsync(string version, CancellationToken token);
}
2 changes: 1 addition & 1 deletion src/Knapcode.NuGetTools.Logic/Wrappers/INuGetLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ public interface INuGetLogic
IFrameworkLogic Framework { get; }
IVersionLogic Version { get; }
IVersionRangeLogic VersionRange { get; }
IReadOnlyList<NuGetAssembly> Assemblies { get; }
IReadOnlyList<string> AssemblyNames { get; }
}
33 changes: 27 additions & 6 deletions src/Knapcode.NuGetTools.Logic/Wrappers/NuGetAssembly.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
using System.Collections.ObjectModel;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;

namespace Knapcode.NuGetTools.Logic.Wrappers;

public record NuGetAssembly(
string RelativePath,
string AssemblyName,
string AssemblyAttributes)
string Sha256Hash,
long FileSize,
string CustomAttributes)
{
public static NuGetAssembly FromType<T>()
public static NuGetAssembly FromAssembly(string relativePath, Assembly assembly)
{
var assembly = typeof(T).Assembly;
var assemblyName = assembly.FullName ?? throw new ArgumentException("No full name could be found.");

string sha256Hash;
long fileSize;
using (var fileStream = new FileStream(assembly.Location, FileMode.Open, FileAccess.Read))
{
sha256Hash = Convert.ToHexString(SHA256.HashData(fileStream));
fileSize = fileStream.Length;
}

var customAttributes = GetCustomAttributes(assembly);

return new NuGetAssembly(
relativePath,
assemblyName,
sha256Hash,
fileSize,
customAttributes);
}

private static string GetCustomAttributes(Assembly assembly)
{
var builder = new StringBuilder();
foreach (var attribute in assembly.CustomAttributes)
{
Expand Down Expand Up @@ -65,9 +88,7 @@ public static NuGetAssembly FromType<T>()
builder.AppendLine(")]");
}

var assemblyAttributes = builder.ToString();

return new NuGetAssembly(assemblyName, assemblyAttributes);
return builder.ToString();
}

private static void AppendArgument(StringBuilder builder, CustomAttributeTypedArgument arg)
Expand Down
6 changes: 6 additions & 0 deletions src/Knapcode.NuGetTools.Logic/Wrappers/NuGetPackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Knapcode.NuGetTools.Logic.Wrappers;

public record NuGetPackage(
string Id,
string Version,
IReadOnlyList<NuGetAssembly> Assemblies);
20 changes: 20 additions & 0 deletions src/Knapcode.NuGetTools.Website/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,26 @@ public async Task<IActionResult> FrameworkPrecedence([FromRoute] string nuGetVer
return View(versionedOutput);
}

[HttpGet("/{nuGetVersion}/package-metadata")]
public async Task<IActionResult> PackageMetadata([FromRoute] string nuGetVersion, CancellationToken token)
{
var redirect = await GetVersionRedirectAsync(token);
if (redirect != null)
{
return redirect;
}

var packages = await _toolsFactory.GetPackagesAsync(nuGetVersion, token);
if (packages == null)
{
return NotFound();
}

var output = await GetSelectedVersionOutputAsync(nuGetVersion, packages, token);

return View(output);
}

private async Task<SelectedVersionOutput> GetSelectedVersionOutputAsync(IVersionedService service, CancellationToken token)
{
return await GetSelectedVersionOutputAsync(service.Version, (object?)null, token);
Expand Down
Loading

0 comments on commit bff5f8c

Please sign in to comment.