From a6ceae65fcae556ff9315570ede517a2bfaa0e38 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Sat, 5 Aug 2023 15:49:26 -0500 Subject: [PATCH] refactor: Add SonarCloud Analyzer and fix analysis warnings --- .editorconfig | 6 ++++++ src/Directory.Packages.props | 1 + .../Models/ProcessedCustomFormatCache.cs | 4 ++-- .../QualityProfileStatCalculator.cs | 10 +++++----- .../ReleaseProfileTransactionPhase.cs | 2 +- .../Processors/Config/TemplateConfigCreator.cs | 17 ----------------- src/Recyclarr.Cli/Recyclarr.Cli.csproj | 4 ++++ .../RuntimeValidationService.cs | 5 ++--- .../Networking/UntrustedCertClientFactory.cs | 2 ++ src/Recyclarr.Common/Recyclarr.Common.csproj | 4 ++++ src/Recyclarr.Common/ResourceDataReader.cs | 10 +++++----- src/Recyclarr.Gui/Recyclarr.Gui.csproj | 4 ++++ .../EnvironmentVariablesDeserializer.cs | 2 ++ .../Config/Parsing/ConfigYamlDataObjects.cs | 1 + .../Parsing/ConfigYamlDataObjectsValidation.cs | 4 ---- .../Parsing/ErrorHandling/SyntaxErrorHelper.cs | 2 +- .../Config/Secrets/SecretsDeserializer.cs | 2 ++ .../Recyclarr.TrashLib.csproj | 4 ++++ 18 files changed, 46 insertions(+), 38 deletions(-) diff --git a/.editorconfig b/.editorconfig index cb87733a..e9ca7855 100644 --- a/.editorconfig +++ b/.editorconfig @@ -226,6 +226,12 @@ dotnet_diagnostic.ca1001.severity = none dotnet_diagnostic.ca1032.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_accessor_owner_body = expression_body resharper_alignment_tab_fill_style = use_spaces diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 175941ab..e5e7b92b 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -24,6 +24,7 @@ + diff --git a/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs b/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs index a115fe72..5f8630cb 100644 --- a/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs +++ b/src/Recyclarr.Cli/Pipelines/CustomFormat/Models/ProcessedCustomFormatCache.cs @@ -19,11 +19,11 @@ public class ProcessedCustomFormatCache : IPipelineCache 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) { - return _customFormats.FirstOrDefault(x => x.Id == id); + return _customFormats.Find(x => x.Id == id); } } diff --git a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs index eba04148..27ed1e63 100644 --- a/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs +++ b/src/Recyclarr.Cli/Pipelines/QualityProfile/PipelinePhases/QualityProfileStatCalculator.cs @@ -37,12 +37,13 @@ public class QualityProfileStatCalculator 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("Cutoff", oldDto.Items.FindCutoff(oldDto.Cutoff), newDto.Items.FindCutoff(newDto.Cutoff)); Log("Cutoff Score", oldDto.CutoffFormatScore, newDto.CutoffFormatScore); Log("Minimum Score", oldDto.MinFormatScore, newDto.MinFormatScore); + return; void Log(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); - var updatedQualities = JToken.FromObject(oldDto.Items); - stats.QualitiesChanged = !JToken.DeepEquals(dtoQualities, updatedQualities); + stats.QualitiesChanged = !JToken.DeepEquals( + JToken.FromObject(oldDto.Items), JToken.FromObject(newDto.Items)); } private void ScoreUpdates( diff --git a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs index 989db9dd..12361e4b 100644 --- a/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs +++ b/src/Recyclarr.Cli/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs @@ -52,7 +52,7 @@ public class ReleaseProfileTransactionPhase .Where(sonarrProfile => { return sonarrProfile.Name.StartsWithIgnoreCase("[Trash]") && - !profiles.Any(profile => sonarrProfile.Name.EndsWithIgnoreCase(profile.Name)); + !profiles.Exists(profile => sonarrProfile.Name.EndsWithIgnoreCase(profile.Name)); }) .ToList(); } diff --git a/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs b/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs index e342384d..52cc970e 100644 --- a/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs +++ b/src/Recyclarr.Cli/Processors/Config/TemplateConfigCreator.cs @@ -14,8 +14,6 @@ public class TemplateConfigCreator : IConfigCreator private readonly IConfigTemplateGuideService _templates; private readonly IAppPaths _paths; - // private readonly IConfigManipulator _configManipulator; - // private readonly IAnsiConsole _console; public TemplateConfigCreator( ILogger log, @@ -25,8 +23,6 @@ public class TemplateConfigCreator : IConfigCreator _log = log; _templates = templates; _paths = paths; - // _configManipulator = configManipulator; - // _console = console; } public bool CanHandle(ICreateConfigSettings settings) @@ -66,19 +62,6 @@ public class TemplateConfigCreator : IConfigCreator { _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("Base URL:")); - // var apiKey = _console.Prompt(new TextPrompt("API Key:")); - // return config with - // { - // BaseUrl = baseUrl, - // ApiKey = apiKey - // }; - // }); } catch (FileExistsException e) { diff --git a/src/Recyclarr.Cli/Recyclarr.Cli.csproj b/src/Recyclarr.Cli/Recyclarr.Cli.csproj index f20fcd33..da8cb40a 100644 --- a/src/Recyclarr.Cli/Recyclarr.Cli.csproj +++ b/src/Recyclarr.Cli/Recyclarr.Cli.csproj @@ -13,6 +13,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs b/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs index 751a2b61..d4a0dbe9 100644 --- a/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs +++ b/src/Recyclarr.Common/FluentValidation/RuntimeValidationService.cs @@ -9,9 +9,8 @@ public class RuntimeValidationService : IRuntimeValidationService private static Type? GetValidatorInterface(Type type) { - return type.GetInterfaces() - .FirstOrDefault(i - => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>)); + return Array.Find(type.GetInterfaces(), + i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>)); } public RuntimeValidationService(IEnumerable validators) diff --git a/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs b/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs index d0a24d04..006f12ae 100644 --- a/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs +++ b/src/Recyclarr.Common/Networking/UntrustedCertClientFactory.cs @@ -1,7 +1,9 @@ +using System.Diagnostics.CodeAnalysis; using Flurl.Http.Configuration; namespace Recyclarr.Common.Networking; +[SuppressMessage("SonarCloud", "S4830:Server certificates should be verified during SSL/TLS connections")] public class UntrustedCertClientFactory : DefaultHttpClientFactory { public override HttpMessageHandler CreateMessageHandler() diff --git a/src/Recyclarr.Common/Recyclarr.Common.csproj b/src/Recyclarr.Common/Recyclarr.Common.csproj index a1c3cf73..c4c8fd42 100644 --- a/src/Recyclarr.Common/Recyclarr.Common.csproj +++ b/src/Recyclarr.Common/Recyclarr.Common.csproj @@ -6,6 +6,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Recyclarr.Common/ResourceDataReader.cs b/src/Recyclarr.Common/ResourceDataReader.cs index 21b62bac..e03dc644 100644 --- a/src/Recyclarr.Common/ResourceDataReader.cs +++ b/src/Recyclarr.Common/ResourceDataReader.cs @@ -5,7 +5,7 @@ namespace Recyclarr.Common; public class ResourceDataReader : IResourceDataReader { - private readonly Assembly? _assembly; + private readonly Assembly _assembly; private readonly string? _namespace; private readonly string _subdirectory; @@ -19,7 +19,8 @@ public class ResourceDataReader : IResourceDataReader { _subdirectory = subdirectory; _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) @@ -49,8 +50,7 @@ public class ResourceDataReader : IResourceDataReader private string FindResourcePath(string resourcePath) { - var foundResource = _assembly?.GetManifestResourceNames() - .FirstOrDefault(x => x.EndsWith(resourcePath)); + var foundResource = Array.Find(_assembly.GetManifestResourceNames(), x => x.EndsWith(resourcePath)); if (foundResource is null) { throw new ArgumentException($"Embedded resource not found: {resourcePath}"); @@ -61,7 +61,7 @@ public class ResourceDataReader : IResourceDataReader private string GetResourceData(string resourcePath) { - using var stream = _assembly?.GetManifestResourceStream(resourcePath); + using var stream = _assembly.GetManifestResourceStream(resourcePath); if (stream is null) { throw new ArgumentException($"Unable to open embedded resource: {resourcePath}"); diff --git a/src/Recyclarr.Gui/Recyclarr.Gui.csproj b/src/Recyclarr.Gui/Recyclarr.Gui.csproj index 32153d2d..008570ba 100644 --- a/src/Recyclarr.Gui/Recyclarr.Gui.csproj +++ b/src/Recyclarr.Gui/Recyclarr.Gui.csproj @@ -14,6 +14,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs b/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs index e90f4676..09821931 100644 --- a/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs +++ b/src/Recyclarr.TrashLib/Config/EnvironmentVariables/EnvironmentVariablesDeserializer.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Recyclarr.Common; using YamlDotNet.Core; using YamlDotNet.Core.Events; @@ -5,6 +6,7 @@ using YamlDotNet.Serialization; namespace Recyclarr.TrashLib.Config.EnvironmentVariables; +[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")] public record EnvironmentVariableTag; public class EnvironmentVariablesDeserializer : INodeDeserializer diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs index f428575a..33dca1b9 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjects.cs @@ -87,6 +87,7 @@ public record ReleaseProfileConfigYaml // 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. +[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")] public record RadarrConfigYaml : ServiceConfigYaml; [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs index f1d14823..f98f88bd 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigYamlDataObjectsValidation.cs @@ -13,10 +13,6 @@ public class ServiceConfigYamlValidator : AbstractValidator .WithMessage("{PropertyName} must start with 'http' or 'https'") .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.CustomFormats) diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs b/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs index 59724258..bf60e077 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/ErrorHandling/SyntaxErrorHelper.cs @@ -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! 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, $"A list/array/sequence is not allowed for {currentType.Name}"); diff --git a/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs b/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs index e34bbc30..eea5aa1f 100644 --- a/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs +++ b/src/Recyclarr.TrashLib/Config/Secrets/SecretsDeserializer.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; namespace Recyclarr.TrashLib.Config.Secrets; +[SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty")] public record SecretTag; public class SecretsDeserializer : INodeDeserializer diff --git a/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj b/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj index 0832554c..69094efa 100644 --- a/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj +++ b/src/Recyclarr.TrashLib/Recyclarr.TrashLib.csproj @@ -13,6 +13,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive +