Skip to content

Commit

Permalink
Add support for retrieving latest releases for a given application.
Browse files Browse the repository at this point in the history
  • Loading branch information
peters committed Mar 1, 2024
1 parent 51868a9 commit 48c3ccc
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 5 deletions.
143 changes: 143 additions & 0 deletions src/Snap/Core/SnapNugetService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using SharpCompress.Readers;
using Snap.AnyOS;
using Snap.Core.Models;
using Snap.Extensions;
using Snap.Logging;
using Snap.NuGet;

namespace Snap.Core;

public sealed record SnapReleaseDetails
{
[JsonInclude]
public SemanticVersion Version { get; init; }
[JsonInclude]
public string Channel { get; init; }
[JsonInclude]
public DateTime CreatedDateUtc { get; init; }

public SnapReleaseDetails()
{

}

public SnapReleaseDetails([NotNull] string channel, [NotNull] SnapRelease release)
{
ArgumentNullException.ThrowIfNull(channel);
ArgumentNullException.ThrowIfNull(release);
Version = release.Version;
Channel = channel;
CreatedDateUtc = release.CreatedDateUtc;
}
}

public interface ISnapNugetService
{
Task<List<SnapReleaseDetails>> GetLatestReleasesAsync([NotNull] string applicationName, [NotNull] string rid,
[NotNull] SnapNugetFeed nugetFeed, CancellationToken cancellationToken);
}

public sealed class SnapNugetService : ISnapNugetService
{
readonly ISnapFilesystem _fileSystem;
readonly ISnapAppReader _appReader;
readonly INugetService _nugetService;
readonly ISnapOsSpecialFolders _specialFolders;
readonly Func<MemoryStream> _memoryStreamAllocator;
readonly ILog _logger;

public SnapNugetService([NotNull] ISnapNugetLogger snapNugetManagerLogger, [CanBeNull] Func<MemoryStream> memoryStreamAllocator = null) : this(
Snapx.SnapOs.Filesystem, new SnapAppReader(), new NugetService(Snapx.SnapOs.Filesystem, snapNugetManagerLogger), Snapx.SnapOs.SpecialFolders, memoryStreamAllocator) =>
ArgumentNullException.ThrowIfNull(snapNugetManagerLogger);

internal SnapNugetService(ISnapFilesystem fileSystem, ISnapAppReader appReader, INugetService nugetService, ISnapOsSpecialFolders specialFolders, [CanBeNull] Func<MemoryStream> memoryStreamAllocator = null)
{
_logger = LogProvider.For<SnapNugetService>();
_fileSystem = fileSystem;
_appReader = appReader;
_nugetService = nugetService;
_specialFolders = specialFolders;
_memoryStreamAllocator = memoryStreamAllocator ?? (() => new MemoryStream());
}

public async Task<List<SnapReleaseDetails>> GetLatestReleasesAsync(string applicationName,
string rid,
SnapNugetFeed nugetFeed,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(applicationName);
ArgumentNullException.ThrowIfNull(rid);
ArgumentNullException.ThrowIfNull(nugetFeed);

var packageSource = nugetFeed.BuildNugetSources(_specialFolders.NugetCacheDirectory).Single();
var packageId = $"{applicationName}-{rid}_snapx";

var snapReleasesDownloadResult =
await _nugetService.DownloadLatestAsync(packageId, packageSource, false, true, cancellationToken);

var sourceLocation = packageSource.IsLocalOrUncPath()
? $"path: {_fileSystem.PathGetFullPath(packageSource.SourceUri.AbsolutePath)}. Does the location exist?"
: packageSource.Name;

if (!snapReleasesDownloadResult.SuccessSafe())
{
_logger.Error($"Unknown error while downloading releases nupkg {packageId} from {sourceLocation}. Status: {snapReleasesDownloadResult.Status}.");
return [];
}

using var packageArchiveReader = new PackageArchiveReader(snapReleasesDownloadResult.PackageStream, true);
var snapReleases = await GetReleasesAsync(packageArchiveReader, cancellationToken);
if (snapReleases == null)
{
_logger.Error($"Unknown error while reading releases nupkg {packageId} from {sourceLocation}. Status: {snapReleasesDownloadResult.Status}");
return [];
}

snapReleasesDownloadResult.PackageStream.Seek(0, SeekOrigin.Begin);

var channels = snapReleases.Releases.SelectMany(x => x.Channels).ToHashSet();

var releases = new List<SnapReleaseDetails>();

foreach (var channel in channels)
{
var mostRecentRelease = snapReleases.Where(x => x.Channels.Contains(channel)).MaxBy(x => x.Version);
if (mostRecentRelease == null)
{
continue;
}

releases.Add(new SnapReleaseDetails(channel, mostRecentRelease));
}

return releases;
}

async Task<SnapAppsReleases> GetReleasesAsync([NotNull] IAsyncPackageCoreReader packageArchiveReader, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(packageArchiveReader);

var snapReleasesFilename = _fileSystem.PathCombine(SnapConstants.NuspecRootTargetPath, SnapConstants.ReleasesFilename);
await using var snapReleasesCompressedStream =
await packageArchiveReader
.GetStreamAsync(snapReleasesFilename, cancellationToken)
.ReadToEndAsync(cancellationToken: cancellationToken);
await using var snapReleasesUncompressedStream = _memoryStreamAllocator();
using var reader = ReaderFactory.Open(snapReleasesCompressedStream);
reader.MoveToNextEntry();
reader.WriteEntryTo(snapReleasesUncompressedStream);
snapReleasesUncompressedStream.Seek(0, SeekOrigin.Begin);
return await _appReader.BuildSnapAppsReleasesFromStreamAsync(snapReleasesUncompressedStream);
}
}
9 changes: 9 additions & 0 deletions src/Snap/Extensions/SnapExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,15 @@ internal static INuGetPackageSources BuildNugetSources([NotNull] this SnapApp sn

return new NuGetPackageSources(inMemorySettings, nugetFeeds);
}

internal static INuGetPackageSources BuildNugetSources([NotNull] this SnapNugetFeed nugetFeed, [NotNull] string tempDirectory)
{
if (nugetFeed == null) throw new ArgumentNullException(nameof(nugetFeed));
if (tempDirectory == null) throw new ArgumentNullException(nameof(tempDirectory));
var inMemorySettings = new NugetInMemorySettings(tempDirectory);
var packageSource = nugetFeed.BuildPackageSource(inMemorySettings);
return new NuGetPackageSources(inMemorySettings, [packageSource]);
}

internal static INuGetPackageSources BuildNugetSources([NotNull] this SnapApps snapApps, INuGetPackageSources nuGetPackageSources)
{
Expand Down
12 changes: 7 additions & 5 deletions src/Snap/NuGet/NugetLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@

namespace Snap.NuGet;

internal interface ISnapNugetLogger : ILogger;
public interface ISnapNugetLogger : ILogger;

internal class NugetLogger : LoggerBase, ISnapNugetLogger
public class NugetLogger : LoggerBase, ISnapNugetLogger
{
static ILog _logger;

public NugetLogger(ILog logger)
internal NugetLogger(ILog logger) => _logger = logger ?? LogProvider.For<NugetLogger>();

public NugetLogger() : this(LogProvider.For<NugetLogger>())
{
_logger = logger ?? LogProvider.For<NugetLogger>();

}

public override void Log(ILogMessage message)
Expand Down Expand Up @@ -53,4 +55,4 @@ public override Task LogAsync(ILogMessage message)
}

static readonly Task CompletedTask = Task.FromResult(0);
}
}

0 comments on commit 48c3ccc

Please sign in to comment.