From 77fef8df962f171886db1b0fed6c1bae8dbd79b7 Mon Sep 17 00:00:00 2001 From: Marlon Regenhardt Date: Wed, 31 Jan 2024 23:05:26 +0100 Subject: [PATCH] Activate .NET code analysis (#48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enable .NET source code analysis * Fix CA2016 (Forward the CancellationToken parameter to methods that take one) * +Fix for CA2016 (Forward the CancellationToken parameter to methods that take one) * Fix CA1826 (Use property instead of Linq Enumerable method) * Fix CA1825 (Avoid zero-length array allocations) * Fix CA1822 (Mark members as static) * Fix CA2208 (Instantiate argument exceptions correctly) * Fix CA2254 (Template should be a static expression) * Fix CA1816 (Call GC.SuppressFinalize correctly) * Remove unnecessary build property * Fix CA1510 (Use throw helper ThrowIfNull) * Fix CA1861 (Avoid constant arrays as arguments) * Fix CA1862 (Use the 'StringComparison' method overloads ) * Fix CA1854 (Prefer the IDictionary.TryGetValue(TKey, out TValue) method) * Fix CA1859 (Use concrete types when possible for improved performance) * Fix CA1854 (Use span-based 'string.Concat') * Un-fix CA1862 (Use the 'StringComparison' method overloads ) EF Core needs this one. * use Array.Empty * Fix CA1860 (Avoid using 'Enumerable.Any()' extension method) --------- Co-authored-by: Cédric Luthi Co-authored-by: Regenhardt Marlon Co-authored-by: FroggieFrog --- .editorconfig | 3 +++ src/BaGetter.Aliyun/AliyunStorageService.cs | 3 +-- src/BaGetter.Aws/S3StorageService.cs | 5 ++--- .../Search/AzureSearchService.cs | 2 +- .../Search/IndexActionBuilder.cs | 4 ++-- .../Table/TableOperationBuilder.cs | 2 ++ .../Table/TablePackageDatabase.cs | 6 +++--- .../Table/TableSearchService.cs | 2 +- .../ApiKeyAuthenticationService.cs | 2 +- .../Converters/StringArrayToJsonConverter.cs | 3 ++- .../PackageArchiveReaderExtensions.cs | 10 ++++++---- .../Indexing/SymbolIndexingService.cs | 4 ++-- .../Metadata/RegistrationBuilder.cs | 4 ++-- src/BaGetter.Core/PackageDatabase.cs | 2 +- .../Search/DatabaseSearchService.cs | 5 +++-- .../ServiceIndex/BaGetterServiceIndex.cs | 2 +- .../Storage/FileStorageService.cs | 4 ++-- .../Storage/SymbolStorageService.cs | 10 +++++----- .../Upstream/Clients/V2UpstreamClient.cs | 19 +++++++++++-------- .../Upstream/Clients/V3UpstreamClient.cs | 7 ++++--- .../Upstream/DownloadsImporter.cs | 6 +++--- .../Upstream/PackageDownloadsJsonSource.cs | 8 ++++---- .../Validation/RequiredIfAttribute.cs | 3 +-- .../Migrations/20181212113156_Initial.cs | 1 + .../Migrations/20190311172227_Initial.cs | 3 ++- .../Migrations/20180804082816_Initial.cs | 1 + .../Migrations/20180804082808_Initial.cs | 1 + src/BaGetter.Gcp/GoogleCloudStorageService.cs | 3 +-- .../Catalog/CatalogProcessor.cs | 6 +++--- src/BaGetter.Protocol/Search/SearchClient.cs | 2 +- src/BaGetter.Web/BaGetterEndpointBuilder.cs | 2 ++ .../IApplicationBuilderExtensions.cs | 2 +- src/BaGetter.Web/Pages/Package.cshtml.cs | 8 ++++---- src/BaGetter/ConfigureBaGetterOptions.cs | 2 +- src/Directory.Build.props | 5 +++++ tests/BaGetter.Tests/HostIntegrationTests.cs | 2 +- 36 files changed, 87 insertions(+), 67 deletions(-) diff --git a/.editorconfig b/.editorconfig index 82aaab78..ad71addb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -36,6 +36,9 @@ indent_size = 4 # .NET Coding Conventions +# .NET source code analysis +dotnet_code_quality.CA1826.exclude_ordefault_methods = true + # Organize usings dotnet_sort_system_directives_first = true:warning diff --git a/src/BaGetter.Aliyun/AliyunStorageService.cs b/src/BaGetter.Aliyun/AliyunStorageService.cs index 28e435bf..5406b74d 100644 --- a/src/BaGetter.Aliyun/AliyunStorageService.cs +++ b/src/BaGetter.Aliyun/AliyunStorageService.cs @@ -17,8 +17,7 @@ public class AliyunStorageService : IStorageService public AliyunStorageService(IOptionsSnapshot options, OssClient client) { - if (options == null) - throw new ArgumentNullException(nameof(options)); + ArgumentNullException.ThrowIfNull(options); _bucket = options.Value.Bucket; _prefix = options.Value.Prefix; diff --git a/src/BaGetter.Aws/S3StorageService.cs b/src/BaGetter.Aws/S3StorageService.cs index f0ab9546..493c86d3 100644 --- a/src/BaGetter.Aws/S3StorageService.cs +++ b/src/BaGetter.Aws/S3StorageService.cs @@ -18,8 +18,7 @@ public class S3StorageService : IStorageService public S3StorageService(IOptionsSnapshot options, AmazonS3Client client) { - if (options == null) - throw new ArgumentNullException(nameof(options)); + ArgumentNullException.ThrowIfNull(options); _bucket = options.Value.Bucket; _prefix = options.Value.Prefix; @@ -42,7 +41,7 @@ public async Task GetAsync(string path, CancellationToken cancellationTo { using (var request = await _client.GetObjectAsync(_bucket, PrepareKey(path), cancellationToken)) { - await request.ResponseStream.CopyToAsync(stream); + await request.ResponseStream.CopyToAsync(stream, cancellationToken); } stream.Seek(0, SeekOrigin.Begin); diff --git a/src/BaGetter.Azure/Search/AzureSearchService.cs b/src/BaGetter.Azure/Search/AzureSearchService.cs index ebced566..d3300ae1 100644 --- a/src/BaGetter.Azure/Search/AzureSearchService.cs +++ b/src/BaGetter.Azure/Search/AzureSearchService.cs @@ -205,7 +205,7 @@ private string BuildSeachQuery(string query, string packageType, string framewor return queryBuilder.ToString(); } - private string BuildSearchFilter(bool includePrerelease, bool includeSemVer2) + private static string BuildSearchFilter(bool includePrerelease, bool includeSemVer2) { var searchFilters = SearchFilters.Default; diff --git a/src/BaGetter.Azure/Search/IndexActionBuilder.cs b/src/BaGetter.Azure/Search/IndexActionBuilder.cs index 14f0ea98..aea0ca9c 100644 --- a/src/BaGetter.Azure/Search/IndexActionBuilder.cs +++ b/src/BaGetter.Azure/Search/IndexActionBuilder.cs @@ -21,7 +21,7 @@ public virtual IReadOnlyList> UpdatePackage( return AddOrUpdatePackage(registration, isUpdate: true); } - private IReadOnlyList> AddOrUpdatePackage( + private static IReadOnlyList> AddOrUpdatePackage( PackageRegistration registration, bool isUpdate) { @@ -105,7 +105,7 @@ private IReadOnlyList> AddOrUpdatePackage( return result; } - private string EncodePackageId(string key) + private static string EncodePackageId(string key) { // Keys can only contain letters, digits, underscore(_), dash(-), or equal sign(=). // TODO: Align with NuGet.org's algorithm. diff --git a/src/BaGetter.Azure/Table/TableOperationBuilder.cs b/src/BaGetter.Azure/Table/TableOperationBuilder.cs index 7cd79357..6637ea04 100644 --- a/src/BaGetter.Azure/Table/TableOperationBuilder.cs +++ b/src/BaGetter.Azure/Table/TableOperationBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using BaGetter.Core; using Microsoft.Azure.Cosmos.Table; @@ -8,6 +9,7 @@ namespace BaGetter.Azure { + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Would be a breaking change since it's part of the public API")] public class TableOperationBuilder { public TableOperation AddPackage(Package package) diff --git a/src/BaGetter.Azure/Table/TablePackageDatabase.cs b/src/BaGetter.Azure/Table/TablePackageDatabase.cs index 5ac7bd2f..0adacf52 100644 --- a/src/BaGetter.Azure/Table/TablePackageDatabase.cs +++ b/src/BaGetter.Azure/Table/TablePackageDatabase.cs @@ -82,8 +82,8 @@ public async Task AddDownloadAsync( attempt++; _logger.LogWarning( e, - $"Retrying due to precondition failure, attempt {{Attempt}} of {MaxPreconditionFailures}..", - attempt); + "Retrying due to precondition failure, attempt {Attempt} of {MaxPreconditionFailures}", + attempt, MaxPreconditionFailures); } } } @@ -196,7 +196,7 @@ public async Task UnlistPackageAsync(string id, NuGetVersion version, Canc cancellationToken); } - private List MinimalColumnSet => new List { "PartitionKey" }; + private static List MinimalColumnSet => new List { "PartitionKey" }; private async Task TryUpdatePackageAsync(TableOperation operation, CancellationToken cancellationToken) { diff --git a/src/BaGetter.Azure/Table/TableSearchService.cs b/src/BaGetter.Azure/Table/TableSearchService.cs index a1d8b80c..2e00cdf0 100644 --- a/src/BaGetter.Azure/Table/TableSearchService.cs +++ b/src/BaGetter.Azure/Table/TableSearchService.cs @@ -133,7 +133,7 @@ private async Task> LoadPackagesAsync( return results; } - private string GenerateSearchFilter(string searchText, bool includePrerelease, bool includeSemVer2) + private static string GenerateSearchFilter(string searchText, bool includePrerelease, bool includeSemVer2) { var result = ""; diff --git a/src/BaGetter.Core/Authentication/ApiKeyAuthenticationService.cs b/src/BaGetter.Core/Authentication/ApiKeyAuthenticationService.cs index 3c357ee7..47a0c69e 100644 --- a/src/BaGetter.Core/Authentication/ApiKeyAuthenticationService.cs +++ b/src/BaGetter.Core/Authentication/ApiKeyAuthenticationService.cs @@ -11,7 +11,7 @@ public class ApiKeyAuthenticationService : IAuthenticationService public ApiKeyAuthenticationService(IOptionsSnapshot options) { - if (options == null) throw new ArgumentNullException(nameof(options)); + ArgumentNullException.ThrowIfNull(options); _apiKey = string.IsNullOrEmpty(options.Value.ApiKey) ? null : options.Value.ApiKey; } diff --git a/src/BaGetter.Core/Entities/Converters/StringArrayToJsonConverter.cs b/src/BaGetter.Core/Entities/Converters/StringArrayToJsonConverter.cs index 4e2d213a..84910b4b 100644 --- a/src/BaGetter.Core/Entities/Converters/StringArrayToJsonConverter.cs +++ b/src/BaGetter.Core/Entities/Converters/StringArrayToJsonConverter.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Newtonsoft.Json; @@ -10,7 +11,7 @@ public class StringArrayToJsonConverter : ValueConverter public StringArrayToJsonConverter() : base( v => JsonConvert.SerializeObject(v), - v => (!string.IsNullOrEmpty(v)) ? JsonConvert.DeserializeObject(v) : new string[0]) + v => (!string.IsNullOrEmpty(v)) ? JsonConvert.DeserializeObject(v) : Array.Empty()) { } } diff --git a/src/BaGetter.Core/Extensions/PackageArchiveReaderExtensions.cs b/src/BaGetter.Core/Extensions/PackageArchiveReaderExtensions.cs index 4468a68d..df9a730f 100644 --- a/src/BaGetter.Core/Extensions/PackageArchiveReaderExtensions.cs +++ b/src/BaGetter.Core/Extensions/PackageArchiveReaderExtensions.cs @@ -109,18 +109,20 @@ private static Uri ParseUri(string uriString) return new Uri(uriString); } + private static readonly char[] Separator = { ',', ';', '\t', '\n', '\r' }; + private static string[] ParseAuthors(string authors) { - if (string.IsNullOrEmpty(authors)) return new string[0]; + if (string.IsNullOrEmpty(authors)) return Array.Empty(); - return authors.Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + return authors.Split(Separator, StringSplitOptions.RemoveEmptyEntries); } private static string[] ParseTags(string tags) { - if (string.IsNullOrEmpty(tags)) return new string[0]; + if (string.IsNullOrEmpty(tags)) return Array.Empty(); - return tags.Split(new[] { ',', ';', ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + return tags.Split(Separator, StringSplitOptions.RemoveEmptyEntries); } private static (Uri repositoryUrl, string repositoryType) GetRepositoryMetadata(NuspecReader nuspec) diff --git a/src/BaGetter.Core/Indexing/SymbolIndexingService.cs b/src/BaGetter.Core/Indexing/SymbolIndexingService.cs index 1876ba6f..580ec8eb 100644 --- a/src/BaGetter.Core/Indexing/SymbolIndexingService.cs +++ b/src/BaGetter.Core/Indexing/SymbolIndexingService.cs @@ -133,7 +133,7 @@ bool IsValidSymbolFileInfo(FileInfo file) return entries.Select(e => new FileInfo(e)).All(IsValidSymbolFileInfo); } - private async Task ExtractPortablePdbAsync( + private static async Task ExtractPortablePdbAsync( PackageArchiveReader symbolPackage, string pdbPath, CancellationToken cancellationToken) @@ -147,7 +147,7 @@ private async Task ExtractPortablePdbAsync( { using (var rawPdbStream = await symbolPackage.GetStreamAsync(pdbPath, cancellationToken)) { - pdbStream = await rawPdbStream.AsTemporaryFileStreamAsync(); + pdbStream = await rawPdbStream.AsTemporaryFileStreamAsync(cancellationToken); string signature; using (var pdbReaderProvider = MetadataReaderProvider.FromPortablePdbStream(pdbStream, MetadataStreamOptions.LeaveOpen)) diff --git a/src/BaGetter.Core/Metadata/RegistrationBuilder.cs b/src/BaGetter.Core/Metadata/RegistrationBuilder.cs index b66c3175..0435bebf 100644 --- a/src/BaGetter.Core/Metadata/RegistrationBuilder.cs +++ b/src/BaGetter.Core/Metadata/RegistrationBuilder.cs @@ -32,7 +32,7 @@ public virtual BaGetterRegistrationIndexResponse BuildIndex(PackageRegistration new BaGetterRegistrationIndexPage { RegistrationPageUrl = _url.GetRegistrationIndexUrl(registration.PackageId), - Count = registration.Packages.Count(), + Count = registration.Packages.Count, Lower = sortedPackages.First().Version.ToNormalizedString().ToLowerInvariant(), Upper = sortedPackages.Last().Version.ToNormalizedString().ToLowerInvariant(), ItemsOrNull = sortedPackages.Select(ToRegistrationIndexPageItem).ToList(), @@ -92,7 +92,7 @@ private BaGetRegistrationIndexPageItem ToRegistrationIndexPageItem(Package packa }, }; - private IReadOnlyList ToDependencyGroups(Package package) + private static List ToDependencyGroups(Package package) { return package.Dependencies .GroupBy(d => d.TargetFramework) diff --git a/src/BaGetter.Core/PackageDatabase.cs b/src/BaGetter.Core/PackageDatabase.cs index a41dfdab..92c9cf3b 100644 --- a/src/BaGetter.Core/PackageDatabase.cs +++ b/src/BaGetter.Core/PackageDatabase.cs @@ -132,7 +132,7 @@ private async Task TryUpdatePackageAsync( var package = await _context.Packages .Where(p => p.Id == id) .Where(p => p.NormalizedVersionString == version.ToNormalizedString()) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(cancellationToken); if (package != null) { diff --git a/src/BaGetter.Core/Search/DatabaseSearchService.cs b/src/BaGetter.Core/Search/DatabaseSearchService.cs index b542facc..6cc4916f 100644 --- a/src/BaGetter.Core/Search/DatabaseSearchService.cs +++ b/src/BaGetter.Core/Search/DatabaseSearchService.cs @@ -145,7 +145,8 @@ public async Task FindDependentsAsync(string packageId, Canc return _searchBuilder.BuildDependents(dependents); } - private IQueryable ApplySearchQuery(IQueryable query, string search) + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1862:Use the 'StringComparison' method overloads to perform case-insensitive string comparisons", Justification = "Not for EF queries")] + private static IQueryable ApplySearchQuery(IQueryable query, string search) { if (string.IsNullOrEmpty(search)) { @@ -157,7 +158,7 @@ private IQueryable ApplySearchQuery(IQueryable query, string s return query.Where(p => p.Id.ToLower().Contains(search)); } - private IQueryable ApplySearchFilters( + private static IQueryable ApplySearchFilters( IQueryable query, bool includePrerelease, bool includeSemVer2, diff --git a/src/BaGetter.Core/ServiceIndex/BaGetterServiceIndex.cs b/src/BaGetter.Core/ServiceIndex/BaGetterServiceIndex.cs index ffe90e98..160d461a 100644 --- a/src/BaGetter.Core/ServiceIndex/BaGetterServiceIndex.cs +++ b/src/BaGetter.Core/ServiceIndex/BaGetterServiceIndex.cs @@ -15,7 +15,7 @@ public BaGetterServiceIndex(IUrlGenerator url) _url = url ?? throw new ArgumentNullException(nameof(url)); } - private IEnumerable BuildResource(string name, string url, params string[] versions) + private static IEnumerable BuildResource(string name, string url, params string[] versions) { foreach (var version in versions) { diff --git a/src/BaGetter.Core/Storage/FileStorageService.cs b/src/BaGetter.Core/Storage/FileStorageService.cs index c6fb9016..b14addd8 100644 --- a/src/BaGetter.Core/Storage/FileStorageService.cs +++ b/src/BaGetter.Core/Storage/FileStorageService.cs @@ -18,7 +18,7 @@ public class FileStorageService : IStorageService public FileStorageService(IOptionsSnapshot options) { - if (options == null) throw new ArgumentNullException(nameof(options)); + ArgumentNullException.ThrowIfNull(options); // Resolve relative path components ('.'/'..') and ensure there is a trailing slash. _storePath = Path.GetFullPath(options.Value.Path); @@ -51,7 +51,7 @@ public async Task PutAsync( string contentType, CancellationToken cancellationToken = default) { - if (content == null) throw new ArgumentNullException(nameof(content)); + ArgumentNullException.ThrowIfNull(content); if (string.IsNullOrEmpty(contentType)) throw new ArgumentException("Content type is required", nameof(contentType)); cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/BaGetter.Core/Storage/SymbolStorageService.cs b/src/BaGetter.Core/Storage/SymbolStorageService.cs index 310062a1..789af68f 100644 --- a/src/BaGetter.Core/Storage/SymbolStorageService.cs +++ b/src/BaGetter.Core/Storage/SymbolStorageService.cs @@ -47,27 +47,27 @@ public async Task GetPortablePdbContentStreamOrNullAsync(string filename } } - private string GetPathForKey(string filename, string key) + private static string GetPathForKey(string filename, string key) { // Ensure the filename doesn't try to escape out of the current directory. var tempPath = Path.GetDirectoryName(Path.GetTempPath()); var expandedPath = Path.GetDirectoryName(Path.Combine(tempPath, filename)); - + if (expandedPath != tempPath) { - throw new ArgumentException(nameof(filename)); + throw new ArgumentException($"Invalid file name: \"{filename}\" (can't escape the current directory)", nameof(filename)); } if (!key.All(char.IsLetterOrDigit)) { - throw new ArgumentException(nameof(key)); + throw new ArgumentException($"Invalid key: \"{key}\" (must contain exclusively letters and digits)", nameof(key)); } // The key's first 32 characters are the GUID, the remaining characters are the age. // See: https://github.com/dotnet/symstore/blob/98717c63ec8342acf8a07aa5c909b88bd0c664cc/docs/specs/SSQP_Key_Conventions.md#portable-pdb-signature // Debuggers should always use the age "ffffffff", however Visual Studio 2019 // users have reported other age values. We will ignore the age. - key = key.Substring(0, 32) + "ffffffff"; + key = string.Concat(key.AsSpan(0, 32), "ffffffff"); return Path.Combine( SymbolsPathPrefix, diff --git a/src/BaGetter.Core/Upstream/Clients/V2UpstreamClient.cs b/src/BaGetter.Core/Upstream/Clients/V2UpstreamClient.cs index a1f78466..e46f2462 100644 --- a/src/BaGetter.Core/Upstream/Clients/V2UpstreamClient.cs +++ b/src/BaGetter.Core/Upstream/Clients/V2UpstreamClient.cs @@ -27,15 +27,14 @@ public class V2UpstreamClient : IUpstreamClient, IDisposable private readonly SourceRepository _repository; private readonly INuGetLogger _ngLogger; private readonly ILogger _logger; + private static readonly char[] TagsSeparators = {';'}; + private static readonly char[] AuthorsSeparators = new[] { ',', ';', '\t', '\n', '\r' }; public V2UpstreamClient( IOptionsSnapshot options, ILogger logger) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + ArgumentNullException.ThrowIfNull(options); if (options.Value?.PackageSource?.AbsolutePath == null) { @@ -126,7 +125,11 @@ public async Task DownloadPackageOrNullAsync( } } - public void Dispose() => _cache.Dispose(); + public void Dispose() + { + _cache.Dispose(); + GC.SuppressFinalize(this); + } private Package ToPackage(IPackageSearchMetadata package) { @@ -151,18 +154,18 @@ private Package ToPackage(IPackageSearchMetadata package) PackageTypes = new List(), RepositoryUrl = null, RepositoryType = null, - Tags = package.Tags?.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries), + Tags = package.Tags?.Split(TagsSeparators, StringSplitOptions.RemoveEmptyEntries), Dependencies = ToDependencies(package) }; } - private string[] ParseAuthors(string authors) + private static string[] ParseAuthors(string authors) { if (string.IsNullOrEmpty(authors)) return Array.Empty(); return authors - .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + .Split(AuthorsSeparators, StringSplitOptions.RemoveEmptyEntries) .Select(a => a.Trim()) .ToArray(); } diff --git a/src/BaGetter.Core/Upstream/Clients/V3UpstreamClient.cs b/src/BaGetter.Core/Upstream/Clients/V3UpstreamClient.cs index 747d5876..ddef5fca 100644 --- a/src/BaGetter.Core/Upstream/Clients/V3UpstreamClient.cs +++ b/src/BaGetter.Core/Upstream/Clients/V3UpstreamClient.cs @@ -18,6 +18,7 @@ public class V3UpstreamClient : IUpstreamClient { private readonly NuGetClient _client; private readonly ILogger _logger; + private static readonly char[] Separator = { ',', ';', '\t', '\n', '\r' }; public V3UpstreamClient(NuGetClient client, ILogger logger) { @@ -117,7 +118,7 @@ private Package ToPackage(PackageMetadata metadata) }; } - private Uri ParseUri(string uriString) + private static Uri ParseUri(string uriString) { if (uriString == null) return null; @@ -129,12 +130,12 @@ private Uri ParseUri(string uriString) return uri; } - private string[] ParseAuthors(string authors) + private static string[] ParseAuthors(string authors) { if (string.IsNullOrEmpty(authors)) return Array.Empty(); return authors - .Split(new[] { ',', ';', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries) + .Split(Separator, StringSplitOptions.RemoveEmptyEntries) .Select(a => a.Trim()) .ToArray(); } diff --git a/src/BaGetter.Core/Upstream/DownloadsImporter.cs b/src/BaGetter.Core/Upstream/DownloadsImporter.cs index d933f0fd..357266da 100644 --- a/src/BaGetter.Core/Upstream/DownloadsImporter.cs +++ b/src/BaGetter.Core/Upstream/DownloadsImporter.cs @@ -29,7 +29,7 @@ public DownloadsImporter( public async Task ImportAsync(CancellationToken cancellationToken) { var packageDownloads = await _downloadsSource.GetPackageDownloadsAsync(); - var packages = await _context.Packages.CountAsync(); + var packages = await _context.Packages.CountAsync(cancellationToken); var batches = (packages / BatchSize) + 1; for (var batch = 0; batch < batches; batch++) @@ -41,8 +41,8 @@ public async Task ImportAsync(CancellationToken cancellationToken) var packageId = package.Id.ToLowerInvariant(); var packageVersion = package.NormalizedVersionString.ToLowerInvariant(); - if (!packageDownloads.ContainsKey(packageId) || - !packageDownloads[packageId].ContainsKey(packageVersion)) + if (!packageDownloads.TryGetValue(packageId, out var value) || + !value.ContainsKey(packageVersion)) { continue; } diff --git a/src/BaGetter.Core/Upstream/PackageDownloadsJsonSource.cs b/src/BaGetter.Core/Upstream/PackageDownloadsJsonSource.cs index d12d5291..83d4120e 100644 --- a/src/BaGetter.Core/Upstream/PackageDownloadsJsonSource.cs +++ b/src/BaGetter.Core/Upstream/PackageDownloadsJsonSource.cs @@ -56,9 +56,10 @@ public async Task>> GetPackageDownlo continue; } - if (!results.ContainsKey(id)) + if (!results.TryGetValue(id, out var value)) { - results.Add(id, new Dictionary()); + value = new Dictionary(); + results.Add(id, value); } foreach (var token in record) @@ -67,8 +68,7 @@ public async Task>> GetPackageDownlo { var version = string.Intern(NuGetVersion.Parse(token[0].ToString()).ToNormalizedString().ToLowerInvariant()); var downloads = token[1].ToObject(); - - results[id][version] = downloads; + value[version] = downloads; } } } diff --git a/src/BaGetter.Core/Validation/RequiredIfAttribute.cs b/src/BaGetter.Core/Validation/RequiredIfAttribute.cs index b0d0500b..00732533 100644 --- a/src/BaGetter.Core/Validation/RequiredIfAttribute.cs +++ b/src/BaGetter.Core/Validation/RequiredIfAttribute.cs @@ -104,8 +104,7 @@ public override string FormatErrorMessage(string name) /// protected override ValidationResult IsValid(object value, ValidationContext validationContext) { - if (validationContext == null) - throw new ArgumentNullException(nameof(validationContext)); + ArgumentNullException.ThrowIfNull(validationContext); var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty); if (otherProperty == null) diff --git a/src/BaGetter.Database.MySql/Migrations/20181212113156_Initial.cs b/src/BaGetter.Database.MySql/Migrations/20181212113156_Initial.cs index 27c2af32..53a3114f 100644 --- a/src/BaGetter.Database.MySql/Migrations/20181212113156_Initial.cs +++ b/src/BaGetter.Database.MySql/Migrations/20181212113156_Initial.cs @@ -6,6 +6,7 @@ namespace BaGetter.Database.MySql.Migrations { public partial class Initial : Migration { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1861:Avoid constant arrays as arguments", Justification = "Generated and only runs once.")] protected override void Up(MigrationBuilder migrationBuilder) { // Since Latin1 is no longer the default, we have to add it here in, so fresh migrations still run. diff --git a/src/BaGetter.Database.PostgreSql/Migrations/20190311172227_Initial.cs b/src/BaGetter.Database.PostgreSql/Migrations/20190311172227_Initial.cs index 5db4d4c3..be8f1413 100644 --- a/src/BaGetter.Database.PostgreSql/Migrations/20190311172227_Initial.cs +++ b/src/BaGetter.Database.PostgreSql/Migrations/20190311172227_Initial.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; @@ -6,6 +6,7 @@ namespace BaGetter.Database.PostgreSql.Migrations { public partial class Initial : Migration { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1861:Avoid constant arrays as arguments", Justification = "Generated and only runs once.")] protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AlterDatabase() diff --git a/src/BaGetter.Database.SqlServer/Migrations/20180804082816_Initial.cs b/src/BaGetter.Database.SqlServer/Migrations/20180804082816_Initial.cs index 51f86ead..58ac67b9 100644 --- a/src/BaGetter.Database.SqlServer/Migrations/20180804082816_Initial.cs +++ b/src/BaGetter.Database.SqlServer/Migrations/20180804082816_Initial.cs @@ -6,6 +6,7 @@ namespace BaGetter.Database.SqlServer.Migrations { public partial class Initial : Migration { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1861:Avoid constant arrays as arguments", Justification = "Generated and only runs once.")] protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( diff --git a/src/BaGetter.Database.Sqlite/Migrations/20180804082808_Initial.cs b/src/BaGetter.Database.Sqlite/Migrations/20180804082808_Initial.cs index 074a8dbe..1a1f5cd0 100644 --- a/src/BaGetter.Database.Sqlite/Migrations/20180804082808_Initial.cs +++ b/src/BaGetter.Database.Sqlite/Migrations/20180804082808_Initial.cs @@ -5,6 +5,7 @@ namespace BaGetter.Database.Sqlite.Migrations { public partial class Initial : Migration { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1861:Avoid constant arrays as arguments", Justification = "Generated and only runs once.")] protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( diff --git a/src/BaGetter.Gcp/GoogleCloudStorageService.cs b/src/BaGetter.Gcp/GoogleCloudStorageService.cs index ae581de4..1c361bcc 100644 --- a/src/BaGetter.Gcp/GoogleCloudStorageService.cs +++ b/src/BaGetter.Gcp/GoogleCloudStorageService.cs @@ -18,8 +18,7 @@ public class GoogleCloudStorageService : IStorageService public GoogleCloudStorageService(IOptionsSnapshot options) { - if (options == null) - throw new ArgumentNullException(nameof(options)); + ArgumentNullException.ThrowIfNull(options); _bucketName = options.Value.BucketName; } diff --git a/src/BaGetter.Protocol/Catalog/CatalogProcessor.cs b/src/BaGetter.Protocol/Catalog/CatalogProcessor.cs index 61656a41..f5598651 100644 --- a/src/BaGetter.Protocol/Catalog/CatalogProcessor.cs +++ b/src/BaGetter.Protocol/Catalog/CatalogProcessor.cs @@ -133,7 +133,7 @@ private async Task ProcessPageAsync( if (newCursor.HasValue && success) { - await _cursor.SetAsync(newCursor.Value); + await _cursor.SetAsync(newCursor.Value, cancellationToken); } return success; @@ -146,12 +146,12 @@ private async Task ProcessLeafAsync(CatalogLeafItem leafItem, Cancellation { if (leafItem.IsPackageDelete()) { - var packageDelete = await _client.GetPackageDeleteLeafAsync(leafItem.CatalogLeafUrl); + var packageDelete = await _client.GetPackageDeleteLeafAsync(leafItem.CatalogLeafUrl, cancellationToken); success = await _leafProcessor.ProcessPackageDeleteAsync(packageDelete, cancellationToken); } else if (leafItem.IsPackageDetails()) { - var packageDetails = await _client.GetPackageDetailsLeafAsync(leafItem.CatalogLeafUrl); + var packageDetails = await _client.GetPackageDetailsLeafAsync(leafItem.CatalogLeafUrl, cancellationToken); success = await _leafProcessor.ProcessPackageDetailsAsync(packageDetails, cancellationToken); } else diff --git a/src/BaGetter.Protocol/Search/SearchClient.cs b/src/BaGetter.Protocol/Search/SearchClient.cs index 44294d29..38556511 100644 --- a/src/BaGetter.Protocol/Search/SearchClient.cs +++ b/src/BaGetter.Protocol/Search/SearchClient.cs @@ -28,7 +28,7 @@ public async Task SearchAsync( // See: https://github.com/loic-sharma/BaGet/issues/314 var client = await _clientfactory.GetSearchClientAsync(cancellationToken); - return await client.SearchAsync(query, skip, take, includePrerelease, includeSemVer2); + return await client.SearchAsync(query, skip, take, includePrerelease, includeSemVer2, cancellationToken); } } } diff --git a/src/BaGetter.Web/BaGetterEndpointBuilder.cs b/src/BaGetter.Web/BaGetterEndpointBuilder.cs index 5995049f..01966ccb 100644 --- a/src/BaGetter.Web/BaGetterEndpointBuilder.cs +++ b/src/BaGetter.Web/BaGetterEndpointBuilder.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using BaGetter.Web; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; @@ -5,6 +6,7 @@ namespace BaGetter { + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Would be a breaking change since it's part of the public API")] public class BaGetterEndpointBuilder { public void MapEndpoints(IEndpointRouteBuilder endpoints) diff --git a/src/BaGetter.Web/Extensions/IApplicationBuilderExtensions.cs b/src/BaGetter.Web/Extensions/IApplicationBuilderExtensions.cs index c3e55d03..04a6943e 100644 --- a/src/BaGetter.Web/Extensions/IApplicationBuilderExtensions.cs +++ b/src/BaGetter.Web/Extensions/IApplicationBuilderExtensions.cs @@ -7,7 +7,7 @@ public static class IApplicationBuilderExtensions { public static IApplicationBuilder UseOperationCancelledMiddleware(this IApplicationBuilder app) { - if (app == null) throw new ArgumentNullException(nameof(app)); + ArgumentNullException.ThrowIfNull(app); return app.UseMiddleware(); } diff --git a/src/BaGetter.Web/Pages/Package.cshtml.cs b/src/BaGetter.Web/Pages/Package.cshtml.cs index 34c1b53d..44f1e743 100644 --- a/src/BaGetter.Web/Pages/Package.cshtml.cs +++ b/src/BaGetter.Web/Pages/Package.cshtml.cs @@ -110,7 +110,7 @@ public async Task OnGetAsync(string id, string version, CancellationToken cancel PackageDownloadUrl = _url.GetPackageDownloadUrl(Package.Id, packageVersion); } - private IReadOnlyList ToDependencyGroups(Package package) + private static List ToDependencyGroups(Package package) { return package .Dependencies @@ -135,7 +135,7 @@ private IReadOnlyList ToDependencyGroups(Package package) .ToList(); } - private string PrettifyTargetFramework(string targetFramework) + private static string PrettifyTargetFramework(string targetFramework) { if (targetFramework == null) return "All Frameworks"; @@ -179,7 +179,7 @@ private string PrettifyTargetFramework(string targetFramework) return $"{frameworkName} {frameworkVersion}"; } - private IReadOnlyList ToVersions(IReadOnlyList packages, NuGetVersion selectedVersion) + private static List ToVersions(IReadOnlyList packages, NuGetVersion selectedVersion) { return packages .Select(p => new VersionModel @@ -205,7 +205,7 @@ private async Task GetReadmeHtmlStringOrNullAsync( using (var reader = new StreamReader(readmeStream)) { - readme = await reader.ReadToEndAsync(); + readme = await reader.ReadToEndAsync(cancellationToken); } } diff --git a/src/BaGetter/ConfigureBaGetterOptions.cs b/src/BaGetter/ConfigureBaGetterOptions.cs index 47d93cbb..fd473aff 100644 --- a/src/BaGetter/ConfigureBaGetterOptions.cs +++ b/src/BaGetter/ConfigureBaGetterOptions.cs @@ -111,7 +111,7 @@ public ValidateOptionsResult Validate(string name, BaGetterOptions options) $"Allowed values: {string.Join(", ", ValidSearchTypes)}"); } - if (failures.Any()) return ValidateOptionsResult.Fail(failures); + if (failures.Count != 0) return ValidateOptionsResult.Fail(failures); return ValidateOptionsResult.Success; } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c1aa91fc..469a75dd 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -21,6 +21,11 @@ $(NoWarn);1591 + + + latest-Minimum + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb diff --git a/tests/BaGetter.Tests/HostIntegrationTests.cs b/tests/BaGetter.Tests/HostIntegrationTests.cs index 5012e570..a04f3166 100644 --- a/tests/BaGetter.Tests/HostIntegrationTests.cs +++ b/tests/BaGetter.Tests/HostIntegrationTests.cs @@ -62,7 +62,7 @@ public void DefaultsToSqlite() private IServiceProvider BuildServiceProvider(Dictionary configs = null) { var host = Program - .CreateHostBuilder(new string[0]) + .CreateHostBuilder(Array.Empty()) .ConfigureAppConfiguration((ctx, config) => { config.AddInMemoryCollection(configs ?? new Dictionary());