Skip to content

Commit

Permalink
Added: Support for Readme & Changelog in NuGet Packages
Browse files Browse the repository at this point in the history
  • Loading branch information
Sewer56 committed Aug 21, 2022
1 parent 436f64e commit 43a42e2
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,11 @@ public void DownloadPackage()

Directory.Delete(tempLocation.FullName, true);
}

[Fact]
public void DownloadNuSpec()
{
var nuspec = _nugetRepository.DownloadNuspecAsync(new PackageIdentity(TestPackageName, new NuGetVersion("1.0.0"))).Result;
Assert.NotNull(nuspec);
}
}
16 changes: 16 additions & 0 deletions source/Reloaded.Mod.Loader.Update.Packaging/Publisher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ public static async Task<ReleaseMetadata> PublishAsync(PublishArgs args)
{
packageBuilder.Title = args.ModTuple.Config.ModName;

// Add readme
if (!string.IsNullOrEmpty(args.ReadmePath))
{
const string readmeFilePath = "README.md";
packageBuilder.Readme = readmeFilePath;
packageBuilder.Files.Add(new PhysicalPackageFile()
{
SourcePath = args.ReadmePath,
TargetPath = readmeFilePath
});
}

// Add changelog
if (!string.IsNullOrEmpty(args.ChangelogPath))
packageBuilder.ReleaseNotes = File.ReadAllText(args.ChangelogPath);

var mods = args.ModTuple.Config.ModDependencies.Select(x => new PackageDependency(x, VersionRange.All));
packageBuilder.DependencyGroups.Add(new PackageDependencyGroup(NuGetFramework.AnyFramework, mods));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<PackageReference Include="Sewer56.Update" Version="3.2.0" />
<PackageReference Include="Sewer56.Update.Extractors.SevenZipSharp" Version="1.1.2" GeneratePathProperty="true" />
<PackageReference Include="Sewer56.Update.Packaging" Version="2.3.4" />
<PackageReference Include="Sewer56.Update.Resolvers.NuGet" Version="1.2.0" />
<PackageReference Include="Sewer56.Update.Resolvers.NuGet" Version="1.2.1" />
<PackageReference Include="Sewer56.Update.Resolvers.GameBanana" Version="1.3.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public async Task<IEnumerable<IDownloadablePackage>> SearchAsync(string text, in
var result = new List<IDownloadablePackage>();

foreach (var res in searchResults)
result.Add(await WebDownloadablePackage.FromNuGetAsync(res, _repository));
result.Add(await WebDownloadablePackage.FromNuGetAsync(res, _repository, true, true));

return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using NuGet.Protocol;
using IOEx = Reloaded.Mod.Loader.IO.Utility.IOEx;

namespace Reloaded.Mod.Loader.Update.Providers.Web;
Expand Down Expand Up @@ -161,12 +162,15 @@ private async Task GetNameAndSize(Uri url)
}

#region NuGet

/// <summary>
/// Creates a web downloadable package from a NuGet source.
/// </summary>
/// <param name="pkg">The search result.</param>
/// <param name="repository">The NuGet repository to use.</param>
public static async Task<WebDownloadablePackage> FromNuGetAsync(IPackageSearchMetadata pkg, INugetRepository repository)
/// <param name="getReadme">If true, tries to pull readme from the server.</param>
/// <param name="getReleaseNotes">If true, tries to pull release notes from the server.</param>
public static async Task<WebDownloadablePackage> FromNuGetAsync(IPackageSearchMetadata pkg, INugetRepository repository, bool getReadme = false, bool getReleaseNotes = false)
{
var result = new WebDownloadablePackage()
{
Expand All @@ -178,23 +182,45 @@ public static async Task<WebDownloadablePackage> FromNuGetAsync(IPackageSearchMe
Description = pkg.Description,
Version = pkg.Identity.Version,
ProjectUri = pkg.ProjectUrl,
DownloadCount = pkg.DownloadCount
DownloadCount = pkg.DownloadCount,
Changelog = pkg.Summary
};

var resolver = GetNuGetUpdateResolver(pkg, repository);
result._url = new Uri((await resolver.GetDownloadUrlAsync(pkg.Identity.Version, new ReleaseMetadataVerificationInfo(), CancellationToken.None))!);
_ = InitNuGetAsyncData(result, pkg, repository, resolver);
_ = InitNuGetAsyncData(result, pkg, repository, resolver, getReadme, getReleaseNotes);

if (pkg.IconUrl != null)
result.Images = new[] { new DownloadableImage() { Uri = pkg.IconUrl } };

return result;
}

private static async Task InitNuGetAsyncData(WebDownloadablePackage package, IPackageSearchMetadata pkg, INugetRepository repository, NuGetUpdateResolver updateResolver)
[SuppressMessage("ReSharper", "AsyncVoidLambda")]
private static async Task InitNuGetAsyncData(WebDownloadablePackage package, IPackageSearchMetadata pkg,
INugetRepository repository, NuGetUpdateResolver updateResolver, bool getReadme, bool getReleaseNotes)
{
var tasks = new Task[4];
tasks[0] = Task.Run(async () => package.Published = await InitNuGetPublishedAsync(pkg, repository));
tasks[1] = Task.Run(async () => package.FileSize = await InitNuGetFileSizeAsync(updateResolver, pkg));
if (getReadme && pkg.ReadmeUrl != null)
tasks[2] = Task.Run(async () => package.MarkdownReadme = await SharedHttpClient.CachedAndCompressed.GetStringAsync(pkg.ReadmeUrl));

if (getReleaseNotes)
tasks[3] = Task.Run(async () => package.Changelog = await InitNuGetReleaseNotes(pkg, repository));

await Task.WhenAll(tasks);
}

private static async Task<string?> InitNuGetReleaseNotes(IPackageSearchMetadata packageSearchMetadata,
INugetRepository repository)
{
package.Published = await InitNuGetPublishedAsync(pkg, repository);
package.FileSize = await InitNuGetFileSizeAsync(updateResolver, pkg);
var nuspec = await repository.DownloadNuspecAsync(packageSearchMetadata.Identity, default);
if (nuspec == null)
return null;

var reader = new NuspecReader(new MemoryStream(nuspec));
return reader.GetReleaseNotes();
}

private static async Task<long> InitNuGetFileSizeAsync(NuGetUpdateResolver resolver, IPackageSearchMetadata res)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
<PackageReference Include="HtmlAgilityPack" Version="1.11.43" />
<PackageReference Include="Mapster" Version="7.3.0" />
<PackageReference Include="NetCoreInstallChecker" Version="3.0.0" />
<PackageReference Include="NuGet.Packaging" Version="6.2.1" />
<PackageReference Include="NuGet.Packaging.Core" Version="6.2.1" />
<PackageReference Include="NuGet.Protocol" Version="6.2.1" />
<PackageReference Include="NuGet.Packaging" Version="6.3.0" />
<PackageReference Include="NuGet.Packaging.Core" Version="6.3.0" />
<PackageReference Include="NuGet.Protocol" Version="6.3.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public interface INugetRepository
/// <param name="token">A cancellation token to allow cancellation of the task.</param>
Task<DownloadResourceResult> DownloadPackageAsync(IPackageSearchMetadata packageMetadata, CancellationToken token = default);

/// <summary>
/// Downloads a specified NuGet .nuspec.
/// </summary>
/// <param name="identity">The package identity to use.</param>
/// <param name="token">A cancellation token to allow cancellation of the task.</param>
Task<byte[]?> DownloadNuspecAsync(PackageIdentity identity, CancellationToken token = default);

/// <summary>
/// Retrieves the details of an individual package.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using NuGet.Protocol.Core.Types;
using System.Reflection;

namespace Reloaded.Mod.Loader.Update.Utilities.Nuget;

/// <summary>
Expand All @@ -20,6 +23,7 @@ public class NugetRepository : INugetRepository
private AsyncLazy<DownloadResource> _downloadResource = null!;
private AsyncLazy<PackageMetadataResource> _packageMetadataResource = null!;
private AsyncLazy<PackageSearchResource> _packageSearchResource = null!;
private string? _downloadResourceUrl;

private NugetRepository(string sourceUrl) => SourceUrl = sourceUrl;

Expand All @@ -31,14 +35,28 @@ public static NugetRepository FromSourceUrl(string nugetSourceUrl, string name =
nugetHelper.FriendlyName = name;
nugetHelper._packageSource = new PackageSource(nugetSourceUrl);
nugetHelper._sourceRepository = new SourceRepository(nugetHelper._packageSource, Repository.Provider.GetCoreV3());

nugetHelper._downloadResource = new AsyncLazy<DownloadResource>(async () => await nugetHelper._sourceRepository.GetResourceAsync<DownloadResource>());
nugetHelper._packageMetadataResource = new AsyncLazy<PackageMetadataResource>(async () => await nugetHelper._sourceRepository.GetResourceAsync<PackageMetadataResource>());
nugetHelper._packageSearchResource = new AsyncLazy<PackageSearchResource>(async () => await nugetHelper._sourceRepository.GetResourceAsync<PackageSearchResource>());

return nugetHelper;
}

/// <inheritdoc />
public async Task<byte[]?> DownloadNuspecAsync(PackageIdentity identity, CancellationToken token = default)
{
var resourceUrl = await GetDownloadResourceUrl();
if (string.IsNullOrEmpty(resourceUrl))
return null;

var baseUrl = new Uri(resourceUrl);
var idLowercase = identity.Id.ToLower();
var versionLower = identity.Version.ToString().ToLower();
var url = new Uri(baseUrl, $"/{idLowercase}/{versionLower}/{idLowercase}.nuspec");
return await SharedHttpClient.CachedAndCompressed.GetByteArrayAsync(url, token);
}

/// <inheritdoc />
public async Task<IEnumerable<IPackageSearchMetadata>> Search(string searchString, bool includePrereleases, int skip = 0, int results = 50, CancellationToken token = default)
{
Expand Down Expand Up @@ -175,4 +193,27 @@ private async Task FindDependenciesRecursiveAsync(IPackageSearchMetadata package
}
}
}

/// <summary>
/// [WARNING: REFLECTION]
/// </summary>
private async ValueTask<string?> GetDownloadResourceUrl()
{
if (!string.IsNullOrEmpty(_downloadResourceUrl))
return _downloadResourceUrl;

try
{
var downloadResource = await _downloadResource;

// No public API for this, ah shit, here we go again.
var dynMethod = downloadResource.GetType().GetField("_packageBaseAddressUrl", BindingFlags.NonPublic | BindingFlags.Instance);
_downloadResourceUrl = (string?)dynMethod!.GetValue(downloadResource)!;
return _downloadResourceUrl;
}
catch (Exception)
{
return null;
}
}
}

0 comments on commit 43a42e2

Please sign in to comment.