From 82c1b9725a7d7ecd3700e37c1c9defc4827672c9 Mon Sep 17 00:00:00 2001 From: AdmiringWorm Date: Thu, 28 Nov 2024 12:08:21 +0100 Subject: [PATCH] (#37) Implement note rule CPMR0068 This implements the note rule CPMR0068 that verifies whether the owners field matches the authors field or not. This is equivalent to what is implemented in Package Validator, and should be enhanced in the future. --- ...aveAddedExpectedRegistrations.verified.txt | 1 + ...edRegistrationsInLegacyMethod.verified.txt | 1 + ...WhenAuthorAndMaintainerIsSame.verified.txt | 8 ++ ...ndMaintainerUsesDifferentCase.verified.txt | 8 ++ ...ipleSameAuthorsAndMaintainers.verified.txt | 8 ++ ...ailableRulesForImplementation.verified.txt | 8 ++ .../Rules/AuthorMatchesMaintainerRuleTests.cs | 110 ++++++++++++++++++ .../PublicAPI.Unshipped.txt | 3 + .../Rules/AuthorMatchesMaintainerRule.cs | 59 ++++++++++ 9 files changed, 206 insertions(+) create mode 100644 src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerIsSame.verified.txt create mode 100644 src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerUsesDifferentCase.verified.txt create mode 100644 src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenMultipleSameAuthorsAndMaintainers.verified.txt create mode 100644 src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldReturnAvailableRulesForImplementation.verified.txt create mode 100644 src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.cs create mode 100644 src/Chocolatey.Community.Validation/Rules/AuthorMatchesMaintainerRule.cs diff --git a/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrations.verified.txt b/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrations.verified.txt index 4a7a3b6..28dd920 100644 --- a/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrations.verified.txt +++ b/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrations.verified.txt @@ -2,6 +2,7 @@ RegisteredServices: { chocolatey.infrastructure.rules.IMetadataRule: [ Chocolatey.Community.Validation.Rules.AnyElementRules, + Chocolatey.Community.Validation.Rules.AuthorMatchesMaintainerRule, Chocolatey.Community.Validation.Rules.CopyrightElementRules, Chocolatey.Community.Validation.Rules.DependenciesElementRules, Chocolatey.Community.Validation.Rules.DescriptionElementRules, diff --git a/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrationsInLegacyMethod.verified.txt b/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrationsInLegacyMethod.verified.txt index 4a7a3b6..28dd920 100644 --- a/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrationsInLegacyMethod.verified.txt +++ b/src/Chocolatey.Community.Validation.Tests/Registration/ChocolateyCCRRegistrationModuleTests.ShouldHaveAddedExpectedRegistrationsInLegacyMethod.verified.txt @@ -2,6 +2,7 @@ RegisteredServices: { chocolatey.infrastructure.rules.IMetadataRule: [ Chocolatey.Community.Validation.Rules.AnyElementRules, + Chocolatey.Community.Validation.Rules.AuthorMatchesMaintainerRule, Chocolatey.Community.Validation.Rules.CopyrightElementRules, Chocolatey.Community.Validation.Rules.DependenciesElementRules, Chocolatey.Community.Validation.Rules.DescriptionElementRules, diff --git a/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerIsSame.verified.txt b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerIsSame.verified.txt new file mode 100644 index 0000000..7f121e9 --- /dev/null +++ b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerIsSame.verified.txt @@ -0,0 +1,8 @@ +[ + { + HelpUrl: https://ch0.co/rules/cpmr0068, + Id: CPMR0068, + Message: The package maintainer field (owners) matches the software author field (authors) in the nuspec., + Severity: Note + } +] \ No newline at end of file diff --git a/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerUsesDifferentCase.verified.txt b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerUsesDifferentCase.verified.txt new file mode 100644 index 0000000..7f121e9 --- /dev/null +++ b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenAuthorAndMaintainerUsesDifferentCase.verified.txt @@ -0,0 +1,8 @@ +[ + { + HelpUrl: https://ch0.co/rules/cpmr0068, + Id: CPMR0068, + Message: The package maintainer field (owners) matches the software author field (authors) in the nuspec., + Severity: Note + } +] \ No newline at end of file diff --git a/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenMultipleSameAuthorsAndMaintainers.verified.txt b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenMultipleSameAuthorsAndMaintainers.verified.txt new file mode 100644 index 0000000..7f121e9 --- /dev/null +++ b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldFlagRuleWhenMultipleSameAuthorsAndMaintainers.verified.txt @@ -0,0 +1,8 @@ +[ + { + HelpUrl: https://ch0.co/rules/cpmr0068, + Id: CPMR0068, + Message: The package maintainer field (owners) matches the software author field (authors) in the nuspec., + Severity: Note + } +] \ No newline at end of file diff --git a/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldReturnAvailableRulesForImplementation.verified.txt b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldReturnAvailableRulesForImplementation.verified.txt new file mode 100644 index 0000000..3d77216 --- /dev/null +++ b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.ShouldReturnAvailableRulesForImplementation.verified.txt @@ -0,0 +1,8 @@ +[ + { + Severity: Note, + Id: CPMR0068, + Summary: The package maintainer field (owners) matches the software author field (authors) in the nuspec., + HelpUrl: https://ch0.co/rules/cpmr0068 + } +] \ No newline at end of file diff --git a/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.cs b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.cs new file mode 100644 index 0000000..bad7bd6 --- /dev/null +++ b/src/Chocolatey.Community.Validation.Tests/Rules/AuthorMatchesMaintainerRuleTests.cs @@ -0,0 +1,110 @@ +namespace Chocolatey.Community.Validation.Tests.Rules +{ + using System.Threading.Tasks; + using chocolatey; + using Chocolatey.Community.Validation.Rules; + using NUnit.Framework; + + [Category("Notes")] + public class AuthorMatchesMaintainerRuleTests : RuleTestBase + { + [Test] + public async Task ShouldFlagRuleWhenAuthorAndMaintainerIsSame() + { + const string testContent = @" + + + + short-copyright + [Deprecated] Test PKG + 1.0.0 + Package-Author + Package-Author + https://test-url.com/ + + +"; + + await VerifyNuspec(testContent); + } + [Test] + public async Task ShouldFlagRuleWhenAuthorAndMaintainerUsesDifferentCase() + { + const string testContent = @" + + + + short-copyright + [Deprecated] Test PKG + 1.0.0 + Package-Author + package-author + https://test-url.com/ + + +"; + + await VerifyNuspec(testContent); + } + + [Test] + public async Task ShouldFlagRuleWhenMultipleSameAuthorsAndMaintainers() + { + const string testContent = @" + + + + short-copyright + [Deprecated] Test PKG + 1.0.0 + Package-Author Package-Maintainer + Package-Author Package-Maintainer + https://test-url.com/ + + +"; + + await VerifyNuspec(testContent); + } + + [TestCaseSource(nameof(EmptyTestValues))] + public async Task ShouldNotFlagOnEmptyValues(string value) + { + const string testContent = @" + + + + short-copyright + [Deprecated] Test PKG + 1.0.0 + {0} + {0} + https://test-url.com/ + + +"; + + await VerifyEmptyResults(testContent.FormatWith(value)); + } + + [Test] + public async Task ShouldNotFlagWhenDifferentMaintanerCountThanAuthors() + { + const string testContent = @" + + + + short-copyright + [Deprecated] Test PKG + 1.0.0 + Package-Author + Package-Author Package-Maintainer + https://test-url.com/ + + +"; + + await VerifyEmptyResults(testContent); + } + } +} diff --git a/src/Chocolatey.Community.Validation/PublicAPI.Unshipped.txt b/src/Chocolatey.Community.Validation/PublicAPI.Unshipped.txt index 68cd5a9..430af42 100644 --- a/src/Chocolatey.Community.Validation/PublicAPI.Unshipped.txt +++ b/src/Chocolatey.Community.Validation/PublicAPI.Unshipped.txt @@ -6,6 +6,8 @@ Chocolatey.Community.Validation.Registration.ChocolateyCCRRegistrationModule.Reg Chocolatey.Community.Validation.Registration.ChocolateyCCRRegistrationModule.register_dependencies(chocolatey.infrastructure.app.registration.IContainerRegistrator! registrator, chocolatey.infrastructure.app.configuration.ChocolateyConfiguration? configuration) -> void Chocolatey.Community.Validation.Rules.AnyElementRules Chocolatey.Community.Validation.Rules.AnyElementRules.AnyElementRules() -> void +Chocolatey.Community.Validation.Rules.AuthorMatchesMaintainerRule +Chocolatey.Community.Validation.Rules.AuthorMatchesMaintainerRule.AuthorMatchesMaintainerRule() -> void Chocolatey.Community.Validation.Rules.CCRMetadataRuleBase Chocolatey.Community.Validation.Rules.CCRMetadataRuleBase.CCRMetadataRuleBase() -> void Chocolatey.Community.Validation.Rules.CopyrightElementRules @@ -29,6 +31,7 @@ Chocolatey.Community.Validation.Validations.CommunityRepositoryPushValidation.Co Chocolatey.Community.Validation.Validations.CommunityRepositoryPushValidation.Validate(chocolatey.infrastructure.app.configuration.ChocolateyConfiguration! config) -> System.Collections.Generic.ICollection! Chocolatey.Community.Validation.Validations.CommunityRepositoryPushValidation.validate(chocolatey.infrastructure.app.configuration.ChocolateyConfiguration! config) -> System.Collections.Generic.ICollection! override Chocolatey.Community.Validation.Rules.AnyElementRules.Validate(NuGet.Packaging.NuspecReader! reader) -> System.Collections.Generic.IEnumerable! +override Chocolatey.Community.Validation.Rules.AuthorMatchesMaintainerRule.Validate(NuGet.Packaging.NuspecReader! reader) -> System.Collections.Generic.IEnumerable! override Chocolatey.Community.Validation.Rules.CopyrightElementRules.Validate(NuGet.Packaging.NuspecReader! reader) -> System.Collections.Generic.IEnumerable! override Chocolatey.Community.Validation.Rules.DependenciesElementRules.Validate(NuGet.Packaging.NuspecReader! reader) -> System.Collections.Generic.IEnumerable! override Chocolatey.Community.Validation.Rules.DescriptionElementRules.Validate(NuGet.Packaging.NuspecReader! reader) -> System.Collections.Generic.IEnumerable! diff --git a/src/Chocolatey.Community.Validation/Rules/AuthorMatchesMaintainerRule.cs b/src/Chocolatey.Community.Validation/Rules/AuthorMatchesMaintainerRule.cs new file mode 100644 index 0000000..6e817cb --- /dev/null +++ b/src/Chocolatey.Community.Validation/Rules/AuthorMatchesMaintainerRule.cs @@ -0,0 +1,59 @@ +namespace Chocolatey.Community.Validation.Rules +{ + using System; + using System.Collections.Generic; + using System.Linq; + using chocolatey.infrastructure.rules; + + public sealed class AuthorMatchesMaintainerRule : CCRMetadataRuleBase + { + private const string RuleId = "CPMR0068"; + + public override IEnumerable Validate(global::NuGet.Packaging.NuspecReader reader) + { + if (reader is null) + { + throw new ArgumentNullException(nameof(reader)); + } + + var owners = GetNormalized(reader.GetOwners()).ToArray(); + var authors = GetNormalized(reader.GetAuthors()).ToArray(); + + if (owners.Length != authors.Length || owners.Length == 0) + { + yield break; + } + + for (var i = 0; i < owners.Length; i++) + { + if (string.Compare(owners[i], authors[i], StringComparison.OrdinalIgnoreCase) != 0) + { + yield break; + } + } + + yield return GetRule(RuleId); + } + + protected internal override IEnumerable<(RuleType severity, string? id, string summary)> GetRulesInformation() + { + yield return (RuleType.Note, RuleId, "The package maintainer field (owners) matches the software author field (authors) in the nuspec."); + } + + private static IEnumerable GetNormalized(string content) + { + if (string.IsNullOrWhiteSpace(content)) + { + yield break; + } + + foreach (var owner in content.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)) + { + if (!string.IsNullOrWhiteSpace(owner)) + { + yield return owner; + } + } + } + } +}