refactor: Add SonarCloud Analyzer and fix analysis warnings

pull/201/head
Robert Dailey 10 months ago
parent 8596168757
commit a6ceae65fc

@ -226,6 +226,12 @@ dotnet_diagnostic.ca1001.severity = none
dotnet_diagnostic.ca1032.severity = none dotnet_diagnostic.ca1032.severity = none
dotnet_diagnostic.ca1310.severity = none dotnet_diagnostic.ca1310.severity = none
# Jump statements should not be redundant
# https://rules.sonarsource.com/csharp/RSPEC-3626/
# Justification: Used by Resharper to separate local methods from logic
dotnet_diagnostic.s3626.severity = none
dotnet_diagnostic.s1751.severity = none
# ReSharper properties # ReSharper properties
resharper_accessor_owner_body = expression_body resharper_accessor_owner_body = expression_body
resharper_alignment_tab_fill_style = use_spaces resharper_alignment_tab_fill_style = use_spaces

@ -24,6 +24,7 @@
<PackageVersion Include="Serilog.Expressions" Version="3.4.1" /> <PackageVersion Include="Serilog.Expressions" Version="3.4.1" />
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.7.0.75501" />
<PackageVersion Include="Spectre.Console" Version="0.47.0" /> <PackageVersion Include="Spectre.Console" Version="0.47.0" />
<PackageVersion Include="Spectre.Console.Analyzer" Version="0.47.0" /> <PackageVersion Include="Spectre.Console.Analyzer" Version="0.47.0" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.47.0" /> <PackageVersion Include="Spectre.Console.Cli" Version="0.47.0" />

@ -19,11 +19,11 @@ public class ProcessedCustomFormatCache : IPipelineCache
public CustomFormatData? LookupByTrashId(string trashId) public CustomFormatData? LookupByTrashId(string trashId)
{ {
return _customFormats.FirstOrDefault(x => x.TrashId.EqualsIgnoreCase(trashId)); return _customFormats.Find(x => x.TrashId.EqualsIgnoreCase(trashId));
} }
public CustomFormatData? LookupByServiceId(int id) public CustomFormatData? LookupByServiceId(int id)
{ {
return _customFormats.FirstOrDefault(x => x.Id == id); return _customFormats.Find(x => x.Id == id);
} }
} }

@ -37,12 +37,13 @@ public class QualityProfileStatCalculator
return stats; return stats;
} }
private void ProfileUpdates(ProfileWithStats stats, QualityProfileDto newDto, QualityProfileDto oldDto) private void ProfileUpdates(ProfileWithStats stats, QualityProfileDto oldDto, QualityProfileDto newDto)
{ {
Log("Upgrade Allowed", oldDto.UpgradeAllowed, newDto.UpgradeAllowed); Log("Upgrade Allowed", oldDto.UpgradeAllowed, newDto.UpgradeAllowed);
Log("Cutoff", oldDto.Items.FindCutoff(oldDto.Cutoff), newDto.Items.FindCutoff(newDto.Cutoff)); Log("Cutoff", oldDto.Items.FindCutoff(oldDto.Cutoff), newDto.Items.FindCutoff(newDto.Cutoff));
Log("Cutoff Score", oldDto.CutoffFormatScore, newDto.CutoffFormatScore); Log("Cutoff Score", oldDto.CutoffFormatScore, newDto.CutoffFormatScore);
Log("Minimum Score", oldDto.MinFormatScore, newDto.MinFormatScore); Log("Minimum Score", oldDto.MinFormatScore, newDto.MinFormatScore);
return; return;
void Log<T>(string msg, T oldValue, T newValue) void Log<T>(string msg, T oldValue, T newValue)
@ -52,11 +53,10 @@ public class QualityProfileStatCalculator
} }
} }
private static void QualityUpdates(ProfileWithStats stats, QualityProfileDto newDto, QualityProfileDto oldDto) private static void QualityUpdates(ProfileWithStats stats, QualityProfileDto oldDto, QualityProfileDto newDto)
{ {
var dtoQualities = JToken.FromObject(newDto.Items); stats.QualitiesChanged = !JToken.DeepEquals(
var updatedQualities = JToken.FromObject(oldDto.Items); JToken.FromObject(oldDto.Items), JToken.FromObject(newDto.Items));
stats.QualitiesChanged = !JToken.DeepEquals(dtoQualities, updatedQualities);
} }
private void ScoreUpdates( private void ScoreUpdates(

@ -52,7 +52,7 @@ public class ReleaseProfileTransactionPhase
.Where(sonarrProfile => .Where(sonarrProfile =>
{ {
return sonarrProfile.Name.StartsWithIgnoreCase("[Trash]") && return sonarrProfile.Name.StartsWithIgnoreCase("[Trash]") &&
!profiles.Any(profile => sonarrProfile.Name.EndsWithIgnoreCase(profile.Name)); !profiles.Exists(profile => sonarrProfile.Name.EndsWithIgnoreCase(profile.Name));
}) })
.ToList(); .ToList();
} }

