From 47688cac68e3558efe90de369a52df8cee82b8ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 17:52:52 +0000 Subject: [PATCH 1/9] Initial plan From 284a78f64bd1c6e97c793dc609712958a5957442 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:01:01 +0000 Subject: [PATCH 2/9] Upgrade to .NET 10 and Aspire 13 Co-authored-by: ejsmith <282584+ejsmith@users.noreply.github.com> --- .github/workflows/build-arm64.yml | 2 +- .github/workflows/build.yaml | 4 ++-- .github/workflows/copilot-setup-steps.yml | 2 +- .github/workflows/elasticsearch-docker-7.yml | 2 +- .github/workflows/elasticsearch-docker-8.yml | 2 +- Dockerfile | 12 ++++++------ global.json | 2 +- src/Directory.Build.props | 2 +- .../Exceptionless.AppHost.csproj | 10 +++++----- src/Exceptionless.Core/Exceptionless.Core.csproj | 8 ++++---- src/Exceptionless.Job/Exceptionless.Job.csproj | 4 ++-- src/Exceptionless.Web/Exceptionless.Web.csproj | 6 +++--- tests/Exceptionless.Tests/Exceptionless.Tests.csproj | 6 +++--- 13 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build-arm64.yml b/.github/workflows/build-arm64.yml index d0940add98..e79cf0217b 100644 --- a/.github/workflows/build-arm64.yml +++ b/.github/workflows/build-arm64.yml @@ -25,7 +25,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.* + dotnet-version: 10.0.* dotnet-quality: ga - name: Build Reason env: diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f21407f00d..dd2cd039a3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -38,7 +38,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.* + dotnet-version: 10.0.* dotnet-quality: ga - name: Version @@ -62,7 +62,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.* + dotnet-version: 10.0.* dotnet-quality: ga - name: Start Services diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index f1161cee5a..f5a2e1df25 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -25,7 +25,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.* + dotnet-version: 10.0.* dotnet-quality: ga - name: Start Services diff --git a/.github/workflows/elasticsearch-docker-7.yml b/.github/workflows/elasticsearch-docker-7.yml index 642b645f8c..964cba1de5 100644 --- a/.github/workflows/elasticsearch-docker-7.yml +++ b/.github/workflows/elasticsearch-docker-7.yml @@ -18,7 +18,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.* + dotnet-version: 10.0.* dotnet-quality: ga - name: Build Reason env: diff --git a/.github/workflows/elasticsearch-docker-8.yml b/.github/workflows/elasticsearch-docker-8.yml index af64a4217b..43f0f1dbf7 100644 --- a/.github/workflows/elasticsearch-docker-8.yml +++ b/.github/workflows/elasticsearch-docker-8.yml @@ -18,7 +18,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.* + dotnet-version: 10.0.* dotnet-quality: ga - name: Build Reason env: diff --git a/Dockerfile b/Dockerfile index cef8c1ef34..5b174756d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /app COPY ./*.slnx ./NuGet.Config ./ @@ -35,7 +35,7 @@ RUN dotnet publish -c Release -o out # job -FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS job +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS job WORKDIR /app COPY --from=job-publish /app/src/Exceptionless.Job/out ./ @@ -52,7 +52,7 @@ RUN dotnet publish -c Release -o out /p:SkipSpaPublish=true # api -FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS api +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS api WORKDIR /app COPY --from=api-publish /app/src/Exceptionless.Web/out ./ @@ -72,7 +72,7 @@ RUN dotnet publish -c Release -o out # app -FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS app +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS app WORKDIR /app COPY --from=app-publish /app/src/Exceptionless.Web/out ./ @@ -146,7 +146,7 @@ USER elasticsearch RUN wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh && \ chmod +x dotnet-install.sh && \ - ./dotnet-install.sh --channel 9.0 --runtime aspnetcore && \ + ./dotnet-install.sh --channel 10.0 --runtime aspnetcore && \ rm dotnet-install.sh EXPOSE 8080 9200 @@ -206,7 +206,7 @@ USER elasticsearch RUN wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh && \ chmod +x dotnet-install.sh && \ - ./dotnet-install.sh --channel 9.0 --runtime aspnetcore && \ + ./dotnet-install.sh --channel 10.0 --runtime aspnetcore && \ rm dotnet-install.sh EXPOSE 8080 9200 diff --git a/global.json b/global.json index 7f873f4e9d..1e7fdfa95f 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100-rc*", + "version": "10.0.100", "rollForward": "latestMinor" } } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 8e86c5e5b2..0ee873a676 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable Exceptionless true diff --git a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj index 8313740fcd..0ed68c563e 100644 --- a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj +++ b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj @@ -1,16 +1,16 @@ - + Exe - net9.0 + net10.0 enable enable a9c2ddcc-e51d-4cd1-9782-96e1d74eec87 - - - + + + diff --git a/src/Exceptionless.Core/Exceptionless.Core.csproj b/src/Exceptionless.Core/Exceptionless.Core.csproj index c2328fe955..1ac3f03180 100644 --- a/src/Exceptionless.Core/Exceptionless.Core.csproj +++ b/src/Exceptionless.Core/Exceptionless.Core.csproj @@ -29,11 +29,11 @@ - - - + + + - + diff --git a/src/Exceptionless.Job/Exceptionless.Job.csproj b/src/Exceptionless.Job/Exceptionless.Job.csproj index 0434e18925..62a6b1a3f2 100644 --- a/src/Exceptionless.Job/Exceptionless.Job.csproj +++ b/src/Exceptionless.Job/Exceptionless.Job.csproj @@ -5,7 +5,7 @@ - + @@ -24,7 +24,7 @@ - + diff --git a/src/Exceptionless.Web/Exceptionless.Web.csproj b/src/Exceptionless.Web/Exceptionless.Web.csproj index 6bce409dd7..15ae430b64 100644 --- a/src/Exceptionless.Web/Exceptionless.Web.csproj +++ b/src/Exceptionless.Web/Exceptionless.Web.csproj @@ -15,8 +15,8 @@ - - + + @@ -39,7 +39,7 @@ - + diff --git a/tests/Exceptionless.Tests/Exceptionless.Tests.csproj b/tests/Exceptionless.Tests/Exceptionless.Tests.csproj index aff7daf6b1..6c2d69c748 100644 --- a/tests/Exceptionless.Tests/Exceptionless.Tests.csproj +++ b/tests/Exceptionless.Tests/Exceptionless.Tests.csproj @@ -9,9 +9,9 @@ - - - + + + From 382f45bbd4127c7d4051deb7988663386b088009 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:14:25 +0000 Subject: [PATCH 3/9] Fix Aspire 13 breaking changes and build issues Co-authored-by: ejsmith <282584+ejsmith@users.noreply.github.com> --- .../Exceptionless.AppHost.csproj | 2 +- .../Extensions/ElasticsearchExtensions.cs | 3 +- .../Extensions/KibanaConfigWriterHook.cs | 36 ------------------- src/Exceptionless.AppHost/Program.cs | 8 ++--- .../Exceptionless.Insulation.csproj | 16 ++++----- .../Exceptionless.Job.csproj | 5 --- .../Exceptionless.Web.csproj | 4 --- src/Exceptionless.Web/Startup.cs | 2 +- .../Exceptionless.Tests/AppWebHostFactory.cs | 2 +- 9 files changed, 15 insertions(+), 63 deletions(-) delete mode 100644 src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs diff --git a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj index 0ed68c563e..c06307f1fa 100644 --- a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj +++ b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs index a8d8c87e7d..fb5bab6647 100644 --- a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs +++ b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs @@ -80,7 +80,8 @@ public static IResourceBuilder WithKibana(this IResourceB { containerName ??= $"{builder.Resource.Name}-kibana"; - builder.ApplicationBuilder.Services.TryAddLifecycleHook(); + // TODO: Re-enable Kibana config writer hook after updating to Aspire 13 eventing model + // builder.ApplicationBuilder.Services.AddHostedService(); var resource = new KibanaResource(containerName); var resourceBuilder = builder.ApplicationBuilder.AddResource(resource) diff --git a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs deleted file mode 100644 index f5a338048a..0000000000 --- a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text; -using Aspire.Hosting.Lifecycle; - -namespace Aspire.Hosting; - -internal class KibanaConfigWriterHook : IDistributedApplicationLifecycleHook -{ - public async Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken) - { - if (appModel.Resources.OfType().SingleOrDefault() is not { } kibanaResource) - return; - - var elasticsearchInstances = appModel.Resources.OfType(); - - if (!elasticsearchInstances.Any()) - return; - - var hostsVariableBuilder = new StringBuilder(); - - foreach (var elasticsearchInstance in elasticsearchInstances) - { - if (elasticsearchInstance.PrimaryEndpoint.IsAllocated) - { - var connectionString = await elasticsearchInstance.GetConnectionStringAsync(); - if (hostsVariableBuilder.Length > 0) - hostsVariableBuilder.Append(","); - hostsVariableBuilder.Append(elasticsearchInstance.PrimaryEndpoint.Scheme).Append("://").Append(elasticsearchInstance.PrimaryEndpoint.ContainerHost).Append(":").Append(elasticsearchInstance.PrimaryEndpoint.Port); - } - } - - kibanaResource.Annotations.Add(new EnvironmentCallbackAnnotation(context => - { - context.EnvironmentVariables.Add("ELASTICSEARCH_HOSTS", hostsVariableBuilder.ToString()); - })); - } -} diff --git a/src/Exceptionless.AppHost/Program.cs b/src/Exceptionless.AppHost/Program.cs index 561ae1856b..d5fa011c4d 100644 --- a/src/Exceptionless.AppHost/Program.cs +++ b/src/Exceptionless.AppHost/Program.cs @@ -48,16 +48,16 @@ .WithUrlForEndpoint("http", u => u.DisplayText = "Api") .WithHttpHealthCheck("/health"); -builder.AddNpmApp("Web", "../../src/Exceptionless.Web/ClientApp", "dev") +builder.AddJavaScriptApp("Web", "../../src/Exceptionless.Web/ClientApp", "dev") .WithReference(api) .WithEnvironment("ASPNETCORE_URLS", "http://localhost:5200") .WithUrlForEndpoint("http", u => u.DisplayText = "Web") - .WithEndpoint(port: 5173, targetPort: 5173, scheme: "http", env: "PORT", isProxied: false); + .WithHttpEndpoint(port: 5173, env: "PORT", isProxied: false); -builder.AddNpmApp("AngularWeb", "../../src/Exceptionless.Web/ClientApp.angular", "serve") +builder.AddJavaScriptApp("AngularWeb", "../../src/Exceptionless.Web/ClientApp.angular", "serve") .WithReference(api) .WithEnvironment("ASPNETCORE_URLS", "http://localhost:5200") .WithUrlForEndpoint("http", u => u.DisplayText = "Angular Web") - .WithEndpoint(port: 5100, targetPort: 5100, scheme: "http", env: "PORT", isProxied: false); + .WithHttpEndpoint(port: 5100, env: "PORT", isProxied: false); builder.Build().Run(); diff --git a/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj b/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj index c690f0385f..8765950f6d 100644 --- a/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj +++ b/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj @@ -8,12 +8,12 @@ - - - - - - + + + + + + @@ -22,10 +22,6 @@ - - - - diff --git a/src/Exceptionless.Job/Exceptionless.Job.csproj b/src/Exceptionless.Job/Exceptionless.Job.csproj index 62a6b1a3f2..e7295e6120 100644 --- a/src/Exceptionless.Job/Exceptionless.Job.csproj +++ b/src/Exceptionless.Job/Exceptionless.Job.csproj @@ -5,7 +5,6 @@ - @@ -23,10 +22,6 @@ - - - - diff --git a/src/Exceptionless.Web/Exceptionless.Web.csproj b/src/Exceptionless.Web/Exceptionless.Web.csproj index 15ae430b64..4e870c1ae8 100644 --- a/src/Exceptionless.Web/Exceptionless.Web.csproj +++ b/src/Exceptionless.Web/Exceptionless.Web.csproj @@ -16,7 +16,6 @@ - @@ -38,9 +37,6 @@ - - - diff --git a/src/Exceptionless.Web/Startup.cs b/src/Exceptionless.Web/Startup.cs index c20655cd01..5c85ade982 100644 --- a/src/Exceptionless.Web/Startup.cs +++ b/src/Exceptionless.Web/Startup.cs @@ -53,7 +53,7 @@ public void ConfigureServices(IServiceCollection services) { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.RequireHeaderSymmetry = false; - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); }); diff --git a/tests/Exceptionless.Tests/AppWebHostFactory.cs b/tests/Exceptionless.Tests/AppWebHostFactory.cs index 1c19b83822..8e60a7a067 100644 --- a/tests/Exceptionless.Tests/AppWebHostFactory.cs +++ b/tests/Exceptionless.Tests/AppWebHostFactory.cs @@ -43,7 +43,7 @@ protected override IHostBuilder CreateHostBuilder() .AddYamlFile("appsettings.yml", optional: false, reloadOnChange: false) .Build(); - return Program.CreateHostBuilder(config, Environments.Development); + return Exceptionless.Web.Program.CreateHostBuilder(config, Environments.Development); } async Task IAsyncLifetime.DisposeAsync() From f2f0f14afb64db9075d043b3ae615ebb7e7905cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:15:38 +0000 Subject: [PATCH 4/9] Add upgrade notes documentation Co-authored-by: ejsmith <282584+ejsmith@users.noreply.github.com> --- UPGRADE_NOTES.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 UPGRADE_NOTES.md diff --git a/UPGRADE_NOTES.md b/UPGRADE_NOTES.md new file mode 100644 index 0000000000..c74c146fbc --- /dev/null +++ b/UPGRADE_NOTES.md @@ -0,0 +1,101 @@ +# .NET 10 and Aspire 13 Upgrade Notes + +## Upgrade Summary + +Successfully upgraded from .NET 9 and Aspire 9.5 to .NET 10 and Aspire 13.0.0. + +## New Features Available + +### .NET 10 Features + +#### Performance Improvements +- **JIT Inlining Enhancements**: Improved JIT compiler inlining decisions for better runtime performance +- **Cryptography Performance**: Significant performance improvements in cryptographic operations +- **Runtime Optimizations**: Enhanced garbage collection and memory management + +#### Container Support +- **Streamlined Containerization**: Improved workflows for building and deploying containerized applications +- **Better Docker Integration**: Enhanced support for multi-stage builds and optimized images + +#### C# 14 Language Features +- **File-based Apps**: Support for single-file C# scripts that can be executed directly from the CLI +- **Enhanced Pattern Matching**: New pattern matching capabilities for cleaner code +- **Primary Constructors**: Simplified class constructor syntax + +#### API Improvements +- **Minimal APIs**: Enhanced minimal API support with better parameter binding +- **Native AOT**: Continued improvements to Native AOT compilation for faster startup and smaller deployments +- **OpenTelemetry**: Enhanced built-in telemetry and observability features + +### Aspire 13 Features + +#### Polyglot Application Platform +- **First-class JavaScript Support**: JavaScript apps are now first-class citizens alongside .NET and Python +- **AddJavaScriptApp API**: New unified API for orchestrating npm, yarn, and pnpm-based applications +- **Automatic Package Manager Detection**: Smart detection of the package manager used by JavaScript projects + +#### Enhanced Orchestration +- **Improved Vite Support**: Better hot reload, port mapping, and Dockerfile generation for Vite apps +- **Multi-stage Docker Publishing**: Optimized Dockerfile generation with Node version detection +- **Static Port Configuration**: Ability to specify static host ports for consistent callback URLs + +#### Eventing Model +- **New Eventing Infrastructure**: Modernized eventing system replacing lifecycle hooks +- **Resource-specific Events**: More granular events like `BeforeResourceStartedEvent` and `ResourceEndpointsAllocatedEvent` +- **Better Event Subscription**: Improved patterns for subscribing to application events + +#### Developer Experience +- **Enhanced Dashboard**: Improved Aspire dashboard for monitoring distributed applications +- **Better Debugging**: Enhanced debugging experience for distributed applications +- **Streamlined Local Development**: Improved local development experience with automatic port allocation + +## Breaking Changes Addressed + +### Aspire 13 Breaking Changes +1. **Package Rename**: `Aspire.Hosting.NodeJs` → `Aspire.Hosting.JavaScript` +2. **API Change**: `AddNpmApp` → `AddJavaScriptApp` with automatic package manager detection +3. **Lifecycle Hooks**: Old lifecycle hook system deprecated in favor of new eventing model +4. **Endpoint API**: `WithEndpoint` parameters changed to use `WithHttpEndpoint` + +### .NET 10 Breaking Changes +1. **ForwardedHeaders**: `KnownNetworks` property renamed to `KnownIPNetworks` +2. **Framework Packages**: Several packages (System.Net.Http, System.Text.RegularExpressions, System.Text.Encodings.Web) are now included in the framework and should be removed from project references +3. **HealthChecks**: Microsoft.Extensions.Diagnostics.HealthChecks is now included in ASP.NET Core and doesn't need explicit package reference + +## Recommendations for Future Work + +### Immediate Improvements +1. **Update KibanaConfigWriterHook**: Migrate to Aspire 13's new eventing model +2. **Leverage C# 14 Features**: Consider using primary constructors and enhanced pattern matching in new code +3. **OpenTelemetry Integration**: Take advantage of enhanced built-in telemetry features + +### Performance Optimizations +1. **Review Cryptography Usage**: Update crypto operations to leverage .NET 10 performance improvements +2. **Container Optimization**: Review Dockerfiles to take advantage of new multi-stage build optimizations +3. **JIT Compiler Benefits**: Profile hot paths to measure JIT inlining improvements + +### Developer Experience +1. **Aspire Dashboard**: Utilize the enhanced dashboard for better observability during development +2. **JavaScript Integration**: Consider migrating to AddJavaScriptApp pattern for better package manager support +3. **Static Ports**: Use static port configuration for better development consistency + +## Migration Notes + +### Completed +- ✅ All projects targeting net10.0 +- ✅ Aspire packages updated to 13.0.0 +- ✅ Microsoft.Extensions packages updated to 10.0.0 +- ✅ Docker images updated to .NET 10 +- ✅ GitHub Actions workflows updated to .NET 10 +- ✅ Deprecated APIs replaced + +### Pending +- ⏳ KibanaConfigWriterHook eventing model update (temporarily disabled with TODO) +- ⏳ Full testing of Aspire 13 JavaScript integration +- ⏳ Performance profiling to measure .NET 10 improvements + +## References +- [.NET 10 What's New](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview) +- [Aspire 13 What's New](https://aspire.dev/whats-new/aspire-13/) +- [Aspire 13 Breaking Changes](https://learn.microsoft.com/en-us/dotnet/aspire/compatibility/13.0/) +- [C# 14 Language Features](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14) From a66d92976ccf78d4020f29cbc2eb2b0de74fa281 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:29:02 +0000 Subject: [PATCH 5/9] Fix Kibana configuration, update VSCode paths, and remove upgrade notes Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .vscode/launch.json | 6 +- UPGRADE_NOTES.md | 101 ------------------ .../Extensions/ElasticsearchExtensions.cs | 6 +- .../Extensions/KibanaConfigWriterHook.cs | 42 ++++++++ 4 files changed, 48 insertions(+), 107 deletions(-) delete mode 100644 UPGRADE_NOTES.md create mode 100644 src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index dd21f34004..2253f36821 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Exceptionless.AppHost/bin/Debug/net9.0/Exceptionless.AppHost.dll", + "program": "${workspaceFolder}/src/Exceptionless.AppHost/bin/Debug/net10.0/Exceptionless.AppHost.dll", "args": [], "cwd": "${workspaceFolder}/src/Exceptionless.AppHost", "stopAtEntry": false, @@ -23,7 +23,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Exceptionless.Web/bin/Debug/net9.0/Exceptionless.Web.dll", + "program": "${workspaceFolder}/src/Exceptionless.Web/bin/Debug/net10.0/Exceptionless.Web.dll", "args": [], "cwd": "${workspaceFolder}/src/Exceptionless.Web", "stopAtEntry": false, @@ -40,7 +40,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Exceptionless.Job/bin/Debug/net9.0/Exceptionless.Job.dll", + "program": "${workspaceFolder}/src/Exceptionless.Job/bin/Debug/net10.0/Exceptionless.Job.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, diff --git a/UPGRADE_NOTES.md b/UPGRADE_NOTES.md deleted file mode 100644 index c74c146fbc..0000000000 --- a/UPGRADE_NOTES.md +++ /dev/null @@ -1,101 +0,0 @@ -# .NET 10 and Aspire 13 Upgrade Notes - -## Upgrade Summary - -Successfully upgraded from .NET 9 and Aspire 9.5 to .NET 10 and Aspire 13.0.0. - -## New Features Available - -### .NET 10 Features - -#### Performance Improvements -- **JIT Inlining Enhancements**: Improved JIT compiler inlining decisions for better runtime performance -- **Cryptography Performance**: Significant performance improvements in cryptographic operations -- **Runtime Optimizations**: Enhanced garbage collection and memory management - -#### Container Support -- **Streamlined Containerization**: Improved workflows for building and deploying containerized applications -- **Better Docker Integration**: Enhanced support for multi-stage builds and optimized images - -#### C# 14 Language Features -- **File-based Apps**: Support for single-file C# scripts that can be executed directly from the CLI -- **Enhanced Pattern Matching**: New pattern matching capabilities for cleaner code -- **Primary Constructors**: Simplified class constructor syntax - -#### API Improvements -- **Minimal APIs**: Enhanced minimal API support with better parameter binding -- **Native AOT**: Continued improvements to Native AOT compilation for faster startup and smaller deployments -- **OpenTelemetry**: Enhanced built-in telemetry and observability features - -### Aspire 13 Features - -#### Polyglot Application Platform -- **First-class JavaScript Support**: JavaScript apps are now first-class citizens alongside .NET and Python -- **AddJavaScriptApp API**: New unified API for orchestrating npm, yarn, and pnpm-based applications -- **Automatic Package Manager Detection**: Smart detection of the package manager used by JavaScript projects - -#### Enhanced Orchestration -- **Improved Vite Support**: Better hot reload, port mapping, and Dockerfile generation for Vite apps -- **Multi-stage Docker Publishing**: Optimized Dockerfile generation with Node version detection -- **Static Port Configuration**: Ability to specify static host ports for consistent callback URLs - -#### Eventing Model -- **New Eventing Infrastructure**: Modernized eventing system replacing lifecycle hooks -- **Resource-specific Events**: More granular events like `BeforeResourceStartedEvent` and `ResourceEndpointsAllocatedEvent` -- **Better Event Subscription**: Improved patterns for subscribing to application events - -#### Developer Experience -- **Enhanced Dashboard**: Improved Aspire dashboard for monitoring distributed applications -- **Better Debugging**: Enhanced debugging experience for distributed applications -- **Streamlined Local Development**: Improved local development experience with automatic port allocation - -## Breaking Changes Addressed - -### Aspire 13 Breaking Changes -1. **Package Rename**: `Aspire.Hosting.NodeJs` → `Aspire.Hosting.JavaScript` -2. **API Change**: `AddNpmApp` → `AddJavaScriptApp` with automatic package manager detection -3. **Lifecycle Hooks**: Old lifecycle hook system deprecated in favor of new eventing model -4. **Endpoint API**: `WithEndpoint` parameters changed to use `WithHttpEndpoint` - -### .NET 10 Breaking Changes -1. **ForwardedHeaders**: `KnownNetworks` property renamed to `KnownIPNetworks` -2. **Framework Packages**: Several packages (System.Net.Http, System.Text.RegularExpressions, System.Text.Encodings.Web) are now included in the framework and should be removed from project references -3. **HealthChecks**: Microsoft.Extensions.Diagnostics.HealthChecks is now included in ASP.NET Core and doesn't need explicit package reference - -## Recommendations for Future Work - -### Immediate Improvements -1. **Update KibanaConfigWriterHook**: Migrate to Aspire 13's new eventing model -2. **Leverage C# 14 Features**: Consider using primary constructors and enhanced pattern matching in new code -3. **OpenTelemetry Integration**: Take advantage of enhanced built-in telemetry features - -### Performance Optimizations -1. **Review Cryptography Usage**: Update crypto operations to leverage .NET 10 performance improvements -2. **Container Optimization**: Review Dockerfiles to take advantage of new multi-stage build optimizations -3. **JIT Compiler Benefits**: Profile hot paths to measure JIT inlining improvements - -### Developer Experience -1. **Aspire Dashboard**: Utilize the enhanced dashboard for better observability during development -2. **JavaScript Integration**: Consider migrating to AddJavaScriptApp pattern for better package manager support -3. **Static Ports**: Use static port configuration for better development consistency - -## Migration Notes - -### Completed -- ✅ All projects targeting net10.0 -- ✅ Aspire packages updated to 13.0.0 -- ✅ Microsoft.Extensions packages updated to 10.0.0 -- ✅ Docker images updated to .NET 10 -- ✅ GitHub Actions workflows updated to .NET 10 -- ✅ Deprecated APIs replaced - -### Pending -- ⏳ KibanaConfigWriterHook eventing model update (temporarily disabled with TODO) -- ⏳ Full testing of Aspire 13 JavaScript integration -- ⏳ Performance profiling to measure .NET 10 improvements - -## References -- [.NET 10 What's New](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10/overview) -- [Aspire 13 What's New](https://aspire.dev/whats-new/aspire-13/) -- [Aspire 13 Breaking Changes](https://learn.microsoft.com/en-us/dotnet/aspire/compatibility/13.0/) -- [C# 14 Language Features](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14) diff --git a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs index fb5bab6647..16f1f54ed5 100644 --- a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs +++ b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs @@ -80,8 +80,7 @@ public static IResourceBuilder WithKibana(this IResourceB { containerName ??= $"{builder.Resource.Name}-kibana"; - // TODO: Re-enable Kibana config writer hook after updating to Aspire 13 eventing model - // builder.ApplicationBuilder.Services.AddHostedService(); + var elasticsearchResources = builder.ApplicationBuilder.Resources.OfType(); var resource = new KibanaResource(containerName); var resourceBuilder = builder.ApplicationBuilder.AddResource(resource) @@ -90,7 +89,8 @@ public static IResourceBuilder WithKibana(this IResourceB .WithHttpEndpoint(targetPort: KibanaPort, name: containerName) .WithUrlForEndpoint(containerName, u => u.DisplayText = "Kibana") .WithEnvironment("xpack.security.enabled", "false") - .ExcludeFromManifest(); + .ExcludeFromManifest() + .ConfigureElasticsearchHosts(elasticsearchResources); configureContainer?.Invoke(resourceBuilder); diff --git a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs new file mode 100644 index 0000000000..37f7e751c9 --- /dev/null +++ b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs @@ -0,0 +1,42 @@ +using System.Text; +using Aspire.Hosting.ApplicationModel; + +namespace Aspire.Hosting; + +internal static class KibanaConfigWriterExtensions +{ + public static IResourceBuilder ConfigureElasticsearchHosts( + this IResourceBuilder builder, + IEnumerable elasticsearchResources) + { + builder.WithAnnotation(new EnvironmentCallbackAnnotation(async context => + { + var hostsVariableBuilder = new StringBuilder(); + + foreach (var elasticsearchInstance in elasticsearchResources) + { + if (elasticsearchInstance.PrimaryEndpoint.IsAllocated) + { + if (hostsVariableBuilder.Length > 0) + hostsVariableBuilder.Append(","); + + var endpoint = elasticsearchInstance.PrimaryEndpoint; + hostsVariableBuilder.Append(endpoint.Scheme) + .Append("://") + .Append(endpoint.Host) + .Append(":") + .Append(endpoint.Port); + } + } + + if (hostsVariableBuilder.Length > 0) + { + context.EnvironmentVariables["ELASTICSEARCH_HOSTS"] = hostsVariableBuilder.ToString(); + } + + await Task.CompletedTask; + })); + + return builder; + } +} From c19bb00f72f0ea0f423e6772b7d65b7b2da750a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:09:28 +0000 Subject: [PATCH 6/9] Fix Aspire SDK version and add TODO for Kibana lifecycle hook migration Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../Exceptionless.AppHost.csproj | 2 +- .../Extensions/ElasticsearchExtensions.cs | 8 +-- .../Extensions/KibanaConfigWriterHook.cs | 52 +++++++++---------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj index c06307f1fa..4b6abfe247 100644 --- a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj +++ b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj @@ -1,5 +1,5 @@ - + Exe net10.0 diff --git a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs index 16f1f54ed5..74f7d7352a 100644 --- a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs +++ b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs @@ -80,7 +80,10 @@ public static IResourceBuilder WithKibana(this IResourceB { containerName ??= $"{builder.Resource.Name}-kibana"; - var elasticsearchResources = builder.ApplicationBuilder.Resources.OfType(); + // TODO: Update KibanaConfigWriterHook to use Aspire 13 eventing model (IDistributedApplicationEventingSubscriber) + // The IDistributedApplicationLifecycleHook interface is obsolete in Aspire 13. + // See: https://learn.microsoft.com/en-us/dotnet/aspire/compatibility/13.0/ + // builder.ApplicationBuilder.Services.TryAddLifecycleHook(); var resource = new KibanaResource(containerName); var resourceBuilder = builder.ApplicationBuilder.AddResource(resource) @@ -89,8 +92,7 @@ public static IResourceBuilder WithKibana(this IResourceB .WithHttpEndpoint(targetPort: KibanaPort, name: containerName) .WithUrlForEndpoint(containerName, u => u.DisplayText = "Kibana") .WithEnvironment("xpack.security.enabled", "false") - .ExcludeFromManifest() - .ConfigureElasticsearchHosts(elasticsearchResources); + .ExcludeFromManifest(); configureContainer?.Invoke(resourceBuilder); diff --git a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs index 37f7e751c9..c06f3bcb88 100644 --- a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs +++ b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs @@ -1,42 +1,38 @@ +#if FALSE // TODO: Re-enable and update to Aspire 13 eventing model (IDistributedApplicationEventingSubscriber) using System.Text; -using Aspire.Hosting.ApplicationModel; +using Aspire.Hosting.Lifecycle; namespace Aspire.Hosting; -internal static class KibanaConfigWriterExtensions +internal class KibanaConfigWriterHook : IDistributedApplicationLifecycleHook { - public static IResourceBuilder ConfigureElasticsearchHosts( - this IResourceBuilder builder, - IEnumerable elasticsearchResources) + public async Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken) { - builder.WithAnnotation(new EnvironmentCallbackAnnotation(async context => - { - var hostsVariableBuilder = new StringBuilder(); + if (appModel.Resources.OfType().SingleOrDefault() is not { } kibanaResource) + return; - foreach (var elasticsearchInstance in elasticsearchResources) - { - if (elasticsearchInstance.PrimaryEndpoint.IsAllocated) - { - if (hostsVariableBuilder.Length > 0) - hostsVariableBuilder.Append(","); - - var endpoint = elasticsearchInstance.PrimaryEndpoint; - hostsVariableBuilder.Append(endpoint.Scheme) - .Append("://") - .Append(endpoint.Host) - .Append(":") - .Append(endpoint.Port); - } - } + var elasticsearchInstances = appModel.Resources.OfType(); + + if (!elasticsearchInstances.Any()) + return; - if (hostsVariableBuilder.Length > 0) + var hostsVariableBuilder = new StringBuilder(); + + foreach (var elasticsearchInstance in elasticsearchInstances) + { + if (elasticsearchInstance.PrimaryEndpoint.IsAllocated) { - context.EnvironmentVariables["ELASTICSEARCH_HOSTS"] = hostsVariableBuilder.ToString(); + var connectionString = await elasticsearchInstance.GetConnectionStringAsync(); + if (hostsVariableBuilder.Length > 0) + hostsVariableBuilder.Append(","); + hostsVariableBuilder.Append(elasticsearchInstance.PrimaryEndpoint.Scheme).Append("://").Append(elasticsearchInstance.PrimaryEndpoint.Host).Append(":").Append(elasticsearchInstance.PrimaryEndpoint.Port); } + } - await Task.CompletedTask; + kibanaResource.Annotations.Add(new EnvironmentCallbackAnnotation(context => + { + context.EnvironmentVariables.Add("ELASTICSEARCH_HOSTS", hostsVariableBuilder.ToString()); })); - - return builder; } } +#endif From 51cc6ca491d7f82839df70e58a48a2addf1d58ab Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Thu, 13 Nov 2025 20:00:12 +1300 Subject: [PATCH 7/9] PR Feedback --- .vscode/extensions.json | 3 +- .vscode/launch.json | 225 +++++++++--------- .../Exceptionless.AppHost.csproj | 6 +- .../Extensions/ElasticsearchExtensions.cs | 5 +- .../Extensions/KibanaConfigWriterHook.cs | 60 +++-- src/Exceptionless.AppHost/Program.cs | 12 +- .../Exceptionless.Tests/AppWebHostFactory.cs | 2 +- 7 files changed, 156 insertions(+), 157 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c30d935b28..a8d2e79ec3 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,11 +4,12 @@ "streetsidesoftware.code-spell-checker", "tintoy.msbuild-project-tools", "humao.rest-client", - "ms-kubernetes-tools.vscode-kubernetes-tools", "svelte.svelte-vscode", "bradlc.vscode-tailwindcss", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", + "microsoft-aspire.aspire-vscode", + "ms-kubernetes-tools.vscode-kubernetes-tools", "ms-playwright.playwright", "selemondev.vscode-shadcn-svelte", "vitest.explorer" diff --git a/.vscode/launch.json b/.vscode/launch.json index 2253f36821..90a926b9f8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,120 +1,109 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Aspire", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Exceptionless.AppHost/bin/Debug/net10.0/Exceptionless.AppHost.dll", - "args": [], - "cwd": "${workspaceFolder}/src/Exceptionless.AppHost", - "stopAtEntry": false, - "serverReadyAction": { - "action": "openExternally", - "pattern": "\\bNow listening on:\\s+(https?://\\S+)" - }, - "env": { - "AppMode": "Development" - } - }, - { - "name": "Web", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Exceptionless.Web/bin/Debug/net10.0/Exceptionless.Web.dll", - "args": [], - "cwd": "${workspaceFolder}/src/Exceptionless.Web", - "stopAtEntry": false, - "serverReadyAction": { - "action": "openExternally", - "pattern": "\\bNow listening on:\\s+(https?://\\S+)" - }, - "env": { - "AppMode": "Development" - } - }, - { - "name": "Job", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/src/Exceptionless.Job/bin/Debug/net10.0/Exceptionless.Job.dll", - "args": [], - "cwd": "${workspaceFolder}", - "stopAtEntry": false, - "console": "internalConsole", - "env": { - "AppMode": "Development" - } - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - }, - { - "name": "frontend: Attach to Chrome", - "port": 9222, - "request": "attach", - "type": "chrome", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp" - }, - { - "name": "frontend: Attach to Edge", - "port": 9222, - "request": "attach", - "type": "msedge", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp" - }, - { - "type": "msedge", - "request": "launch", - "name": "frontend: Web (Edge)", - "url": "http://localhost:5173/next", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", - "preLaunchTask": "npm run dev" - }, - { - "type": "msedge", - "request": "launch", - "name": "frontend: Web Dev Api (Edge)", - "url": "http://localhost:5173/next", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", - "preLaunchTask": "npm run dev:api" - }, - { - "type": "msedge", - "request": "launch", - "name": "frontend: Storybook (Edge)", - "url": "http://localhost:6006", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", - "preLaunchTask": "npm run storybook" - }, - { - "type": "chrome", - "request": "launch", - "name": "frontend: Web (Chrome)", - "url": "http://localhost:5173/next", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", - "preLaunchTask": "npm run dev" - }, - { - "type": "chrome", - "request": "launch", - "name": "frontend: Web Dev Api (Chrome)", - "url": "http://localhost:5173/next", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", - "preLaunchTask": "npm run dev:api" - }, - { - "type": "chrome", - "request": "launch", - "name": "frontend: Storybook (Chrome)", - "url": "http://localhost:6006", - "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", - "preLaunchTask": "npm run storybook" - } - ] + "version": "0.2.0", + "configurations": [ + { + "type": "aspire", + "request": "launch", + "name": "Aspire", + "program": "${workspaceFolder}/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj" + }, + { + "name": "Web", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/Exceptionless.Web/bin/Debug/net10.0/Exceptionless.Web.dll", + "args": [], + "cwd": "${workspaceFolder}/src/Exceptionless.Web", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "AppMode": "Development" + } + }, + { + "name": "Job", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/Exceptionless.Job/bin/Debug/net10.0/Exceptionless.Job.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "console": "internalConsole", + "env": { + "AppMode": "Development" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + }, + { + "name": "frontend: Attach to Chrome", + "port": 9222, + "request": "attach", + "type": "chrome", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp" + }, + { + "name": "frontend: Attach to Edge", + "port": 9222, + "request": "attach", + "type": "msedge", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp" + }, + { + "type": "msedge", + "request": "launch", + "name": "frontend: Web (Edge)", + "url": "http://localhost:5173/next", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", + "preLaunchTask": "npm run dev" + }, + { + "type": "msedge", + "request": "launch", + "name": "frontend: Web Dev Api (Edge)", + "url": "http://localhost:5173/next", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", + "preLaunchTask": "npm run dev:api" + }, + { + "type": "msedge", + "request": "launch", + "name": "frontend: Storybook (Edge)", + "url": "http://localhost:6006", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", + "preLaunchTask": "npm run storybook" + }, + { + "type": "chrome", + "request": "launch", + "name": "frontend: Web (Chrome)", + "url": "http://localhost:5173/next", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", + "preLaunchTask": "npm run dev" + }, + { + "type": "chrome", + "request": "launch", + "name": "frontend: Web Dev Api (Chrome)", + "url": "http://localhost:5173/next", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", + "preLaunchTask": "npm run dev:api" + }, + { + "type": "chrome", + "request": "launch", + "name": "frontend: Storybook (Chrome)", + "url": "http://localhost:6006", + "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", + "preLaunchTask": "npm run storybook" + } + ] } diff --git a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj index 4b6abfe247..e3f7cfe7e8 100644 --- a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj +++ b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj @@ -1,5 +1,4 @@ - - + Exe net10.0 @@ -8,7 +7,6 @@ a9c2ddcc-e51d-4cd1-9782-96e1d74eec87 - @@ -20,4 +18,4 @@ - \ No newline at end of file + diff --git a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs index 74f7d7352a..59aa12a2a7 100644 --- a/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs +++ b/src/Exceptionless.AppHost/Extensions/ElasticsearchExtensions.cs @@ -80,10 +80,7 @@ public static IResourceBuilder WithKibana(this IResourceB { containerName ??= $"{builder.Resource.Name}-kibana"; - // TODO: Update KibanaConfigWriterHook to use Aspire 13 eventing model (IDistributedApplicationEventingSubscriber) - // The IDistributedApplicationLifecycleHook interface is obsolete in Aspire 13. - // See: https://learn.microsoft.com/en-us/dotnet/aspire/compatibility/13.0/ - // builder.ApplicationBuilder.Services.TryAddLifecycleHook(); + builder.ApplicationBuilder.Services.TryAddEventingSubscriber(); var resource = new KibanaResource(containerName); var resourceBuilder = builder.ApplicationBuilder.AddResource(resource) diff --git a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs index c06f3bcb88..005504ad74 100644 --- a/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs +++ b/src/Exceptionless.AppHost/Extensions/KibanaConfigWriterHook.cs @@ -1,38 +1,52 @@ -#if FALSE // TODO: Re-enable and update to Aspire 13 eventing model (IDistributedApplicationEventingSubscriber) -using System.Text; +using System.Text; +using Aspire.Hosting.Eventing; using Aspire.Hosting.Lifecycle; namespace Aspire.Hosting; -internal class KibanaConfigWriterHook : IDistributedApplicationLifecycleHook +internal class KibanaConfigWriterHook : IDistributedApplicationEventingSubscriber { - public async Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken) + public Task SubscribeAsync(IDistributedApplicationEventing eventing, DistributedApplicationExecutionContext executionContext, CancellationToken cancellationToken) { - if (appModel.Resources.OfType().SingleOrDefault() is not { } kibanaResource) - return; + var elasticsearchResources = new List(); - var elasticsearchInstances = appModel.Resources.OfType(); - - if (!elasticsearchInstances.Any()) - return; + eventing.Subscribe((evt, _) => + { + switch (evt.Resource) + { + case ElasticsearchResource elastic: + elasticsearchResources.Add(elastic); + break; + } - var hostsVariableBuilder = new StringBuilder(); + return Task.CompletedTask; + }); - foreach (var elasticsearchInstance in elasticsearchInstances) + eventing.Subscribe((evt, _) => { - if (elasticsearchInstance.PrimaryEndpoint.IsAllocated) + if (evt.Resource is not KibanaResource kibanaResource) + return Task.CompletedTask; + + if (elasticsearchResources.Count is 0) + return Task.CompletedTask; + + var sb = new StringBuilder(); + foreach (var resource in elasticsearchResources.Where(elasticsearchInstance => elasticsearchInstance.PrimaryEndpoint.IsAllocated)) { - var connectionString = await elasticsearchInstance.GetConnectionStringAsync(); - if (hostsVariableBuilder.Length > 0) - hostsVariableBuilder.Append(","); - hostsVariableBuilder.Append(elasticsearchInstance.PrimaryEndpoint.Scheme).Append("://").Append(elasticsearchInstance.PrimaryEndpoint.Host).Append(":").Append(elasticsearchInstance.PrimaryEndpoint.Port); + if (sb.Length > 0) + sb.Append(','); + + sb.Append($"{resource.PrimaryEndpoint.Scheme}://{resource.PrimaryEndpoint.Host}:{resource.PrimaryEndpoint.Port}"); } - } - kibanaResource.Annotations.Add(new EnvironmentCallbackAnnotation(context => - { - context.EnvironmentVariables.Add("ELASTICSEARCH_HOSTS", hostsVariableBuilder.ToString()); - })); + kibanaResource.Annotations.Add(new EnvironmentCallbackAnnotation(context => + { + context.EnvironmentVariables.Add("ELASTICSEARCH_HOSTS", sb.ToString()); + })); + + return Task.CompletedTask; + }); + + return Task.CompletedTask; } } -#endif diff --git a/src/Exceptionless.AppHost/Program.cs b/src/Exceptionless.AppHost/Program.cs index d5fa011c4d..e12fed4043 100644 --- a/src/Exceptionless.AppHost/Program.cs +++ b/src/Exceptionless.AppHost/Program.cs @@ -21,7 +21,7 @@ .WithImageTag("v1.27.10") .WithLifetime(ContainerLifetime.Persistent) .WithContainerName("Exceptionless-Mail") - .WithEndpoint(8025, 8025, "http") + .WithHttpEndpoint(8025, 8025, "http") .WithUrlForEndpoint("http", u => u.DisplayText = "Mail") .WithEndpoint(1025, 1025); @@ -48,16 +48,16 @@ .WithUrlForEndpoint("http", u => u.DisplayText = "Api") .WithHttpHealthCheck("/health"); -builder.AddJavaScriptApp("Web", "../../src/Exceptionless.Web/ClientApp", "dev") +builder.AddViteApp("Web", "../../src/Exceptionless.Web/ClientApp") .WithReference(api) .WithEnvironment("ASPNETCORE_URLS", "http://localhost:5200") - .WithUrlForEndpoint("http", u => u.DisplayText = "Web") - .WithHttpEndpoint(port: 5173, env: "PORT", isProxied: false); + .WithUrlForEndpoint("Web", u => u.DisplayText = "Web") + .WithHttpEndpoint(port: 5173, targetPort: 5173, name: "Web", env: "PORT", isProxied: false); builder.AddJavaScriptApp("AngularWeb", "../../src/Exceptionless.Web/ClientApp.angular", "serve") .WithReference(api) .WithEnvironment("ASPNETCORE_URLS", "http://localhost:5200") - .WithUrlForEndpoint("http", u => u.DisplayText = "Angular Web") - .WithHttpEndpoint(port: 5100, env: "PORT", isProxied: false); + .WithUrlForEndpoint("AngularWeb", u => u.DisplayText = "Angular Web") + .WithHttpEndpoint(port: 5100, targetPort: 5100, name: "AngularWeb", env: "PORT", isProxied: false); builder.Build().Run(); diff --git a/tests/Exceptionless.Tests/AppWebHostFactory.cs b/tests/Exceptionless.Tests/AppWebHostFactory.cs index 8e60a7a067..dda75b7c8e 100644 --- a/tests/Exceptionless.Tests/AppWebHostFactory.cs +++ b/tests/Exceptionless.Tests/AppWebHostFactory.cs @@ -43,7 +43,7 @@ protected override IHostBuilder CreateHostBuilder() .AddYamlFile("appsettings.yml", optional: false, reloadOnChange: false) .Build(); - return Exceptionless.Web.Program.CreateHostBuilder(config, Environments.Development); + return Web.Program.CreateHostBuilder(config, Environments.Development); } async Task IAsyncLifetime.DisposeAsync() From 26cab69e1ceb6858b32bcb031027dcd71433e2a9 Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Thu, 13 Nov 2025 20:30:57 +1300 Subject: [PATCH 8/9] Updates package dependencies Updates various package dependencies across multiple projects. This includes updates to FluentValidation, Serilog.Sinks.Console, OpenTelemetry, Swashbuckle.AspNetCore.Newtonsoft, FluentRest.NewtonsoftJson, and Microsoft.NET.Test.Sdk. Also adds System.Net.Http and System.Text.RegularExpressions to resolve vulnerability warnings related to transitive dependencies. --- src/Exceptionless.Core/Exceptionless.Core.csproj | 2 +- .../Exceptionless.Insulation.csproj | 6 +++++- src/Exceptionless.Job/Exceptionless.Job.csproj | 12 ++++++------ src/Exceptionless.Web/Exceptionless.Web.csproj | 14 +++++++------- .../Exceptionless.Tests/Exceptionless.Tests.csproj | 4 ++-- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Exceptionless.Core/Exceptionless.Core.csproj b/src/Exceptionless.Core/Exceptionless.Core.csproj index 1ac3f03180..e42893e952 100644 --- a/src/Exceptionless.Core/Exceptionless.Core.csproj +++ b/src/Exceptionless.Core/Exceptionless.Core.csproj @@ -22,7 +22,7 @@ - + diff --git a/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj b/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj index 8765950f6d..9ee1cf456e 100644 --- a/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj +++ b/src/Exceptionless.Insulation/Exceptionless.Insulation.csproj @@ -18,10 +18,14 @@ - + + + + + diff --git a/src/Exceptionless.Job/Exceptionless.Job.csproj b/src/Exceptionless.Job/Exceptionless.Job.csproj index e7295e6120..244ad58392 100644 --- a/src/Exceptionless.Job/Exceptionless.Job.csproj +++ b/src/Exceptionless.Job/Exceptionless.Job.csproj @@ -10,12 +10,12 @@ - - - - - - + + + + + + diff --git a/src/Exceptionless.Web/Exceptionless.Web.csproj b/src/Exceptionless.Web/Exceptionless.Web.csproj index 4e870c1ae8..bce5f5c8ea 100644 --- a/src/Exceptionless.Web/Exceptionless.Web.csproj +++ b/src/Exceptionless.Web/Exceptionless.Web.csproj @@ -24,17 +24,17 @@ - - - - - - + + + + + + - + diff --git a/tests/Exceptionless.Tests/Exceptionless.Tests.csproj b/tests/Exceptionless.Tests/Exceptionless.Tests.csproj index 6c2d69c748..0305fe17d8 100644 --- a/tests/Exceptionless.Tests/Exceptionless.Tests.csproj +++ b/tests/Exceptionless.Tests/Exceptionless.Tests.csproj @@ -7,12 +7,12 @@ - + - + From 8c68fbdb9f220a81d38ffe2094db1c3e3d99367e Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Thu, 13 Nov 2025 20:50:48 +1300 Subject: [PATCH 9/9] WIP - Upgrade swashbuckle --- .../Exceptionless.Web.csproj | 2 +- src/Exceptionless.Web/Startup.cs | 30 +++++-------------- .../Utility/RequestBodyOperationFilter.cs | 19 ++++++++---- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/Exceptionless.Web/Exceptionless.Web.csproj b/src/Exceptionless.Web/Exceptionless.Web.csproj index bce5f5c8ea..af79fd1463 100644 --- a/src/Exceptionless.Web/Exceptionless.Web.csproj +++ b/src/Exceptionless.Web/Exceptionless.Web.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Exceptionless.Web/Startup.cs b/src/Exceptionless.Web/Startup.cs index 5c85ade982..41885463eb 100644 --- a/src/Exceptionless.Web/Startup.cs +++ b/src/Exceptionless.Web/Startup.cs @@ -22,7 +22,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.NewtonsoftJson; using Microsoft.Net.Http.Headers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json; using Serilog; using Serilog.Events; @@ -135,26 +135,12 @@ public void ConfigureServices(IServiceCollection services) Type = SecuritySchemeType.ApiKey }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement { - { - new OpenApiSecurityScheme { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Basic" } - }, - Array.Empty() - }, - { - new OpenApiSecurityScheme { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } - }, - Array.Empty() - }, - { - new OpenApiSecurityScheme { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Token" } - }, - Array.Empty() - } - }); + c.AddSecurityRequirement(document => new OpenApiSecurityRequirement + { + { new OpenApiSecuritySchemeReference("Basic", document), [] }, + { new OpenApiSecuritySchemeReference("Bearer", document), [] }, + { new OpenApiSecuritySchemeReference("Token", document), [] } + }); string xmlDocPath = Path.Combine(AppContext.BaseDirectory, "Exceptionless.Web.xml"); if (File.Exists(xmlDocPath)) @@ -236,7 +222,7 @@ ApplicationException applicationException when applicationException.Message.Cont Predicate = hcr => hcr.Tags.Contains("Critical") || (options.RunJobsInProcess && hcr.Tags.Contains("AllJobs")) }); - var readyTags = new List { "Critical" }; + List readyTags = ["Critical"]; if (!options.EventSubmissionDisabled) readyTags.Add("Storage"); app.UseReadyHealthChecks(readyTags.ToArray()); diff --git a/src/Exceptionless.Web/Utility/RequestBodyOperationFilter.cs b/src/Exceptionless.Web/Utility/RequestBodyOperationFilter.cs index 1c73e36bb9..3298480620 100644 --- a/src/Exceptionless.Web/Utility/RequestBodyOperationFilter.cs +++ b/src/Exceptionless.Web/Utility/RequestBodyOperationFilter.cs @@ -1,9 +1,11 @@ +using System.Text.Json.Nodes; using Microsoft.AspNetCore.Mvc; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; -public class RequestBodyContentAttribute : Attribute { } +public class RequestBodyContentAttribute : Attribute +{ +} public class RequestBodyOperationFilter : IOperationFilter { @@ -17,12 +19,17 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) if (consumesAttribute is null) return; - operation.RequestBody = new OpenApiRequestBody { Required = true }; + operation.RequestBody = new OpenApiRequestBody + { + Required = true, + Content = new Dictionary() + }; + foreach (string contentType in consumesAttribute.ContentTypes) { - operation.RequestBody.Content.Add(contentType, new OpenApiMediaType + operation.RequestBody.Content!.Add(contentType, new OpenApiMediaType { - Schema = new OpenApiSchema { Type = "string", Example = new OpenApiString(String.Empty) } + Schema = new OpenApiSchema { Type = JsonSchemaType.String, Example = JsonValue.Create(String.Empty) } }); } }