diff --git a/Nexus.sln b/Nexus.sln index e96999fb..8576bade 100644 --- a/Nexus.sln +++ b/Nexus.sln @@ -28,8 +28,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nexus.Tests", "tests\Nexus. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nexus", "src\Nexus\Nexus.csproj", "{6DDD84D7-8FCF-4E49-A5DB-EAF95B0E040F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestExtensionProject", "tests\TestExtensionProject\TestExtensionProject.csproj", "{395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-client", "src\clients\dotnet-client\dotnet-client.csproj", "{FD8E5EA0-674B-492D-8D1E-1D0B8374B71D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nexus.UI", "src\Nexus.UI\Nexus.UI.csproj", "{121840FD-19CC-4C48-86DE-AF0B6C70A089}" @@ -84,18 +82,6 @@ Global {6DDD84D7-8FCF-4E49-A5DB-EAF95B0E040F}.Release|x64.Build.0 = Release|Any CPU {6DDD84D7-8FCF-4E49-A5DB-EAF95B0E040F}.Release|x86.ActiveCfg = Release|Any CPU {6DDD84D7-8FCF-4E49-A5DB-EAF95B0E040F}.Release|x86.Build.0 = Release|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Debug|x64.ActiveCfg = Debug|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Debug|x64.Build.0 = Debug|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Debug|x86.ActiveCfg = Debug|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Debug|x86.Build.0 = Debug|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Release|Any CPU.Build.0 = Release|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Release|x64.ActiveCfg = Release|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Release|x64.Build.0 = Release|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Release|x86.ActiveCfg = Release|Any CPU - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F}.Release|x86.Build.0 = Release|Any CPU {FD8E5EA0-674B-492D-8D1E-1D0B8374B71D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD8E5EA0-674B-492D-8D1E-1D0B8374B71D}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD8E5EA0-674B-492D-8D1E-1D0B8374B71D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -175,7 +161,6 @@ Global GlobalSection(NestedProjects) = preSolution {E8ADD668-F935-4AE3-B98B-8B6480ACE533} = {3DBD52E4-26F8-413F-A954-2BE86E572F3E} {6DDD84D7-8FCF-4E49-A5DB-EAF95B0E040F} = {D4BFDD2B-4F50-4AF3-8906-6C003E70BA56} - {395E2F06-D665-4EEA-9BD7-2A2F8BCE4B7F} = {3DBD52E4-26F8-413F-A954-2BE86E572F3E} {FD8E5EA0-674B-492D-8D1E-1D0B8374B71D} = {DF0E788F-9A27-498B-8B06-4626DD78B0B5} {121840FD-19CC-4C48-86DE-AF0B6C70A089} = {D4BFDD2B-4F50-4AF3-8906-6C003E70BA56} {AFCE2F13-F54D-439A-B796-DC3F32E1A12B} = {328E38EE-662B-42B8-8732-9CDFA632C469} diff --git a/src/Nexus/PackageManagement/PackageController.cs b/src/Nexus/PackageManagement/PackageController.cs index f3f7efa2..c1afab7f 100644 --- a/src/Nexus/PackageManagement/PackageController.cs +++ b/src/Nexus/PackageManagement/PackageController.cs @@ -2,15 +2,7 @@ // Copyright (c) [2024] [nexus-main] using System.Diagnostics; -using System.IO.Compression; -using System.Net; using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; -using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Tar; using Nexus.Core; using Nexus.Core.V1; @@ -25,12 +17,6 @@ ILogger logger public const string BUILTIN_PROVIDER = "nexus"; - private const int MAX_PAGES = 20; - - private const int PER_PAGE = 100; - - private static readonly HttpClient _httpClient = new(); - private readonly ILogger _logger = logger; private PackageLoadContext? _loadContext; @@ -46,11 +32,6 @@ public async Task DiscoverAsync(CancellationToken cancellationToken) BUILTIN_PROVIDER => ["current"], "local" => await DiscoverLocalAsync(cancellationToken), "git-tag" => await DiscoverGitTagsAsync(cancellationToken), - "github-releases" => await DiscoverGithubReleasesAsync(cancellationToken), - "gitlab-packages-generic-v4" => await DiscoverGitLabPackagesGenericAsync(cancellationToken), - /* this approach does not work, see rationale below (#region gitlab-releases-v4) - * "gitlab-releases-v4" => await DiscoverGitLabReleasesAsync(cancellationToken); - */ _ => throw new ArgumentException($"The provider {PackageReference.Provider} is not supported."), }; @@ -109,11 +90,6 @@ internal async Task RestoreAsync(string restoreRoot, CancellationToken c { "local" => await RestoreLocalAsync(actualRestoreRoot, cancellationToken), "git-tag" => await RestoreGitTagAsync(actualRestoreRoot, cancellationToken), - "github-releases" => await RestoreGitHubReleasesAsync(actualRestoreRoot, cancellationToken), - "gitlab-packages-generic-v4" => await RestoreGitLabPackagesGenericAsync(actualRestoreRoot, cancellationToken), - /* this approach does not work, see rationale below (#region gitlab-releases-v4) *///case "gitlab-releases-v4": - // restoreFolderPath = await RestoreGitLabReleasesAsync(actualRestoreRoot, cancellationToken); - // break; _ => throw new ArgumentException($"The provider {PackageReference.Provider} is not supported."), }; return restoreFolderPath; @@ -146,49 +122,38 @@ private static void CloneFolder(string source, string target) } } - private static async Task DownloadAndExtractAsync( - string assetName, - string assetUrl, - string targetPath, - Dictionary headers) + private static async Task PublishProjectAsync( + string csprojFilePath, + string targetFolderPath, + string publishFolderPath, + string repository, + CancellationToken cancellationToken + ) { - // get download stream - async Task GetAssetResponseAsync() - { - using var assetRequest = new HttpRequestMessage(HttpMethod.Get, assetUrl); + if (!File.Exists(csprojFilePath)) + throw new Exception($"The .csproj file {csprojFilePath} does not exist."); - foreach (var entry in headers) - { - assetRequest.Headers.Add(entry.Key, entry.Value); - } + Directory.CreateDirectory(targetFolderPath); - var assetResponse = await _httpClient - .SendAsync(assetRequest, HttpCompletionOption.ResponseHeadersRead); + var startInfo = new ProcessStartInfo + { + CreateNoWindow = true, + FileName = "dotnet", + Arguments = $"publish {csprojFilePath} -c Release -o {publishFolderPath}", + RedirectStandardError = true + }; - assetResponse.EnsureSuccessStatusCode(); + using var process = Process.Start(startInfo) ?? throw new Exception("Process is null."); - return assetResponse; - } + await process.WaitForExitAsync(cancellationToken); - // download and extract - if (assetName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) - { - using var assetResponse = await GetAssetResponseAsync(); - using var stream = await assetResponse.Content.ReadAsStreamAsync(); - using var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); - zipArchive.ExtractToDirectory(targetPath); - } - else if (assetName.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) - { - using var assetResponse = await GetAssetResponseAsync(); - using var stream = await assetResponse.Content.ReadAsStreamAsync(); - using var gzipStream = new GZipInputStream(stream); - using var tarArchive = TarArchive.CreateInputTarArchive(gzipStream, Encoding.UTF8); - tarArchive.ExtractContents(targetPath); - } - else + if (process.ExitCode != 0) { - throw new Exception("Only assets of type .zip or .tar.gz are supported."); + var error = process is null + ? default : + $" Reason: {await process.StandardError.ReadToEndAsync(cancellationToken)}"; + + throw new Exception($"Unable to publish project {repository}.{error}"); } } @@ -200,7 +165,7 @@ private Task DiscoverLocalAsync(CancellationToken cancellationToken) var configuration = PackageReference.Configuration; if (!configuration.TryGetValue("path", out var path)) - throw new ArgumentException("The 'path' parameter is missing in the extension reference."); + throw new ArgumentException("The 'path' parameter is missing in the source registration."); if (!Directory.Exists(path)) throw new DirectoryNotFoundException($"The extension path {path} does not exist."); @@ -219,38 +184,76 @@ private Task DiscoverLocalAsync(CancellationToken cancellationToken) return Task.FromResult(result.ToArray()); } - private Task RestoreLocalAsync(string restoreRoot, CancellationToken cancellationToken) + private async Task RestoreLocalAsync(string restoreRoot, CancellationToken cancellationToken) { - return Task.Run(() => - { - var configuration = PackageReference.Configuration; + var configuration = PackageReference.Configuration; + + if (!configuration.TryGetValue("path", out var path)) + throw new ArgumentException("The 'path' parameter is missing in the source registration."); + + if (!configuration.TryGetValue("version", out var version)) + throw new ArgumentException("The 'version' parameter is missing in the source registration."); - if (!configuration.TryGetValue("path", out var path)) - throw new ArgumentException("The 'path' parameter is missing in the extension reference."); + if (!configuration.TryGetValue("csproj", out var csproj)) + throw new ArgumentException("The 'csproj' parameter is missing in the source registration."); - if (!configuration.TryGetValue("version", out var version)) - throw new ArgumentException("The 'version' parameter is missing in the extension reference."); + var sourceFolderPath = Path.Combine(path, version); - var sourcePath = Path.Combine(path, version); + if (!Directory.Exists(sourceFolderPath)) + throw new DirectoryNotFoundException($"The source path {sourceFolderPath} does not exist."); - if (!Directory.Exists(sourcePath)) - throw new DirectoryNotFoundException($"The source path {sourcePath} does not exist."); + var pathHash = new Guid(path.Hash()).ToString(); + var targetFolderPath = Path.Combine(restoreRoot, pathHash, version); - var pathHash = new Guid(path.Hash()).ToString(); - var targetPath = Path.Combine(restoreRoot, pathHash, version); + if (!Directory.Exists(targetFolderPath) || !Directory.EnumerateFileSystemEntries(targetFolderPath).Any()) + { + var publishFolderPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - if (!Directory.Exists(targetPath) || !Directory.EnumerateFileSystemEntries(targetPath).Any()) + try { - _logger.LogDebug("Restore package from source {Source} to {Target}", sourcePath, targetPath); - CloneFolder(sourcePath, targetPath); + // Publish project + var csprojFilePath = Path.Combine(sourceFolderPath, csproj); + + await PublishProjectAsync( + csprojFilePath, + targetFolderPath, + publishFolderPath, + path, + cancellationToken + ); + + // Clone folder + CloneFolder(publishFolderPath, targetFolderPath); + } + catch + { + // try delete restore folder + try + { + if (Directory.Exists(targetFolderPath)) + Directory.Delete(targetFolderPath, recursive: true); + } + catch { } + + throw; } - else + finally { - _logger.LogDebug("Package is already restored"); + // try delete publish folder + try + { + if (Directory.Exists(publishFolderPath)) + Directory.Delete(publishFolderPath, recursive: true); + } + catch { } } + } + else + { + _logger.LogDebug("Package is already restored"); + } - return targetPath; - }, cancellationToken); + return targetFolderPath; } #endregion @@ -265,7 +268,7 @@ private async Task DiscoverGitTagsAsync(CancellationToken cancellation var configuration = PackageReference.Configuration; if (!configuration.TryGetValue("repository", out var repository)) - throw new ArgumentException("The 'repository' parameter is missing in the extension reference."); + throw new ArgumentException("The 'repository' parameter is missing in the source registration."); var startInfo = new ProcessStartInfo { @@ -326,34 +329,37 @@ private async Task RestoreGitTagAsync(string restoreRoot, CancellationTo var configuration = PackageReference.Configuration; if (!configuration.TryGetValue("repository", out var repository)) - throw new ArgumentException("The 'repository' parameter is missing in the extension reference."); + throw new ArgumentException("The 'repository' parameter is missing in the source registration."); if (!configuration.TryGetValue("tag", out var tag)) - throw new ArgumentException("The 'tag' parameter is missing in the extension reference."); + throw new ArgumentException("The 'tag' parameter is missing in the source registration."); if (!configuration.TryGetValue("csproj", out var csproj)) - throw new ArgumentException("The 'csproj' parameter is missing in the extension reference."); + throw new ArgumentException("The 'csproj' parameter is missing in the source registration."); var escapedUriWithoutSchemeAndUserInfo = new Uri(repository) .GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Scheme & ~UriComponents.UserInfo, UriFormat.UriEscaped); - var targetPath = Path.Combine(restoreRoot, escapedUriWithoutSchemeAndUserInfo.Replace('/', '_').ToLower(), tag); + var targetFolderPath = Path.Combine(restoreRoot, escapedUriWithoutSchemeAndUserInfo.Replace('/', '_').ToLower(), tag); - if (!Directory.Exists(targetPath) || !Directory.EnumerateFileSystemEntries(targetPath).Any()) + if (!Directory.Exists(targetFolderPath) || !Directory.EnumerateFileSystemEntries(targetFolderPath).Any()) { - var cloneFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - var publishFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var cloneFolderPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var publishFolderPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + var escapedUriWithoutUserInfo = new Uri(repository) + .GetComponents(UriComponents.AbsoluteUri & ~UriComponents.UserInfo, UriFormat.UriEscaped); try { // Clone respository - Directory.CreateDirectory(cloneFolder); + Directory.CreateDirectory(cloneFolderPath); var startInfo1 = new ProcessStartInfo { CreateNoWindow = true, FileName = "git", - Arguments = $"clone --depth 1 --branch {tag} --recurse-submodules {repository} {cloneFolder}", + Arguments = $"clone --depth 1 --branch {tag} --recurse-submodules {repository} {cloneFolderPath}", RedirectStandardError = true }; @@ -362,9 +368,6 @@ private async Task RestoreGitTagAsync(string restoreRoot, CancellationTo if (process1.ExitCode != 0) { - var escapedUriWithoutUserInfo = new Uri(repository) - .GetComponents(UriComponents.AbsoluteUri & ~UriComponents.UserInfo, UriFormat.UriEscaped); - var error = process1 is null ? default : $" Reason: {await process1.StandardError.ReadToEndAsync(cancellationToken)}"; @@ -373,46 +376,26 @@ private async Task RestoreGitTagAsync(string restoreRoot, CancellationTo } // Publish project - var sourceFilePath = Path.Combine(cloneFolder, csproj); - - if (!File.Exists(sourceFilePath)) - throw new Exception($"The .csproj file {csproj} does not exist."); + var csprojFilePath = Path.Combine(cloneFolderPath, csproj); - Directory.CreateDirectory(targetPath); - - var startInfo2 = new ProcessStartInfo - { - CreateNoWindow = true, - FileName = "dotnet", - Arguments = $"publish {sourceFilePath} -c Release -o {publishFolder}", - RedirectStandardError = true - }; - - using var process2 = Process.Start(startInfo2) ?? throw new Exception("Process is null."); - await process2.WaitForExitAsync(cancellationToken); - - if (process2.ExitCode != 0) - { - var escapedUriWithoutUserInfo = new Uri(repository) - .GetComponents(UriComponents.AbsoluteUri & ~UriComponents.UserInfo, UriFormat.UriEscaped); - - var error = process2 is null - ? default : - $" Reason: {await process2.StandardError.ReadToEndAsync(cancellationToken)}"; - - throw new Exception($"Unable to publish project.{error}"); - } + await PublishProjectAsync( + csprojFilePath, + targetFolderPath, + publishFolderPath, + escapedUriWithoutUserInfo, + cancellationToken + ); // Clone folder - CloneFolder(publishFolder, targetPath); + CloneFolder(publishFolderPath, targetFolderPath); } catch { // try delete restore folder try { - if (Directory.Exists(targetPath)) - Directory.Delete(targetPath, recursive: true); + if (Directory.Exists(targetFolderPath)) + Directory.Delete(targetFolderPath, recursive: true); } catch { } @@ -423,16 +406,16 @@ private async Task RestoreGitTagAsync(string restoreRoot, CancellationTo // try delete clone folder try { - if (Directory.Exists(cloneFolder)) - Directory.Delete(cloneFolder, recursive: true); + if (Directory.Exists(cloneFolderPath)) + Directory.Delete(cloneFolderPath, recursive: true); } catch { } // try delete publish folder try { - if (Directory.Exists(publishFolder)) - Directory.Delete(publishFolder, recursive: true); + if (Directory.Exists(publishFolderPath)) + Directory.Delete(publishFolderPath, recursive: true); } catch { } } @@ -442,464 +425,9 @@ private async Task RestoreGitTagAsync(string restoreRoot, CancellationTo _logger.LogDebug("Package is already restored"); } - return targetPath; - } - - #endregion - - #region github-releases - - private async Task DiscoverGithubReleasesAsync(CancellationToken cancellationToken) - { - var result = new List(); - var configuration = PackageReference.Configuration; - - if (!configuration.TryGetValue("project-path", out var projectPath)) - throw new ArgumentException("The 'project-path' parameter is missing in the extension reference."); - - var server = $"https://api.github.com"; - var requestUrl = $"{server}/repos/{projectPath}/releases?per_page={PER_PAGE}&page={1}"; - - for (int i = 0; i < MAX_PAGES; i++) - { - using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - if (configuration.TryGetValue("token", out var token)) - request.Headers.Add("Authorization", $"token {token}"); - - request.Headers.Add("User-Agent", "Nexus"); - request.Headers.Add("Accept", "application/vnd.github.v3+json"); - - using var response = await _httpClient.SendAsync(request, cancellationToken); - response.EnsureSuccessStatusCode(); - - var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); - var jsonDocument = await JsonDocument.ParseAsync(contentStream, cancellationToken: cancellationToken); - - foreach (var githubRelease in jsonDocument.RootElement.EnumerateArray()) - { - var releaseTagName = githubRelease.GetProperty("tag_name").GetString() ?? throw new Exception("tag_name is null"); - result.Add(releaseTagName); - _logger.LogDebug("Discovered package version {PackageVersion}", releaseTagName); - } - - // look for more pages - response.Headers.TryGetValues("Link", out var links); - - if (links is null || !links.Any()) - break; - - requestUrl = links - .First() - .Split(",") - .Where(current => current.Contains("rel=\"next\"")) - .Select(current => GitHubRegex.Match(current).Groups[1].Value) - .FirstOrDefault(); - - if (requestUrl == default) - break; - } - - return result.ToArray(); - } - - private async Task RestoreGitHubReleasesAsync(string restoreRoot, CancellationToken cancellationToken) - { - var configuration = PackageReference.Configuration; - - if (!configuration.TryGetValue("project-path", out var projectPath)) - throw new ArgumentException("The 'project-path' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("tag", out var tag)) - throw new ArgumentException("The 'tag' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("asset-selector", out var assetSelector)) - throw new ArgumentException("The 'asset-selector' parameter is missing in the extension reference."); - - var targetPath = Path.Combine(restoreRoot, projectPath.Replace('/', '_').ToLower(), tag); - - if (!Directory.Exists(targetPath) || !Directory.EnumerateFileSystemEntries(targetPath).Any()) - { - var server = $"https://api.github.com"; - var requestUrl = $"{server}/repos/{projectPath}/releases/tags/{tag}"; - - using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - if (configuration.TryGetValue("token", out var token)) - request.Headers.Add("Authorization", $"token {token}"); - - request.Headers.Add("User-Agent", "Nexus"); - request.Headers.Add("Accept", "application/vnd.github.v3+json"); - - using var response = await _httpClient.SendAsync(request, cancellationToken); - response.EnsureSuccessStatusCode(); - - var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); - var jsonDocument = await JsonDocument.ParseAsync(contentStream, cancellationToken: cancellationToken); - - // find asset - var gitHubRelease = jsonDocument.RootElement; - var releaseTagName = gitHubRelease.GetProperty("tag_name").GetString(); - - var asset = gitHubRelease - .GetProperty("assets") - .EnumerateArray() - .FirstOrDefault(current => Regex.IsMatch(current.GetProperty("name").GetString() ?? throw new Exception("assets is null"), assetSelector)); - - if (asset.ValueKind != JsonValueKind.Undefined) - { - // get asset download URL - var assetUrl = asset.GetProperty("url").GetString() ?? throw new Exception("url is null"); - var assetBrowserUrl = asset.GetProperty("browser_download_url").GetString() ?? throw new Exception("browser_download_url is null"); - - // get download stream - var headers = new Dictionary(); - - if (configuration.TryGetValue("token", out var assetToken)) - headers["Authorization"] = $"token {assetToken}"; - - headers["User-Agent"] = "Nexus"; - headers["Accept"] = "application/octet-stream"; - - _logger.LogDebug("Restore package from source {Source} to {Target}", assetBrowserUrl, targetPath); - await DownloadAndExtractAsync(assetBrowserUrl, assetUrl, targetPath, headers); - } - else - { - throw new Exception("No matching assets found."); - } - } - else - { - _logger.LogDebug("Package is already restored"); - } - - return targetPath; + return targetFolderPath; } #endregion - #region gitlab-packages-generic-v4 - - // curl --header "PRIVATE-TOKEN: N1umphXDULLvgzhT7uyx" \ - // --upload-file assets.tar.gz \ - // "https:///api/v4/projects//packages/generic///assets.tar.gz" - - private async Task DiscoverGitLabPackagesGenericAsync(CancellationToken cancellationToken) - { - var result = new List(); - var configuration = PackageReference.Configuration; - - if (!configuration.TryGetValue("server", out var server)) - throw new ArgumentException("The 'server' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("project-path", out var projectPath)) - throw new ArgumentException("The 'project-path' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("package", out var package)) - throw new ArgumentException("The 'package' parameter is missing in the extension reference."); - - configuration.TryGetValue("token", out var token); - - var headers = new Dictionary(); - - if (!string.IsNullOrWhiteSpace(token)) - headers["PRIVATE-TOKEN"] = token; - - await foreach (var gitlabPackage in GetGitLabPackagesGenericAsync(server, projectPath, package, headers, cancellationToken)) - { - var packageVersion = gitlabPackage.GetProperty("version").GetString() ?? throw new Exception("version is null"); - result.Add(packageVersion); - _logger.LogDebug("Discovered package version {PackageVersion}", packageVersion); - } - - result.Reverse(); - - return [.. result]; - } - - private async Task RestoreGitLabPackagesGenericAsync(string restoreRoot, CancellationToken cancellationToken) - { - var configuration = PackageReference.Configuration; - - if (!configuration.TryGetValue("server", out var server)) - throw new ArgumentException("The 'server' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("project-path", out var projectPath)) - throw new ArgumentException("The 'project-path' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("package", out var package)) - throw new ArgumentException("The 'package' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("version", out var version)) - throw new ArgumentException("The 'version' parameter is missing in the extension reference."); - - if (!configuration.TryGetValue("asset-selector", out var assetSelector)) - throw new ArgumentException("The 'asset-selector' parameter is missing in the extension reference."); - - configuration.TryGetValue("token", out var token); - - var targetPath = Path.Combine(restoreRoot, projectPath.Replace('/', '_').ToLower(), version); - - if (!Directory.Exists(targetPath) || !Directory.EnumerateFileSystemEntries(targetPath).Any()) - { - var headers = new Dictionary(); - - if (!string.IsNullOrWhiteSpace(token)) - headers["PRIVATE-TOKEN"] = token; - - // get package id - var gitlabPackage = default(JsonElement); - - await foreach (var currentPackage in - GetGitLabPackagesGenericAsync(server, projectPath, package, headers, cancellationToken)) - { - var packageVersion = currentPackage.GetProperty("version").GetString(); - - if (packageVersion == version) - gitlabPackage = currentPackage; - } - - if (gitlabPackage.ValueKind == JsonValueKind.Undefined) - throw new Exception("The specified version could not be found."); - - var packageId = gitlabPackage.GetProperty("id").GetInt32(); - - // list package files (https://docs.gitlab.com/ee/api/packages.html#list-package-files) - var encodedProjectPath = WebUtility.UrlEncode(projectPath); - var requestUrl = $"{server}/api/v4/projects/{encodedProjectPath}/packages/{packageId}/package_files"; - - using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - foreach (var entry in headers) - { - request.Headers.Add(entry.Key, entry.Value); - } - - using var response = await _httpClient.SendAsync(request, cancellationToken); - response.EnsureSuccessStatusCode(); - - var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); - var jsonDocument = await JsonDocument.ParseAsync(contentStream, cancellationToken: cancellationToken); - - // find asset - var asset = jsonDocument.RootElement.EnumerateArray() - .FirstOrDefault(current => Regex.IsMatch(current.GetProperty("file_name").GetString() ?? throw new Exception("file_name is null"), assetSelector)); - - var fileName = asset.GetProperty("file_name").GetString() ?? throw new Exception("file_name is null"); - - if (asset.ValueKind != JsonValueKind.Undefined) - { - // download package file (https://docs.gitlab.com/ee/user/packages/generic_packages/index.html#download-package-file) - var assetUrl = $"{server}/api/v4/projects/{encodedProjectPath}/packages/generic/{package}/{version}/{fileName}"; - _logger.LogDebug("Restore package from source {Source} to {Target}", assetUrl, targetPath); - await DownloadAndExtractAsync(fileName, assetUrl, targetPath, headers); - } - else - { - throw new Exception("No matching assets found."); - } - } - else - { - _logger.LogDebug("Package is already restored"); - } - - return targetPath; - } - - private static async IAsyncEnumerable GetGitLabPackagesGenericAsync( - string server, string projectPath, string package, Dictionary headers, [EnumeratorCancellation] CancellationToken cancellationToken) - { - // list packages (https://docs.gitlab.com/ee/api/packages.html#within-a-project) - var encodedProjectPath = WebUtility.UrlEncode(projectPath); - var requestUrl = $"{server}/api/v4/projects/{encodedProjectPath}/packages?package_type=generic&package_name={package}&per_page={PER_PAGE}&page={1}"; - - for (int i = 0; i < MAX_PAGES; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - foreach (var entry in headers) - { - request.Headers.Add(entry.Key, entry.Value); - } - - using var response = await _httpClient.SendAsync(request, cancellationToken); - response.EnsureSuccessStatusCode(); - - var message = await response.Content.ReadAsStringAsync(cancellationToken); - - var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); - var jsonDocument = await JsonDocument.ParseAsync(contentStream, cancellationToken: cancellationToken); - - foreach (var gitlabPackage in jsonDocument.RootElement.EnumerateArray()) - { - yield return gitlabPackage; - } - - // look for more pages - response.Headers.TryGetValues("Link", out var links); - - if (links is null) - throw new Exception("link is null"); - - if (!links.Any()) - break; - - requestUrl = links - .First() - .Split(",") - .Where(current => current.Contains("rel=\"next\"")) - .Select(current => GitlabRegex.Match(current).Groups[1].Value) - .FirstOrDefault(); - - if (requestUrl == default) - break; - - continue; - } - } - - [GeneratedRegex("\\<(https:.*)\\>; rel=\"next\"")] - private static partial Regex GitHubRegex { get; } - - [GeneratedRegex("\\<(https:.*)\\>; rel=\"next\"")] - private static partial Regex GitlabRegex { get; } - - #endregion - - #region gitlab-releases-v4 - - /* The GitLab Releases approach does work until trying to download the previously uploaded file. - * GitLab allows only cookie-based downloads, tokens are not supported. Probably to stop the - * exact intentation to download data in an automated way. */ - - //private async Task> DiscoverGitLabReleasesAsync(CancellationToken cancellationToken) - //{ - // var result = new Dictionary(); - - // if (!_packageReference.TryGetValue("server", out var server)) - // throw new ArgumentException("The 'server' parameter is missing in the extension reference."); - - // if (!_packageReference.TryGetValue("project-path", out var projectPath)) - // throw new ArgumentException("The 'project-path' parameter is missing in the extension reference."); - - // var encodedProjectPath = WebUtility.UrlEncode(projectPath); - // var requestUrl = $"{server}/api/v4/projects/{encodedProjectPath}/releases?per_page={PER_PAGE}&page={1}"; - - // for (int i = 0; i < MAX_PAGES; i++) - // { - // using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - // if (_packageReference.TryGetValue("token", out var token)) - // request.Headers.Add("PRIVATE-TOKEN", token); - - // using var response = await _httpClient.SendAsync(request); - // response.EnsureSuccessStatusCode(); - - // var contentStream = await response.Content.ReadAsStreamAsync(); - // var jsonDocument = await JsonDocument.ParseAsync(contentStream); - - // foreach (var gitlabRelease in jsonDocument.RootElement.EnumerateArray()) - // { - // var releaseTagName = gitlabRelease.GetProperty("tag_name").GetString(); - - // var isSemanticVersion = PackageLoadContext - // .TryParseWithPrefix(releaseTagName, out var semanticVersion); - - // if (isSemanticVersion) - // result[semanticVersion] = releaseTagName; - - // _logger.LogDebug("Discovered package version {PackageVersion}", releaseTagName); - // } - - // // look for more pages - // response.Headers.TryGetValues("Link", out var links); - - // if (!links.Any()) - // break; - - // requestUrl = links - // .First() - // .Split(",") - // .Where(current => current.Contains("rel=\"next\"")) - // .Select(current => Regex.Match(current, @"\<(https:.*)\>; rel=""next""").Groups[1].Value) - // .FirstOrDefault(); - - // if (requestUrl == default) - // break; - - // continue; - // } - - // return result; - //} - - //private async Task RestoreGitLabReleasesAsync(string restoreRoot, CancellationToken cancellationToken) - //{ - // if (!_packageReference.TryGetValue("server", out var server)) - // throw new ArgumentException("The 'server' parameter is missing in the extension reference."); - - // if (!_packageReference.TryGetValue("project-path", out var projectPath)) - // throw new ArgumentException("The 'ProjectPath' parameter is missing in the extension reference."); - - // if (!_packageReference.TryGetValue("tag", out var tag)) - // throw new ArgumentException("The 'tag' parameter is missing in the extension reference."); - - // if (!_packageReference.TryGetValue("asset-selector", out var assetSelector)) - // throw new ArgumentException("The 'asset-selector' parameter is missing in the extension reference."); - - // var targetPath = Path.Combine(restoreRoot, projectPath.Replace('/', '_').ToLower(), tag); - - // if (!Directory.Exists(targetPath) || Directory.EnumerateFileSystemEntries(targetPath).Any()) - // { - // var encodedProjectPath = WebUtility.UrlEncode(projectPath); - // var requestUrl = $"{server}/api/v4/projects/{encodedProjectPath}/releases/{tag}"; - - // using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); - - // if (_packageReference.TryGetValue("token", out var token)) - // request.Headers.Add("PRIVATE-TOKEN", token); - - // using var response = await _httpClient.SendAsync(request); - // response.EnsureSuccessStatusCode(); - - // var contentStream = await response.Content.ReadAsStreamAsync(); - // var jsonDocument = await JsonDocument.ParseAsync(contentStream); - - // // find asset - // var gitHubRelease = jsonDocument.RootElement; - // var releaseTagName = gitHubRelease.GetProperty("tag_name").GetString(); - - // var isSemanticVersion = PackageLoadContext - // .TryParseWithPrefix(releaseTagName, out var semanticVersion); - - // var asset = gitHubRelease - // .GetProperty("assets").GetProperty("links") - // .EnumerateArray() - // .FirstOrDefault(current => Regex.IsMatch(current.GetProperty("name").GetString(), assetSelector)); - - // if (asset.ValueKind != JsonValueKind.Undefined) - // { - // var assetUrl = new Uri(asset.GetProperty("direct_asset_url").GetString()); - // _logger.LogDebug("Restore package from source {Source}", assetUrl); - // await DownloadAndExtractAsync(fileName, assetUrl, targetPath, headers, cancellationToken); - // } - // else - // { - // throw new Exception("No matching assets found."); - // } - // } - // else - // { - // _logger.LogDebug("Package is already restored"); - // } - // - // return targetPath; - //} - - #endregion - } diff --git a/src/Nexus/Services/PackageService.cs b/src/Nexus/Services/PackageService.cs index 17ae36e6..4aad5433 100644 --- a/src/Nexus/Services/PackageService.cs +++ b/src/Nexus/Services/PackageService.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Text.Json; -using Nexus.Core; using Nexus.Core.V1; using Nexus.Utilities; diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/Nexus.Sources.PackageControllerTestsSource.csproj b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/Nexus.Sources.PackageControllerTestsSource.csproj new file mode 100644 index 00000000..71225847 --- /dev/null +++ b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/Nexus.Sources.PackageControllerTestsSource.csproj @@ -0,0 +1,13 @@ + + + + $(TargetFrameworkVersion) + + + + + runtime;native + + + + diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/PackageControllerTestsSource.cs b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/PackageControllerTestsSource.cs new file mode 100644 index 00000000..b19d2a89 --- /dev/null +++ b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/PackageControllerTestsSource.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Logging; +using Nexus.DataModel; +using Nexus.Extensibility; + +namespace Nexus.Sources; + +public class PackageControllerTestsSource : IDataSource +{ + public Task SetContextAsync(DataSourceContext context, ILogger logger, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task GetCatalogRegistrationsAsync(string path, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task EnrichCatalogAsync(ResourceCatalog catalog, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task<(DateTime Begin, DateTime End)> GetTimeRangeAsync(string catalogId, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task GetAvailabilityAsync(string catalogId, DateTime begin, DateTime end, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task ReadAsync(DateTime begin, DateTime end, ReadRequest[] requests, ReadDataHandler readData, IProgress progress, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v0.1.0/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.0-alpha1+12345 postfix/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.0-alpha1+12345 postfix/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.0-beta1+12346 postfix/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.0-beta1+12346 postfix/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.0-beta2+12347 postfix/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.0-beta2+12347 postfix/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.1 postfix/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.0.1 postfix/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.1.1 postfix/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v1.1.1 postfix/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v2.0.0 postfix/dummy b/tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource/v2.0.0 postfix/dummy new file mode 100644 index 00000000..e69de29b diff --git a/tests/Nexus.Tests/Other/PackageControllerTests.cs b/tests/Nexus.Tests/Other/PackageControllerTests.cs index 206a7340..75213850 100644 --- a/tests/Nexus.Tests/Other/PackageControllerTests.cs +++ b/tests/Nexus.Tests/Other/PackageControllerTests.cs @@ -120,34 +120,18 @@ public class PackageControllerTests #region Load [Fact] - public async Task CanLoadAndUnloadAsync() + public async Task CanLoadAndUnload() { - // prepare extension - var extensionFolderPath = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var pathHash = new Guid(extensionFolderPath.Hash()).ToString(); - var configuration = "Debug"; - var csprojPath = "./../../../../tests/TestExtensionProject/TestExtensionProject.csproj"; + var extensionFolderPath = "../../../../tests/TestExtensionProject"; + var extensionFolderPathHash = new Guid(extensionFolderPath.Hash()).ToString(); - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"publish --output {Path.Combine(extensionFolderPath, "v1.0.0-unit.test")} --configuration {configuration} {csprojPath}" - } - }; - - process.Start(); - process.WaitForExit(); - - Assert.Equal(0, process.ExitCode); - - // prepare restore root + // create restore folder var restoreRoot = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); + Directory.CreateDirectory(restoreRoot); try { - var version = "v1.0.0-unit.test"; + var version = "v1"; var packageReference = new PackageReference( Provider: "local", @@ -155,11 +139,12 @@ public async Task CanLoadAndUnloadAsync() { // required ["path"] = extensionFolderPath, - ["version"] = version + ["version"] = version, + ["csproj"] = "TestExtensionProject.csproj" } ); - var fileToDelete = Path.Combine(restoreRoot, "local", pathHash, version, "TestExtensionProject.dll"); + var fileToDelete = Path.Combine(restoreRoot, "local", extensionFolderPathHash, version, "TestExtensionProject.dll"); var weakReference = await Load_Run_and_Unload_Async(restoreRoot, fileToDelete, packageReference); for (int i = 0; weakReference.IsAlive && i < 10; i++) @@ -178,12 +163,6 @@ public async Task CanLoadAndUnloadAsync() Directory.Delete(restoreRoot, recursive: true); } catch { } - - try - { - Directory.Delete(extensionFolderPath, recursive: true); - } - catch { } } } @@ -227,9 +206,6 @@ private async Task Load_Run_and_Unload_Async( [Fact] public async Task CanDiscover_local() { - // create dirs - var root = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var expected = new[] { "v2.0.0 postfix", @@ -241,62 +217,38 @@ public async Task CanDiscover_local() "v0.1.0" }; - foreach (var version in expected) - { - var dataFolderPath = Path.Combine(root, version); - Directory.CreateDirectory(Path.Combine(dataFolderPath, "sub")); - - await File.Create(Path.Combine(dataFolderPath, "sub", "a.deps.json")).DisposeAsync(); - await File.Create(Path.Combine(dataFolderPath, "sub", "a.dll")).DisposeAsync(); - } - - try - { - var packageReference = new PackageReference( - Provider: "local", - Configuration: new Dictionary - { - ["path"] = root, - } - ); + var packageReference = new PackageReference( + Provider: "local", + Configuration: new Dictionary + { + ["path"] = "../../../../tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource", + } + ); - var packageController = new PackageController(packageReference, NullLogger.Instance); + var packageController = new PackageController(packageReference, NullLogger.Instance); - var actual = (await packageController - .DiscoverAsync(CancellationToken.None)) - .ToArray(); + var actual = (await packageController + .DiscoverAsync(CancellationToken.None)) + .ToArray(); - Assert.Equal(expected.Length, actual.Length); + Assert.Equal(expected.Length, actual.Length); - foreach (var (expectedItem, actualItem) in expected.Zip(actual)) - { - Assert.Equal(expectedItem, actualItem); - } - } - finally + foreach (var (expectedItem, actualItem) in expected.Zip(actual)) { - Directory.Delete(root, recursive: true); + Assert.Equal(expectedItem, actualItem); } } [Fact] public async Task CanRestore_local() { - // create extension folder - var version = "v1.0.1 postfix"; - var extensionRoot = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var extensionRootHash = new Guid(extensionRoot.Hash()).ToString(); - var extensionFolderPath = Path.Combine(extensionRoot, version); - Directory.CreateDirectory(Path.Combine(extensionFolderPath, "sub", "sub")); - - await File.Create(Path.Combine(extensionFolderPath, "sub", "a.deps.json")).DisposeAsync(); - await File.Create(Path.Combine(extensionFolderPath, "sub", "a.dll")).DisposeAsync(); - await File.Create(Path.Combine(extensionFolderPath, "sub", "b.dll")).DisposeAsync(); - await File.Create(Path.Combine(extensionFolderPath, "sub", "sub", "c.data")).DisposeAsync(); + var version = "v0.1.0"; + var extensionFolderPath = "../../../../tests/Nexus.Tests/Other/Nexus.Sources.PackageControllerTestsSource"; + var extensionFolderPathHash = new Guid(extensionFolderPath.Hash()).ToString(); // create restore folder var restoreRoot = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var restoreFolderPath = Path.Combine(restoreRoot, "local", extensionRootHash, version); + var restoreFolderPath = Path.Combine(restoreRoot, "local", extensionFolderPathHash, version); Directory.CreateDirectory(restoreRoot); try @@ -306,22 +258,20 @@ public async Task CanRestore_local() Configuration: new Dictionary { // required - ["path"] = extensionRoot, - ["version"] = version + ["path"] = extensionFolderPath, + ["version"] = version, + ["csproj"] = "Nexus.Sources.PackageControllerTestsSource.csproj" } ); var packageController = new PackageController(packageReference, NullLogger.Instance); await packageController.RestoreAsync(restoreRoot, CancellationToken.None); + var expectedFilePath = Path.Combine(restoreFolderPath, "Nexus.Sources.PackageControllerTestsSource.deps.json"); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.deps.json"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "b.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "sub", "c.data"))); + Assert.True(File.Exists(expectedFilePath)); } finally { - Directory.Delete(extensionRoot, recursive: true); Directory.Delete(restoreRoot, recursive: true); } } @@ -408,268 +358,4 @@ public async Task CanRestore_git_tag() } #endregion - - #region Provider: github_releases - - [Fact] - public async Task CanDiscover_github_releases() - { - var expected = new[] - { - "v2.0.0", - "v1.1.1", - "v1.0.1", - "v1.0.0-beta2+12347", - "v1.0.0-beta1+12346", - "v1.0.0-alpha1+12345", - "v0.1.0" - }; - - var packageReference = new PackageReference( - Provider: "github-releases", - Configuration: new Dictionary - { - // required - ["project-path"] = "nexus-main/github-releases-provider-test-project", - - // optional token with scope(s): repo - ["token"] = Encoding.ASCII.GetString(_token) - } - ); - - var packageController = new PackageController(packageReference, NullLogger.Instance); - - var actual = (await packageController - .DiscoverAsync(CancellationToken.None)) - .ToArray(); - - Assert.Equal(expected.Length, actual.Length); - - foreach (var (expectedItem, actualItem) in expected.Zip(actual)) - { - Assert.Equal(expectedItem, actualItem); - } - } - - [Theory] - [InlineData(@"assets.*\.tar.gz")] - [InlineData(@"assets.*\.zip")] - public async Task CanRestore_github_releases(string assetSelector) - { - var version = "v1.0.1"; - - // create restore folder - var restoreRoot = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var restoreFolderPath = Path.Combine(restoreRoot, "github-releases", "nexus-main_github-releases-provider-test-project", version); - Directory.CreateDirectory(restoreRoot); - - try - { - var packageReference = new PackageReference( - Provider: "github-releases", - Configuration: new Dictionary - { - // required - ["project-path"] = "nexus-main/github-releases-provider-test-project", - ["tag"] = "v1.0.1", - ["asset-selector"] = assetSelector, - - // optional token with scope(s): repo - ["token"] = Encoding.ASCII.GetString(_token) - } - ); - - var packageController = new PackageController(packageReference, NullLogger.Instance); - await packageController.RestoreAsync(restoreRoot, CancellationToken.None); - - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.deps.json"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "b.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "sub", "c.data"))); - } - finally - { - Directory.Delete(restoreRoot, recursive: true); - } - } - - #endregion - - #region Provider: gitlab-releases-v4 - - [Fact(Skip = "The current approach does not work. See rationale in #region gitlab-releases-v4.")] - public async Task CanDiscover_gitlab_releases_v4() - { - var expected = new[] - { - "v2.0.0", - "v1.1.1", - "v1.0.1", - "v1.0.0-beta2+12347", - "v1.0.0-beta1+12346", - "v1.0.0-alpha1+12345", - "v0.1.0" - }; - - var packageReference = new PackageReference( - Provider: "gitlab-releases-v4", - Configuration: new Dictionary - { - // required - ["server"] = "https://gitlab.com", - ["project-path"] = "nexus-main/Test-Group/my-awesome-test-project", - - // optional token with scope(s): read_api - ["token"] = "doQyXYqgmFxS1LUsupue" - } - ); - - var packageController = new PackageController(packageReference, NullLogger.Instance); - - var actual = (await packageController - .DiscoverAsync(CancellationToken.None)) - .ToArray(); - - Assert.Equal(expected.Length, actual.Length); - - foreach (var (expectedItem, actualItem) in expected.Zip(actual)) - { - Assert.Equal(expectedItem, actualItem); - } - } - - [Theory(Skip = "The current approach does not work. See rationale in #region gitlab-releases-v4.")] - [InlineData(@"assets.*\.tar.gz")] - [InlineData(@"assets.*\.zip")] - public async Task CanRestore_gitlab_releases_v4(string assetSelector) - { - var version = "v1.0.1"; - - // create restore folder - var restoreRoot = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var restoreFolderPath = Path.Combine(restoreRoot, "gitlab-releases-v4", $"nexus-main_test-group_my-awesome-test-project", version); - Directory.CreateDirectory(restoreRoot); - - try - { - var packageReference = new PackageReference( - Provider: "gitlab-releases-v4", - Configuration: new Dictionary - { - // required - ["server"] = "https://gitlab.com", - ["project-path"] = "nexus-main/Test-Group/my-awesome-test-project", - ["tag"] = "v1.0.1", - ["asset-selector"] = assetSelector, - - // optional token with scope(s): read_api - ["token"] = "glpat-sQVhisTwk56oJyGJhzx1" - } - ); - - var packageController = new PackageController(packageReference, NullLogger.Instance); - await packageController.RestoreAsync(restoreRoot, CancellationToken.None); - - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.deps.json"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "b.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "sub", "c.data"))); - } - finally - { - Directory.Delete(restoreRoot, recursive: true); - } - } - - #endregion - - #region Provider: gitlab-packages-generic-v4 - - [Fact] - public async Task CanDiscover_gitlab_packages_generic_v4() - { - var expected = new[] - { - "v2.0.0", - "v1.1.1", - "v1.0.1", - "v1.0.0-beta2+12347", - "v1.0.0-beta1+12346", - "v1.0.0-alpha1+12345", - "v0.1.0" - }; - - var packageReference = new PackageReference( - Provider: "gitlab-packages-generic-v4", - Configuration: new Dictionary - { - // required - ["server"] = "https://gitlab.com", - ["project-path"] = "nexus-main/Test-Group/my-awesome-test-project", - ["package"] = "test-package", - - // optional token with scope(s): read_api - ["token"] = "glpat-sQVhisTwk56oJyGJhzx1", - } - ); - - var packageController = new PackageController(packageReference, NullLogger.Instance); - - var actual = (await packageController - .DiscoverAsync(CancellationToken.None)) - .ToArray(); - - Assert.Equal(expected.Length, actual.Length); - - foreach (var (expectedItem, actualItem) in expected.Zip(actual)) - { - Assert.Equal(expectedItem, actualItem); - } - } - - [Theory] - [InlineData(@"assets.*\.tar.gz")] - [InlineData(@"assets.*\.zip")] - public async Task CanRestore_gitlab_packages_generic_v4(string assetSelector) - { - var version = "v1.0.1"; - - // create restore folder - var restoreRoot = Path.Combine(Path.GetTempPath(), $"Nexus.Tests.{Guid.NewGuid()}"); - var restoreFolderPath = Path.Combine(restoreRoot, "gitlab-packages-generic-v4", $"nexus-main_test-group_my-awesome-test-project", version); - Directory.CreateDirectory(restoreRoot); - - try - { - var packageReference = new PackageReference( - Provider: "gitlab-packages-generic-v4", - Configuration: new Dictionary - { - // required - ["server"] = "https://gitlab.com", - ["project-path"] = "nexus-main/Test-Group/my-awesome-test-project", - ["package"] = "test-package", - ["version"] = "v1.0.1", - ["asset-selector"] = assetSelector, - - // optional token with scope(s): read_api - ["token"] = "glpat-sQVhisTwk56oJyGJhzx1", - } - ); - - var packageController = new PackageController(packageReference, NullLogger.Instance); - await packageController.RestoreAsync(restoreRoot, CancellationToken.None); - - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.deps.json"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "a.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "b.dll"))); - Assert.True(File.Exists(Path.Combine(restoreFolderPath, "sub", "sub", "c.data"))); - } - finally - { - Directory.Delete(restoreRoot, recursive: true); - } - } - - #endregion } \ No newline at end of file diff --git a/tests/TestExtensionProject/TestDataSource.cs b/tests/TestExtensionProject/v1/TestDataSource.cs similarity index 100% rename from tests/TestExtensionProject/TestDataSource.cs rename to tests/TestExtensionProject/v1/TestDataSource.cs diff --git a/tests/TestExtensionProject/TestDataWriter.cs b/tests/TestExtensionProject/v1/TestDataWriter.cs similarity index 100% rename from tests/TestExtensionProject/TestDataWriter.cs rename to tests/TestExtensionProject/v1/TestDataWriter.cs diff --git a/tests/TestExtensionProject/TestExtensionProject.csproj b/tests/TestExtensionProject/v1/TestExtensionProject.csproj similarity index 72% rename from tests/TestExtensionProject/TestExtensionProject.csproj rename to tests/TestExtensionProject/v1/TestExtensionProject.csproj index 31ecfc23..2f9013f3 100644 --- a/tests/TestExtensionProject/TestExtensionProject.csproj +++ b/tests/TestExtensionProject/v1/TestExtensionProject.csproj @@ -5,7 +5,7 @@ - + false runtime diff --git a/tests/extensibility/dotnet-extensibility-tests/dotnet-extensibility-tests.csproj b/tests/extensibility/dotnet-extensibility-tests/dotnet-extensibility-tests.csproj index 731b3708..003412ef 100644 --- a/tests/extensibility/dotnet-extensibility-tests/dotnet-extensibility-tests.csproj +++ b/tests/extensibility/dotnet-extensibility-tests/dotnet-extensibility-tests.csproj @@ -16,7 +16,7 @@ - +