@ -14,8 +14,6 @@ public class TemplateConfigCreator : IConfigCreator
private readonly IConfigTemplateGuideService _templates; private readonly IConfigTemplateGuideService _templates;
private readonly IAppPaths _paths; private readonly IAppPaths _paths;
// private readonly IConfigManipulator _configManipulator;
// private readonly IAnsiConsole _console;
public TemplateConfigCreator( public TemplateConfigCreator(
ILogger log, ILogger log,
@ -25,8 +23,6 @@ public class TemplateConfigCreator : IConfigCreator
_log = log; _log = log;
_templates = templates; _templates = templates;
_paths = paths; _paths = paths;
// _configManipulator = configManipulator;
// _console = console;
} }
public bool CanHandle(ICreateConfigSettings settings) public bool CanHandle(ICreateConfigSettings settings)
@ -66,19 +62,6 @@ public class TemplateConfigCreator : IConfigCreator
{ {
_log.Information("Created configuration file: {Path}", destinationFile); _log.Information("Created configuration file: {Path}", destinationFile);
} }
// -- See comment in ConfigManipulator.cs --
// _configManipulator.LoadAndSave(templateFile, destinationFile, (instanceName, config) =>
// {
// _console.MarkupLineInterpolated($"Enter configuration info for instance [green]{instanceName}[/]:");
// var baseUrl = _console.Prompt(new TextPrompt<string>("Base URL:"));
// var apiKey = _console.Prompt(new TextPrompt<string>("API Key:"));
// return config with
// {
// BaseUrl = baseUrl,
// ApiKey = apiKey
// };
// });
} }
catch (FileExistsException e) catch (FileExistsException e)
{ {

@ -13,6 +13,10 @@
<PackageReference Include="Serilog.Expressions" /> <PackageReference Include="Serilog.Expressions" />
<PackageReference Include="Serilog.Sinks.Console" /> <PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" /> <PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="SonarAnalyzer.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Spectre.Console.Analyzer" PrivateAssets="All" /> <PackageReference Include="Spectre.Console.Analyzer" PrivateAssets="All" />
<PackageReference Include="Spectre.Console.Cli" /> <PackageReference Include="Spectre.Console.Cli" />
<PackageReference Include="TestableIO.System.IO.Abstractions" /> <PackageReference Include="TestableIO.System.IO.Abstractions" />

@ -9,9 +9,8 @@ public class RuntimeValidationService : IRuntimeValidationService
private static Type? GetValidatorInterface(Type type) private static Type? GetValidatorInterface(Type type)
{ {
return type.GetInterfaces() return Array.Find(type.GetInterfaces(),
.FirstOrDefault(i i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>));
=> i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>));
} }
public RuntimeValidationService(IEnumerable<IValidator> validators) public RuntimeValidationService(IEnumerable<IValidator> validators)

