diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f036fd9d..932b9c21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - name: Install .NET Core uses: actions/setup-dotnet@v4 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: Build & test (Release) run: dotnet test src -c Release diff --git a/Dockerfile b/Dockerfile index 0c8c702f..a6722338 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,20 @@ -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0 AS build -ARG TARGETARCH -WORKDIR /app - -RUN mkdir -p src/UnityNuGet && mkdir -p src/UnityNuGet.Server && mkdir -p src/UnityNuGet.Tests - -COPY src/*.sln src -COPY src/UnityNuGet/*.csproj src/UnityNuGet -COPY src/UnityNuGet.Server/*.csproj src/UnityNuGet.Server -COPY src/UnityNuGet.Tests/*.csproj src/UnityNuGet.Tests -RUN dotnet restore src -a $TARGETARCH - -COPY . ./ -RUN dotnet publish src -a $TARGETARCH -c Release -o /app/src/out - -# Build runtime image -FROM mcr.microsoft.com/dotnet/aspnet:7.0 -WORKDIR /app -COPY --from=build /app/src/out . -ENTRYPOINT ["dotnet", "UnityNuGet.Server.dll"] +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG TARGETARCH +WORKDIR /app + +RUN mkdir -p src/UnityNuGet && mkdir -p src/UnityNuGet.Server && mkdir -p src/UnityNuGet.Tests + +COPY src/*.sln src +COPY src/UnityNuGet/*.csproj src/UnityNuGet +COPY src/UnityNuGet.Server/*.csproj src/UnityNuGet.Server +COPY src/UnityNuGet.Tests/*.csproj src/UnityNuGet.Tests +RUN dotnet restore src -a $TARGETARCH + +COPY . ./ +RUN dotnet publish src -a $TARGETARCH -c Release -o /app/src/out + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:8.0 +WORKDIR /app +COPY --from=build /app/src/out . +ENTRYPOINT ["dotnet", "UnityNuGet.Server.dll"] diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..b91d59b2 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,8 @@ + + + + enable + true + + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 00000000..a423acc8 --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,24 @@ + + + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/UnityNuGet.Server/Controllers/ApiController.cs b/src/UnityNuGet.Server/Controllers/ApiController.cs index 95aade7a..43d3cc15 100644 --- a/src/UnityNuGet.Server/Controllers/ApiController.cs +++ b/src/UnityNuGet.Server/Controllers/ApiController.cs @@ -1,130 +1,124 @@ -using System.IO; -using System.Linq; -using System.Text; -using Microsoft.AspNetCore.Mvc; -using UnityNuGet.Npm; - -namespace UnityNuGet.Server.Controllers -{ - /// - /// Main entry to emulate the following NPM endpoints: - /// - /// - "-/all": query all packages (return json) - /// - "{packageId}": query a specific package (return json) - /// - "{package_id}/-/{package_file}": download a specific package - /// - [Route("")] - [ApiController] - public class ApiController : ControllerBase - { - private readonly RegistryCacheSingleton _cacheSingleton; - private readonly RegistryCacheReport _registryCacheReport; - - public ApiController(RegistryCacheSingleton cacheSingleton, RegistryCacheReport registryCacheReport) - { - _cacheSingleton = cacheSingleton; - _registryCacheReport = registryCacheReport; - } +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.AspNetCore.Mvc; +using UnityNuGet.Npm; + +namespace UnityNuGet.Server.Controllers +{ + /// + /// Main entry to emulate the following NPM endpoints: + /// + /// - "-/all": query all packages (return json) + /// - "{packageId}": query a specific package (return json) + /// - "{package_id}/-/{package_file}": download a specific package + /// + [Route("")] + [ApiController] + public class ApiController(RegistryCacheSingleton cacheSingleton, RegistryCacheReport registryCacheReport) : ControllerBase + { + private readonly RegistryCacheSingleton _cacheSingleton = cacheSingleton; + private readonly RegistryCacheReport _registryCacheReport = registryCacheReport; // GET / - [HttpGet("")] - public IActionResult Home() - { - return Ok(); - } - - // GET -/all - [HttpGet("-/all")] - public JsonResult GetAll() - { - if (!TryGetInstance(out var instance, out var error)) return new JsonResult(error); - - var result = instance?.All(); - return new JsonResult(result); - } - - // GET {packageId} - [HttpGet("{id}")] - public JsonResult GetPackage(string id) - { - if (!TryGetInstance(out var instance, out var error)) return new JsonResult(error); - - var package = instance?.GetPackage(id); - if (package == null) - { - return new JsonResult(NpmError.NotFound); - } - - return new JsonResult(package); - } - - // GET {package_id}/-/{package_file} - [HttpGet("{id}/-/{file}")] - [HttpHead("{id}/-/{file}")] - public IActionResult DownloadPackage(string id, string file) - { - if (!TryGetInstance(out var instance, out var error)) return new JsonResult(error); - - var package = instance?.GetPackage(id); - if (package == null) - { - return new JsonResult(NpmError.NotFound); - } - - if (!file.StartsWith(id + "-") || !file.EndsWith(".tgz")) - { - return new JsonResult(NpmError.NotFound); - } - - var filePath = instance?.GetPackageFilePath(file); - if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) - { - return new JsonResult(NpmError.NotFound); - } - - // This method can be called with HEAD request, so in that case we just calculate the content length - if (Request.Method.Equals("HEAD")) - { - Response.ContentType = "application/octet-stream"; - Response.ContentLength = new FileInfo(filePath).Length; - return Ok(); - } - else - { - return new PhysicalFileResult(filePath, "application/octet-stream") { FileDownloadName = file }; - } - } - - private bool TryGetInstance(out RegistryCache? cacheInstance, out NpmError? npmError) - { - var instance = _cacheSingleton.Instance; - cacheInstance = instance; - - if (instance == null) - { - if (_registryCacheReport.ErrorMessages.Any()) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("Error initializing the server:"); - - foreach (var error in _registryCacheReport.ErrorMessages) - { - stringBuilder.AppendLine(error); - } - - npmError = new NpmError("not_initialized", stringBuilder.ToString()); - } - else - { - npmError = new NpmError("not_initialized", $"The server is initializing ({_registryCacheReport.Progress:F1}% completed). Please retry later..."); - } - } - else - { - npmError = null; - } - - return instance != null; - } - } -} + [HttpGet("")] + public IActionResult Home() + { + return Ok(); + } + + // GET -/all + [HttpGet("-/all")] + public JsonResult GetAll() + { + if (!TryGetInstance(out var instance, out var error)) return new JsonResult(error); + + var result = instance?.All(); + return new JsonResult(result); + } + + // GET {packageId} + [HttpGet("{id}")] + public JsonResult GetPackage(string id) + { + if (!TryGetInstance(out var instance, out var error)) return new JsonResult(error); + + var package = instance?.GetPackage(id); + if (package == null) + { + return new JsonResult(NpmError.NotFound); + } + + return new JsonResult(package); + } + + // GET {package_id}/-/{package_file} + [HttpGet("{id}/-/{file}")] + [HttpHead("{id}/-/{file}")] + public IActionResult DownloadPackage(string id, string file) + { + if (!TryGetInstance(out var instance, out var error)) return new JsonResult(error); + + var package = instance?.GetPackage(id); + if (package == null) + { + return new JsonResult(NpmError.NotFound); + } + + if (!file.StartsWith(id + "-") || !file.EndsWith(".tgz")) + { + return new JsonResult(NpmError.NotFound); + } + + var filePath = instance?.GetPackageFilePath(file); + if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) + { + return new JsonResult(NpmError.NotFound); + } + + // This method can be called with HEAD request, so in that case we just calculate the content length + if (Request.Method.Equals("HEAD")) + { + Response.ContentType = "application/octet-stream"; + Response.ContentLength = new FileInfo(filePath).Length; + return Ok(); + } + else + { + return new PhysicalFileResult(filePath, "application/octet-stream") { FileDownloadName = file }; + } + } + + private bool TryGetInstance(out RegistryCache? cacheInstance, out NpmError? npmError) + { + var instance = _cacheSingleton.Instance; + cacheInstance = instance; + + if (instance == null) + { + if (_registryCacheReport.ErrorMessages.Any()) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Error initializing the server:"); + + foreach (var error in _registryCacheReport.ErrorMessages) + { + stringBuilder.AppendLine(error); + } + + npmError = new NpmError("not_initialized", stringBuilder.ToString()); + } + else + { + npmError = new NpmError("not_initialized", $"The server is initializing ({_registryCacheReport.Progress:F1}% completed). Please retry later..."); + } + } + else + { + npmError = null; + } + + return instance != null; + } + } +} diff --git a/src/UnityNuGet.Server/NuGetRedirectLogger.cs b/src/UnityNuGet.Server/NuGetRedirectLogger.cs index 1dd600ff..3e03d271 100644 --- a/src/UnityNuGet.Server/NuGetRedirectLogger.cs +++ b/src/UnityNuGet.Server/NuGetRedirectLogger.cs @@ -1,47 +1,42 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using NuGet.Common; -using ILogger = Microsoft.Extensions.Logging.ILogger; -using LogLevel = NuGet.Common.LogLevel; - -namespace UnityNuGet.Server -{ - /// - /// A NuGet logger redirecting to a - /// - public class NuGetRedirectLogger : LoggerBase - { - private readonly ILogger _logger; - - public NuGetRedirectLogger(ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - public override void Log(ILogMessage message) - { - LoggerExtensions.Log(_logger, GetLogLevel(message.Level), "{Message}", message.Message); - } - - public override Task LogAsync(ILogMessage message) - { - LoggerExtensions.Log(_logger, GetLogLevel(message.Level), "{Message}", message.Message); - return Task.CompletedTask; - } - - private static Microsoft.Extensions.Logging.LogLevel GetLogLevel(LogLevel logLevel) - { - return logLevel switch - { - LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, - LogLevel.Verbose => Microsoft.Extensions.Logging.LogLevel.Trace, - LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information, - LogLevel.Minimal => Microsoft.Extensions.Logging.LogLevel.Information, - LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, - LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, - _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null), - }; - } - } -} +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NuGet.Common; +using ILogger = Microsoft.Extensions.Logging.ILogger; +using LogLevel = NuGet.Common.LogLevel; + +namespace UnityNuGet.Server +{ + /// + /// A NuGet logger redirecting to a + /// + public class NuGetRedirectLogger(ILogger logger) : LoggerBase + { + private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + public override void Log(ILogMessage message) + { + LoggerExtensions.Log(_logger, GetLogLevel(message.Level), "{Message}", message.Message); + } + + public override Task LogAsync(ILogMessage message) + { + LoggerExtensions.Log(_logger, GetLogLevel(message.Level), "{Message}", message.Message); + return Task.CompletedTask; + } + + private static Microsoft.Extensions.Logging.LogLevel GetLogLevel(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, + LogLevel.Verbose => Microsoft.Extensions.Logging.LogLevel.Trace, + LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information, + LogLevel.Minimal => Microsoft.Extensions.Logging.LogLevel.Information, + LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, + LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, + _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null), + }; + } + } +} diff --git a/src/UnityNuGet.Server/RegistryCacheInitializer.cs b/src/UnityNuGet.Server/RegistryCacheInitializer.cs index 9d6f8892..4fdc8f65 100644 --- a/src/UnityNuGet.Server/RegistryCacheInitializer.cs +++ b/src/UnityNuGet.Server/RegistryCacheInitializer.cs @@ -1,82 +1,73 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace UnityNuGet.Server -{ - public class RegistryCacheInitializer : IHostedService - { - private readonly IConfiguration configuration; - private readonly IHostEnvironment hostEnvironment; - private readonly ILoggerFactory loggerFactory; - private readonly RegistryOptions registryOptions; - private readonly RegistryCacheSingleton registryCacheSingleton; - - public RegistryCacheInitializer(IConfiguration configuration, IHostEnvironment hostEnvironment, ILoggerFactory loggerFactory, IOptions registryOptionsAccessor, RegistryCacheSingleton registryCacheSingleton) - { - this.configuration = configuration; - this.hostEnvironment = hostEnvironment; - this.loggerFactory = loggerFactory; - registryOptions = registryOptionsAccessor.Value; - this.registryCacheSingleton = registryCacheSingleton; - } - - public Task StartAsync(CancellationToken cancellationToken) - { - var loggerRedirect = new NuGetRedirectLogger(loggerFactory.CreateLogger("NuGet")); - - Uri uri = registryOptions.RootHttpUrl!; - - bool isDevelopment = hostEnvironment.IsDevelopment(); - if (isDevelopment) - { - var urls = configuration[WebHostDefaults.ServerUrlsKey]; - - // Select HTTPS in production, HTTP in development - var url = (urls?.Split(';').FirstOrDefault(x => !x.StartsWith("https"))) ?? throw new InvalidOperationException($"Unable to find a proper server URL from `{urls}`. Expecting a `http://...` URL in development"); - - // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_running_in_container-and-dotnet_running_in_containers - bool runningInContainer = configuration.GetValue("DOTNET_RUNNING_IN_CONTAINER"); - - uri = new Uri(runningInContainer ? url.Replace("+", "localhost") : url); - } - - // Get the current directory from registry options (prepend binary folder in dev) - string unityPackageFolder; - - if (isDevelopment) - { - var currentDirectory = Path.GetDirectoryName(typeof(Startup).Assembly.Location)!; - unityPackageFolder = Path.Combine(currentDirectory, new DirectoryInfo(registryOptions.RootPersistentFolder!).Name); - } - else - { - if (Path.IsPathRooted(registryOptions.RootPersistentFolder)) - { - unityPackageFolder = registryOptions.RootPersistentFolder; - } - else - { - unityPackageFolder = Path.Combine(Directory.GetCurrentDirectory(), registryOptions.RootPersistentFolder!); - } - } - loggerRedirect.LogInformation($"Using Unity Package folder `{unityPackageFolder}`"); - - // Add the cache accessible from the services - registryCacheSingleton.UnityPackageFolder = unityPackageFolder; - registryCacheSingleton.ServerUri = uri; - registryCacheSingleton.NuGetRedirectLogger = loggerRedirect; - - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - } -} +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace UnityNuGet.Server +{ + public class RegistryCacheInitializer(IConfiguration configuration, IHostEnvironment hostEnvironment, ILoggerFactory loggerFactory, IOptions registryOptionsAccessor, RegistryCacheSingleton registryCacheSingleton) : IHostedService + { + private readonly IConfiguration configuration = configuration; + private readonly IHostEnvironment hostEnvironment = hostEnvironment; + private readonly ILoggerFactory loggerFactory = loggerFactory; + private readonly RegistryOptions registryOptions = registryOptionsAccessor.Value; + private readonly RegistryCacheSingleton registryCacheSingleton = registryCacheSingleton; + + public Task StartAsync(CancellationToken cancellationToken) + { + var loggerRedirect = new NuGetRedirectLogger(loggerFactory.CreateLogger("NuGet")); + + Uri uri = registryOptions.RootHttpUrl!; + + bool isDevelopment = hostEnvironment.IsDevelopment(); + if (isDevelopment) + { + var urls = configuration[WebHostDefaults.ServerUrlsKey]; + + // Select HTTPS in production, HTTP in development + var url = (urls?.Split(';').FirstOrDefault(x => !x.StartsWith("https"))) ?? throw new InvalidOperationException($"Unable to find a proper server URL from `{urls}`. Expecting a `http://...` URL in development"); + + // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_running_in_container-and-dotnet_running_in_containers + bool runningInContainer = configuration.GetValue("DOTNET_RUNNING_IN_CONTAINER"); + + uri = new Uri(runningInContainer ? url.Replace("+", "localhost") : url); + } + + // Get the current directory from registry options (prepend binary folder in dev) + string unityPackageFolder; + + if (isDevelopment) + { + var currentDirectory = Path.GetDirectoryName(typeof(Startup).Assembly.Location)!; + unityPackageFolder = Path.Combine(currentDirectory, new DirectoryInfo(registryOptions.RootPersistentFolder!).Name); + } + else + { + if (Path.IsPathRooted(registryOptions.RootPersistentFolder)) + { + unityPackageFolder = registryOptions.RootPersistentFolder; + } + else + { + unityPackageFolder = Path.Combine(Directory.GetCurrentDirectory(), registryOptions.RootPersistentFolder!); + } + } + loggerRedirect.LogInformation($"Using Unity Package folder `{unityPackageFolder}`"); + + // Add the cache accessible from the services + registryCacheSingleton.UnityPackageFolder = unityPackageFolder; + registryCacheSingleton.ServerUri = uri; + registryCacheSingleton.NuGetRedirectLogger = loggerRedirect; + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } +} diff --git a/src/UnityNuGet.Server/RegistryCacheReport.cs b/src/UnityNuGet.Server/RegistryCacheReport.cs index 4861e521..f50b4ad3 100644 --- a/src/UnityNuGet.Server/RegistryCacheReport.cs +++ b/src/UnityNuGet.Server/RegistryCacheReport.cs @@ -1,84 +1,78 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Options; - -namespace UnityNuGet.Server -{ - /// - /// Main class that stores relevant messages that may appear during the build of the Unity packages with . - /// - public class RegistryCacheReport - { - private readonly IList _informationMessages = new List(); - private readonly IList _warningMessages = new List(); - private readonly IList _errorMessages = new List(); - - private DateTime? _lastUpdate; - - private readonly RegistryCacheSingleton _registryCacheSingleton; - private readonly RegistryOptions _registryOptions; - - public RegistryCacheReport(RegistryCacheSingleton registryCacheSingleton, IOptions registryOptionsAccessor) - { - _registryCacheSingleton = registryCacheSingleton; - _registryOptions = registryOptionsAccessor.Value; - } - - public IEnumerable InformationMeessages => _informationMessages; - - public IEnumerable WarningMessages => _warningMessages; - - public IEnumerable ErrorMessages => _errorMessages; - - public bool Running { get; private set; } - - public double Progress - { - get - { - var currentIndex = _registryCacheSingleton.ProgressPackageIndex; - var totalCount = _registryCacheSingleton.ProgressTotalPackageCount; - var percent = totalCount != 0 ? (double)currentIndex * 100 / totalCount : 0; - - return percent; - } - } - - public TimeSpan? TimeRemainingForNextUpdate - { - get - { - if (_errorMessages.Count == 0) - { - return _lastUpdate != null ? _lastUpdate.Value.Add(_registryOptions.UpdateInterval) - DateTime.UtcNow : null; - } - else - { - return TimeSpan.FromSeconds(0); - } - } - } - - public void AddInformation(string message) => _informationMessages.Add(message); - - public void AddWarning(string message) => _warningMessages.Add(message); - - public void AddError(string message) => _errorMessages.Add(message); - - public void Start() - { - Running = true; - - _informationMessages.Clear(); - _warningMessages.Clear(); - _errorMessages.Clear(); - } - - public void Complete() - { - Running = false; - - _lastUpdate = DateTime.UtcNow; - } - } -} +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Options; + +namespace UnityNuGet.Server +{ + /// + /// Main class that stores relevant messages that may appear during the build of the Unity packages with . + /// + public class RegistryCacheReport(RegistryCacheSingleton registryCacheSingleton, IOptions registryOptionsAccessor) + { + private readonly RegistryCacheSingleton _registryCacheSingleton = registryCacheSingleton; + private readonly RegistryOptions _registryOptions = registryOptionsAccessor.Value; + + private readonly List _informationMessages = []; + private readonly List _warningMessages = []; + private readonly List _errorMessages = []; + + private DateTime? _lastUpdate; + + public IEnumerable InformationMeessages => _informationMessages; + + public IEnumerable WarningMessages => _warningMessages; + + public IEnumerable ErrorMessages => _errorMessages; + + public bool Running { get; private set; } + + public double Progress + { + get + { + var currentIndex = _registryCacheSingleton.ProgressPackageIndex; + var totalCount = _registryCacheSingleton.ProgressTotalPackageCount; + var percent = totalCount != 0 ? (double)currentIndex * 100 / totalCount : 0; + + return percent; + } + } + + public TimeSpan? TimeRemainingForNextUpdate + { + get + { + if (_errorMessages.Count == 0) + { + return _lastUpdate != null ? _lastUpdate.Value.Add(_registryOptions.UpdateInterval) - DateTime.UtcNow : null; + } + else + { + return TimeSpan.FromSeconds(0); + } + } + } + + public void AddInformation(string message) => _informationMessages.Add(message); + + public void AddWarning(string message) => _warningMessages.Add(message); + + public void AddError(string message) => _errorMessages.Add(message); + + public void Start() + { + Running = true; + + _informationMessages.Clear(); + _warningMessages.Clear(); + _errorMessages.Clear(); + } + + public void Complete() + { + Running = false; + + _lastUpdate = DateTime.UtcNow; + } + } +} diff --git a/src/UnityNuGet.Server/RegistryCacheUpdater.cs b/src/UnityNuGet.Server/RegistryCacheUpdater.cs index d3dbdd95..aed12d3f 100644 --- a/src/UnityNuGet.Server/RegistryCacheUpdater.cs +++ b/src/UnityNuGet.Server/RegistryCacheUpdater.cs @@ -1,82 +1,74 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace UnityNuGet.Server -{ - /// - /// Update the RegistryCache at a regular interval - /// - internal sealed class RegistryCacheUpdater : BackgroundService - { - private readonly RegistryCacheReport _registryCacheReport; - private readonly RegistryCacheSingleton _currentRegistryCache; - private readonly ILogger _logger; - private readonly RegistryOptions _registryOptions; - - public RegistryCacheUpdater(RegistryCacheReport registryCacheReport, RegistryCacheSingleton currentRegistryCache, ILogger logger, IOptions registryOptionsAccessor) - { - _registryCacheReport = registryCacheReport; - _currentRegistryCache = currentRegistryCache; - _logger = logger; - _registryOptions = registryOptionsAccessor.Value; - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - try - { - while (!stoppingToken.IsCancellationRequested) - { - _logger.LogInformation("Starting to update RegistryCache"); - - _registryCacheReport.Start(); - - var newRegistryCache = new RegistryCache(_currentRegistryCache.UnityPackageFolder!, _currentRegistryCache.ServerUri!, _registryOptions.UnityScope!, _registryOptions.MinimumUnityVersion!, _registryOptions.PackageNameNuGetPostFix!, _registryOptions.TargetFrameworks!, _currentRegistryCache.NuGetRedirectLogger!) - { - // Update progress - OnProgress = (current, total) => - { - _currentRegistryCache.ProgressTotalPackageCount = total; - _currentRegistryCache.ProgressPackageIndex = current; - }, - OnInformation = message => _registryCacheReport.AddInformation(message), - OnWarning = message => _registryCacheReport.AddWarning(message), - OnError = message => _registryCacheReport.AddError(message) - }; - - await newRegistryCache.Build(); - - if (_registryCacheReport.ErrorMessages.Any()) - { - _logger.LogInformation("RegistryCache not updated due to errors. See previous logs"); - } - else - { - // Update the registry cache in the services - _currentRegistryCache.Instance = newRegistryCache; - - _logger.LogInformation("RegistryCache successfully updated"); - } - - _registryCacheReport.Complete(); - - await Task.Delay((int)_registryOptions.UpdateInterval.TotalMilliseconds, stoppingToken); - } - } - catch (Exception ex) - { - string message = "Error while building a new registry cache."; - - _logger.LogError(ex, "{Message}", message); - - _registryCacheReport.AddError($"{message}. Reason: {ex}"); - _registryCacheReport.Complete(); - } - } - } -} +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace UnityNuGet.Server +{ + /// + /// Update the RegistryCache at a regular interval + /// + internal sealed class RegistryCacheUpdater(RegistryCacheReport registryCacheReport, RegistryCacheSingleton currentRegistryCache, ILogger logger, IOptions registryOptionsAccessor) : BackgroundService + { + private readonly RegistryCacheReport _registryCacheReport = registryCacheReport; + private readonly RegistryCacheSingleton _currentRegistryCache = currentRegistryCache; + private readonly ILogger _logger = logger; + private readonly RegistryOptions _registryOptions = registryOptionsAccessor.Value; + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + try + { + while (!stoppingToken.IsCancellationRequested) + { + _logger.LogInformation("Starting to update RegistryCache"); + + _registryCacheReport.Start(); + + var newRegistryCache = new RegistryCache(_currentRegistryCache.UnityPackageFolder!, _currentRegistryCache.ServerUri!, _registryOptions.UnityScope!, _registryOptions.MinimumUnityVersion!, _registryOptions.PackageNameNuGetPostFix!, _registryOptions.TargetFrameworks!, _currentRegistryCache.NuGetRedirectLogger!) + { + // Update progress + OnProgress = (current, total) => + { + _currentRegistryCache.ProgressTotalPackageCount = total; + _currentRegistryCache.ProgressPackageIndex = current; + }, + OnInformation = message => _registryCacheReport.AddInformation(message), + OnWarning = message => _registryCacheReport.AddWarning(message), + OnError = message => _registryCacheReport.AddError(message) + }; + + await newRegistryCache.Build(); + + if (_registryCacheReport.ErrorMessages.Any()) + { + _logger.LogInformation("RegistryCache not updated due to errors. See previous logs"); + } + else + { + // Update the registry cache in the services + _currentRegistryCache.Instance = newRegistryCache; + + _logger.LogInformation("RegistryCache successfully updated"); + } + + _registryCacheReport.Complete(); + + await Task.Delay((int)_registryOptions.UpdateInterval.TotalMilliseconds, stoppingToken); + } + } + catch (Exception ex) + { + string message = "Error while building a new registry cache."; + + _logger.LogError(ex, "{Message}", message); + + _registryCacheReport.AddError($"{message}. Reason: {ex}"); + _registryCacheReport.Complete(); + } + } + } +} diff --git a/src/UnityNuGet.Server/UnityNuGet.Server.csproj b/src/UnityNuGet.Server/UnityNuGet.Server.csproj index 52e06b76..56d92248 100644 --- a/src/UnityNuGet.Server/UnityNuGet.Server.csproj +++ b/src/UnityNuGet.Server/UnityNuGet.Server.csproj @@ -1,12 +1,10 @@ - + - net7.0 - enable + net8.0 /subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/unitynuget-registry /subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/unitynuget-registry 1be0a769-8d75-4a27-99e0-128afcc0ffee - true @@ -16,11 +14,11 @@ - - - - - + + + + + diff --git a/src/UnityNuGet.Tests/NativeTests.cs b/src/UnityNuGet.Tests/NativeTests.cs index 1dd28f4e..60419540 100644 --- a/src/UnityNuGet.Tests/NativeTests.cs +++ b/src/UnityNuGet.Tests/NativeTests.cs @@ -1,53 +1,53 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using NUnit.Framework; - -namespace UnityNuGet.Tests -{ - [Ignore("Ignore native libs tests")] - public class NativeTests - { - [Test] - public async Task TestBuild() - { - var unityPackages = Path.Combine(Path.GetDirectoryName(typeof(RegistryCacheTests).Assembly.Location), "unity_packages"); - Directory.Delete(unityPackages, true); - - var errorsTriggered = false; - - var registryCache = new RegistryCache( - unityPackages, - new Uri("http://localhost/"), - "org.nuget", - "2019.1", - " (NuGet)", - new RegistryTargetFramework[] { - new RegistryTargetFramework { Name = "netstandard2.1", DefineConstraints = new string[] { "UNITY_2021_2_OR_NEWER"} }, - new RegistryTargetFramework { Name = "netstandard2.0", DefineConstraints = new string[] { "!UNITY_2021_2_OR_NEWER" } }, - }, - new NuGetConsoleLogger()) - { - Filter = "rhino3dm", - OnError = message => - { - errorsTriggered = true; - } - }; - - await registryCache.Build(); - - Assert.False(errorsTriggered, "The registry failed to build, check the logs"); - var allResult = registryCache.All(); - var allResultJson = allResult.ToJson(); - - StringAssert.Contains("org.nuget.rhino3dm", allResultJson); - - var rhinoPackage = registryCache.GetPackage("org.nuget.rhino3dm"); - Assert.NotNull(rhinoPackage); - var rhinopackageJson = rhinoPackage.ToJson(); - StringAssert.Contains("org.nuget.rhino3dm", rhinopackageJson); - StringAssert.Contains("7.11.0", rhinopackageJson); - } - } -} +using System; +using System.IO; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace UnityNuGet.Tests +{ + [Ignore("Ignore native libs tests")] + public class NativeTests + { + [Test] + public async Task TestBuild() + { + var unityPackages = Path.Combine(Path.GetDirectoryName(typeof(RegistryCacheTests).Assembly.Location)!, "unity_packages"); + Directory.Delete(unityPackages, true); + + var errorsTriggered = false; + + var registryCache = new RegistryCache( + unityPackages, + new Uri("http://localhost/"), + "org.nuget", + "2019.1", + " (NuGet)", + [ + new() { Name = "netstandard2.1", DefineConstraints = ["UNITY_2021_2_OR_NEWER"] }, + new() { Name = "netstandard2.0", DefineConstraints = ["!UNITY_2021_2_OR_NEWER"] }, + ], + new NuGetConsoleLogger()) + { + Filter = "rhino3dm", + OnError = message => + { + errorsTriggered = true; + } + }; + + await registryCache.Build(); + + Assert.That(errorsTriggered, Is.False, "The registry failed to build, check the logs"); + var allResult = registryCache.All(); + var allResultJson = allResult.ToJson(); + + Assert.That(allResultJson, Does.Contain("org.nuget.rhino3dm")); + + var rhinoPackage = registryCache.GetPackage("org.nuget.rhino3dm"); + Assert.That(rhinoPackage, Is.Not.Null); + var rhinopackageJson = rhinoPackage!.ToJson(); + Assert.That(rhinopackageJson, Does.Contain("org.nuget.rhino3dm")); + Assert.That(rhinopackageJson, Does.Contain("7.11.0")); + } + } +} diff --git a/src/UnityNuGet.Tests/NuGetHelperTests.cs b/src/UnityNuGet.Tests/NuGetHelperTests.cs index 44329295..17c63a47 100644 --- a/src/UnityNuGet.Tests/NuGetHelperTests.cs +++ b/src/UnityNuGet.Tests/NuGetHelperTests.cs @@ -18,7 +18,7 @@ public class NuGetHelperTests [TestCase("analyzers/Test.resources.dll")] public void IsApplicableAnalyzerResource_Valid(string input) { - Assert.True(NuGetHelper.IsApplicableAnalyzerResource(input)); + Assert.That(NuGetHelper.IsApplicableAnalyzerResource(input), Is.True); } [Test] @@ -31,7 +31,7 @@ public void IsApplicableAnalyzerResource_Valid(string input) [TestCase("analyzers/Test.dll")] public void IsApplicableAnalyzerResource_Invalid(string input) { - Assert.False(NuGetHelper.IsApplicableAnalyzerResource(input)); + Assert.That(NuGetHelper.IsApplicableAnalyzerResource(input), Is.False); } // Examples: @@ -48,7 +48,7 @@ public void IsApplicableAnalyzerResource_Invalid(string input) [TestCase("analyzers/Test.dll")] public void IsApplicableUnitySupportedRoslynVersionFolder_Valid(string input) { - Assert.True(NuGetHelper.IsApplicableUnitySupportedRoslynVersionFolder(input)); + Assert.That(NuGetHelper.IsApplicableUnitySupportedRoslynVersionFolder(input), Is.True); } [Test] @@ -56,7 +56,7 @@ public void IsApplicableUnitySupportedRoslynVersionFolder_Valid(string input) [TestCase("analyzers/dotnet/roslyn4.0/Test.dll")] public void IsApplicableUnitySupportedRoslynVersionFolder_Invalid(string input) { - Assert.False(NuGetHelper.IsApplicableUnitySupportedRoslynVersionFolder(input)); + Assert.That(NuGetHelper.IsApplicableUnitySupportedRoslynVersionFolder(input), Is.False); } [Test] @@ -64,17 +64,17 @@ public void GetCompatiblePackageDependencyGroups_SpecificSingleFramework() { IList packageDependencyGroups = new PackageDependencyGroup[] { - new PackageDependencyGroup(CommonFrameworks.NetStandard13, Array.Empty()), - new PackageDependencyGroup(CommonFrameworks.NetStandard16, Array.Empty()), - new PackageDependencyGroup(CommonFrameworks.NetStandard20, Array.Empty()), - new PackageDependencyGroup(CommonFrameworks.NetStandard21, Array.Empty()) + new(CommonFrameworks.NetStandard13, Array.Empty()), + new(CommonFrameworks.NetStandard16, Array.Empty()), + new(CommonFrameworks.NetStandard20, Array.Empty()), + new(CommonFrameworks.NetStandard21, Array.Empty()) }; - IEnumerable targetFrameworks = new RegistryTargetFramework[] { new RegistryTargetFramework { Framework = CommonFrameworks.NetStandard20 } }; + IEnumerable targetFrameworks = new RegistryTargetFramework[] { new() { Framework = CommonFrameworks.NetStandard20 } }; IEnumerable compatibleDependencyGroups = NuGetHelper.GetCompatiblePackageDependencyGroups(packageDependencyGroups, targetFrameworks); - CollectionAssert.AreEqual(new PackageDependencyGroup[] { packageDependencyGroups[2] }, compatibleDependencyGroups); + Assert.That(compatibleDependencyGroups, Is.EqualTo(new PackageDependencyGroup[] { packageDependencyGroups[2] }).AsCollection); } [Test] @@ -82,17 +82,17 @@ public void GetCompatiblePackageDependencyGroups_SpecificMultipleFrameworks() { IList packageDependencyGroups = new PackageDependencyGroup[] { - new PackageDependencyGroup(CommonFrameworks.NetStandard13, Array.Empty()), - new PackageDependencyGroup(CommonFrameworks.NetStandard16, Array.Empty()), - new PackageDependencyGroup(CommonFrameworks.NetStandard20, Array.Empty()), - new PackageDependencyGroup(CommonFrameworks.NetStandard21, Array.Empty()) + new(CommonFrameworks.NetStandard13, Array.Empty()), + new(CommonFrameworks.NetStandard16, Array.Empty()), + new(CommonFrameworks.NetStandard20, Array.Empty()), + new(CommonFrameworks.NetStandard21, Array.Empty()) }; - IEnumerable targetFrameworks = new RegistryTargetFramework[] { new RegistryTargetFramework { Framework = CommonFrameworks.NetStandard20 }, new RegistryTargetFramework { Framework = CommonFrameworks.NetStandard21 } }; + IEnumerable targetFrameworks = new RegistryTargetFramework[] { new() { Framework = CommonFrameworks.NetStandard20 }, new() { Framework = CommonFrameworks.NetStandard21 } }; IEnumerable compatibleDependencyGroups = NuGetHelper.GetCompatiblePackageDependencyGroups(packageDependencyGroups, targetFrameworks); - CollectionAssert.AreEqual(new PackageDependencyGroup[] { packageDependencyGroups[2], packageDependencyGroups[3] }, compatibleDependencyGroups); + Assert.That(compatibleDependencyGroups, Is.EqualTo(new PackageDependencyGroup[] { packageDependencyGroups[2], packageDependencyGroups[3] }).AsCollection); } [Test] @@ -100,14 +100,14 @@ public void GetCompatiblePackageDependencyGroups_AnyFramework() { IList packageDependencyGroups = new PackageDependencyGroup[] { - new PackageDependencyGroup(new NuGetFramework(SpecialIdentifiers.Any), Array.Empty()) + new(new NuGetFramework(SpecialIdentifiers.Any), Array.Empty()) }; - IEnumerable targetFrameworks = new RegistryTargetFramework[] { new RegistryTargetFramework { Framework = CommonFrameworks.NetStandard20 } }; + IEnumerable targetFrameworks = new RegistryTargetFramework[] { new() { Framework = CommonFrameworks.NetStandard20 } }; IEnumerable compatibleDependencyGroups = NuGetHelper.GetCompatiblePackageDependencyGroups(packageDependencyGroups, targetFrameworks); - CollectionAssert.AreEqual(packageDependencyGroups, compatibleDependencyGroups); + Assert.That(compatibleDependencyGroups, Is.EqualTo(packageDependencyGroups).AsCollection); } } } diff --git a/src/UnityNuGet.Tests/PlatformDefinitionTests.cs b/src/UnityNuGet.Tests/PlatformDefinitionTests.cs index 6c4377b0..7ac0b412 100644 --- a/src/UnityNuGet.Tests/PlatformDefinitionTests.cs +++ b/src/UnityNuGet.Tests/PlatformDefinitionTests.cs @@ -1,132 +1,144 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using NUnit.Framework; - -namespace UnityNuGet.Tests -{ - public class PlatformDefinitionTests - { - [Test] - public void CanFindDefinitions() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - - // Look-up by OS should return the most general configuration - var win = platformDefs.Find(UnityOs.Windows); - Assert.IsNotNull(win); - Assert.AreEqual(win.Cpu, UnityCpu.AnyCpu); - - // Look-up explicit configuration +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; + +namespace UnityNuGet.Tests +{ + public class PlatformDefinitionTests + { + [Test] + public void CanFindDefinitions() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + + // Look-up by OS should return the most general configuration + var win = platformDefs.Find(UnityOs.Windows); + Assert.That(win, Is.Not.Null); + Assert.That(win!.Cpu, Is.EqualTo(UnityCpu.AnyCpu)); + + // Look-up explicit configuration var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64); - Assert.IsNotNull(win64); - Assert.AreEqual(win64.Os, win.Os); - Assert.AreEqual(win64.Cpu, UnityCpu.X64); - Assert.True(win.Children.Contains(win64)); - - // Look-up invalid configuration - var and = platformDefs.Find(UnityOs.Android, UnityCpu.None); - Assert.IsNull(and); - } - - [Test] - public void RemainingPlatforms_NoneVisited() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var visited = new HashSet(); - - // If no platform was visited, the remaining platforms should be the (AnyOS, AnyCPU) config. - var remaining = platformDefs.GetRemainingPlatforms(visited); - Assert.IsNotNull(remaining); - Assert.AreEqual(1, remaining.Count); - Assert.AreEqual(remaining.First(), platformDefs); - } - - [Test] - public void RemainingPlatforms_OneVisited() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - - foreach (var child in platformDefs.Children) + Assert.Multiple(() => { - var visited = new HashSet() { child }; - var remaining = platformDefs.GetRemainingPlatforms(visited); - - // We should get all other children, except the one already visited - Assert.AreEqual(platformDefs.Children.Count, remaining.Count + 1); - foreach (var r in remaining) + Assert.That(win64, Is.Not.Null); + Assert.That(win.Os, Is.EqualTo(win64!.Os)); + }); + Assert.Multiple(() => + { + Assert.That(win64.Cpu, Is.EqualTo(UnityCpu.X64)); + Assert.That(win.Children, Does.Contain(win64)); + }); + + // Look-up invalid configuration + var and = platformDefs.Find(UnityOs.Android, UnityCpu.None); + Assert.That(and, Is.Null); + } + + [Test] + public void RemainingPlatforms_NoneVisited() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var visited = new HashSet(); + + // If no platform was visited, the remaining platforms should be the (AnyOS, AnyCPU) config. + var remaining = platformDefs.GetRemainingPlatforms(visited); + Assert.That(remaining, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(remaining, Has.Count.EqualTo(1)); + Assert.That(platformDefs, Is.EqualTo(remaining.First())); + }); + } + + [Test] + public void RemainingPlatforms_OneVisited() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + + foreach (var child in platformDefs.Children) + { + var visited = new HashSet() { child }; + var remaining = platformDefs.GetRemainingPlatforms(visited); + + // We should get all other children, except the one already visited + Assert.That(remaining.Count + 1, Is.EqualTo(platformDefs.Children.Count)); + foreach (var r in remaining) { - Assert.AreNotEqual(r, child); - Assert.IsTrue(platformDefs.Children.Contains(r)); - } - } - } - - [Test] - public void RemainingPlatforms_LeafVisited() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64); - var visited = new HashSet() { win64 }; - - // The remaining platforms should be all non-windows, as well as all !x64 windows - var expected = platformDefs.Children - .Except(new[] { win64.Parent }) - .Concat( - win64.Parent.Children - .Except(new[] { win64 })) - .ToHashSet(); - var actual = platformDefs.GetRemainingPlatforms(visited); - Assert.IsTrue(expected.SetEquals(actual)); - } - - [TestCase("")] - [TestCase("base")] - public void TestConfigPath_Root(string basePath) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var file = new PlatformFile("a/b/c.dll", platformDefs); - - // We don't use extra paths for the (AnyOS, AnyCPU) configuration - var actual = file.GetDestinationPath(basePath); - var expected = Path.Combine( - basePath, - Path.GetFileName(file.SourcePath)); - Assert.AreEqual(actual, expected); - } - - [TestCase("")] - [TestCase("base")] - public void TestConfigPath_OsOnly(string basePath) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var win = platformDefs.Find(UnityOs.Windows); - var file = new PlatformFile("a/b/c.dll", win); - - var actual = file.GetDestinationPath(basePath); - var expected = Path.Combine( - basePath, - "Windows", - Path.GetFileName(file.SourcePath)); - Assert.AreEqual(actual, expected); - } - - [TestCase("")] - [TestCase("base")] - public void TestConfigPath_Full(string basePath) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64); - var file = new PlatformFile("a/b/c.dll", win64); - - var actual = file.GetDestinationPath(basePath); - var expected = Path.Combine( - basePath, - "Windows", - "x86_64", - Path.GetFileName(file.SourcePath)); - Assert.AreEqual(actual, expected); - } - } -} + Assert.Multiple(() => + { + Assert.That(child, Is.Not.EqualTo(r)); + Assert.That(platformDefs.Children, Does.Contain(r)); + }); + } + } + } + + [Test] + public void RemainingPlatforms_LeafVisited() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64); + var visited = new HashSet() { win64! }; + + // The remaining platforms should be all non-windows, as well as all !x64 windows + var expected = platformDefs.Children + .Except(new[] { win64!.Parent }) + .Concat( + win64.Parent!.Children + .Except(new[] { win64 })) + .ToHashSet(); + var actual = platformDefs.GetRemainingPlatforms(visited); + Assert.That(expected.SetEquals(actual), Is.True); + } + + [TestCase("")] + [TestCase("base")] + public void TestConfigPath_Root(string basePath) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var file = new PlatformFile("a/b/c.dll", platformDefs); + + // We don't use extra paths for the (AnyOS, AnyCPU) configuration + var actual = file.GetDestinationPath(basePath); + var expected = Path.Combine( + basePath, + Path.GetFileName(file.SourcePath)); + Assert.That(expected, Is.EqualTo(actual)); + } + + [TestCase("")] + [TestCase("base")] + public void TestConfigPath_OsOnly(string basePath) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var win = platformDefs.Find(UnityOs.Windows); + var file = new PlatformFile("a/b/c.dll", win!); + + var actual = file.GetDestinationPath(basePath); + var expected = Path.Combine( + basePath, + "Windows", + Path.GetFileName(file.SourcePath)); + Assert.That(expected, Is.EqualTo(actual)); + } + + [TestCase("")] + [TestCase("base")] + public void TestConfigPath_Full(string basePath) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var win64 = platformDefs.Find(UnityOs.Windows, UnityCpu.X64); + var file = new PlatformFile("a/b/c.dll", win64!); + + var actual = file.GetDestinationPath(basePath); + var expected = Path.Combine( + basePath, + "Windows", + "x86_64", + Path.GetFileName(file.SourcePath)); + Assert.That(expected, Is.EqualTo(actual)); + } + } +} diff --git a/src/UnityNuGet.Tests/RegistryCacheTests.cs b/src/UnityNuGet.Tests/RegistryCacheTests.cs index 6e7372ba..e13c464f 100644 --- a/src/UnityNuGet.Tests/RegistryCacheTests.cs +++ b/src/UnityNuGet.Tests/RegistryCacheTests.cs @@ -12,17 +12,17 @@ public async Task TestBuild() { var errorsTriggered = false; - var unityPackages = Path.Combine(Path.GetDirectoryName(typeof(RegistryCacheTests).Assembly.Location), "unity_packages"); + var unityPackages = Path.Combine(Path.GetDirectoryName(typeof(RegistryCacheTests).Assembly.Location)!, "unity_packages"); var registryCache = new RegistryCache( unityPackages, new Uri("http://localhost/"), "org.nuget", "2019.1", " (NuGet)", - new RegistryTargetFramework[] { - new RegistryTargetFramework { Name = "netstandard2.1", DefineConstraints = new string[] { "UNITY_2021_2_OR_NEWER"} }, - new RegistryTargetFramework { Name = "netstandard2.0", DefineConstraints = new string[] { "!UNITY_2021_2_OR_NEWER" } }, - }, + [ + new() { Name = "netstandard2.1", DefineConstraints = ["UNITY_2021_2_OR_NEWER"] }, + new() { Name = "netstandard2.0", DefineConstraints = ["!UNITY_2021_2_OR_NEWER"] }, + ], new NuGetConsoleLogger()) { OnError = message => @@ -36,20 +36,20 @@ public async Task TestBuild() await registryCache.Build(); - Assert.False(errorsTriggered, "The registry failed to build, check the logs"); + Assert.That(errorsTriggered, Is.False, "The registry failed to build, check the logs"); var allResult = registryCache.All(); - Assert.True(allResult.Packages.Count >= 3); + Assert.That(allResult.Packages, Has.Count.GreaterThanOrEqualTo(3)); var allResultJson = allResult.ToJson(); - StringAssert.Contains("org.nuget.scriban", allResultJson); - StringAssert.Contains("org.nuget.system.runtime.compilerservices.unsafe", allResultJson); + Assert.That(allResultJson, Does.Contain("org.nuget.scriban")); + Assert.That(allResultJson, Does.Contain("org.nuget.system.runtime.compilerservices.unsafe")); var scribanPackage = registryCache.GetPackage("org.nuget.scriban"); - Assert.NotNull(scribanPackage); - var scribanPackageJson = scribanPackage.ToJson(); - StringAssert.Contains("org.nuget.scriban", scribanPackageJson); - StringAssert.Contains("2.1.0", scribanPackageJson); + Assert.That(scribanPackage, Is.Not.Null); + var scribanPackageJson = scribanPackage!.ToJson(); + Assert.That(scribanPackageJson, Does.Contain("org.nuget.scriban")); + Assert.That(scribanPackageJson, Does.Contain("2.1.0")); } } } diff --git a/src/UnityNuGet.Tests/RegistryTests.cs b/src/UnityNuGet.Tests/RegistryTests.cs index 21dee362..5d7681e2 100644 --- a/src/UnityNuGet.Tests/RegistryTests.cs +++ b/src/UnityNuGet.Tests/RegistryTests.cs @@ -1,4 +1,7 @@ -using System.Linq; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -10,7 +13,6 @@ using NuGet.Protocol.Core.Types; using NUnit.Framework; using static NuGet.Frameworks.FrameworkConstants; -using System.IO; namespace UnityNuGet.Tests { @@ -23,7 +25,7 @@ public void Make_Sure_That_The_Order_In_The_Registry_Is_Respected() var originalPackageNames = registry.Select(r => r.Key).ToArray(); var sortedPackageNames = originalPackageNames.OrderBy(p => p).ToArray(); - Assert.AreEqual(sortedPackageNames, originalPackageNames); + Assert.That(originalPackageNames, Is.EqualTo(sortedPackageNames)); } [Test] @@ -32,7 +34,7 @@ public void Ensure_That_Packages_Already_Included_In_Net_Standard_Are_not_Includ var registry = Registry.GetInstance(); var packageNames = registry.Select(r => r.Key).Where(DotNetHelper.IsNetStandard20Assembly).ToArray(); - Assert.IsEmpty(packageNames); + Assert.That(packageNames, Is.Empty); } [Test] @@ -57,14 +59,14 @@ public async Task CanParse_PackageWithRuntimes() var runtimeLibs = await RuntimeLibraries .GetSupportedRuntimeLibsAsync(downloadResult.PackageReader, CommonFrameworks.NetStandard20, logger) .ToListAsync(); - Assert.IsTrue(runtimeLibs.Any()); + Assert.That(runtimeLibs, Is.Not.Empty); // Make sure these runtime libraries are only for Windows var platformDefs = PlatformDefinition.CreateAllPlatforms(); var win = platformDefs.Find(UnityOs.Windows); foreach (var (file, os, cpu) in runtimeLibs) { - Assert.AreEqual(platformDefs.Find(os, cpu), win); + Assert.That(platformDefs.Find(os, cpu), Is.EqualTo(win)); } // Get the lib files @@ -73,7 +75,7 @@ public async Task CanParse_PackageWithRuntimes() versions, new RegistryTargetFramework[] { - new RegistryTargetFramework + new() { Framework = CommonFrameworks.NetStandard20, }, @@ -88,7 +90,7 @@ public async Task CanParse_PackageWithRuntimes() var runtimeFiles = runtimeLibs .Select(l => Path.GetFileName(l.file)) .ToHashSet(); - Assert.IsTrue(libFiles.SetEquals(runtimeFiles)); + Assert.That(libFiles.SetEquals(runtimeFiles), Is.True); } [Test] @@ -103,7 +105,7 @@ public async Task Ensure_Min_Version_Is_Correct_Ignoring_Analyzers_And_Native_Li var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); var resource = await repository.GetResourceAsync(); - var nuGetFrameworks = new RegistryTargetFramework[] { new RegistryTargetFramework { Framework = CommonFrameworks.NetStandard20 } }; + var nuGetFrameworks = new RegistryTargetFramework[] { new() { Framework = CommonFrameworks.NetStandard20 } }; var excludedPackages = new string[] { // All versions target "Any" and not .netstandard2.0 / 2.1 @@ -151,7 +153,7 @@ public async Task Ensure_Min_Version_Is_Correct_Ignoring_Analyzers_And_Native_Li if (packageIdentity != null) { - Assert.AreEqual(packageIdentity.Version, versionRange.MinVersion, $"Package {packageId}"); + Assert.That(versionRange!.MinVersion, Is.EqualTo(packageIdentity.Version), $"Package {packageId}"); } else { @@ -159,7 +161,7 @@ public async Task Ensure_Min_Version_Is_Correct_Ignoring_Analyzers_And_Native_Li var downloadResult = await PackageDownloader.GetDownloadResourceResultAsync( new SourceRepository[] { repository }, - new PackageIdentity(registryKvp.Key, registryKvp.Value.Version.MinVersion), + new PackageIdentity(registryKvp.Key, registryKvp.Value.Version!.MinVersion), new PackageDownloadContext(cache), SettingsUtility.GetGlobalPackagesFolder(settings), logger, cancellationToken); @@ -177,5 +179,62 @@ public async Task Ensure_Min_Version_Is_Correct_Ignoring_Analyzers_And_Native_Li } } } + + [Test] + public async Task Ensure_Do_Not_Exceed_The_Maximum_Number_Of_Allowed_Versions() + { + const int maxAllowedVersions = 100; + + var registry = Registry.GetInstance(); + + var logger = NullLogger.Instance; + var cancellationToken = CancellationToken.None; + + var cache = new SourceCacheContext(); + var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); + var resource = await repository.GetResourceAsync(); + + List<(string packageId, int versionCount)> packages = []; + + foreach (var registryKvp in registry.Where(r => !r.Value.Analyzer && !r.Value.Ignored)) + { + var packageId = registryKvp.Key; + + var versionRange = registryKvp.Value.Version; + + var dependencyPackageMetas = await resource.GetMetadataAsync( + packageId, + includePrerelease: false, + includeUnlisted: false, + cache, + logger, + cancellationToken); + + var versions = dependencyPackageMetas.Where(v => versionRange!.Satisfies(v.Identity.Version)).ToArray(); + + if (versions.Length > maxAllowedVersions) + { + packages.Add((registryKvp.Key, versions.Length)); + } + } + + StringBuilder stringBuilder = new(); + + foreach (var (packageId, versionCount) in packages.OrderByDescending(p => p.versionCount)) + { + stringBuilder.AppendLine($"{packageId} -> {versionCount}"); + } + + if (stringBuilder.Length == 0) + { + const bool trueConstant = true; + + Assert.That(trueConstant, Is.True); + } + else + { + Assert.Inconclusive(stringBuilder.ToString()); + } + } } } diff --git a/src/UnityNuGet.Tests/UnityMetaTests.cs b/src/UnityNuGet.Tests/UnityMetaTests.cs index 6a1299c1..4ecc8350 100644 --- a/src/UnityNuGet.Tests/UnityMetaTests.cs +++ b/src/UnityNuGet.Tests/UnityMetaTests.cs @@ -1,162 +1,173 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using NUnit.Framework; - -namespace UnityNuGet.Tests +using System; +using System.Linq; +using System.Text.RegularExpressions; +using NUnit.Framework; + +namespace UnityNuGet.Tests { - public class UnityMetaTests - { - [Test] - public void GetMetaForDll_FormatsDefineConstraintsProperly_WithoutConstraints() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); - var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty(), Array.Empty()); - StringAssert.DoesNotContain("defineConstraints", output); - - // This is on the same line in the template, so ensure it's intact - StringAssert.Contains("\n isPreloaded: 0\n", output); - } - - [Test] - public void GetMetaForDll_FormatsLabelsProperly_WithoutLabels() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); - var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty(), Array.Empty()); - StringAssert.DoesNotContain("labels", output); - - // This is on the same line in the template, so ensure it's intact - StringAssert.Contains("\nPluginImporter:\n", output); +#pragma warning disable CA1861 // Avoid constant arrays as arguments + public class UnityMetaTests + { + [Test] + public void GetMetaForDll_FormatsDefineConstraintsProperly_WithoutConstraints() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); + var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs!, Array.Empty(), Array.Empty()); + Assert.That(output, Does.Not.Contain("defineConstraints")); + + // This is on the same line in the template, so ensure it's intact + Assert.That(output, Does.Contain("\n isPreloaded: 0\n")); + } + + [Test] + public void GetMetaForDll_FormatsLabelsProperly_WithoutLabels() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); + var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs!, Array.Empty(), Array.Empty()); + Assert.That(output, Does.Not.Contain("labels")); + + // This is on the same line in the template, so ensure it's intact + Assert.That(output, Does.Contain("\nPluginImporter:\n")); } [TestCase(new[] { "FIRST" }, "\n defineConstraints:\n - FIRST\n")] - [TestCase(new[] { "FIRST", "SECOND" }, "\n defineConstraints:\n - FIRST\n - SECOND\n")] - public void GetMetaForDll_FormatsDefineConstraintsProperly_WithConstraints( - string[] constraints, string expected) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); - var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty(), constraints); - - StringAssert.Contains(expected, output); - - // This is on the same line in the template, so ensure it's intact - StringAssert.Contains("\n isPreloaded: 0\n", output); - } - - [TestCase(new[] { "FIRST" }, "\nlabels:\n - FIRST\n")] - [TestCase(new[] { "FIRST", "SECOND" }, "\nlabels:\n - FIRST\n - SECOND\n")] - public void GetMetaForDll_FormatsLabelsProperly_WithLabels( - string[] labels, string expected) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); - var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, labels, Array.Empty()); - - StringAssert.Contains(expected, output); - - // This is on the same line in the template, so ensure it's intact - StringAssert.Contains("\nPluginImporter:\n", output); - } - - [TestCase(true, "1")] - [TestCase(false, "0")] - public void GetMetaForDll_FormatsAnyPlatformEnabledProperly(bool value, string expected) - { - PlatformDefinition platformDef; - - if (value) + [TestCase(new[] { "FIRST", "SECOND" }, "\n defineConstraints:\n - FIRST\n - SECOND\n")] + public void GetMetaForDll_FormatsDefineConstraintsProperly_WithConstraints( + string[] constraints, string expected) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); + var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs!, Array.Empty(), constraints); + + Assert.That(output, Does.Contain(expected)); + + // This is on the same line in the template, so ensure it's intact + Assert.That(output, Does.Contain("\n isPreloaded: 0\n")); + } + + [TestCase(new[] { "FIRST" }, "\nlabels:\n - FIRST\n")] + [TestCase(new[] { "FIRST", "SECOND" }, "\nlabels:\n - FIRST\n - SECOND\n")] + public void GetMetaForDll_FormatsLabelsProperly_WithLabels( + string[] labels, string expected) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); + var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs!, labels, Array.Empty()); + + Assert.That(output, Does.Contain(expected)); + + // This is on the same line in the template, so ensure it's intact + Assert.That(output, Does.Contain("\nPluginImporter:\n")); + } + + [TestCase(true, "1")] + [TestCase(false, "0")] + public void GetMetaForDll_FormatsAnyPlatformEnabledProperly(bool value, string expected) + { + PlatformDefinition? platformDef; + + if (value) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + platformDef = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); + } + else + { + platformDef = new PlatformDefinition(UnityOs.AnyOs, UnityCpu.None, isEditorConfig: false); + } + + var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), platformDef!, Array.Empty(), Array.Empty()); + + Assert.That(output, Does.Contain($"\n platformData:\n - first:\n Any:\n second:\n enabled: {expected}\n")); + } + + [Test] + public void GetMetaForDll_ContainsNoWindowsNewlines() + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); + var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs!, Array.Empty(), new[] { "TEST" }); + Assert.That(output, Does.Not.Contain("\r")); + } + + [TestCase(UnityOs.Android, "Android", "Android")] + [TestCase(UnityOs.WebGL, "WebGL", "WebGL")] + [TestCase(UnityOs.iOS, "iPhone", "iOS")] + public void GetMetaForDll_NonEditor(UnityOs os, string platformName, string osName) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var output = UnityMeta.GetMetaForDll( + Guid.NewGuid(), + platformDefs.Find(os)!, + Array.Empty(), + Array.Empty()); + + // There should be a single 'Exclude Android: 0' match + var excludeRegex = new Regex("Exclude (.*): 0"); + var excludeMatches = excludeRegex.Matches(output); + Assert.That(excludeMatches, Is.Not.Null); + Assert.That(excludeMatches, Has.Count.EqualTo(1)); + Assert.Multiple(() => { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - platformDef = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); - } - else + Assert.That(excludeMatches.Single().Groups, Has.Count.EqualTo(2)); + Assert.That(osName, Is.EqualTo(excludeMatches.Single().Groups[1].Value)); + }); + + // There should be a single 'enabled: 1' match + var enableRegex = new Regex("enabled: 1"); + var enableMatches = enableRegex.Matches(output); + Assert.That(enableMatches, Is.Not.Null); + Assert.Multiple(() => { - platformDef = new PlatformDefinition(UnityOs.AnyOs, UnityCpu.None, isEditorConfig: false); - } - - var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), platformDef, Array.Empty(), Array.Empty()); - - StringAssert.Contains($"\n platformData:\n - first:\n Any:\n second:\n enabled: {expected}\n", output); - } - - [Test] - public void GetMetaForDll_ContainsNoWindowsNewlines() - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var anyOs = platformDefs.Find(UnityOs.AnyOs, UnityCpu.AnyCpu); - var output = UnityMeta.GetMetaForDll(Guid.NewGuid(), anyOs, Array.Empty(), new[] { "TEST" }); - StringAssert.DoesNotContain("\r", output); - } - - [TestCase(UnityOs.Android, "Android", "Android")] - [TestCase(UnityOs.WebGL, "WebGL", "WebGL")] - [TestCase(UnityOs.iOS, "iPhone", "iOS")] - public void GetMetaForDll_NonEditor(UnityOs os, string platformName, string osName) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var output = UnityMeta.GetMetaForDll( - Guid.NewGuid(), - platformDefs.Find(os), - Array.Empty(), - Array.Empty()); - - // There should be a single 'Exclude Android: 0' match - var excludeRegex = new Regex("Exclude (.*): 0"); - var excludeMatches = excludeRegex.Matches(output); - Assert.IsNotNull(excludeMatches); - Assert.AreEqual(excludeMatches.Count, 1); - Assert.AreEqual(excludeMatches.Single().Groups.Count, 2); - Assert.AreEqual(excludeMatches.Single().Groups[1].Value, osName); - - // There should be a single 'enabled: 1' match - var enableRegex = new Regex("enabled: 1"); + Assert.That(enableMatches, Has.Count.EqualTo(1)); + + Assert.That(output, Does.Contain($"- first:\n {platformName}: {osName}\n second:\n enabled: 1\n")); + }); + } + + [TestCase(UnityOs.Windows, new[] { "Win", "Win64" })] + [TestCase(UnityOs.Linux, new[] { "Linux64" })] + [TestCase(UnityOs.OSX, new[] { "OSXUniversal" })] + public void GetMetaForDll_Editor(UnityOs os, string[] osNames) + { + var platformDefs = PlatformDefinition.CreateAllPlatforms(); + var pDef = platformDefs.Find(os); + var output = UnityMeta.GetMetaForDll( + Guid.NewGuid(), + pDef!, + Array.Empty(), + Array.Empty()); + + // There should be only 'Exclude Editor: 0' and 'Exclude {{ osName }}: 0' matches + var excludeRegex = new Regex("Exclude (.*): 0"); + var excludeMatches = excludeRegex.Matches(output); + Assert.That(excludeMatches, Is.Not.Null); + var actualExcludes = excludeMatches + .Select(match => match.Groups[1].Value) + .ToHashSet(); + + var expectedExcludes = osNames + .Append("Editor") + .ToHashSet(); + Assert.That(actualExcludes.SetEquals(expectedExcludes), Is.True); + + // There should be as many 'enabled: 1' matches as exclude matches + var enableRegex = new Regex("enabled: 1"); var enableMatches = enableRegex.Matches(output); - Assert.IsNotNull(enableMatches); - Assert.AreEqual(enableMatches.Count, 1); - - StringAssert.Contains($"- first:\n {platformName}: {osName}\n second:\n enabled: 1\n", output); - } - - [TestCase(UnityOs.Windows, new[] { "Win", "Win64" })] - [TestCase(UnityOs.Linux, new[] { "Linux64" })] - [TestCase(UnityOs.OSX, new[] { "OSXUniversal" })] - public void GetMetaForDll_Editor(UnityOs os, string[] osNames) - { - var platformDefs = PlatformDefinition.CreateAllPlatforms(); - var pDef = platformDefs.Find(os); - var output = UnityMeta.GetMetaForDll( - Guid.NewGuid(), - pDef, - Array.Empty(), - Array.Empty()); - - // There should be only 'Exclude Editor: 0' and 'Exclude {{ osName }}: 0' matches - var excludeRegex = new Regex("Exclude (.*): 0"); - var excludeMatches = excludeRegex.Matches(output); - Assert.IsNotNull(excludeMatches); - var actualExcludes = excludeMatches - .Select(match => match.Groups[1].Value) - .ToHashSet(); - - var expectedExcludes = osNames - .Append("Editor") - .ToHashSet(); - Assert.IsTrue(actualExcludes.SetEquals(expectedExcludes)); - - // There should be as many 'enabled: 1' matches as exclude matches - var enableRegex = new Regex("enabled: 1"); - var enableMatches = enableRegex.Matches(output); - Assert.IsNotNull(enableMatches); - Assert.AreEqual(enableMatches.Count, excludeMatches.Count); - - foreach (var osName in actualExcludes) + Assert.Multiple(() => { - var platformName = (osName == "Editor") ? osName : "Standalone"; - StringAssert.Contains($"- first:\n {platformName}: {osName}\n second:\n enabled: 1\n", output); - } - } + Assert.That(enableMatches, Is.Not.Null); + Assert.That(excludeMatches, Has.Count.EqualTo(enableMatches.Count)); + }); + + foreach (var osName in actualExcludes) + { + var platformName = (osName == "Editor") ? osName : "Standalone"; + Assert.That(output, Does.Contain($"- first:\n {platformName}: {osName}\n second:\n enabled: 1\n")); + } + } } -} +#pragma warning restore CA1861 // Avoid constant arrays as arguments +} diff --git a/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj b/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj index 96e44cdd..b0f45a29 100644 --- a/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj +++ b/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj @@ -1,16 +1,19 @@ - + - net7.0 + net8.0 false false - true - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/UnityNuGet/DotNetHelper.cs b/src/UnityNuGet/DotNetHelper.cs index fb8be2ed..baf4df53 100644 --- a/src/UnityNuGet/DotNetHelper.cs +++ b/src/UnityNuGet/DotNetHelper.cs @@ -1,238 +1,236 @@ -using System.Collections.Generic; - -namespace UnityNuGet -{ - /// - /// Helper class to identify which NuGet packages are replaced by .NET Standard 2.0 / 2.1 - /// - static class DotNetHelper - { - public static bool IsNetStandard20Assembly(string packageId) => NetStandard20Assemblies.Contains(packageId); - - public static bool IsNetStandard21Assembly(string packageId) => NetStandard21Assemblies.Contains(packageId); - - // Retrieved from NuGet package (/build/netstandard2.0/ref): https://www.nuget.org/packages/NETStandard.Library/2.0.3 - private static readonly HashSet NetStandard20Assemblies = new() - { - "Microsoft.Win32.Primitives", - "System.AppContext", - "System.Collections.Concurrent", - "System.Collections", - "System.Collections.NonGeneric", - "System.Collections.Specialized", - "System.ComponentModel.Composition", - "System.ComponentModel", - "System.ComponentModel.EventBasedAsync", - "System.ComponentModel.Primitives", - "System.ComponentModel.TypeConverter", - "System.Console", - "System.Core", - "System.Data.Common", - "System.Data", - "System.Diagnostics.Contracts", - "System.Diagnostics.Debug", - "System.Diagnostics.FileVersionInfo", - "System.Diagnostics.Process", - "System.Diagnostics.StackTrace", - "System.Diagnostics.TextWriterTraceListener", - "System.Diagnostics.Tools", - "System.Diagnostics.TraceSource", - "System.Diagnostics.Tracing", - "System", - "System.Drawing", - "System.Drawing.Primitives", - "System.Dynamic.Runtime", - "System.Globalization.Calendars", - "System.Globalization", - "System.Globalization.Extensions", - "System.IO.Compression", - "System.IO.Compression.FileSystem", - "System.IO.Compression.ZipFile", - "System.IO", - "System.IO.FileSystem", - "System.IO.FileSystem.DriveInfo", - "System.IO.FileSystem.Primitives", - "System.IO.FileSystem.Watcher", - "System.IO.IsolatedStorage", - "System.IO.MemoryMappedFiles", - "System.IO.Pipes", - "System.IO.UnmanagedMemoryStream", - "System.Linq", - "System.Linq.Expressions", - "System.Linq.Parallel", - "System.Linq.Queryable", - "System.Net", - "System.Net.Http", - "System.Net.NameResolution", - "System.Net.NetworkInformation", - "System.Net.Ping", - "System.Net.Primitives", - "System.Net.Requests", - "System.Net.Security", - "System.Net.Sockets", - "System.Net.WebHeaderCollection", - "System.Net.WebSockets.Client", - "System.Net.WebSockets", - "System.Numerics", - "System.ObjectModel", - "System.Reflection", - "System.Reflection.Extensions", - "System.Reflection.Primitives", - "System.Resources.Reader", - "System.Resources.ResourceManager", - "System.Resources.Writer", - "System.Runtime.CompilerServices.VisualC", - "System.Runtime", - "System.Runtime.Extensions", - "System.Runtime.Handles", - "System.Runtime.InteropServices", - "System.Runtime.InteropServices.RuntimeInformation", - "System.Runtime.Numerics", - "System.Runtime.Serialization", - "System.Runtime.Serialization.Formatters", - "System.Runtime.Serialization.Json", - "System.Runtime.Serialization.Primitives", - "System.Runtime.Serialization.Xml", - "System.Security.Claims", - "System.Security.Cryptography.Algorithms", - "System.Security.Cryptography.Csp", - "System.Security.Cryptography.Encoding", - "System.Security.Cryptography.Primitives", - "System.Security.Cryptography.X509Certificates", - "System.Security.Principal", - "System.Security.SecureString", - "System.ServiceModel.Web", - "System.Text.Encoding", - "System.Text.Encoding.Extensions", - "System.Text.RegularExpressions", - "System.Threading", - "System.Threading.Overlapped", - "System.Threading.Tasks", - "System.Threading.Tasks.Parallel", - "System.Threading.Thread", - "System.Threading.ThreadPool", - "System.Threading.Timer", - "System.Transactions", - "System.ValueTuple", - "System.Web", - "System.Windows", - "System.Xml", - "System.Xml.Linq", - "System.Xml.ReaderWriter", - "System.Xml.Serialization", - "System.Xml.XDocument", - "System.Xml.XmlDocument", - "System.Xml.XmlSerializer", - "System.Xml.XPath", - "System.Xml.XPath.XDocument" - }; - - // Retrieved from NuGet package (/ref/netstandard2.1): https://www.nuget.org/packages/NETStandard.Library.Ref - private static readonly HashSet NetStandard21Assemblies = new() - { - "Microsoft.Win32.Primitives", - "System.AppContext", - "System.Buffers", - "System.Collections.Concurrent", - "System.Collections", - "System.Collections.NonGeneric", - "System.Collections.Specialized", - "System.ComponentModel", - "System.ComponentModel.EventBasedAsync", - "System.ComponentModel.Primitives", - "System.ComponentModel.TypeConverter", - "System.Console", - "System.Data.Common", - "System.Diagnostics.Contracts", - "System.Diagnostics.Debug", - "System.Diagnostics.FileVersionInfo", - "System.Diagnostics.Process", - "System.Diagnostics.StackTrace", - "System.Diagnostics.TextWriterTraceListener", - "System.Diagnostics.Tools", - "System.Diagnostics.TraceSource", - "System.Diagnostics.Tracing", - "System.Drawing.Primitives", - "System.Dynamic.Runtime", - "System.Globalization.Calendars", - "System.Globalization", - "System.Globalization.Extensions", - "System.IO.Compression", - "System.IO.Compression.ZipFile", - "System.IO", - "System.IO.FileSystem", - "System.IO.FileSystem.DriveInfo", - "System.IO.FileSystem.Primitives", - "System.IO.FileSystem.Watcher", - "System.IO.IsolatedStorage", - "System.IO.MemoryMappedFiles", - "System.IO.Pipes", - "System.IO.UnmanagedMemoryStream", - "System.Linq", - "System.Linq.Expressions", - "System.Linq.Parallel", - "System.Linq.Queryable", - "System.Memory", - "System.Net.Http", - "System.Net.NameResolution", - "System.Net.NetworkInformation", - "System.Net.Ping", - "System.Net.Primitives", - "System.Net.Requests", - "System.Net.Security", - "System.Net.Sockets", - "System.Net.WebHeaderCollection", - "System.Net.WebSockets.Client", - "System.Net.WebSockets", - "System.Numerics.Vectors", - "System.ObjectModel", - "System.Reflection.DispatchProxy", - "System.Reflection", - "System.Reflection.Emit", - "System.Reflection.Emit.ILGeneration", - "System.Reflection.Emit.Lightweight", - "System.Reflection.Extensions", - "System.Reflection.Primitives", - "System.Resources.Reader", - "System.Resources.ResourceManager", - "System.Resources.Writer", - "System.Runtime.CompilerServices.VisualC", - "System.Runtime", - "System.Runtime.Extensions", - "System.Runtime.Handles", - "System.Runtime.InteropServices", - "System.Runtime.InteropServices.RuntimeInformation", - "System.Runtime.Numerics", - "System.Runtime.Serialization.Formatters", - "System.Runtime.Serialization.Json", - "System.Runtime.Serialization.Primitives", - "System.Runtime.Serialization.Xml", - "System.Security.Claims", - "System.Security.Cryptography.Algorithms", - "System.Security.Cryptography.Csp", - "System.Security.Cryptography.Encoding", - "System.Security.Cryptography.Primitives", - "System.Security.Cryptography.X509Certificates", - "System.Security.Principal", - "System.Security.SecureString", - "System.Text.Encoding", - "System.Text.Encoding.Extensions", - "System.Text.RegularExpressions", - "System.Threading", - "System.Threading.Overlapped", - "System.Threading.Tasks", - "System.Threading.Tasks.Extensions", - "System.Threading.Tasks.Parallel", - "System.Threading.Thread", - "System.Threading.ThreadPool", - "System.Threading.Timer", - "System.ValueTuple", - "System.Xml.ReaderWriter", - "System.Xml.XDocument", - "System.Xml.XmlDocument", - "System.Xml.XmlSerializer", - "System.Xml.XPath", - "System.Xml.XPath.XDocument", - }; - } -} +using System.Collections.Generic; + +namespace UnityNuGet +{ + /// + /// Helper class to identify which NuGet packages are replaced by .NET Standard 2.0 / 2.1 + /// + static class DotNetHelper + { + public static bool IsNetStandard20Assembly(string packageId) => NetStandard20Assemblies.Contains(packageId); + + public static bool IsNetStandard21Assembly(string packageId) => NetStandard21Assemblies.Contains(packageId); + + // Retrieved from NuGet package (/build/netstandard2.0/ref): https://www.nuget.org/packages/NETStandard.Library/2.0.3 + private static readonly HashSet NetStandard20Assemblies = [ + "Microsoft.Win32.Primitives", + "System.AppContext", + "System.Collections.Concurrent", + "System.Collections", + "System.Collections.NonGeneric", + "System.Collections.Specialized", + "System.ComponentModel.Composition", + "System.ComponentModel", + "System.ComponentModel.EventBasedAsync", + "System.ComponentModel.Primitives", + "System.ComponentModel.TypeConverter", + "System.Console", + "System.Core", + "System.Data.Common", + "System.Data", + "System.Diagnostics.Contracts", + "System.Diagnostics.Debug", + "System.Diagnostics.FileVersionInfo", + "System.Diagnostics.Process", + "System.Diagnostics.StackTrace", + "System.Diagnostics.TextWriterTraceListener", + "System.Diagnostics.Tools", + "System.Diagnostics.TraceSource", + "System.Diagnostics.Tracing", + "System", + "System.Drawing", + "System.Drawing.Primitives", + "System.Dynamic.Runtime", + "System.Globalization.Calendars", + "System.Globalization", + "System.Globalization.Extensions", + "System.IO.Compression", + "System.IO.Compression.FileSystem", + "System.IO.Compression.ZipFile", + "System.IO", + "System.IO.FileSystem", + "System.IO.FileSystem.DriveInfo", + "System.IO.FileSystem.Primitives", + "System.IO.FileSystem.Watcher", + "System.IO.IsolatedStorage", + "System.IO.MemoryMappedFiles", + "System.IO.Pipes", + "System.IO.UnmanagedMemoryStream", + "System.Linq", + "System.Linq.Expressions", + "System.Linq.Parallel", + "System.Linq.Queryable", + "System.Net", + "System.Net.Http", + "System.Net.NameResolution", + "System.Net.NetworkInformation", + "System.Net.Ping", + "System.Net.Primitives", + "System.Net.Requests", + "System.Net.Security", + "System.Net.Sockets", + "System.Net.WebHeaderCollection", + "System.Net.WebSockets.Client", + "System.Net.WebSockets", + "System.Numerics", + "System.ObjectModel", + "System.Reflection", + "System.Reflection.Extensions", + "System.Reflection.Primitives", + "System.Resources.Reader", + "System.Resources.ResourceManager", + "System.Resources.Writer", + "System.Runtime.CompilerServices.VisualC", + "System.Runtime", + "System.Runtime.Extensions", + "System.Runtime.Handles", + "System.Runtime.InteropServices", + "System.Runtime.InteropServices.RuntimeInformation", + "System.Runtime.Numerics", + "System.Runtime.Serialization", + "System.Runtime.Serialization.Formatters", + "System.Runtime.Serialization.Json", + "System.Runtime.Serialization.Primitives", + "System.Runtime.Serialization.Xml", + "System.Security.Claims", + "System.Security.Cryptography.Algorithms", + "System.Security.Cryptography.Csp", + "System.Security.Cryptography.Encoding", + "System.Security.Cryptography.Primitives", + "System.Security.Cryptography.X509Certificates", + "System.Security.Principal", + "System.Security.SecureString", + "System.ServiceModel.Web", + "System.Text.Encoding", + "System.Text.Encoding.Extensions", + "System.Text.RegularExpressions", + "System.Threading", + "System.Threading.Overlapped", + "System.Threading.Tasks", + "System.Threading.Tasks.Parallel", + "System.Threading.Thread", + "System.Threading.ThreadPool", + "System.Threading.Timer", + "System.Transactions", + "System.ValueTuple", + "System.Web", + "System.Windows", + "System.Xml", + "System.Xml.Linq", + "System.Xml.ReaderWriter", + "System.Xml.Serialization", + "System.Xml.XDocument", + "System.Xml.XmlDocument", + "System.Xml.XmlSerializer", + "System.Xml.XPath", + "System.Xml.XPath.XDocument" + ]; + + // Retrieved from NuGet package (/ref/netstandard2.1): https://www.nuget.org/packages/NETStandard.Library.Ref + private static readonly HashSet NetStandard21Assemblies = [ + "Microsoft.Win32.Primitives", + "System.AppContext", + "System.Buffers", + "System.Collections.Concurrent", + "System.Collections", + "System.Collections.NonGeneric", + "System.Collections.Specialized", + "System.ComponentModel", + "System.ComponentModel.EventBasedAsync", + "System.ComponentModel.Primitives", + "System.ComponentModel.TypeConverter", + "System.Console", + "System.Data.Common", + "System.Diagnostics.Contracts", + "System.Diagnostics.Debug", + "System.Diagnostics.FileVersionInfo", + "System.Diagnostics.Process", + "System.Diagnostics.StackTrace", + "System.Diagnostics.TextWriterTraceListener", + "System.Diagnostics.Tools", + "System.Diagnostics.TraceSource", + "System.Diagnostics.Tracing", + "System.Drawing.Primitives", + "System.Dynamic.Runtime", + "System.Globalization.Calendars", + "System.Globalization", + "System.Globalization.Extensions", + "System.IO.Compression", + "System.IO.Compression.ZipFile", + "System.IO", + "System.IO.FileSystem", + "System.IO.FileSystem.DriveInfo", + "System.IO.FileSystem.Primitives", + "System.IO.FileSystem.Watcher", + "System.IO.IsolatedStorage", + "System.IO.MemoryMappedFiles", + "System.IO.Pipes", + "System.IO.UnmanagedMemoryStream", + "System.Linq", + "System.Linq.Expressions", + "System.Linq.Parallel", + "System.Linq.Queryable", + "System.Memory", + "System.Net.Http", + "System.Net.NameResolution", + "System.Net.NetworkInformation", + "System.Net.Ping", + "System.Net.Primitives", + "System.Net.Requests", + "System.Net.Security", + "System.Net.Sockets", + "System.Net.WebHeaderCollection", + "System.Net.WebSockets.Client", + "System.Net.WebSockets", + "System.Numerics.Vectors", + "System.ObjectModel", + "System.Reflection.DispatchProxy", + "System.Reflection", + "System.Reflection.Emit", + "System.Reflection.Emit.ILGeneration", + "System.Reflection.Emit.Lightweight", + "System.Reflection.Extensions", + "System.Reflection.Primitives", + "System.Resources.Reader", + "System.Resources.ResourceManager", + "System.Resources.Writer", + "System.Runtime.CompilerServices.VisualC", + "System.Runtime", + "System.Runtime.Extensions", + "System.Runtime.Handles", + "System.Runtime.InteropServices", + "System.Runtime.InteropServices.RuntimeInformation", + "System.Runtime.Numerics", + "System.Runtime.Serialization.Formatters", + "System.Runtime.Serialization.Json", + "System.Runtime.Serialization.Primitives", + "System.Runtime.Serialization.Xml", + "System.Security.Claims", + "System.Security.Cryptography.Algorithms", + "System.Security.Cryptography.Csp", + "System.Security.Cryptography.Encoding", + "System.Security.Cryptography.Primitives", + "System.Security.Cryptography.X509Certificates", + "System.Security.Principal", + "System.Security.SecureString", + "System.Text.Encoding", + "System.Text.Encoding.Extensions", + "System.Text.RegularExpressions", + "System.Threading", + "System.Threading.Overlapped", + "System.Threading.Tasks", + "System.Threading.Tasks.Extensions", + "System.Threading.Tasks.Parallel", + "System.Threading.Thread", + "System.Threading.ThreadPool", + "System.Threading.Timer", + "System.ValueTuple", + "System.Xml.ReaderWriter", + "System.Xml.XDocument", + "System.Xml.XmlDocument", + "System.Xml.XmlSerializer", + "System.Xml.XPath", + "System.Xml.XPath.XDocument", + ]; + } +} diff --git a/src/UnityNuGet/Npm/NpmError.cs b/src/UnityNuGet/Npm/NpmError.cs index 4c77053a..17a8a992 100644 --- a/src/UnityNuGet/Npm/NpmError.cs +++ b/src/UnityNuGet/Npm/NpmError.cs @@ -1,24 +1,18 @@ -using Newtonsoft.Json; - -namespace UnityNuGet.Npm -{ - /// - /// A simple object to return npm errors. Used mainly for returning - /// - public class NpmError : NpmObject - { - public static readonly NpmError NotFound = new("not_found", "document not found"); - - public NpmError(string error, string reason) - { - Error = error; - Reason = reason; - } - - [JsonProperty("error")] - public string Error { get; } - - [JsonProperty("reason")] - public string Reason { get; } - } -} +using Newtonsoft.Json; + +namespace UnityNuGet.Npm +{ + /// + /// A simple object to return npm errors. Used mainly for returning + /// + public class NpmError(string error, string reason) : NpmObject + { + public static readonly NpmError NotFound = new("not_found", "document not found"); + + [JsonProperty("error")] + public string Error { get; } = error; + + [JsonProperty("reason")] + public string Reason { get; } = reason; + } +} diff --git a/src/UnityNuGet/Npm/NpmPackage.cs b/src/UnityNuGet/Npm/NpmPackage.cs index dc5420ec..5275f683 100644 --- a/src/UnityNuGet/Npm/NpmPackage.cs +++ b/src/UnityNuGet/Npm/NpmPackage.cs @@ -1,51 +1,51 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace UnityNuGet.Npm -{ - /// - /// Describes a full NPM package (used as a response to `{packageId}`) - /// - public class NpmPackage : NpmObject - { - public NpmPackage() - { - Revision = "1-0"; - DistTags = new(); - Versions = new(); - Time = new(); - Users = new(); - } - - [JsonProperty("_id")] - public string? Id { get; set; } - - [JsonProperty("_rev")] - public string Revision { get; set; } - - [JsonProperty("name")] - public string? Name { get; set; } - - [JsonProperty("license")] - public string? License { get; set; } - - [JsonProperty("description")] - public string? Description { get; set; } - - [JsonProperty("dist-tags")] - public Dictionary DistTags { get; } - - [JsonProperty("versions")] - public Dictionary Versions { get; } - - [JsonProperty("time")] - public Dictionary Time { get; } - - [JsonProperty("repository", NullValueHandling = NullValueHandling.Ignore)] - public NpmSourceRepository? Repository { get; set; } - - [JsonProperty("users")] - public Dictionary Users { get; } - } -} +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace UnityNuGet.Npm +{ + /// + /// Describes a full NPM package (used as a response to `{packageId}`) + /// + public class NpmPackage : NpmObject + { + public NpmPackage() + { + Revision = "1-0"; + DistTags = []; + Versions = []; + Time = []; + Users = []; + } + + [JsonProperty("_id")] + public string? Id { get; set; } + + [JsonProperty("_rev")] + public string Revision { get; set; } + + [JsonProperty("name")] + public string? Name { get; set; } + + [JsonProperty("license")] + public string? License { get; set; } + + [JsonProperty("description")] + public string? Description { get; set; } + + [JsonProperty("dist-tags")] + public Dictionary DistTags { get; } + + [JsonProperty("versions")] + public Dictionary Versions { get; } + + [JsonProperty("time")] + public Dictionary Time { get; } + + [JsonProperty("repository", NullValueHandling = NullValueHandling.Ignore)] + public NpmSourceRepository? Repository { get; set; } + + [JsonProperty("users")] + public Dictionary Users { get; } + } +} diff --git a/src/UnityNuGet/Npm/NpmPackageInfo.cs b/src/UnityNuGet/Npm/NpmPackageInfo.cs index 31834ecb..b2401a70 100644 --- a/src/UnityNuGet/Npm/NpmPackageInfo.cs +++ b/src/UnityNuGet/Npm/NpmPackageInfo.cs @@ -1,40 +1,40 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace UnityNuGet.Npm -{ - /// - /// Describes a package for "all" listing, used by - /// - public class NpmPackageInfo : NpmObject - { - public NpmPackageInfo() - { - Maintainers = new(); - Versions = new(); - Keywords = new(); - } - - [JsonProperty("name")] - public string? Name { get; set; } - - [JsonProperty("description")] - public string? Description { get; set; } - - [JsonProperty("maintainers")] - public List Maintainers { get; } - - [JsonProperty("versions")] - public Dictionary Versions { get; } - - [JsonProperty("time")] - public DateTimeOffset? Time { get; set; } - - [JsonProperty("keywords")] - public List Keywords { get; } - - [JsonProperty("author")] - public string? Author { get; set; } - } -} +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace UnityNuGet.Npm +{ + /// + /// Describes a package for "all" listing, used by + /// + public class NpmPackageInfo : NpmObject + { + public NpmPackageInfo() + { + Maintainers = []; + Versions = []; + Keywords = []; + } + + [JsonProperty("name")] + public string? Name { get; set; } + + [JsonProperty("description")] + public string? Description { get; set; } + + [JsonProperty("maintainers")] + public List Maintainers { get; } + + [JsonProperty("versions")] + public Dictionary Versions { get; } + + [JsonProperty("time")] + public DateTimeOffset? Time { get; set; } + + [JsonProperty("keywords")] + public List Keywords { get; } + + [JsonProperty("author")] + public string? Author { get; set; } + } +} diff --git a/src/UnityNuGet/Npm/NpmPackageListAllResponse.cs b/src/UnityNuGet/Npm/NpmPackageListAllResponse.cs index 7293a718..34ac33f0 100644 --- a/src/UnityNuGet/Npm/NpmPackageListAllResponse.cs +++ b/src/UnityNuGet/Npm/NpmPackageListAllResponse.cs @@ -1,50 +1,48 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace UnityNuGet.Npm -{ - /// - /// Represents a `-/all` listing package NPM response - /// - /// - /// NOTE: only used to serialize from C# to JSON (JSON To C# is not implemented) - /// - public class NpmPackageListAllResponse : NpmObject - { - public NpmPackageListAllResponse() - { - Unused = 99999; - Packages = new(); - } - - [JsonProperty("_updated")] - public int Unused { get; set; } - - [JsonIgnore] - public Dictionary Packages { get; } - - // everything else gets stored here - [JsonExtensionData] -#pragma warning disable IDE0051 // Remove unused private members - private IDictionary AdditionalData -#pragma warning restore IDE0051 // Remove unused private members - { - get - { - var marshalPackages = new Dictionary(); - foreach (var packagePair in Packages) - { - marshalPackages.Add(packagePair.Key, JObject.FromObject(packagePair.Value)); - } - - return marshalPackages; - } - } - - public void Reset() - { - Packages.Clear(); - } - } -} +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace UnityNuGet.Npm +{ + /// + /// Represents a `-/all` listing package NPM response + /// + /// + /// NOTE: only used to serialize from C# to JSON (JSON To C# is not implemented) + /// + public class NpmPackageListAllResponse : NpmObject + { + public NpmPackageListAllResponse() + { + Unused = 99999; + Packages = []; + } + + [JsonProperty("_updated")] + public int Unused { get; set; } + + [JsonIgnore] + public Dictionary Packages { get; } + + // Everything else gets stored here + [JsonExtensionData] + private IDictionary AdditionalData + { + get + { + var marshalPackages = new Dictionary(); + foreach (var packagePair in Packages) + { + marshalPackages.Add(packagePair.Key, JObject.FromObject(packagePair.Value)); + } + + return marshalPackages; + } + } + + public void Reset() + { + Packages.Clear(); + } + } +} diff --git a/src/UnityNuGet/Npm/NpmPackageRegistry.cs b/src/UnityNuGet/Npm/NpmPackageRegistry.cs index 3db6003b..4eb69f90 100644 --- a/src/UnityNuGet/Npm/NpmPackageRegistry.cs +++ b/src/UnityNuGet/Npm/NpmPackageRegistry.cs @@ -1,38 +1,38 @@ -using System.Collections.Generic; - -namespace UnityNuGet.Npm -{ - /// - /// Used to store all type of responses (all packages, or single packages but also unlisted packages) - /// - public class NpmPackageRegistry : NpmObject - { - public NpmPackageRegistry() - { - Packages = new(); - ListedPackageInfos = new(); - UnlistedPackageInfos = new(); - } - - public Dictionary Packages { get; } - - public NpmPackageListAllResponse ListedPackageInfos { get; } - - public NpmPackageListAllResponse UnlistedPackageInfos { get; } - - public void AddPackage(NpmPackageCacheEntry entry, bool isListed) - { - var package = entry.Package!; - Packages.Add(package.Id!, package); - var packageInfos = isListed ? ListedPackageInfos : UnlistedPackageInfos; - packageInfos.Packages.Add(package.Id!, entry.Info!); - } - - public void Reset() - { - Packages.Clear(); - ListedPackageInfos.Reset(); - UnlistedPackageInfos.Reset(); - } - } -} +using System.Collections.Generic; + +namespace UnityNuGet.Npm +{ + /// + /// Used to store all type of responses (all packages, or single packages but also unlisted packages) + /// + public class NpmPackageRegistry : NpmObject + { + public NpmPackageRegistry() + { + Packages = []; + ListedPackageInfos = new(); + UnlistedPackageInfos = new(); + } + + public Dictionary Packages { get; } + + public NpmPackageListAllResponse ListedPackageInfos { get; } + + public NpmPackageListAllResponse UnlistedPackageInfos { get; } + + public void AddPackage(NpmPackageCacheEntry entry, bool isListed) + { + var package = entry.Package!; + Packages.Add(package.Id!, package); + var packageInfos = isListed ? ListedPackageInfos : UnlistedPackageInfos; + packageInfos.Packages.Add(package.Id!, entry.Info!); + } + + public void Reset() + { + Packages.Clear(); + ListedPackageInfos.Reset(); + UnlistedPackageInfos.Reset(); + } + } +} diff --git a/src/UnityNuGet/Npm/NpmPackageVersion.cs b/src/UnityNuGet/Npm/NpmPackageVersion.cs index de90dee1..932f7f92 100644 --- a/src/UnityNuGet/Npm/NpmPackageVersion.cs +++ b/src/UnityNuGet/Npm/NpmPackageVersion.cs @@ -1,51 +1,51 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace UnityNuGet.Npm -{ - /// - /// Describes a version of a - /// - public class NpmPackageVersion : NpmObject - { - public NpmPackageVersion() - { - Dependencies = new(); - Distribution = new(); - Scripts = new(); - } - - [JsonProperty("name")] - public string? Name { get; set; } - - [JsonProperty("version")] - public string? Version { get; set; } - - [JsonProperty("dist")] - public NpmDistribution Distribution { get; } - - [JsonProperty("dependencies")] - public Dictionary Dependencies { get; } - - [JsonProperty("_id")] - public string? Id { get; set; } - - [JsonProperty("unity", NullValueHandling = NullValueHandling.Ignore)] - public string? Unity { get; set; } - - [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)] - public string? Description { get; set; } - - [JsonProperty("displayName", NullValueHandling = NullValueHandling.Ignore)] - public string? DisplayName { get; set; } - - [JsonProperty("scripts", NullValueHandling = NullValueHandling.Ignore)] - public Dictionary Scripts { get; } - - [JsonProperty("repository", NullValueHandling = NullValueHandling.Ignore)] - public NpmSourceRepository? Repository { get; set; } - - [JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)] - public string? Author { get; set; } - } -} +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace UnityNuGet.Npm +{ + /// + /// Describes a version of a + /// + public class NpmPackageVersion : NpmObject + { + public NpmPackageVersion() + { + Dependencies = []; + Distribution = new(); + Scripts = []; + } + + [JsonProperty("name")] + public string? Name { get; set; } + + [JsonProperty("version")] + public string? Version { get; set; } + + [JsonProperty("dist")] + public NpmDistribution Distribution { get; } + + [JsonProperty("dependencies")] + public Dictionary Dependencies { get; } + + [JsonProperty("_id")] + public string? Id { get; set; } + + [JsonProperty("unity", NullValueHandling = NullValueHandling.Ignore)] + public string? Unity { get; set; } + + [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)] + public string? Description { get; set; } + + [JsonProperty("displayName", NullValueHandling = NullValueHandling.Ignore)] + public string? DisplayName { get; set; } + + [JsonProperty("scripts", NullValueHandling = NullValueHandling.Ignore)] + public Dictionary Scripts { get; } + + [JsonProperty("repository", NullValueHandling = NullValueHandling.Ignore)] + public NpmSourceRepository? Repository { get; set; } + + [JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)] + public string? Author { get; set; } + } +} diff --git a/src/UnityNuGet/NuGetHelper.cs b/src/UnityNuGet/NuGetHelper.cs index 7266c3d0..ff4f6111 100644 --- a/src/UnityNuGet/NuGetHelper.cs +++ b/src/UnityNuGet/NuGetHelper.cs @@ -8,10 +8,11 @@ namespace UnityNuGet { - static class NuGetHelper + static partial class NuGetHelper { // https://learn.microsoft.com/en-us/visualstudio/extensibility/roslyn-version-support - private static readonly Regex roslynVersionRegex = new(@"/roslyn(\d+)\.(\d+)\.?(\d*)/"); + [GeneratedRegex(@"/roslyn(\d+)\.(\d+)\.?(\d*)/")] + private static partial Regex RoslynVersion(); // https://docs.unity3d.com/Manual/roslyn-analyzers.html private static readonly Version unityRoslynSupportedVersion = new(3, 8, 0); @@ -63,7 +64,7 @@ bool IsResource() public static bool IsApplicableUnitySupportedRoslynVersionFolder(string file) { - var roslynVersionMatch = roslynVersionRegex.Match(file); + var roslynVersionMatch = RoslynVersion().Match(file); bool hasRoslynVersionFolder = roslynVersionMatch.Success; bool hasUnitySupportedRoslynVersionFolder = hasRoslynVersionFolder && diff --git a/src/UnityNuGet/PlatformDefinition.cs b/src/UnityNuGet/PlatformDefinition.cs index 08d8e277..04e247ae 100644 --- a/src/UnityNuGet/PlatformDefinition.cs +++ b/src/UnityNuGet/PlatformDefinition.cs @@ -1,407 +1,392 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace UnityNuGet -{ - /// - /// All operating systems supported by Unity - /// - public enum UnityOs - { - AnyOs, - Windows, - Linux, - OSX, - Android, - WebGL, - iOS - } - - /// - /// All CPUs supported by Unity - /// - public enum UnityCpu - { - AnyCpu, - X64, - X86, - ARM64, - ARMv7, - None, +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace UnityNuGet +{ + /// + /// All operating systems supported by Unity + /// + public enum UnityOs + { + AnyOs, + Windows, + Linux, + OSX, + Android, + WebGL, + iOS + } + + /// + /// All CPUs supported by Unity + /// + public enum UnityCpu + { + AnyCpu, + X64, + X86, + ARM64, + ARMv7, + None, + } + + /// + /// Extensions for and + /// + internal static class UnityEnumExtensions + { + /// + /// Converts a to string. + /// + /// The value to be converted. + /// This method should be used for constructing package paths for OS-dependent files. + /// A string representation of the provided value. + public static string GetPathName(this UnityOs os) + { + return os switch + { + UnityOs.AnyOs => string.Empty, + _ => GetName(os), + }; + } + + /// + /// Converts a to a string representation accepted by the Unity .meta file format. + /// + /// The value to be converted. + /// A string representation of the provided value. + public static string GetName(this UnityOs os) + { + return os switch + { + UnityOs.AnyOs => "AnyOS", + UnityOs.Windows => "Windows", + UnityOs.Linux => "Linux", + UnityOs.OSX => "OSX", + UnityOs.Android => "Android", + UnityOs.WebGL => "WebGL", + UnityOs.iOS => "iOS", + _ => throw new ArgumentException($"Unknown OS {os}"), + }; + } + + /// + /// Converts a to string. + /// + /// The value to be converted. + /// This method should be used for constructing package paths for CPU-dependent files. + /// A string representation of the provided value. + public static string GetPathName(this UnityCpu cpu) + { + return cpu switch + { + UnityCpu.AnyCpu => string.Empty, + _ => GetName(cpu), + }; + } + + /// + /// Converts a to a string representation accepted by the Unity .meta file format. + /// + /// The value to be converted. + /// A string representation of the provided value. + public static string GetName(this UnityCpu cpu) + { + return cpu switch + { + UnityCpu.AnyCpu => "AnyCPU", + UnityCpu.X64 => "x86_64", + UnityCpu.X86 => "x86", + UnityCpu.ARM64 => "ARM64", + UnityCpu.ARMv7 => "ARMv7", + UnityCpu.None => "None", + _ => throw new ArgumentException($"Unknown CPU {cpu}"), + }; + } } - /// - /// Extensions for and - /// - internal static class UnityEnumExtensions - { - /// - /// Converts a to string. - /// - /// The value to be converted. - /// This method should be used for constructing package paths for OS-dependent files. - /// A string representation of the provided value. - public static string GetPathName(this UnityOs os) - { - return os switch - { - UnityOs.AnyOs => string.Empty, - _ => GetName(os), - }; - } - - /// - /// Converts a to a string representation accepted by the Unity .meta file format. - /// - /// The value to be converted. - /// A string representation of the provided value. - public static string GetName(this UnityOs os) - { - return os switch - { - UnityOs.AnyOs => "AnyOS", - UnityOs.Windows => "Windows", - UnityOs.Linux => "Linux", - UnityOs.OSX => "OSX", - UnityOs.Android => "Android", - UnityOs.WebGL => "WebGL", - UnityOs.iOS => "iOS", - _ => throw new ArgumentException($"Unknown OS {os}"), - }; - } - - /// - /// Converts a to string. - /// - /// The value to be converted. - /// This method should be used for constructing package paths for CPU-dependent files. - /// A string representation of the provided value. - public static string GetPathName(this UnityCpu cpu) - { - return cpu switch - { - UnityCpu.AnyCpu => string.Empty, - _ => GetName(cpu), - }; - } - - /// - /// Converts a to a string representation accepted by the Unity .meta file format. - /// - /// The value to be converted. - /// A string representation of the provided value. - public static string GetName(this UnityCpu cpu) - { - return cpu switch - { - UnityCpu.AnyCpu => "AnyCPU", - UnityCpu.X64 => "x86_64", - UnityCpu.X86 => "x86", - UnityCpu.ARM64 => "ARM64", - UnityCpu.ARMv7 => "ARMv7", - UnityCpu.None => "None", - _ => throw new ArgumentException($"Unknown CPU {cpu}"), - }; - } + /// + /// A subtree of hierarchical (os, cpu) configuration tuples that are supported by Unity. + /// + /// + /// The root node is typically the most general configuration, i.e. + /// supporting and . Leaf nodes are typically specialized, + /// targeting a specific OS and CPU flavor. + /// + /// + /// Creates a new instance. + /// + /// The OS. + /// The CPU flavor. + /// True if the Unity editor is available in this (os, cpu) tuple. + internal class PlatformDefinition(UnityOs os, UnityCpu cpu, bool isEditorConfig) + { + private readonly UnityOs _os = os; + private readonly UnityCpu _cpu = cpu; + private readonly bool _isEditor = isEditorConfig; + private readonly List _children = []; + + private PlatformDefinition? _parent = null; + + /// + /// The parent that is a superset of this. + /// + public PlatformDefinition? Parent + { + get => _parent; + + private set + { + _parent = value; + } + } + + /// + /// The child configurations this is a superset of. + /// + public IReadOnlyList Children + { + get => _children; + + private set + { + _children.AddRange(value); + + foreach (var child in _children) + { + child.Parent = this; + } + } + } + + /// + /// The distance from the root in the configuration tree. + /// + public int Depth + => (_parent == null) ? 0 : (1 + _parent.Depth); + + /// + /// The operating system. + /// + public UnityOs Os + => _os; + + /// + /// The CPU flavor. + /// + public UnityCpu Cpu + => _cpu; + + /// + public override string ToString() + => $"{_os}.{_cpu}"; + + /// + /// Attempts to find a that matches the given + /// and among the descendants of this configuration. + /// + /// The operating system to match. + /// The CPU flavor to match. + /// A matching . + public PlatformDefinition? Find(UnityOs os, UnityCpu? cpu = default) + { + // Test self + if ((_os == os) && ((cpu == null) || (_cpu == cpu))) + { + return this; + } + + // Recurse to children + return _children + .Select(c => c.Find(os, cpu)) + .Where(c => c != null) + .FirstOrDefault(); + } + + /// + /// Attempts to find a for which the Unity editor is available + /// among the descendants of this configuration. + /// + /// A matching . + public PlatformDefinition? FindEditor() + { + // Test self + if (_isEditor) + { + return this; + } + + // Recurse to children + return _children + .Select(c => c.FindEditor()) + .Where(c => c != null) + .FirstOrDefault(); + } + + /// + /// Returns the difference set of configurations this is a superset of, + /// that are not already part of the given visited set. + /// + /// The set of already visited configurations, that should not be part of the returned set. + /// + /// A set of configurations this is a superset of, + /// that are not already part of the given visited set. + /// + public HashSet GetRemainingPlatforms(IReadOnlySet visitedPlatforms) + { + var remainingPlatforms = new HashSet + { + // Push the root + this + }; + + for (bool found = true; found;) + { + found = false; + + foreach (var p in remainingPlatforms) + { + // Remove p if already visited + if (visitedPlatforms.Contains(p)) + { + remainingPlatforms.Remove(p); + found = true; + break; + } + + // If p has descendants that were visited, we can't use it and need to expand it + if (p.HasVisitedDescendants(visitedPlatforms)) + { + remainingPlatforms.Remove(p); + + foreach (var c in p.Children) + { + remainingPlatforms.Add(c); + } + + found = true; + break; + } + } + } + + return remainingPlatforms; + } + + /// + /// Creates the tree of all known (os, cpu) configurations supported by Unity + /// + /// + public static PlatformDefinition CreateAllPlatforms() + { + var root = new PlatformDefinition(UnityOs.AnyOs, UnityCpu.AnyCpu, isEditorConfig: true) + { + Children = new List() + { + new(UnityOs.Windows, UnityCpu.AnyCpu, isEditorConfig: true) + { + Children = new List() + { + new(UnityOs.Windows, UnityCpu.X64, isEditorConfig: true), + new(UnityOs.Windows, UnityCpu.X86, isEditorConfig: false), + }, + }, + new(UnityOs.Linux, UnityCpu.X64, isEditorConfig: true), + new(UnityOs.Android, UnityCpu.ARMv7, isEditorConfig: false), + new(UnityOs.WebGL, UnityCpu.AnyCpu, isEditorConfig: false), + new(UnityOs.iOS, UnityCpu.AnyCpu, isEditorConfig: false), + new(UnityOs.OSX, UnityCpu.AnyCpu, isEditorConfig: true) + { + Children = new List() + { + new(UnityOs.OSX, UnityCpu.X64, isEditorConfig: true), + new(UnityOs.OSX, UnityCpu.ARM64, isEditorConfig: true), + }, + } + } + }; + + return root; + } + + /// + /// Returns true if either this or any of its descendants + /// is also part of the given set of visited configurations. + /// The set of visited configurations to test against. + /// + private bool HasVisitedDescendants(IReadOnlySet visitedPlatforms) + { + if (visitedPlatforms.Contains(this)) + { + return true; + } + + foreach (var c in _children) + { + if (c.HasVisitedDescendants(visitedPlatforms)) + { + return true; + } + } + + return false; + } } - /// - /// A subtree of hierarchical (os, cpu) configuration tuples that are supported by Unity. - /// - /// - /// The root node is typically the most general configuration, i.e. - /// supporting and . Leaf nodes are typically specialized, - /// targeting a specific OS and CPU flavor. - /// - internal class PlatformDefinition - { - private readonly UnityOs _os; - private readonly UnityCpu _cpu; - private readonly bool _isEditor; - private readonly List _children; - - private PlatformDefinition? _parent; - - /// - /// Creates a new instance. - /// - /// The OS. - /// The CPU flavor. - /// True if the Unity editor is available in this (os, cpu) tuple. - public PlatformDefinition(UnityOs os, UnityCpu cpu, bool isEditorConfig) - { - _os = os; - _cpu = cpu; - _parent = null; - _children = new(); - _isEditor = isEditorConfig; - } - - /// - /// The parent that is a superset of this. - /// - public PlatformDefinition? Parent - { - get => _parent; - - private set - { - _parent = value; - } - } - - /// - /// The child configurations this is a superset of. - /// - public IReadOnlyList Children - { - get => _children; - - private set - { - _children.AddRange(value); - - foreach (var child in _children) - { - child.Parent = this; - } - } - } - - /// - /// The distance from the root in the configuration tree. - /// - public int Depth - => (_parent == null) ? 0 : (1 + _parent.Depth); - - /// - /// The operating system. - /// - public UnityOs Os - => _os; - - /// - /// The CPU flavor. - /// - public UnityCpu Cpu - => _cpu; - - /// - public override string ToString() - => $"{_os}.{_cpu}"; - - /// - /// Attempts to find a that matches the given - /// and among the descendants of this configuration. - /// - /// The operating system to match. - /// The CPU flavor to match. - /// A matching . - public PlatformDefinition? Find(UnityOs os, UnityCpu? cpu = default) - { - // Test self - if ((_os == os) && ((cpu == null) || (_cpu == cpu))) - { - return this; - } - - // Recurse to children - return _children - .Select(c => c.Find(os, cpu)) - .Where(c => c != null) - .FirstOrDefault(); - } - - /// - /// Attempts to find a for which the Unity editor is available - /// among the descendants of this configuration. - /// - /// A matching . - public PlatformDefinition? FindEditor() - { - // Test self - if (_isEditor) - { - return this; - } - - // Recurse to children - return _children - .Select(c => c.FindEditor()) - .Where(c => c != null) - .FirstOrDefault(); - } - - /// - /// Returns the difference set of configurations this is a superset of, - /// that are not already part of the given visited set. - /// - /// The set of already visited configurations, that should not be part of the returned set. - /// - /// A set of configurations this is a superset of, - /// that are not already part of the given visited set. - /// - public HashSet GetRemainingPlatforms(IReadOnlySet visitedPlatforms) - { - var remainingPlatforms = new HashSet - { - // Push the root - this - }; - - for (bool found = true; found;) - { - found = false; - - foreach (var p in remainingPlatforms) - { - // Remove p if already visited - if (visitedPlatforms.Contains(p)) - { - remainingPlatforms.Remove(p); - found = true; - break; - } - - // If p has descendants that were visited, we can't use it and need to expand it - if (p.HasVisitedDescendants(visitedPlatforms)) - { - remainingPlatforms.Remove(p); - - foreach (var c in p.Children) - { - remainingPlatforms.Add(c); - } - - found = true; - break; - } - } - } - - return remainingPlatforms; - } - - /// - /// Creates the tree of all known (os, cpu) configurations supported by Unity - /// - /// - public static PlatformDefinition CreateAllPlatforms() - { - var root = new PlatformDefinition(UnityOs.AnyOs, UnityCpu.AnyCpu, isEditorConfig: true) - { - Children = new List() - { - new PlatformDefinition(UnityOs.Windows, UnityCpu.AnyCpu, isEditorConfig: true) - { - Children = new List() - { - new PlatformDefinition(UnityOs.Windows, UnityCpu.X64, isEditorConfig: true), - new PlatformDefinition(UnityOs.Windows, UnityCpu.X86, isEditorConfig: false), - }, - }, - new PlatformDefinition(UnityOs.Linux, UnityCpu.X64, isEditorConfig: true), - new PlatformDefinition(UnityOs.Android, UnityCpu.ARMv7, isEditorConfig: false), - new PlatformDefinition(UnityOs.WebGL, UnityCpu.AnyCpu, isEditorConfig: false), - new PlatformDefinition(UnityOs.iOS, UnityCpu.AnyCpu, isEditorConfig: false), - new PlatformDefinition(UnityOs.OSX, UnityCpu.AnyCpu, isEditorConfig: true) - { - Children = new List() - { - new PlatformDefinition(UnityOs.OSX, UnityCpu.X64, isEditorConfig: true), - new PlatformDefinition(UnityOs.OSX, UnityCpu.ARM64, isEditorConfig: true), - }, - } - } - }; - - return root; - } - - /// - /// Returns true if either this or any of its descendants - /// is also part of the given set of visited configurations. - /// The set of visited configurations to test against. - /// - private bool HasVisitedDescendants(IReadOnlySet visitedPlatforms) - { - if (visitedPlatforms.Contains(this)) - { - return true; - } - - foreach (var c in _children) - { - if (c.HasVisitedDescendants(visitedPlatforms)) - { - return true; - } - } - - return false; - } - } - - /// - /// Associates a file path with the the file is compatible with. - /// - internal class PlatformFile - { - private readonly PlatformDefinition _platform; - private readonly string _sourcePath; - - /// - /// Creates a new instance. - /// - /// The full path of the file in the original NuGet package. - /// The platform the file is compatible with. - public PlatformFile(string sourcePath, PlatformDefinition platform) - { - _sourcePath = sourcePath; - _platform = platform; - } - - /// - /// The full path of the file in the original NuGet package. - /// - public string SourcePath - => _sourcePath; - - /// - /// The the file is compatible with. - /// - public PlatformDefinition Platform - => _platform; - - /// - /// Returns the full path of the file in the UPM package. - /// - /// The file path to start from. - /// The full path of the file in the UPM package. - public string GetDestinationPath(string basePath) - { - // We start with just the base path - var fullPath = basePath; - var depth = _platform.Depth; - - if (depth > 0) - { - // Our configuration is not AnyOS, add a / to our full path - fullPath = Path.Combine(fullPath, _platform.Os.GetPathName()); - } - - if (depth > 1) - { - // Our CPU os not AnyCPU, add a / to our full path - fullPath = Path.Combine(fullPath, _platform.Cpu.GetPathName()); - } - - // Finally, append the file name and return - var fileName = Path.GetFileName(_sourcePath); - fullPath = Path.Combine(fullPath, fileName); - return fullPath; - } - } -} + /// + /// Associates a file path with the the file is compatible with. + /// + /// + /// Creates a new instance. + /// + /// The full path of the file in the original NuGet package. + /// The platform the file is compatible with. + internal class PlatformFile(string sourcePath, PlatformDefinition platform) + { + private readonly PlatformDefinition _platform = platform; + private readonly string _sourcePath = sourcePath; + + /// + /// The full path of the file in the original NuGet package. + /// + public string SourcePath + => _sourcePath; + + /// + /// The the file is compatible with. + /// + public PlatformDefinition Platform + => _platform; + + /// + /// Returns the full path of the file in the UPM package. + /// + /// The file path to start from. + /// The full path of the file in the UPM package. + public string GetDestinationPath(string basePath) + { + // We start with just the base path + var fullPath = basePath; + var depth = _platform.Depth; + + if (depth > 0) + { + // Our configuration is not AnyOS, add a / to our full path + fullPath = Path.Combine(fullPath, _platform.Os.GetPathName()); + } + + if (depth > 1) + { + // Our CPU os not AnyCPU, add a / to our full path + fullPath = Path.Combine(fullPath, _platform.Cpu.GetPathName()); + } + + // Finally, append the file name and return + var fileName = Path.GetFileName(_sourcePath); + fullPath = Path.Combine(fullPath, fileName); + return fullPath; + } + } +} diff --git a/src/UnityNuGet/Registry.cs b/src/UnityNuGet/Registry.cs index de0d0f04..a9c19297 100644 --- a/src/UnityNuGet/Registry.cs +++ b/src/UnityNuGet/Registry.cs @@ -1,44 +1,39 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.Serialization; -using Newtonsoft.Json; - -namespace UnityNuGet -{ - /// - /// Loads the `registry.json` file at startup - /// - [Serializable] - public sealed class Registry : Dictionary - { - private const string RegistryFileName = "registry.json"; - private static readonly object LockRead = new(); - private static Registry? _registry = null; - - // A comparer is established for cases where the dependency name is not set to the correct case. - // Example: https://www.nuget.org/packages/NeoSmart.Caching.Sqlite/0.1.0#dependencies-body-tab - public Registry() : base(StringComparer.OrdinalIgnoreCase) - { - } - - private Registry(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - - public static Registry Parse(string json) - { - ArgumentNullException.ThrowIfNull(json); - return JsonConvert.DeserializeObject(json, JsonCommonExtensions.Settings)!; - } - - public static Registry GetInstance() - { - lock (LockRead) - { - _registry ??= Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(typeof(Registry).Assembly.Location)!, RegistryFileName))); - } - return _registry; - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using Newtonsoft.Json; + +namespace UnityNuGet +{ + /// + /// Loads the `registry.json` file at startup + /// + [Serializable] + public sealed class Registry : Dictionary + { + private const string RegistryFileName = "registry.json"; + private static readonly object LockRead = new(); + private static Registry? _registry = null; + + // A comparer is established for cases where the dependency name is not set to the correct case. + // Example: https://www.nuget.org/packages/NeoSmart.Caching.Sqlite/0.1.0#dependencies-body-tab + public Registry() : base(StringComparer.OrdinalIgnoreCase) + { + } + + public static Registry Parse(string json) + { + ArgumentNullException.ThrowIfNull(json); + return JsonConvert.DeserializeObject(json, JsonCommonExtensions.Settings)!; + } + + public static Registry GetInstance() + { + lock (LockRead) + { + _registry ??= Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(typeof(Registry).Assembly.Location)!, RegistryFileName))); + } + return _registry; + } + } +} diff --git a/src/UnityNuGet/RegistryCache.cs b/src/UnityNuGet/RegistryCache.cs index c63f4b3a..323a2f0e 100644 --- a/src/UnityNuGet/RegistryCache.cs +++ b/src/UnityNuGet/RegistryCache.cs @@ -280,7 +280,7 @@ private async Task BuildInternal() var resolvedDependencyGroups = NuGetHelper.GetCompatiblePackageDependencyGroups(packageMeta.DependencySets, _targetFrameworks).ToList(); - if (!packageEntry.Analyzer && !resolvedDependencyGroups.Any()) + if (!packageEntry.Analyzer && resolvedDependencyGroups.Count == 0) { using var downloadResult = await GetPackageDownloadResourceResult(packageIdentity); @@ -570,7 +570,7 @@ RegistryEntry packageEntry { if (!collectedItems.TryGetValue(item, out var frameworksPerGroup)) { - frameworksPerGroup = new HashSet(); + frameworksPerGroup = []; collectedItems.Add(item, frameworksPerGroup); } frameworksPerGroup.Add(targetFramework); @@ -643,16 +643,16 @@ RegistryEntry packageEntry meta = UnityMeta.GetMetaForDll( GetStableGuid(identity, fileInUnityPackage), new PlatformDefinition(UnityOs.AnyOs, UnityCpu.None, isEditorConfig: false), - new string[] { "RoslynAnalyzer" }, - Array.Empty()); + ["RoslynAnalyzer"], + []); } else { meta = UnityMeta.GetMetaForDll( GetStableGuid(identity, fileInUnityPackage), new PlatformDefinition(UnityOs.AnyOs, UnityCpu.None, isEditorConfig: false), - Array.Empty(), - Array.Empty()); + [], + []); } } else @@ -765,7 +765,7 @@ RegistryEntry packageEntry } else { - folders = Array.Empty(); + folders = []; } string folder = string.Empty; @@ -789,7 +789,7 @@ RegistryEntry packageEntry // use NET_STANDARD var defineConstraints = hasMultiNetStandard || hasOnlyNetStandard21 - || isPackageNetStandard21Assembly ? frameworks.First(x => x.Framework == item.TargetFramework).DefineConstraints : Array.Empty(); + || isPackageNetStandard21Assembly ? frameworks.First(x => x.Framework == item.TargetFramework).DefineConstraints : []; meta = UnityMeta.GetMetaForDll( GetStableGuid(identity, fileInUnityPackage), @@ -902,7 +902,7 @@ RegistryEntry packageEntry if (licenseUrlText != null) { licenseUrlText = licenseUrlText.Trim(); - if (licenseUrlText.StartsWith("<")) + if (licenseUrlText.StartsWith('<')) { try { @@ -1112,7 +1112,7 @@ private static UnityAsmdef CreateAnalyzerAmsdef(PackageIdentity packageIdentity) return new() { Name = $"{packageIdentity.Id}_Unity", // Add _Unity suffix because Unity has a validation so that assemblies names do not collide with asmdefs assembly names - IncludePlatforms = new string[] { "Editor" } + IncludePlatforms = ["Editor"] }; } @@ -1164,8 +1164,15 @@ private void LogError(string message) private static List SplitCommaSeparatedString(string input) { var list = new List(); - if (input == null) return list; - foreach (var entry in input.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)) + + if (input == null) + { + return list; + } + + char[] separators = [',', ';']; + + foreach (var entry in input.Split(separators, StringSplitOptions.RemoveEmptyEntries)) { list.Add(entry.Trim()); } diff --git a/src/UnityNuGet/RegistryEntry.cs b/src/UnityNuGet/RegistryEntry.cs index 2c2c67d8..8c00a404 100644 --- a/src/UnityNuGet/RegistryEntry.cs +++ b/src/UnityNuGet/RegistryEntry.cs @@ -1,27 +1,27 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using NuGet.Versioning; - -namespace UnityNuGet -{ - /// - /// An entry in the - /// - public class RegistryEntry - { - [JsonProperty("ignore")] - public bool Ignored { get; set; } - - [JsonProperty("listed")] - public bool Listed { get; set; } - - [JsonProperty("version")] - public VersionRange? Version { get; set; } - - [JsonProperty("defineConstraints")] - public List DefineConstraints { get; set; } = new(); - - [JsonProperty("analyzer")] - public bool Analyzer { get; set; } - } -} +using System.Collections.Generic; +using Newtonsoft.Json; +using NuGet.Versioning; + +namespace UnityNuGet +{ + /// + /// An entry in the + /// + public class RegistryEntry + { + [JsonProperty("ignore")] + public bool Ignored { get; set; } + + [JsonProperty("listed")] + public bool Listed { get; set; } + + [JsonProperty("version")] + public VersionRange? Version { get; set; } + + [JsonProperty("defineConstraints")] + public List DefineConstraints { get; set; } = []; + + [JsonProperty("analyzer")] + public bool Analyzer { get; set; } + } +} diff --git a/src/UnityNuGet/UnityNuGet.csproj b/src/UnityNuGet/UnityNuGet.csproj index 1e3306b1..6943335e 100644 --- a/src/UnityNuGet/UnityNuGet.csproj +++ b/src/UnityNuGet/UnityNuGet.csproj @@ -1,11 +1,8 @@ - + - net7.0 - enable + net8.0 0.14.0 - 9.0 - true @@ -23,14 +20,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/UnityNuGet/UnityPackage.cs b/src/UnityNuGet/UnityPackage.cs index ae6be33d..b07b41d8 100644 --- a/src/UnityNuGet/UnityPackage.cs +++ b/src/UnityNuGet/UnityPackage.cs @@ -1,41 +1,41 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace UnityNuGet -{ - /// - /// - /// - public class UnityPackage : JsonObjectBase - { - public UnityPackage() - { - Keywords = new List(); - Dependencies = new Dictionary(); - } - - [JsonProperty("name")] - public string? Name { get; set; } - - [JsonProperty("displayName")] - public string? DisplayName { get; set; } - - [JsonProperty("version")] - public string? Version { get; set; } - - [JsonProperty("unity")] - public string? Unity { get; set; } - - [JsonProperty("description")] - public string? Description { get; set; } - - [JsonProperty("keywords")] - public List Keywords { get; } - - [JsonProperty("category")] - public string? Category { get; set; } - - [JsonProperty("dependencies")] - public Dictionary Dependencies { get; } - } -} +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace UnityNuGet +{ + /// + /// + /// + public class UnityPackage : JsonObjectBase + { + public UnityPackage() + { + Keywords = []; + Dependencies = []; + } + + [JsonProperty("name")] + public string? Name { get; set; } + + [JsonProperty("displayName")] + public string? DisplayName { get; set; } + + [JsonProperty("version")] + public string? Version { get; set; } + + [JsonProperty("unity")] + public string? Unity { get; set; } + + [JsonProperty("description")] + public string? Description { get; set; } + + [JsonProperty("keywords")] + public List Keywords { get; } + + [JsonProperty("category")] + public string? Category { get; set; } + + [JsonProperty("dependencies")] + public Dictionary Dependencies { get; } + } +} diff --git a/src/global.json b/src/global.json index 3a95bf88..f7fb55b4 100644 --- a/src/global.json +++ b/src/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100", + "version": "8.0.100", "rollForward": "latestMinor", "allowPrerelease": false }