diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3f64877d..e53e3137 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: '8.0.x'
+ dotnet-version: '9.0.x'
- name: Build & test (Release)
run: dotnet test src -c Release --logger "console;verbosity=normal"
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000..b6cefc44
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,6 @@
+{
+ "recommendations": [
+ "editorconfig.editorconfig",
+ "ms-dotnettools.csharp"
+ ]
+}
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 7538bf22..42adbc71 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG TARGETARCH
WORKDIR /app
@@ -16,7 +16,7 @@ COPY . ./
RUN dotnet publish src -a $TARGETARCH -c Release -o /app/src/out
# Build runtime image
-FROM mcr.microsoft.com/dotnet/aspnet:8.0
+FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app/src/out .
ENTRYPOINT ["dotnet", "UnityNuGet.Server.dll"]
diff --git a/examples/docker/docker-compose.yml b/examples/docker/docker-compose.yml
index f2182a61..364d750c 100644
--- a/examples/docker/docker-compose.yml
+++ b/examples/docker/docker-compose.yml
@@ -1,4 +1,4 @@
-version: "3.9"
+---
services:
unitynuget:
build: ../..
diff --git a/readme.md b/readme.md
index d82e1cd0..73acd887 100644
--- a/readme.md
+++ b/readme.md
@@ -1,6 +1,6 @@
# UnityNuGet [![Build Status](https://github.com/xoofx/UnityNuGet/workflows/ci/badge.svg?branch=master)](https://github.com/xoofx/UnityNuGet/actions) [![Static Badge](https://img.shields.io/badge/server-status-blue)](https://unitynuget-registry.azurewebsites.net/status) [![Static Badge](https://img.shields.io/badge/server-feed-blue)](https://unitynuget-registry.azurewebsites.net/-/all)
-
+
This project provides a seamlessly integration of a [curated list](registry.json) of NuGet packages within the Unity Package Manager.
@@ -8,7 +8,7 @@ This project provides a seamlessly integration of a [curated list](registry.json
## Installation
-### Add scope registry (manifest.json):
+### Add scope registry (manifest.json)
In order to use this service you simply need to edit the `Packages/manifest.json` in your project and add the following scoped registry:
@@ -24,16 +24,16 @@ In order to use this service you simply need to edit the `Packages/manifest.json
}
],
"dependencies": {
- "org.nuget.scriban": "2.1.0"
+ "org.nuget.scriban": "2.1.0"
}
}
```
-### Add scope registry (Package Manager UI):
+### Add scope registry (Package Manager UI)
-Instructions: https://docs.unity3d.com/Manual/class-PackageManager.html
+Instructions:
-```
+```yaml
Name: Unity NuGet
Url: https://unitynuget-registry.azurewebsites.net
@@ -43,21 +43,21 @@ Scope(s): org.nuget
### Disable Assembly Version Validation
-This step is necessary to ensure that binding redirects for [strongly named assemblies](https://learn.microsoft.com/en-us/dotnet/standard/assembly/strong-named) in NuGet packages resolve correctly to paths _within the Unity project_.
+This step is necessary to ensure that binding redirects for [strongly named assemblies](https://learn.microsoft.com/en-us/dotnet/standard/assembly/strong-named) in NuGet packages resolve correctly to paths _within the Unity project_.
- In Unity 2022.2+, this is the [default behavior](https://forum.unity.com/threads/editor-assembly-loading-issues-unloading-broken-assembly-could-not-load-signature.754508/#post-8647791), so no action is required.
- For earlier Unity versions, uncheck "Project Settings > Player > Other Settings > Configuration > Assembly Version Validation"
### Verify installation
-> WARNING: If you are encountering weird compilation errors with UnityNuGet and you have been using UnityNuGet already,
+> WARNING: If you are encountering weird compilation errors with UnityNuGet and you have been using UnityNuGet already,
> it could be that we have updated packages on the server, and in that case, you need to clear the cache containing
-> all Unity NPM packages downdloaded from the `unitynuget-registry.azurewebsites.net` registry.
+> all Unity NPM packages downloaded from the `unitynuget-registry.azurewebsites.net` registry.
> On Windows, this cache is located at: `%localappdata%\Unity\cache\npm\unitynuget-registry.azurewebsites.net`
>
-> Cache locations by OS: https://docs.unity3d.com/Manual/upm-cache.html
+> Cache locations by OS:
-When opening the Package Manager Window, you should see a few packages coming from NuGet (with the postfix text ` (NuGet)`)
+When opening the Package Manager Window, you should see a few packages coming from NuGet (with the postfix text ` (NuGet)`)
![UnityEditorWithNuGet](img/unity_editor_with_nuget.jpg)
@@ -68,7 +68,7 @@ This service provides only a [curated list](registry.json) of NuGet packages
Your NuGet package needs to respect a few constraints in order to be listed in the curated list:
- It must have non-preview versions (e.g `1.0.0` but not `1.0.0-preview.1`)
-- It must provide `.NETStandard2.0` assemblies as part of its package
+- It must provide at least `.NETStandard2.0` (and optionally `.NETStandard2.1`) assemblies as part of its package
You can send a PR to this repository to modify the [registry.json](registry.json) file (don't forget to maintain the alphabetical order)
@@ -76,7 +76,7 @@ You also need to **specify the lowest version of your package that has support f
Beware that **all transitive dependencies of the package** must be **explicitly listed** in the registry as well.
-> NOTE:
+> NOTE:
> * We reserve the right to decline a package to be available through this service
> * The server will be updated only when a new version tag is pushed on the main branch.
@@ -84,15 +84,16 @@ Beware that **all transitive dependencies of the package** must be **explicitly
Only compatible with **`Unity 2019.1`** and potentially with newer version.
-> NOTE: This service is currently only tested with **`Unity 2019.x, 2020.x and 2021.x`**
+> NOTE: This service is currently only tested with **`Unity 2019.x, 2020.x, 2021.x, 2022.x, 2023.x and 6`**
>
> It may not work with a more recent version of Unity
## Docker
> Available in [ghcr (GitHub Container Registry)](https://github.com/xoofx/UnityNuGet/pkgs/container/unitynuget).
->
+>
> Supported platforms:
+>
> - linux/amd64
> - linux/arm64
@@ -144,7 +145,7 @@ On Azure through my own Azure credits coming from my MVP subscription, enjoy!
### **Why can't you add all NuGet packages?**
-The reason is that many NuGet packages are not compatible with Unity, or do not provide `.NETStandard2.0` assemblies or are not relevant for being used within Unity.
+The reason is that many NuGet packages are not compatible with Unity, or do not provide `.NETStandard2.0` or `.NETStandard2.1` assemblies or are not relevant for being used within Unity.
Also currently the Package Manager doesn't provide a way to filter easily packages, so the UI is currently not adequate to list lots of packages.
@@ -154,19 +155,21 @@ Since 2019.1.x, Unity is compatible with `.NETStandard2.0` and it is the .NET pr
Having a `.NETStandard2.0` for NuGet packages for Unity can ensure that the experience to add a package to your project is consistent and well supported.
-> More information: https://docs.unity3d.com/Manual/dotnetProfileSupport.html
+As of Unity 2021.x it also supports `.NETStandard2.1` so packages providing this target will be compatible with this version of Unity or newer.
+
+> More information:
### **How this service is working?**
-This project implements a simplified compatible NPM server in C# using ASP.NET Core and converts NuGet packages to Unity packages before serving them.
+This project implements a simplified compatible NPM server in C# using ASP.NET Core and converts NuGet packages to Unity packages before serving them.
-Every 10min, packages are updated from NuGet so that if a new version is published, from the curated list of NuGet packages, it will be available through this service.
+Every 10 minutes, packages are updated from NuGet so that if a new version is published, from the curated list of NuGet packages, it will be available through this service.
Once converted, these packages are cached on the disk on the server.
## License
-This software is released under the [BSD-Clause 2 license](https://opensource.org/licenses/BSD-2-Clause).
+This software is released under the [BSD-Clause 2 license](https://opensource.org/licenses/BSD-2-Clause).
## Author
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index b91d59b2..370d3f4c 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,6 +2,7 @@
enable
+ true
true
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 1c7de547..053e0020 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -1,24 +1,24 @@
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/UnityNuGet.Server/Program.cs b/src/UnityNuGet.Server/Program.cs
index d154f6c1..00328f47 100644
--- a/src/UnityNuGet.Server/Program.cs
+++ b/src/UnityNuGet.Server/Program.cs
@@ -1,36 +1,44 @@
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using UnityNuGet;
+using UnityNuGet.Server;
+
+var builder = WebApplication.CreateBuilder(args);
-namespace UnityNuGet.Server
-{
- public static class Program
- {
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
+// Add the registry cache initializer
+builder.Services.AddHostedService();
+// Add the registry cache updater
+builder.Services.AddHostedService();
+// Add the registry cache report
+builder.Services.AddSingleton();
+builder.Services.AddSingleton();
+
+builder.Services.Configure(builder.Configuration.GetSection("Registry"));
+builder.Services.AddSingleton, ValidateRegistryOptions>();
+builder.Services.AddOptionsWithValidateOnStart();
+
+builder.Services.AddApplicationInsightsTelemetry();
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- //webBuilder.UseSetting("detailedErrors", "true");
- webBuilder.ConfigureServices((context, services) =>
- {
- // Add the registry cache initializer
- services.AddHostedService();
- // Add the registry cache updater
- services.AddHostedService();
- // Add the registry cache report
- services.AddSingleton();
- services.AddSingleton();
+// Also enable NewtonsoftJson serialization
+builder.Services.AddControllers().AddNewtonsoftJson();
+
+var app = builder.Build();
- services.AddOptions()
- .Bind(context.Configuration.GetSection("Registry"))
- .ValidateDataAnnotations();
- });
- webBuilder.UseStartup();
- });
- }
+if (app.Environment.IsDevelopment())
+{
+ app.UseDeveloperExceptionPage();
+ app.LogRequestHeaders(app.Services.GetRequiredService());
+}
+else
+{
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
}
+app.UseRouting();
+app.MapControllers();
+app.MapStatus();
+
+app.Run();
diff --git a/src/UnityNuGet.Server/RegistryCacheInitializer.cs b/src/UnityNuGet.Server/RegistryCacheInitializer.cs
index 4fdc8f65..e8d9079c 100644
--- a/src/UnityNuGet.Server/RegistryCacheInitializer.cs
+++ b/src/UnityNuGet.Server/RegistryCacheInitializer.cs
@@ -44,7 +44,7 @@ public Task StartAsync(CancellationToken cancellationToken)
if (isDevelopment)
{
- var currentDirectory = Path.GetDirectoryName(typeof(Startup).Assembly.Location)!;
+ var currentDirectory = Path.GetDirectoryName(AppContext.BaseDirectory)!;
unityPackageFolder = Path.Combine(currentDirectory, new DirectoryInfo(registryOptions.RootPersistentFolder!).Name);
}
else
diff --git a/src/UnityNuGet.Server/RegistryCacheUpdater.cs b/src/UnityNuGet.Server/RegistryCacheUpdater.cs
index aed12d3f..4de59968 100644
--- a/src/UnityNuGet.Server/RegistryCacheUpdater.cs
+++ b/src/UnityNuGet.Server/RegistryCacheUpdater.cs
@@ -59,10 +59,19 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
await Task.Delay((int)_registryOptions.UpdateInterval.TotalMilliseconds, stoppingToken);
}
+ }
+ catch (TaskCanceledException)
+ {
+ string message = "RegistryCache update canceled";
+
+ _logger.LogInformation("{Message}", message);
+
+ _registryCacheReport.AddInformation($"{message}.");
+ _registryCacheReport.Complete();
}
catch (Exception ex)
{
- string message = "Error while building a new registry cache.";
+ string message = "Error while building a new registry cache";
_logger.LogError(ex, "{Message}", message);
diff --git a/src/UnityNuGet.Server/Startup.cs b/src/UnityNuGet.Server/Startup.cs
deleted file mode 100644
index 0e26fdef..00000000
--- a/src/UnityNuGet.Server/Startup.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-
-namespace UnityNuGet.Server
-{
- public class Startup
- {
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddApplicationInsightsTelemetry();
-
- // Also enable NewtonsoftJson serialization
- services.AddControllers().AddNewtonsoftJson();
- }
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostEnvironment env, ILoggerFactory loggerFactory)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- app.LogRequestHeaders(loggerFactory);
- }
- else
- {
- // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
- app.UseHsts();
- }
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
-
- endpoints.MapStatus();
- });
- }
- }
-}
diff --git a/src/UnityNuGet.Server/UnityNuGet.Server.csproj b/src/UnityNuGet.Server/UnityNuGet.Server.csproj
index 7aabd46f..746efbf3 100644
--- a/src/UnityNuGet.Server/UnityNuGet.Server.csproj
+++ b/src/UnityNuGet.Server/UnityNuGet.Server.csproj
@@ -1,10 +1,11 @@
- net8.0
+ net9.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
+ false
diff --git a/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj b/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj
index b0f45a29..3b8c8484 100644
--- a/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj
+++ b/src/UnityNuGet.Tests/UnityNuGet.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
false
false
diff --git a/src/UnityNuGet/Registry.cs b/src/UnityNuGet/Registry.cs
index a9c19297..1b011c9c 100644
--- a/src/UnityNuGet/Registry.cs
+++ b/src/UnityNuGet/Registry.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Threading;
using Newtonsoft.Json;
namespace UnityNuGet
@@ -12,7 +13,7 @@ namespace UnityNuGet
public sealed class Registry : Dictionary
{
private const string RegistryFileName = "registry.json";
- private static readonly object LockRead = new();
+ private static readonly Lock LockRead = new();
private static Registry? _registry = null;
// A comparer is established for cases where the dependency name is not set to the correct case.
@@ -31,7 +32,7 @@ public static Registry GetInstance()
{
lock (LockRead)
{
- _registry ??= Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(typeof(Registry).Assembly.Location)!, RegistryFileName)));
+ _registry ??= Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(AppContext.BaseDirectory)!, RegistryFileName)));
}
return _registry;
}
diff --git a/src/UnityNuGet/RegistryOptions.cs b/src/UnityNuGet/RegistryOptions.cs
index b61bc157..ee6cdc6c 100644
--- a/src/UnityNuGet/RegistryOptions.cs
+++ b/src/UnityNuGet/RegistryOptions.cs
@@ -1,45 +1,47 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using Newtonsoft.Json;
-using NuGet.Frameworks;
-
-namespace UnityNuGet
-{
- public class RegistryOptions
- {
- [Required]
- public Uri? RootHttpUrl { get; set; }
-
- [Required]
- [RegularExpression(@"[a-z]+\.[a-z]+")]
- public string? UnityScope { get; set; }
-
- [Required]
- [RegularExpression(@"\d+\.\d+")]
- public string? MinimumUnityVersion { get; set; }
-
- [Required]
- public string? PackageNameNuGetPostFix { get; set; }
-
- [Required]
- public string? RootPersistentFolder { get; set; }
-
- [Required]
- public TimeSpan UpdateInterval { get; set; }
-
- [Required]
- public RegistryTargetFramework[]? TargetFrameworks { get; set; }
- }
-
- public class RegistryTargetFramework
- {
- [Required]
- public string? Name { get; set; }
-
- [Required]
- public string[]? DefineConstraints { get; set; }
-
- [JsonIgnore]
- internal NuGetFramework? Framework { get; set; }
- }
-}
+using System;
+using System.ComponentModel.DataAnnotations;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+using NuGet.Frameworks;
+
+namespace UnityNuGet
+{
+ public class RegistryOptions
+ {
+ [Required]
+ public Uri? RootHttpUrl { get; set; }
+
+ [Required]
+ [RegularExpression(@"[a-z]+\.[a-z]+")]
+ public string? UnityScope { get; set; }
+
+ [Required]
+ [RegularExpression(@"\d+\.\d+")]
+ public string? MinimumUnityVersion { get; set; }
+
+ [Required]
+ public string? PackageNameNuGetPostFix { get; set; }
+
+ [Required]
+ public string? RootPersistentFolder { get; set; }
+
+ [Required]
+ public TimeSpan UpdateInterval { get; set; }
+
+ [Required]
+ [ValidateEnumeratedItems]
+ public RegistryTargetFramework[]? TargetFrameworks { get; set; }
+ }
+
+ public class RegistryTargetFramework
+ {
+ [Required]
+ public string? Name { get; set; }
+
+ [Required]
+ public string[]? DefineConstraints { get; set; }
+
+ [JsonIgnore]
+ internal NuGetFramework? Framework { get; set; }
+ }
+}
diff --git a/src/UnityNuGet/UnityNuGet.csproj b/src/UnityNuGet/UnityNuGet.csproj
index 6943335e..fa3c9ee8 100644
--- a/src/UnityNuGet/UnityNuGet.csproj
+++ b/src/UnityNuGet/UnityNuGet.csproj
@@ -1,8 +1,9 @@
- net8.0
+ net9.0
0.14.0
+ true
diff --git a/src/UnityNuGet/ValidateRegistryOptions.cs b/src/UnityNuGet/ValidateRegistryOptions.cs
new file mode 100644
index 00000000..15adc9aa
--- /dev/null
+++ b/src/UnityNuGet/ValidateRegistryOptions.cs
@@ -0,0 +1,7 @@
+using Microsoft.Extensions.Options;
+
+namespace UnityNuGet
+{
+ [OptionsValidator]
+ public sealed partial class ValidateRegistryOptions : IValidateOptions;
+}
diff --git a/src/global.json b/src/global.json
index f7fb55b4..40584df3 100644
--- a/src/global.json
+++ b/src/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "9.0.100",
"rollForward": "latestMinor",
"allowPrerelease": false
}