@ -1,7 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using Flurl.Http.Configuration; using Flurl.Http.Configuration;
namespace Recyclarr.Common.Networking; namespace Recyclarr.Common.Networking;
[SuppressMessage("SonarCloud", "S4830:Server certificates should be verified during SSL/TLS connections")]
public class UntrustedCertClientFactory : DefaultHttpClientFactory public class UntrustedCertClientFactory : DefaultHttpClientFactory
{ {
public override HttpMessageHandler CreateMessageHandler() public override HttpMessageHandler CreateMessageHandler()

@ -6,6 +6,10 @@
<PackageReference Include="JetBrains.Annotations" /> <PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Newtonsoft.Json" /> <PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Serilog" /> <PackageReference Include="Serilog" />
<PackageReference Include="SonarAnalyzer.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Spectre.Console" /> <PackageReference Include="Spectre.Console" />
<PackageReference Include="System.Reactive" /> <PackageReference Include="System.Reactive" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Extensions" /> <PackageReference Include="TestableIO.System.IO.Abstractions.Extensions" />

@ -5,7 +5,7 @@ namespace Recyclarr.Common;
public class ResourceDataReader : IResourceDataReader public class ResourceDataReader : IResourceDataReader
{ {
private readonly Assembly? _assembly; private readonly Assembly _assembly;
private readonly string? _namespace; private readonly string? _namespace;
private readonly string _subdirectory; private readonly string _subdirectory;
@ -19,7 +19,8 @@ public class ResourceDataReader : IResourceDataReader
{ {
_subdirectory = subdirectory; _subdirectory = subdirectory;
_namespace = typeWithNamespaceToUse.Namespace; _namespace = typeWithNamespaceToUse.Namespace;
_assembly = Assembly.GetAssembly(typeWithNamespaceToUse); _assembly = Assembly.GetAssembly(typeWithNamespaceToUse)
?? throw new ArgumentException("Cannot get assembly from type", nameof(typeWithNamespaceToUse));
} }
public string ReadData(string filename) public string ReadData(string filename)
@ -49,8 +50,7 @@ public class ResourceDataReader : IResourceDataReader
private string FindResourcePath(string resourcePath) private string FindResourcePath(string resourcePath)
{ {
var foundResource = _assembly?.GetManifestResourceNames() var foundResource = Array.Find(_assembly.GetManifestResourceNames(), x => x.EndsWith(resourcePath));
.FirstOrDefault(x => x.EndsWith(resourcePath));
if (foundResource is null) if (foundResource is null)
{ {
throw new ArgumentException($"Embedded resource not found: {resourcePath}"); throw new ArgumentException($"Embedded resource not found: {resourcePath}");
@ -61,7 +61,7 @@ public class ResourceDataReader : IResourceDataReader
private string GetResourceData(string resourcePath) private string GetResourceData(string resourcePath)
{ {
using var stream = _assembly?.GetManifestResourceStream(resourcePath); using var stream = _assembly.GetManifestResourceStream(resourcePath);
if (stream is null) if (stream is null)
{ {
throw new ArgumentException($"Unable to open embedded resource: {resourcePath}"); throw new ArgumentException($"Unable to open embedded resource: {resourcePath}");

@ -14,6 +14,10 @@
<PackageReference Include="ReactiveUI.Blazor" /> <PackageReference Include="ReactiveUI.Blazor" />
<PackageReference Include="Serilog.AspNetCore" /> <PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Sinks.File" /> <PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="SonarAnalyzer.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Recyclarr.Common; using Recyclarr.Common;
using YamlDotNet.Core; using YamlDotNet.Core;
using YamlDotNet.Core.Events; using YamlDotNet.Core.Events;
@ -5,6 +6,7 @@ using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.EnvironmentVariables; namespace Recyclarr.TrashLib.Config.EnvironmentVariables;
[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")]
public record EnvironmentVariableTag; public record EnvironmentVariableTag;
public class EnvironmentVariablesDeserializer : INodeDeserializer public class EnvironmentVariablesDeserializer : INodeDeserializer

@ -87,6 +87,7 @@ public record ReleaseProfileConfigYaml
// This is usually empty (or the same as ServiceConfigYaml) on purpose. // This is usually empty (or the same as ServiceConfigYaml) on purpose.
// If empty, it is kept around to make it clear that this one is dedicated to Radarr. // If empty, it is kept around to make it clear that this one is dedicated to Radarr.
[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")]
public record RadarrConfigYaml : ServiceConfigYaml; public record RadarrConfigYaml : ServiceConfigYaml;
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]

@ -13,10 +13,6 @@ public class ServiceConfigYamlValidator : AbstractValidator<ServiceConfigYaml>
.WithMessage("{PropertyName} must start with 'http' or 'https'") .WithMessage("{PropertyName} must start with 'http' or 'https'")
.WithName("base_url"); .WithName("base_url");
// RuleFor(x => x.BaseUrl)
// .When(x => x.BaseUrl is {Length: > 0}, ApplyConditionTo.CurrentValidator)
// .WithMessage("{PropertyName} must start with 'http' or 'https'");
RuleFor(x => x.ApiKey).NotEmpty().WithName("api_key"); RuleFor(x => x.ApiKey).NotEmpty().WithName("api_key");
RuleFor(x => x.CustomFormats) RuleFor(x => x.CustomFormats)

@ -20,7 +20,7 @@ public sealed class SyntaxErrorHelper : INodeTypeResolver
// just says "no node type resolver could resolve the type", or something along those lines -- which isn't helpful! // just says "no node type resolver could resolve the type", or something along those lines -- which isn't helpful!
private static void CheckSequenceAssignedToNonSequence(ParsingEvent? nodeEvent, MemberInfo currentType) private static void CheckSequenceAssignedToNonSequence(ParsingEvent? nodeEvent, MemberInfo currentType)
{ {
if (nodeEvent is SequenceStart && !CollectionKeywords.Any(x => currentType.Name.Contains(x))) if (nodeEvent is SequenceStart && !Array.Exists(CollectionKeywords, x => currentType.Name.Contains(x)))
{ {
throw new YamlException(nodeEvent.Start, nodeEvent.End, throw new YamlException(nodeEvent.Start, nodeEvent.End,
$"A list/array/sequence is not allowed for {currentType.Name}"); $"A list/array/sequence is not allowed for {currentType.Name}");

@ -1,9 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using YamlDotNet.Core; using YamlDotNet.Core;
using YamlDotNet.Core.Events; using YamlDotNet.Core.Events;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
namespace Recyclarr.TrashLib.Config.Secrets; namespace Recyclarr.TrashLib.Config.Secrets;
[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")]
public record SecretTag; public record SecretTag;
public class SecretsDeserializer : INodeDeserializer public class SecretsDeserializer : INodeDeserializer

@ -13,6 +13,10 @@
<PackageReference Include="JetBrains.Annotations" /> <PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Newtonsoft.Json.Schema" /> <PackageReference Include="Newtonsoft.Json.Schema" />
<PackageReference Include="Serilog" /> <PackageReference Include="Serilog" />
<PackageReference Include="SonarAnalyzer.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SuperLinq" /> <PackageReference Include="SuperLinq" />
<PackageReference Include="System.Data.HashFunction.FNV" /> <PackageReference Include="System.Data.HashFunction.FNV" />
<PackageReference Include="System.Private.Uri" /> <PackageReference Include="System.Private.Uri" />

Loading…
Cancel
Save