RepoAuditor 0.4.4__tar.gz → 0.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. {repoauditor-0.4.4 → repoauditor-0.4.6}/PKG-INFO +1 -1
  2. repoauditor-0.4.6/docs/custom_plugin.md +454 -0
  3. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/index.md +1 -0
  4. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/usage.md +0 -1
  5. {repoauditor-0.4.4 → repoauditor-0.4.6}/mkdocs.yml +13 -3
  6. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/EntryPoint.py +13 -5
  7. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/EnableRequirementImpl.py +4 -1
  8. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/ValueRequirementImpl.py +4 -1
  9. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Requirement.py +3 -2
  10. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/__init__.py +1 -1
  11. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/EnableRulesetRequirementImpl_UnitTest.py +22 -0
  12. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/BlockMainlineForcePushesRule_UnitTest.py +1 -2
  13. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/DismissStalePullRequestApprovalsRule_UnitTest.py +25 -7
  14. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/EnsureStatusChecksRule_UnitTest.py +1 -2
  15. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireApprovalMostRecentPushRule_UnitTest.py +26 -7
  16. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireApprovalsRule_UnitTest.py +26 -7
  17. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireCodeOwnerReviewRule_UnitTest.py +26 -7
  18. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireConversationResolutionRule_UnitTest.py +26 -7
  19. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequirePullRequests_UnitTest.py +1 -2
  20. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireSignedCommits_UnitTest.py +1 -2
  21. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireStatusChecksToPassRule_UnitTest.py +1 -2
  22. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireUpToDateBranchesRule_UnitTest.py +26 -7
  23. repoauditor-0.4.6/tests/Plugins/GitHubModule/StandardRequirements/StandardValueRequirementImpl_UnitTest.py +54 -0
  24. {repoauditor-0.4.4 → repoauditor-0.4.6}/uv.lock +6 -3
  25. {repoauditor-0.4.4 → repoauditor-0.4.6}/.copier-answers.yml +0 -0
  26. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/CODEOWNERS +0 -0
  27. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  28. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  29. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/custom.md +0 -0
  30. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  31. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/actions/release_impl/action.yml +0 -0
  32. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/pull_request_template.md +0 -0
  33. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/release_sources.yaml +0 -0
  34. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/CICD.yml +0 -0
  35. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/CICD_impl.yml +0 -0
  36. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/EndToEnd.yml +0 -0
  37. {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/codeql.yml +0 -0
  38. {repoauditor-0.4.4 → repoauditor-0.4.6}/.gitignore +0 -0
  39. {repoauditor-0.4.4 → repoauditor-0.4.6}/.pre-commit-config.yaml +0 -0
  40. {repoauditor-0.4.4 → repoauditor-0.4.6}/.python-version +0 -0
  41. {repoauditor-0.4.4 → repoauditor-0.4.6}/CODE_OF_CONDUCT.md +0 -0
  42. {repoauditor-0.4.4 → repoauditor-0.4.6}/GOVERNANCE.md +0 -0
  43. {repoauditor-0.4.4 → repoauditor-0.4.6}/LICENSE +0 -0
  44. {repoauditor-0.4.4 → repoauditor-0.4.6}/MAINTAINERS.md +0 -0
  45. {repoauditor-0.4.4 → repoauditor-0.4.6}/README.md +0 -0
  46. {repoauditor-0.4.4 → repoauditor-0.4.6}/SECURITY.md +0 -0
  47. {repoauditor-0.4.4 → repoauditor-0.4.6}/default_config.yaml +0 -0
  48. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/CONTRIBUTING.md +0 -0
  49. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/DEVELOPMENT.md +0 -0
  50. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/PAT.md +0 -0
  51. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/config_file.md +0 -0
  52. {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/troubleshooting.md +0 -0
  53. {repoauditor-0.4.4 → repoauditor-0.4.6}/minisign_key.pub +0 -0
  54. {repoauditor-0.4.4 → repoauditor-0.4.6}/pyproject.toml +0 -0
  55. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/AutoGitSemVer.yaml +0 -0
  56. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/CommandLineProcessor.py +0 -0
  57. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Display.py +0 -0
  58. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/ExecuteModules.py +0 -0
  59. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Impl/ParallelSequentialProcessor.py +0 -0
  60. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Impl/__init__.py +0 -0
  61. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Module.py +0 -0
  62. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugin.py +0 -0
  63. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/CommunityStandardsQuery.py +0 -0
  64. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Impl/ExistsRequirementImpl.py +0 -0
  65. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Impl/__init__.py +0 -0
  66. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Module.py +0 -0
  67. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/CodeOfConduct.py +0 -0
  68. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/CodeOwners.py +0 -0
  69. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/Contributing.py +0 -0
  70. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/IssueTemplates.py +0 -0
  71. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/LicenseFile.py +0 -0
  72. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/PullRequestTemplates.py +0 -0
  73. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/ReadMe.py +0 -0
  74. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/SecurityPolicy.py +0 -0
  75. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/__init__.py +0 -0
  76. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/__init__.py +0 -0
  77. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandardsPlugin.py +0 -0
  78. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionQuery.py +0 -0
  79. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/AllowDeletions.py +0 -0
  80. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/AllowMainlineForcePushes.py +0 -0
  81. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/DismissStalePullRequestApprovals.py +0 -0
  82. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/DoNotAllowBypassSettings.py +0 -0
  83. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/EnsureStatusChecks.py +0 -0
  84. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/Impl/ClassicEnableRequirementImpl.py +0 -0
  85. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/Impl/ClassicValueRequirementImpl.py +0 -0
  86. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/Impl/__init__.py +0 -0
  87. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireApprovalMostRecentPush.py +0 -0
  88. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireApprovals.py +0 -0
  89. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireCodeOwnerReview.py +0 -0
  90. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireConversationResolution.py +0 -0
  91. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireLinearHistory.py +0 -0
  92. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequirePullRequests.py +0 -0
  93. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireSignedCommits.py +0 -0
  94. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireStatusChecksToPass.py +0 -0
  95. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireUpToDateBranches.py +0 -0
  96. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/__init__.py +0 -0
  97. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/DefaultBranchQuery.py +0 -0
  98. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/DefaultBranchRequirements/Protected.py +0 -0
  99. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/DefaultBranchRequirements/__init__.py +0 -0
  100. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/Common.py +0 -0
  101. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/EnableRulesetRequirementImpl.py +0 -0
  102. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/__init__.py +0 -0
  103. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Module.py +0 -0
  104. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetQuery.py +0 -0
  105. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/BlockMainlineForcePushes.py +0 -0
  106. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/DismissStalePullRequestApprovals.py +0 -0
  107. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/EnsureStatusChecks.py +0 -0
  108. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireApprovalMostRecentPush.py +0 -0
  109. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireApprovals.py +0 -0
  110. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireCodeOwnerReview.py +0 -0
  111. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireCodeScanningResults.py +0 -0
  112. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireConversationResolution.py +0 -0
  113. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireLinearHistory.py +0 -0
  114. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequirePullRequests.py +0 -0
  115. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireSignedCommits.py +0 -0
  116. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireStatusChecksToPass.py +0 -0
  117. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireSuccessfulDeployments.py +0 -0
  118. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireUpToDateBranches.py +0 -0
  119. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RestrictCreations.py +0 -0
  120. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RestrictDeletions.py +0 -0
  121. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RestrictUpdates.py +0 -0
  122. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/__init__.py +0 -0
  123. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardQuery.py +0 -0
  124. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/AutoMerge.py +0 -0
  125. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/DefaultBranch.py +0 -0
  126. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/DeleteHeadBranches.py +0 -0
  127. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/DependabotSecurityUpdates.py +0 -0
  128. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Description.py +0 -0
  129. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Impl/StandardEnableRequirementImpl.py +0 -0
  130. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Impl/StandardValueRequirementImpl.py +0 -0
  131. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Impl/__init__.py +0 -0
  132. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/License.py +0 -0
  133. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/MergeCommit.py +0 -0
  134. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/MergeCommitMessage.py +0 -0
  135. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Private.py +0 -0
  136. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/RebaseMergeCommit.py +0 -0
  137. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SecretScanning.py +0 -0
  138. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SecretScanningPushProtection.py +0 -0
  139. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SquashCommitMerge.py +0 -0
  140. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SquashMergeCommitMessage.py +0 -0
  141. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SuggestUpdatingPullRequestBranches.py +0 -0
  142. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportDiscussions.py +0 -0
  143. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportIssues.py +0 -0
  144. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportProjects.py +0 -0
  145. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportWikis.py +0 -0
  146. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/TemplateRepository.py +0 -0
  147. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/WebCommitSignoff.py +0 -0
  148. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/__init__.py +0 -0
  149. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/__init__.py +0 -0
  150. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHubBase/Module.py +0 -0
  151. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHubBase/__init__.py +0 -0
  152. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHubPlugin.py +0 -0
  153. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/Module.py +0 -0
  154. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/Requirements/Citation.py +0 -0
  155. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/Requirements/__init__.py +0 -0
  156. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/ScientificSoftwareQuery.py +0 -0
  157. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/__init__.py +0 -0
  158. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftwarePlugin.py +0 -0
  159. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/__init__.py +0 -0
  160. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Query.py +0 -0
  161. {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/py.typed +0 -0
  162. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/CommandLineProcessor_UnitTest.py +0 -0
  163. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/EntryPoint_UnitTest.py +0 -0
  164. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/ExecuteModules_UnitTest.py +0 -0
  165. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Module_UnitTest.py +0 -0
  166. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugin_UnitTest.py +0 -0
  167. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards/CommunityStandardsQuery_UnitTest.py +0 -0
  168. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards/CommunityStandards_UnitTest.py +0 -0
  169. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards/ExistsRequirementImpl_UnitTest.py +0 -0
  170. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards_EndToEndTest.py +0 -0
  171. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubEnterprise_EndToEndTest.py +0 -0
  172. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionQuery_UnitTest.py +0 -0
  173. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/DismissStalePullRequestApprovals_UnitTest.py +0 -0
  174. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/EnsureStatusChecks_UnitTest.py +0 -0
  175. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireApprovalMostRecentPush_UnitTest.py +0 -0
  176. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireApprovals_UnitTest.py +0 -0
  177. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireCodeOwnerReview_UnitTest.py +0 -0
  178. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireUpToDateBranches_UnitTest.py +0 -0
  179. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicValueRequirementImpl_UnitTest.py +0 -0
  180. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/DefaultBranchQuery_UnitTest.py +0 -0
  181. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/DefaultBranchRequirements/Protected_UnitTest.py +0 -0
  182. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/GitHubModule_UnitTest.py +0 -0
  183. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/GitHubSession_UnitTest.py +0 -0
  184. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/__init__.py +0 -0
  185. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulsesetQuery_UnitTest.py +0 -0
  186. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardQuery_UnitTest.py +0 -0
  187. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/DependabotSecurityUpdates_UnitTest.py +0 -0
  188. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/Description_UnitTest.py +0 -0
  189. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/License_UnitTest.py +0 -0
  190. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/MergeCommitMessage_UnitTest.py +0 -0
  191. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/Private_UnitTest.py +0 -0
  192. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/SecretScanningPushProtection_UnitTest.py +0 -0
  193. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/SecretScanning_UnitTest.py +0 -0
  194. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/SquashMergeCommitMessage_UnitTest.py +0 -0
  195. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ValueRequrementImpl_UnitTest.py +0 -0
  196. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/fixtures.py +0 -0
  197. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHub_EndToEndTest.py +0 -0
  198. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/ScientificSoftware/ScientificSoftwareQuery_UnitTest.py +0 -0
  199. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/ScientificSoftware_EndToEndTest.py +0 -0
  200. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/CommunityStandards_EndToEndTest.ambr +0 -0
  201. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/GitHubEnterprise_EndToEndTest.ambr +0 -0
  202. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/GitHub_EndToEndTest.ambr +0 -0
  203. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/ScientificSoftware_EndToEndTest.ambr +0 -0
  204. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/conftest.py +0 -0
  205. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/utilities.py +0 -0
  206. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Query_UnitTest.py +0 -0
  207. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Requirement_UnitTest.py +0 -0
  208. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/__init__.py +0 -0
  209. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/__snapshots__/ExecuteModules_UnitTest.ambr +0 -0
  210. {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: RepoAuditor
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: Add your description here
5
5
  Project-URL: Homepage, https://github.com/gt-sse-center/RepoAuditor
6
6
  Project-URL: Documentation, https://github.com/gt-sse-center/RepoAuditor
@@ -0,0 +1,454 @@
1
+ # Writing a Custom Plugin
2
+
3
+ RepoAuditor allows for extensibility by using a plugin system powered by [pluggy](https://pluggy.readthedocs.io/en/stable/).
4
+
5
+ On this page, you will learn the steps involved to develop custom, 3rd party plugins to extend RepoAuditor's functionality for your own use cases.
6
+
7
+ ## Setup
8
+
9
+ To setup, create a new repository. We will call ours `ReadmeCheck` and we will use it to check if a repository has a `README.md` file.
10
+ We know this is a bit redundant but it lays the groundwork for this tutorial.
11
+
12
+ Follow the below commands to setup the basic skeleton project.
13
+
14
+ <!-- termynal -->
15
+ ```sh
16
+ $ mkdir ReadmeCheck && cd ReadmeCheck
17
+ $ touch pyproject.toml README.md
18
+ $ mkdir ReadmeCheck
19
+ $ touch ReadmeCheck/__init__.py
20
+ ```
21
+
22
+ We first start off by creating a very simple `pyproject.toml` file.
23
+
24
+ ```toml title="pyproject.toml"
25
+ [project]
26
+ name = "ReadmeCheck"
27
+ description = "Custom plugin tutorial for RepoAuditor"
28
+ readme = "README.md"
29
+ authors = [
30
+ { name = "Scientific Software Engineering Center at Georgia Tech", email = "sse-center@gatech.edu" },
31
+ ]
32
+ requires-python = ">= 3.10"
33
+ dependencies = [
34
+ "dbrownell-common>=0.16.0",
35
+ "gitpython>=3.1.44",
36
+ "RepoAuditor>=0.4.4",
37
+ ]
38
+ version = "0.0.0"
39
+ classifiers = [
40
+ "Operating System :: MacOS",
41
+ "Operating System :: Microsoft :: Windows",
42
+ "Operating System :: POSIX :: Linux",
43
+ "Programming Language :: Python",
44
+ "Programming Language :: Python :: 3.13",
45
+ "Programming Language :: Python :: 3.12",
46
+ "Programming Language :: Python :: 3.11",
47
+ "Programming Language :: Python :: 3.10",
48
+ ]
49
+
50
+ [project.license]
51
+ text = "MIT"
52
+
53
+ [project.entry-points.RepoAuditor]
54
+ ReadmCheckPlugin = "ReadmeCheck.Plugin"
55
+
56
+ [build-system]
57
+ requires = ["hatchling"]
58
+ build-backend = "hatchling.build"
59
+ ```
60
+
61
+ ## Plugin Components
62
+
63
+ For any RepoAuditor plugin, we need 4 files:
64
+
65
+ 1. `Requirement.py`: Contains the class which inherits from `Requirement`, with the logic to actually check if the requirement is fulfilled.
66
+ 2. `Query.py`: Has a class which inherits from `Query` and obtains the necessary data which is then passed to the requirement(s).
67
+ 3. `Module.py`: A file with a class which inherits from `Module` and defines the dynamic arguments and which queries to execute for obtaining necessary data.
68
+ 4. `Plugin.py`: Tells `pluggy` how to load the plugin so RepoAuditor can see it.
69
+
70
+ The files would be organized as:
71
+
72
+ ```sh
73
+ $ tree
74
+ .
75
+ ├── README.md
76
+ ├── ReadmeCheck
77
+ │   ├── Module.py
78
+ │   ├── Plugin.py
79
+ │   ├── Query.py
80
+ │   ├── Requirement.py
81
+ │   └── __init__.py
82
+ └── pyproject.toml
83
+ ```
84
+
85
+ ### Requirement
86
+
87
+ To define a requirement, we define a class which inherits from `Requirement` and implements three methods:
88
+
89
+ 1. `__init__(self) -> None`: The constructor.
90
+ 2. `GetDynamicArgDefinitions(self, argument_separator: str) -> dict[str, TypeDefinitionItemType]`: A method which defines requirement specific dynamic arguments.
91
+ 3. `_EvaluateImpl(self, query_data: dict[str, Any], requirement_args: dict[str, Any], ) -> Requirement.EvaluateImplResult`: The logic for verifying the requirement.
92
+
93
+ The definition for the `Requirement.py` file would be the following. Please see the inline comments for details about specific lines of code.
94
+
95
+ ```python title="ReadmeCheck/Requirement.py"
96
+ """Contains the Readme object."""
97
+
98
+ import textwrap
99
+ from pathlib import Path
100
+ from typing import Any
101
+
102
+ import typer
103
+ from dbrownell_Common.TyperEx import \
104
+ TypeDefinitionItemType # type: ignore[import-untyped]
105
+ from dbrownell_Common.Types import override # type: ignore[import-untyped]
106
+ from RepoAuditor.Requirement import EvaluateResult, ExecutionStyle, Requirement
107
+
108
+
109
+ class ReadmeRequirement(Requirement):
110
+ """Requirement to validate a repository's README."""
111
+
112
+ def __init__(self) -> None:
113
+ super().__init__(
114
+ name="Readme",
115
+ description="Validates if the repository has a README.",
116
+ style=ExecutionStyle.Parallel,
117
+
118
+ # This is the template for the instructions to resolve
119
+ # a failure caused by this requirement.
120
+ resolution_template=textwrap.dedent(
121
+ """\
122
+ Add a README file to the repository.
123
+ """, ),
124
+
125
+ # This is the template for the rationale behind why this
126
+ # requirement is good to have.
127
+ rationale_template=textwrap.dedent(
128
+ """\
129
+ A README file helps users understand the project.
130
+ """, ),
131
+
132
+ # Flag which decides if an explicit include from the
133
+ # command line is required to enable this requirement.
134
+ requires_explicit_include=False,
135
+ )
136
+
137
+ # Flag deciding if the requirement is enabled by default.
138
+ # Here the default is set to True, but this can be a constructor argument.
139
+ # There might be a case where you wish to opt-out of checking for the
140
+ # README by default, e.g. the docs exist in a separate wiki or service,
141
+ # in which case `self.enabled_by_default` can be set to False.
142
+ self.enabled_by_default: bool = True
143
+
144
+ # The dynamic_arg_name is used in defining the dynamic
145
+ # argument `ReadmeCheck-no-Readme`.
146
+ # We use `no` since the requirement is enabled by default.
147
+ # It is possible that an organization does not care about having a README (perhaps the docs are stored on a wiki),
148
+ # in that case, specifying `--ReadmeCheck-no-Readme` will disable this check.
149
+ self.dynamic_arg_name: str = "no"
150
+
151
+ @override
152
+ def GetDynamicArgDefinitions(
153
+ self,
154
+ argument_separator: str) -> dict[str, TypeDefinitionItemType]:
155
+ """Get the definitions for the arguments to this requirement."""
156
+
157
+ # Defines a dynamic argument `ReadmeCheck-no-Readme` which if enabled
158
+ # allows the requirement to pass if no README file is available.
159
+ return {
160
+ f"{self.dynamic_arg_name}{argument_separator}{self.name}": (
161
+ bool,
162
+ typer.Option(
163
+ default=False,
164
+ help="Allow missing README.",
165
+ ),
166
+ ),
167
+ }
168
+
169
+ @override
170
+ def _EvaluateImpl(
171
+ self,
172
+ query_data: dict[str, Any],
173
+ requirement_args: dict[str, Any],
174
+ ) -> Requirement.EvaluateImplResult:
175
+ """Implementation of the reqiurement evaluation.
176
+
177
+ query_data: The data obtained from the query class (detailed next).
178
+ requirement_args: Reqiurement specific dynamic arguments.
179
+ """
180
+
181
+ # Flag to check if the file exists.
182
+ check_exists: bool = self.enabled_by_default
183
+
184
+ # Check if the dynamic argument is provided,
185
+ # which toggles the check flag
186
+ if requirement_args.get(self.dynamic_arg_name, False):
187
+ check_exists = not check_exists
188
+
189
+ # Get full file path in temporary repo directory and
190
+ # check if it exists.
191
+ # This checks both upper case and lower case variants
192
+ full_file_path = Path(query_data["repo_dir"].name) / "README.md"
193
+
194
+ # If check flag is enabled, we perform the check.
195
+ # The repository directory is in `query_data[repo_dir]`
196
+ # thanks to the Query.py file.
197
+ if check_exists:
198
+
199
+ # If the README file exists, return success.
200
+ if full_file_path.exists():
201
+ return Requirement.EvaluateImplResult(
202
+ EvaluateResult.Success,
203
+ "README found inside the repository",
204
+ )
205
+
206
+ # Else return error.
207
+ return Requirement.EvaluateImplResult(
208
+ EvaluateResult.Error,
209
+ "No README file found.",
210
+ provide_resolution=True,
211
+ provide_rationale=True,
212
+ )
213
+
214
+ # Else check if README does not exist.
215
+ else:
216
+ # If the README file exists, return error.
217
+ if full_file_path.exists():
218
+ return Requirement.EvaluateImplResult(
219
+ EvaluateResult.Error,
220
+ "A README was found inside the repository",
221
+ )
222
+
223
+ # Else return success
224
+ return Requirement.EvaluateImplResult(
225
+ EvaluateResult.Success,
226
+ "No README file was found as expected.",
227
+ provide_resolution=True,
228
+ provide_rationale=True,
229
+ )
230
+
231
+
232
+ # Requirement not enabled, so return DoesNotApply
233
+ return Requirement.EvaluateImplResult(EvaluateResult.DoesNotApply,
234
+ None)
235
+
236
+ ```
237
+
238
+ ### Query
239
+
240
+ The `Query.py` file has a class `ReadmeQuery` whose responsibility is to obtain the data needed by the requirement.
241
+ Since multiple requirements may need ths same data, the `ReadmeQuery` runs once to obtain the data and passes it to all the requirements, thus reducing redundant operations.
242
+
243
+ `ReadmeQuery` needs to inherit from `Query` and implement 2 methods:
244
+
245
+ 1. `__init__(self) -> None`: The constructor.
246
+ 2. `def GetData(self, module_data: dict[str, Any]) -> Optional[dict[str, Any]]`: The method which performs the act of obtaining the necessary data.
247
+
248
+ The `ReadmeQuery` file can thus be defined as:
249
+
250
+ ```python title="ReadmeCheck/Query.py"
251
+ """Contains the ReadmeQuery class."""
252
+
253
+ from tempfile import TemporaryDirectory
254
+ from typing import Any, Optional
255
+
256
+ from dbrownell_Common.Types import override # type: ignore[import-untyped]
257
+ from RepoAuditor.Query import ExecutionStyle, Query
258
+
259
+ from ReadmeCheck.Requirement import ReadmeRequirement
260
+
261
+ class ReadmeQuery(Query):
262
+ """Query with requirements that operate on a repository directory."""
263
+
264
+ # ----------------------------------------------------------------------
265
+ def __init__(self) -> None:
266
+ super().__init__(
267
+ "ReadmeQuery",
268
+ ExecutionStyle.Parallel,
269
+
270
+ # Note that we can specify multiple requirements here,
271
+ # all of which will get the same queried data.
272
+ [
273
+ ReadmeRequirement(),
274
+ ],
275
+ )
276
+
277
+ @override
278
+ def GetData(
279
+ self,
280
+ module_data: dict[str, Any],
281
+ ) -> Optional[dict[str, Any]]:
282
+ """Get the repository directory."""
283
+
284
+ # Clone the git repository to a temp directory
285
+ branch = module_data.get("branch", "main")
286
+ temp_repo_dir = TemporaryDirectory()
287
+ url = module_data["url"]
288
+
289
+ # Import git.Repo here so that it is only imported
290
+ # if the plugin is requested.
291
+ from git import Repo
292
+
293
+ # Use the Repo class to clone the repository
294
+ Repo.clone_from(url, temp_repo_dir.name, branch=branch)
295
+
296
+ # Record the path and the temp directory for later cleanup
297
+ module_data["repo_dir"] = temp_repo_dir
298
+
299
+ # This module_data is passed to each requirement as `query_data`.
300
+ return module_data
301
+
302
+ ```
303
+
304
+ ### Module
305
+
306
+ The `Module.py` file is the final file needed for implementing functionality.
307
+ This file has a class which inherits from `Module` and sets up common dynamic arguments, such as the repository `url`, and the initial module data.
308
+
309
+ We are required to implement 3 methods:
310
+
311
+ 1. `__init__(self) -> None`: The constructor.
312
+ 2. `GetDynamicArgDefinitions(self) -> dict[str, TypeDefinitionItemType]`: A method which defines module-level arguments which can be specified at runtime.
313
+ 3. `GenerateInitialData(self, dynamic_args: dict[str, Any]) -> Optional[dict[str, Any]]`: A method that executes before the data queries are made. Allows one to set up proper permissions and defaults.
314
+
315
+ We define a class `ReadmeCheckModule` which implements these methods as
316
+
317
+ ```python title="ReadmeCheck/Module.py"
318
+ """Contains the ReadmeCheckModule object."""
319
+
320
+ from typing import Any, Optional
321
+
322
+ import typer
323
+ from dbrownell_Common.TyperEx import \
324
+ TypeDefinitionItemType # type: ignore[import-untyped]
325
+ from dbrownell_Common.Types import override # type: ignore[import-untyped]
326
+ from RepoAuditor.Module import ExecutionStyle, Module
327
+
328
+ from ReadmeCheck.Query import ReadmeQuery
329
+
330
+
331
+ class ReadmeCheckModule(Module):
332
+ """Module for validating presence of a README file."""
333
+
334
+ def __init__(self) -> None:
335
+ super().__init__(
336
+ "ReadmeCheck",
337
+ "Validates presence of README file.",
338
+ ExecutionStyle.Parallel,
339
+
340
+ # This architecture allows us to specify multiple queries,
341
+ # each of which can run in parallel.
342
+ [
343
+ ReadmeQuery(),
344
+ ],
345
+ requires_explicit_include=True,
346
+ )
347
+
348
+ @override
349
+ def GetDynamicArgDefinitions(self) -> dict[str, TypeDefinitionItemType]:
350
+ """Get the definitions for the arguments to this requirement."""
351
+ # Here we define the URL which is used to get the repository data.
352
+ return {
353
+ "url": (
354
+ str,
355
+ typer.Option(
356
+ ...,
357
+ help=
358
+ "[REQUIRED] Github URL (e.g. https://github.com/gt-sse-center/RepoAuditor)",
359
+ ),
360
+ ),
361
+ }
362
+
363
+ @override
364
+ def GenerateInitialData(
365
+ self, dynamic_args: dict[str, Any]) -> Optional[dict[str, Any]]:
366
+ """Generate the initial data to be used in the `dynamic_args`, such as session info, etc."""
367
+ # Nothing to do so we just return the dynamic_args.
368
+ # In alternative cases, we can set up things like
369
+ # the GitHub personal access token here.
370
+ return dynamic_args
371
+
372
+ ```
373
+
374
+ ### Plugin
375
+
376
+ This is the easiest file, and has the code which tells `pluggy` how to load our custom plugin into `RepoAuditor`.
377
+
378
+ ```python title="ReadmeCheck/Plugin.py"
379
+ """Contains Plugin functionality."""
380
+
381
+ import pluggy
382
+ from RepoAuditor import APP_NAME
383
+ from RepoAuditor.Module import Module
384
+
385
+ from ReadmeCheck.Module import ReadmeCheckModule
386
+
387
+
388
+ @pluggy.HookimplMarker(APP_NAME)
389
+ def GetModule() -> Module:
390
+ """Return ReadMeCheck Module."""
391
+ return ReadmeCheckModule()
392
+ ```
393
+
394
+ Here, we simply load the app name from `RepoAuditor` as `APP_NAME` and implement a `GetModule` function with the `@pluggy.HookimplMarker` decorator.
395
+
396
+ All the `GetModule` function has to do is return the appropriate module, a.k.a. `ReadmeCheckModule()`.
397
+
398
+ ## Running the Plugin
399
+
400
+ At this point, we are ready to execute our custom plugin.
401
+
402
+ We start by installing the package locally.
403
+
404
+ <!-- termynal -->
405
+ ```sh
406
+ $ uv pip install -e .
407
+ Resolved 25 packages in 13ms
408
+ Built readmecheck @ file:///Users/gt-sse-center/gt-sse/ReadmeCheck
409
+ Prepared 1 package in 648ms
410
+ Uninstalled 1 package in 1ms
411
+ Installed 1 package in 2ms
412
+ ~ readmecheck==0.0.0 (from file:///Users/gt-sse-center/gt-sse/ReadmeCheck)
413
+ ```
414
+
415
+ After this, we simply call `RepoAuditor` and specify our custom module in the `--include` flag, which should yield the following result:
416
+
417
+ <!-- termynal -->
418
+ ```sh
419
+ $ uv run repoauditor --include ReadmeCheck \
420
+ --ReadmeCheck-url https://github.com/gt-sse-center/RepoAuditor
421
+ Processing 1 module...
422
+ Processing 'ReadmeCheck' (1 of 1)...
423
+ Processing 'ReadmeCheck' (1 of 1)...DONE! (0, 0:00:00.697564):00 ✅
424
+ DONE! (0, 0:00:00.697655)
425
+ ╭─────────────────────────── ReadmeCheck ───────────────────────────╮
426
+ │ │
427
+ │ Validates presence of README file. │
428
+ │ │
429
+ │ │
430
+ │ Successful: 1 (100.00%) │
431
+ │ Warnings: 0 (0.00%) │
432
+ │ Errors: 0 (0.00%) │
433
+ │ Skipped: 0 (0.00%) │
434
+ │ │
435
+ │ ╭─ Metrics ─────────────────────────────────────────────────────╮ │
436
+ │ │ │ │
437
+ │ │ │ │
438
+ │ │ Successful: 1 (100.00%) │ │
439
+ │ │ Warnings: 0 (0.00%) │ │
440
+ │ │ Errors: 0 (0.00%) │ │
441
+ │ │ Skipped: 0 (0.00%) │ │
442
+ │ │ │ │
443
+ │ ╰───────────────────────────────────────────────────────────────╯ │
444
+ │ │
445
+ ╰───────────────────────────────────────────────────────────────────╯
446
+ ```
447
+
448
+ This indicates that our plugin was successfully loaded and executed by `RepoAuditor`.
449
+
450
+ ## Conclusion
451
+
452
+ In this tutorial, we learned how to write a custom plugin for `RepoAuditor`, and all the necessary modules and classes we need to implement our plugin.
453
+
454
+ While the plugin detailed here performs a very basic check for the README file, the tutorial demonstrated the steps needed to get started. Actual functionality can be customized as per the needs of an application or organization, and `RepoAuditor` can efficiently execute the plugin.
@@ -15,6 +15,7 @@ We only support Python 3.10 and above. Please make sure you have a suitable pyth
15
15
  - [Personal Access Token](PAT.md)
16
16
  - [Example Usage](usage.md)
17
17
  - [Config File](config_file.md)
18
+ - [Writing a Custom Plugin](custom_plugin.md)
18
19
  - [Development](development)
19
20
 
20
21
  ## Installation
@@ -31,7 +31,6 @@ uvx repoauditor --include GitHub \
31
31
  ```
32
32
 
33
33
  `RepoAuditor` will generate a series of messages describing all the issues in the repository, along with the rationale behind them and the steps for resolution.
34
-
35
34
  <br/>
36
35
 
37
36
  ## Custom Settings
@@ -2,9 +2,10 @@ site_name: RepoAuditor
2
2
  site_description: Audits repositories for best practices.
3
3
  nav:
4
4
  - Home: index.md
5
- - PAT: PAT.md
5
+ - Personal Access Token: PAT.md
6
6
  - Usage: usage.md
7
7
  - Config File: config_file.md
8
+ - Writing a Custom Plugin: custom_plugin.md
8
9
  - Development: DEVELOPMENT.md
9
10
  - Contributing: CONTRIBUTING.md
10
11
  - Troubleshooting: troubleshooting.md
@@ -18,8 +19,17 @@ theme:
18
19
  plugins:
19
20
  - termynal
20
21
 
21
- # Extensions to render emojis
22
+ # Extensions
22
23
  markdown_extensions:
23
- - pymdownx.emoji:
24
+ - pymdownx.highlight:
25
+ use_pygments: true
26
+ anchor_linenums: true
27
+ line_spans: __span
28
+ pygments_lang_class: true
29
+ # auto_title: true
30
+ - pymdownx.inlinehilite
31
+ - pymdownx.snippets
32
+ - pymdownx.superfences
33
+ - pymdownx.emoji: # render emojis
24
34
  emoji_index: !!python/name:material.extensions.emoji.twemoji
25
35
  emoji_generator: !!python/name:material.extensions.emoji.to_svg
@@ -14,6 +14,7 @@ import traceback
14
14
  from pathlib import Path
15
15
  from typing import Annotated, Optional
16
16
 
17
+ import click
17
18
  import pluggy
18
19
  import typer
19
20
  from click.exceptions import UsageError
@@ -39,7 +40,10 @@ class NaturalOrderGrouper(TyperGroup):
39
40
  """Group commands in natural order."""
40
41
 
41
42
  # ----------------------------------------------------------------------
42
- def list_commands(self, *args, **kwargs) -> list[str]: # noqa: ARG002 # pragma: no cover
43
+ def list_commands(
44
+ self,
45
+ ctx: click.Context, # noqa: ARG002
46
+ ) -> list[str]: # pragma: no cover
43
47
  """Return a list of all the commands, to be sorted in a natural ordering."""
44
48
  return list(self.commands.keys())
45
49
 
@@ -233,14 +237,18 @@ def EntryPoint( # noqa: PLR0913 # pragma: no cover
233
237
  ),
234
238
  ] = None,
235
239
  all_warnings_as_error: Annotated[ # noqa: FBT002
236
- bool, typer.Option("--all-warnings-as-error", help="Treat all warnings as errors.")
240
+ bool,
241
+ typer.Option("--all-warnings-as-error", help="Treat all warnings as errors."),
237
242
  ] = False,
238
243
  ignore_all_warnings: Annotated[ # noqa: FBT002
239
244
  bool, typer.Option("--ignore-all-warnings", help="Ignore all warnings.")
240
245
  ] = False,
241
246
  single_threaded: Annotated[ # noqa: FBT002
242
247
  bool,
243
- typer.Option("--single-threaded", help="Do not use multiple threads when evaluating requirements."),
248
+ typer.Option(
249
+ "--single-threaded",
250
+ help="Do not use multiple threads when evaluating requirements.",
251
+ ),
244
252
  ] = False,
245
253
  no_resolution: Annotated[ # noqa: FBT002
246
254
  bool,
@@ -327,8 +335,8 @@ def EntryPoint( # noqa: PLR0913 # pragma: no cover
327
335
 
328
336
  # Since `output` can be None, we use the nullcontext manager to represent None
329
337
  with (
330
- Path(output).open(mode="w+", encoding="UTF-8") if output else contextlib.nullcontext() as file
331
- ):
338
+ Path(output).open(mode="w+", encoding="UTF-8") if output else contextlib.nullcontext()
339
+ ) as file:
332
340
  DisplayResults(
333
341
  dm,
334
342
  all_results,
@@ -42,13 +42,16 @@ class EnableRequirementImpl(Requirement):
42
42
  if subject is None:
43
43
  subject = github_settings_value
44
44
 
45
+ description_template = f"Validates that {subject} is set to {{__expected_value}}."
46
+
45
47
  super().__init__(
46
48
  name,
47
- f"Validates that {subject} is set to {{__expected_value}}.",
49
+ description_template.format(__expected_value=enabled_by_default),
48
50
  ExecutionStyle.Parallel,
49
51
  resolution,
50
52
  rationale,
51
53
  requires_explicit_include=requires_explicit_include,
54
+ description_template=description_template,
52
55
  )
53
56
 
54
57
  self.dynamic_arg_name = dynamic_arg_name
@@ -49,13 +49,16 @@ class ValueRequirementImpl(Requirement):
49
49
  if subject is None:
50
50
  subject = github_value
51
51
 
52
+ description_template = f"Validates that {subject} is set to '{{__expected_value}}'."
53
+
52
54
  super().__init__(
53
55
  name,
54
- f"Validates that {subject} is set to '{{__expected_value}}'.",
56
+ description_template.format(__expected_value=default_value),
55
57
  ExecutionStyle.Parallel,
56
58
  resolution,
57
59
  rationale,
58
60
  requires_explicit_include=requires_explicit_include,
61
+ description_template=description_template,
59
62
  )
60
63
 
61
64
  self.github_value = github_value
@@ -81,6 +81,7 @@ class Requirement(ABC):
81
81
  rationale_template: str,
82
82
  *,
83
83
  requires_explicit_include: bool = False,
84
+ description_template: str | None = None,
84
85
  ) -> None:
85
86
  """Initialize the requirement with the given name, description, and style.
86
87
 
@@ -88,8 +89,8 @@ class Requirement(ABC):
88
89
  """
89
90
  self.name = name
90
91
  # Use description template so we can update this description for each requirement
91
- self.description_template = description
92
- # Use the description as is as backup
92
+ self.description_template = description_template or description
93
+ # Use the provided description as the default
93
94
  self.description = description
94
95
  self.style = style
95
96
 
@@ -2,4 +2,4 @@ APP_NAME = "RepoAuditor"
2
2
 
3
3
  # Note that the default value below will be used until the value
4
4
  # here is explicitly updated by the Continuous Integration system.
5
- __version__ = "0.4.4"
5
+ __version__ = "0.4.6"
@@ -134,3 +134,25 @@ class TestEnableRulesetRequirementImpl:
134
134
  result = requirement.Evaluate(query_data, requirement_args)
135
135
 
136
136
  assert result.result == EvaluateResult.Success
137
+
138
+ def test_NoneRuleType(self, query_data):
139
+ """Test behavior when the rule type provided is None."""
140
+ requirement = EnableRulesetRequirementImpl(
141
+ name="Test Name",
142
+ enabled_by_default=False,
143
+ dynamic_arg_name="yes",
144
+ github_ruleset_type="Test Ruleset",
145
+ github_ruleset_value="test_ruleset",
146
+ get_configuration_value_func=lambda x: x,
147
+ resolution="Test Resolution",
148
+ rationale="Test rationale",
149
+ )
150
+
151
+ # Set correct get_configuration_value_func
152
+ requirement.get_configuration_value_func = requirement._GetValue
153
+
154
+ query_data = {"rules": [{"type": None}]}
155
+ requirement_args = {"yes": True}
156
+ result = requirement.Evaluate(query_data, requirement_args)
157
+
158
+ assert result.result == EvaluateResult.Error
@@ -10,8 +10,7 @@ from RepoAuditor.Plugins.GitHub.RulesetRequirements.BlockMainlineForcePushes imp
10
10
  BlockMainlineForcePushesRule,
11
11
  )
12
12
  from RepoAuditor.Requirement import EvaluateResult
13
-
14
- from . import create_rule
13
+ from tests.Plugins.GitHubModule.RulesetRequirements import create_rule
15
14
 
16
15
 
17
16
  class TestBlockMainlineForcePushesRule: