Merge pull request #14 from StevanFreeborn/stevanfreeborn/feat/13-upgrade-to-dotnet-10

build: upgrade to .NET 10 and update dependencies
This commit is contained in:
Stevan Freeborn
2026-01-11 18:54:14 -06:00
committed by GitHub
10 changed files with 65 additions and 59 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ jobs:
version: ${{ steps.version.outputs.version }} version: ${{ steps.version.outputs.version }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
@@ -37,7 +37,7 @@ jobs:
needs: build needs: build
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Copy compose file to server - name: Copy compose file to server
uses: appleboy/scp-action@v1 uses: appleboy/scp-action@v1
with: with:
+9 -9
View File
@@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup .NET 9 - name: Setup .NET 10
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v5
with: with:
dotnet-version: 9.0.x dotnet-version: 10.0.x
- name: Format code - name: Format code
run: dotnet format src --verify-no-changes --verbosity diagnostic run: dotnet format src --verify-no-changes --verbosity diagnostic
test: test:
@@ -24,11 +24,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup .NET 9 - name: Setup .NET 10
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v5
with: with:
dotnet-version: 9.0.x dotnet-version: 10.0.x
- name: Install report generator - name: Install report generator
run: dotnet tool install -g dotnet-reportgenerator-globaltool run: dotnet tool install -g dotnet-reportgenerator-globaltool
- name: Run tests - name: Run tests
@@ -36,7 +36,7 @@ jobs:
- name: Merge coverage reports - name: Merge coverage reports
run: reportgenerator "-reports:src/tests/*/TestResults/*/coverage.cobertura.xml" "-reporttypes:Html;Cobertura" "-targetdir:src/tests/TestResults/Coverage" run: reportgenerator "-reports:src/tests/*/TestResults/*/coverage.cobertura.xml" "-reporttypes:Html;Cobertura" "-targetdir:src/tests/TestResults/Coverage"
- name: Upload code coverage - name: Upload code coverage
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: coverage-reports name: coverage-reports
path: src/tests/TestResults/Coverage path: src/tests/TestResults/Coverage
+13 -13
View File
@@ -1,6 +1,6 @@
# Steve's Bot 🤖 # Steve's Bot 🤖
This is a comprehensive Discord bot platform built with .NET 9, designed to be a full-featured and extensible solution for having my own assistant in my [Discord server](https://discord.stevanfreeborn.com). This is a comprehensive Discord bot platform built with .NET 10, designed to be a full-featured and extensible solution for having my own assistant in my [Discord server](https://discord.stevanfreeborn.com).
## ✨ Features ## ✨ Features
@@ -32,7 +32,7 @@ This is a comprehensive Discord bot platform built with .NET 9, designed to be a
### Prerequisites ### Prerequisites
- [.NET 9 SDK](https://dotnet.microsoft.com/download) - [.NET 10 SDK](https://dotnet.microsoft.com/download)
- Discord Bot Token (from [Discord Developer Portal](https://discord.com/developers/applications)) - Discord Bot Token (from [Discord Developer Portal](https://discord.com/developers/applications))
- For YouTube integration: YouTube Data API v3 key from [Google Cloud Console](https://console.cloud.google.com/apis/credentials) - For YouTube integration: YouTube Data API v3 key from [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
@@ -150,14 +150,14 @@ src/
### YouTube Webhook Options ### YouTube Webhook Options
| Setting | Description | Required | | Setting | Description | Required |
|----------------------------------------|---------------------------------------|----------| |-----------------------------------------|-----------------------------------|----------|
| `YouTubeClientOptions__BaseUrl` | YouTube Data API base URL | Yes | | `YouTubeClientOptions__BaseUrl` | YouTube Data API base URL | Yes |
| `YouTubeClientOptions__ApiKey` | YouTube Data API v3 key | Yes | | `YouTubeClientOptions__ApiKey` | YouTube Data API v3 key | Yes |
| `SubscriptionOptions__CallbackUrl` | Webhook callback URL | Yes | | `SubscriptionOptions__CallbackUrl` | Webhook callback URL | Yes |
| `SubscriptionOptions__TopicUrl` | YouTube channel topic URL | Yes | | `SubscriptionOptions__TopicUrl` | YouTube channel topic URL | Yes |
| `PubSubClientOptions__BaseUrl` | PubSubHubbub hub URL | Yes | | `PubSubClientOptions__BaseUrl` | PubSubHubbub hub URL | Yes |
| `DiscordNotificationOptions__ChannelId` | Discord channel for notifications | Yes | | `DiscordNotificationOptions__ChannelId` | Discord channel for notifications | Yes |
### Telemetry Options ### Telemetry Options
@@ -226,11 +226,11 @@ The project includes a multi-stage Dockerfile for optimized production builds:
```dockerfile ```dockerfile
# Build stage with .NET SDK # Build stage with .NET SDK
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS base FROM mcr.microsoft.com/dotnet/sdk:10.0 AS base
# ... build steps ... # ... build steps ...
# Runtime stage with optimized ASP.NET runtime # Runtime stage with optimized ASP.NET runtime
FROM mcr.microsoft.com/dotnet/aspnet:9.0 FROM mcr.microsoft.com/dotnet/aspnet:10.0
# ... runtime setup ... # ... runtime setup ...
``` ```
@@ -266,6 +266,6 @@ This project is licensed under the terms found in the [LICENSE.md](LICENSE.md) f
--- ---
Built with ❤️ using .NET 9 and a lot of coffee ☕ Built with ❤️ using .NET 10 and a lot of coffee ☕
*Steve's Bot - Helping Stevan and friends since 2025* 🚀 *Steve's Bot - Helping Stevan and friends since 2025* 🚀
+1 -1
View File
@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AnalysisLevel>latest</AnalysisLevel> <AnalysisLevel>latest</AnalysisLevel>
+2 -2
View File
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS base FROM mcr.microsoft.com/dotnet/sdk:10.0 AS base
WORKDIR /app WORKDIR /app
# solution level # solution level
@@ -29,7 +29,7 @@ COPY . .
FROM base AS publish-stage FROM base AS publish-stage
RUN dotnet publish -c Release -o dist src/StevesBot.Webhook/StevesBot.Webhook.csproj RUN dotnet publish -c Release -o dist src/StevesBot.Webhook/StevesBot.Webhook.csproj
FROM mcr.microsoft.com/dotnet/aspnet:9.0 FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app WORKDIR /app
COPY --from=publish-stage /app/dist ./ COPY --from=publish-stage /app/dist ./
ENTRYPOINT ["dotnet", "StevesBot.Webhook.dll"] ENTRYPOINT ["dotnet", "StevesBot.Webhook.dll"]
+2 -2
View File
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS base FROM mcr.microsoft.com/dotnet/sdk:10.0 AS base
WORKDIR /app WORKDIR /app
# solution level # solution level
@@ -29,7 +29,7 @@ COPY . .
FROM base AS publish-stage FROM base AS publish-stage
RUN dotnet publish -c Release -o dist src/StevesBot.Worker/StevesBot.Worker.csproj RUN dotnet publish -c Release -o dist src/StevesBot.Worker/StevesBot.Worker.csproj
FROM mcr.microsoft.com/dotnet/aspnet:9.0 FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app WORKDIR /app
COPY --from=publish-stage /app/dist ./ COPY --from=publish-stage /app/dist ./
ENTRYPOINT ["dotnet", "StevesBot.Worker.dll"] ENTRYPOINT ["dotnet", "StevesBot.Worker.dll"]
+8 -8
View File
@@ -6,14 +6,14 @@
<ItemGroup> <ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.5" /> <PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.5" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.5" /> <PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.5" /> <PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.5.0" /> <PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.1.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.12.0" /> <PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" /> <PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" /> <PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" /> <PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" /> <PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -2,7 +2,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" /> <PackageReference Include="Microsoft.Extensions.Http.Resilience" />
</ItemGroup> </ItemGroup>
+4 -4
View File
@@ -7,13 +7,13 @@
<ItemGroup> <ItemGroup>
<PackageVersion Include="coverlet.collector" Version="6.0.4" /> <PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" /> <PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
<PackageVersion Include="FluentAssertions" Version="7.2.0" /> <PackageVersion Include="FluentAssertions" Version="[7.2.0]" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.5" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="moq" Version="4.20.72" /> <PackageVersion Include="moq" Version="4.20.72" />
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" /> <PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
<PackageVersion Include="xunit" Version="2.9.3" /> <PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.0" /> <PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Hosting.Server;
using WebSocket = System.Net.WebSockets.WebSocket; using WebSocket = System.Net.WebSockets.WebSocket;
namespace StevesBot.Worker.Tests.Integration.Infrastructure; namespace StevesBot.Worker.Tests.Integration.Infrastructure;
public sealed class TestWebSocketServer : IAsyncLifetime, IDisposable public sealed class TestWebSocketServer : IAsyncLifetime, IDisposable
{ {
private readonly IWebHost _host; private readonly IHost _host;
private readonly CancellationTokenSource _echoCts = new(); private readonly CancellationTokenSource _echoCts = new();
private Uri? _webSocketUri; private Uri? _webSocketUri;
@@ -19,26 +21,30 @@ public sealed class TestWebSocketServer : IAsyncLifetime, IDisposable
public TestWebSocketServer() public TestWebSocketServer()
{ {
_host = new WebHostBuilder() _host = new HostBuilder()
.Configure(app => .ConfigureWebHost(b =>
{ {
app.UseWebSockets(); b.Configure(app =>
app.Run(async context =>
{ {
if (context.Request.Path == "/ws" && context.WebSockets.IsWebSocketRequest) app.UseWebSockets();
app.Run(async context =>
{ {
var webSocket = await context.WebSockets.AcceptWebSocketAsync(); if (context.Request.Path == "/ws" && context.WebSockets.IsWebSocketRequest)
await Echo(webSocket, _echoCts.Token); {
} var webSocket = await context.WebSockets.AcceptWebSocketAsync();
else await Echo(webSocket, _echoCts.Token);
{ }
context.Response.StatusCode = 400; else
} {
context.Response.StatusCode = 400;
}
});
}); });
b.UseKestrel();
b.UseUrls("http://[::1]:0");
}) })
.UseKestrel()
.UseUrls("http://[::1]:0")
.Build(); .Build();
} }
@@ -58,7 +64,8 @@ public sealed class TestWebSocketServer : IAsyncLifetime, IDisposable
private Uri GetWebSocketUri() private Uri GetWebSocketUri()
{ {
var address = _host.ServerFeatures.GetRequiredFeature<IServerAddressesFeature>().Addresses.First(); var serverFeatures = _host.Services.GetRequiredService<IServer>().Features;
var address = serverFeatures.GetRequiredFeature<IServerAddressesFeature>().Addresses.First();
var webSocketAddress = address.Replace("http://", "ws://", StringComparison.OrdinalIgnoreCase); var webSocketAddress = address.Replace("http://", "ws://", StringComparison.OrdinalIgnoreCase);
return new Uri(webSocketAddress + "/ws"); return new Uri(webSocketAddress + "/ws");
} }