diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c84df5d64..c68f45ee7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ // README at: https://github.com/devcontainers/templates/tree/main/src/dotnet { "name": "Sonarr", - "image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0", + "image": "mcr.microsoft.com/devcontainers/dotnet:1-10.0", "features": { "ghcr.io/devcontainers/features/node:1": { "nodeGypDependencies": true, diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 6eb368e80..eaa17c36e 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -23,6 +23,12 @@ runs: - name: Setup .NET uses: actions/setup-dotnet@v5 + - name: Setup NuGet registry source + shell: bash + if: ${{ startsWith(inputs.runtime, 'freebsd') }} + run: + dotnet nuget add source --configfile src/NuGet.Config --name gh-openur https://nuget.pkg.github.com/openur/index.json --username ${{ github.repository_owner }} --password ${{ github.token }} --store-password-in-clear-text + - name: Setup Environment Variables id: variables shell: bash @@ -86,7 +92,7 @@ runs: echo "Building Sonarr for $runtime, Platform: $platform" - dotnet msbuild -restore $slnFile -p:SelfContained=True -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$runtime -p:EnableWindowsTargeting=true -t:PublishAllRids + dotnet msbuild -restore $slnFile -p:SelfContained=true -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$runtime -p:EnableWindowsTargeting=true -t:PublishAllRids - name: Package shell: bash diff --git a/.github/actions/package/package.sh b/.github/actions/package/package.sh index 4c3c8a8ba..ffbf7cd30 100755 --- a/.github/actions/package/package.sh +++ b/.github/actions/package/package.sh @@ -3,7 +3,7 @@ outputFolder=_output artifactsFolder=_artifacts uiFolder="$outputFolder/UI" -framework="${FRAMEWORK:=net8.0}" +framework="${FRAMEWORK:=net10.0}" rm -rf $artifactsFolder mkdir $artifactsFolder diff --git a/.github/workflows/build_v5.yml b/.github/workflows/build_v5.yml index 30844f4c7..fb0f90f73 100644 --- a/.github/workflows/build_v5.yml +++ b/.github/workflows/build_v5.yml @@ -19,7 +19,7 @@ concurrency: cancel-in-progress: true env: - FRAMEWORK: net8.0 + FRAMEWORK: net10.0 RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }} SONARR_MAJOR_VERSION: 5 VERSION: 5.0.0 diff --git a/.vscode/launch.json b/.vscode/launch.json index 4e4c3440d..adcc1ee11 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build dotnet", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/_output/net8.0/Sonarr", + "program": "${workspaceFolder}/_output/net10.0/Sonarr", "args": [], "cwd": "${workspaceFolder}", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console diff --git a/global.json b/global.json index 13d185e52..936a420a5 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "8.0.405" + "version": "10.0.101" } } diff --git a/package.json b/package.json index 3755bcfc4..5e6d2f157 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@fortawesome/free-solid-svg-icons": "6.7.2", "@fortawesome/react-fontawesome": "0.2.2", "@juggle/resize-observer": "3.4.0", - "@microsoft/signalr": "8.0.7", + "@microsoft/signalr": "10.0.0", "@sentry/browser": "7.119.1", "@tanstack/react-query": "5.61.0", "@types/node": "20.16.11", diff --git a/scripts/docs.sh b/scripts/docs.sh index db2bfe9eb..fdc8309b4 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -FRAMEWORK="net8.0" +FRAMEWORK="net10.0" PLATFORM=$1 ARCHITECTURE="${2:-x64}" @@ -38,7 +38,7 @@ dotnet clean $slnFile -c Release dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids dotnet new tool-manifest -dotnet tool install --version 8.0.0 Swashbuckle.AspNetCore.Cli +dotnet tool install --version 10.0.1 Swashbuckle.AspNetCore.Cli # Remove the openapi.json file so we can check if it was created rm $outputFile diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 4754cda5b..5109e2557 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -123,7 +123,7 @@ - + diff --git a/src/NuGet.Config b/src/NuGet.Config index e07e77531..940e4c6cf 100644 --- a/src/NuGet.Config +++ b/src/NuGet.Config @@ -3,7 +3,6 @@ - diff --git a/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj b/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj index fa8b5eb40..f2457a4e8 100644 --- a/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj +++ b/src/NzbDrone.Api.Test/Sonarr.Api.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0 diff --git a/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj b/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj index 9281846c1..8b028efe4 100644 --- a/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj +++ b/src/NzbDrone.Automation.Test/Sonarr.Automation.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0 diff --git a/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj b/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj index 025c769c0..85f910b8e 100644 --- a/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj +++ b/src/NzbDrone.Common.Test/Sonarr.Common.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0 diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs index f042a5fd7..dee92460b 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -59,7 +59,6 @@ namespace NzbDrone.Common.Http _httpDispatcher = httpDispatcher; _logger = logger; - ServicePointManager.DefaultConnectionLimit = 12; _cookieContainerCache = cacheManager.GetCache(typeof(HttpClient)); } diff --git a/src/NzbDrone.Common/Sonarr.Common.csproj b/src/NzbDrone.Common/Sonarr.Common.csproj index d9ab36fd5..b313109b2 100644 --- a/src/NzbDrone.Common/Sonarr.Common.csproj +++ b/src/NzbDrone.Common/Sonarr.Common.csproj @@ -1,28 +1,25 @@  - net8.0 + net10.0 ISMUSL - - - - - - + + + + + + - + - + - - - + - - + diff --git a/src/NzbDrone.Console/Sonarr.Console.csproj b/src/NzbDrone.Console/Sonarr.Console.csproj index 957bf09eb..ef4838971 100644 --- a/src/NzbDrone.Console/Sonarr.Console.csproj +++ b/src/NzbDrone.Console/Sonarr.Console.csproj @@ -1,7 +1,7 @@  Exe - net8.0 + net10.0 ..\NzbDrone.Host\Sonarr.ico diff --git a/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj b/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj index 43c8ddba9..56177aa94 100644 --- a/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/Sonarr.Core.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0 diff --git a/src/NzbDrone.Core/Datastore/WhereBuilder.cs b/src/NzbDrone.Core/Datastore/WhereBuilder.cs index 969364e4f..ba0abca09 100644 --- a/src/NzbDrone.Core/Datastore/WhereBuilder.cs +++ b/src/NzbDrone.Core/Datastore/WhereBuilder.cs @@ -1,9 +1,25 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using Dapper; -namespace NzbDrone.Core.Datastore +namespace NzbDrone.Core.Datastore; + +public abstract class WhereBuilder : ExpressionVisitor { - public abstract class WhereBuilder : ExpressionVisitor + public DynamicParameters Parameters { get; protected set; } + + protected static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression result) { - public DynamicParameters Parameters { get; protected set; } + if (expression is MethodCallExpression { Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType }, Arguments: [var unwrapped] } + && implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition + && (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>))) + { + result = unwrapped; + return true; + } + + result = null; + return false; } } diff --git a/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs b/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs index fe951228b..553b8fea3 100644 --- a/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs +++ b/src/NzbDrone.Core/Datastore/WhereBuilderPostgres.cs @@ -298,15 +298,27 @@ namespace NzbDrone.Core.Datastore } else { - // Static method - // Must be Enumerable.Contains(source, item) - if (body.Method.DeclaringType != typeof(Enumerable) || body.Arguments.Count != 2) + // "MemoryExtensions.Contains" has an optional 3rd ComparisonType parameter, match only when it's null. + if (body.Method.DeclaringType == typeof(MemoryExtensions) + && body.Method.Name == nameof(MemoryExtensions.Contains) + && body.Arguments is [var spanArg, var valueArg, ..] + && (body.Arguments.Count is 2 || (body.Arguments.Count is 3 && body.Arguments[2] is ConstantExpression { Value: null })) + && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg)) + { + list = unwrappedSpanArg; + item = valueArg; + } + + // Static method. Must be Enumerable.Contains(source, item) + else if (body.Method.DeclaringType == typeof(Enumerable) && body.Arguments.Count == 2) + { + list = body.Arguments[0]; + item = body.Arguments[1]; + } + else { throw new NotSupportedException("Unexpected form of Enumerable.Contains"); } - - list = body.Arguments[0]; - item = body.Arguments[1]; } _sb.Append('('); diff --git a/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs b/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs index b91c9e61e..5cca447de 100644 --- a/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs +++ b/src/NzbDrone.Core/Datastore/WhereBuilderSqlite.cs @@ -298,15 +298,27 @@ namespace NzbDrone.Core.Datastore } else { - // Static method - // Must be Enumerable.Contains(source, item) - if (body.Method.DeclaringType != typeof(Enumerable) || body.Arguments.Count != 2) + // "MemoryExtensions.Contains" has an optional 3rd ComparisonType parameter, match only when it's null. + if (body.Method.DeclaringType == typeof(MemoryExtensions) + && body.Method.Name == nameof(MemoryExtensions.Contains) + && body.Arguments is [var spanArg, var valueArg, ..] + && (body.Arguments.Count is 2 || (body.Arguments.Count is 3 && body.Arguments[2] is ConstantExpression { Value: null })) + && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg)) + { + list = unwrappedSpanArg; + item = valueArg; + } + + // Static method. Must be Enumerable.Contains(source, item) + else if (body.Method.DeclaringType == typeof(Enumerable) && body.Arguments.Count == 2) + { + list = body.Arguments[0]; + item = body.Arguments[1]; + } + else { throw new NotSupportedException("Unexpected form of Enumerable.Contains"); } - - list = body.Arguments[0]; - item = body.Arguments[1]; } _sb.Append('('); diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index 5a48dc3a1..7a647f7e0 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -94,7 +94,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission { item.RemainingTime = TimeSpan.FromSeconds(torrent.Eta); } - catch (OverflowException) + catch (Exception ex) when (ex is OverflowException or ArgumentOutOfRangeException) { item.RemainingTime = TimeSpan.FromMilliseconds(torrent.Eta); } diff --git a/src/NzbDrone.Core/Sonarr.Core.csproj b/src/NzbDrone.Core/Sonarr.Core.csproj index 95d383318..e53adcdaf 100644 --- a/src/NzbDrone.Core/Sonarr.Core.csproj +++ b/src/NzbDrone.Core/Sonarr.Core.csproj @@ -1,32 +1,30 @@ - net8.0 + net10.0 - - - - + + + + - - - - - + + + + - - - + + + - - + diff --git a/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj b/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj index 92066b20c..92d6cc86d 100644 --- a/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj +++ b/src/NzbDrone.Host.Test/Sonarr.Host.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0 diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index 4ee6db7f7..f8e7ad196 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -286,7 +286,7 @@ namespace NzbDrone.Host } else if (type == X509ContentType.Pkcs12) { - certificate = new X509Certificate2(cert, password, X509KeyStorageFlags.DefaultKeySet); + certificate = X509CertificateLoader.LoadPkcs12FromFile(cert, password, X509KeyStorageFlags.DefaultKeySet); } else { diff --git a/src/NzbDrone.Host/Sonarr.Host.csproj b/src/NzbDrone.Host/Sonarr.Host.csproj index fc458acc7..3d5cdc46c 100644 --- a/src/NzbDrone.Host/Sonarr.Host.csproj +++ b/src/NzbDrone.Host/Sonarr.Host.csproj @@ -1,13 +1,12 @@  - net8.0 + net10.0 Library - - - - + + + diff --git a/src/NzbDrone.Host/Startup.cs b/src/NzbDrone.Host/Startup.cs index 7a09f72e7..fc1c7e957 100644 --- a/src/NzbDrone.Host/Startup.cs +++ b/src/NzbDrone.Host/Startup.cs @@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using NLog.Extensions.Logging; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; @@ -35,7 +35,7 @@ using Sonarr.Http.ClientSchema; using Sonarr.Http.ErrorManagement; using Sonarr.Http.Frontend; using Sonarr.Http.Middleware; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; +using IPNetwork = System.Net.IPNetwork; using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace NzbDrone.Host @@ -64,11 +64,11 @@ namespace NzbDrone.Host services.Configure(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost; - options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); - options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); - options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); - options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); - options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); + options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8)); + options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12)); + options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16)); + options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("fc00::"), 7)); + options.KnownIPNetworks.Add(new IPNetwork(IPAddress.Parse("fe80::"), 10)); }); services.AddRouting(options => options.LowercaseUrls = true); @@ -139,18 +139,13 @@ namespace NzbDrone.Host Scheme = "apiKey", Description = "Apikey passed as header", In = ParameterLocation.Header, - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "X-Api-Key" - }, }; c.AddSecurityDefinition("X-Api-Key", apiKeyHeader); - c.AddSecurityRequirement(new OpenApiSecurityRequirement + c.AddSecurityRequirement(document => new OpenApiSecurityRequirement { - { apiKeyHeader, Array.Empty() } + [new OpenApiSecuritySchemeReference(apiKeyHeader.Name, document)] = new List(), }); var apikeyQuery = new OpenApiSecurityScheme @@ -160,11 +155,6 @@ namespace NzbDrone.Host Scheme = "apiKey", Description = "Apikey passed as query parameter", In = ParameterLocation.Query, - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "apikey" - }, }; c.AddServer(new OpenApiServer @@ -179,9 +169,9 @@ namespace NzbDrone.Host c.AddSecurityDefinition("apikey", apikeyQuery); - c.AddSecurityRequirement(new OpenApiSecurityRequirement + c.AddSecurityRequirement(document => new OpenApiSecurityRequirement { - { apikeyQuery, Array.Empty() } + [new OpenApiSecuritySchemeReference(apikeyQuery.Name, document)] = new List(), }); c.DescribeAllParametersInCamelCase(); diff --git a/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj b/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj index 3794d2368..fcb20be32 100644 --- a/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj +++ b/src/NzbDrone.Integration.Test/Sonarr.Integration.Test.csproj @@ -1,10 +1,10 @@  - net8.0 + net10.0 Library - + diff --git a/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj b/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj index 3d6862b6a..8034fca5c 100644 --- a/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj +++ b/src/NzbDrone.Libraries.Test/Sonarr.Libraries.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0 diff --git a/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj b/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj index cd1a3678b..31d01ebc0 100644 --- a/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj +++ b/src/NzbDrone.Mono.Test/Sonarr.Mono.Test.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0