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.
- {repoauditor-0.4.4 → repoauditor-0.4.6}/PKG-INFO +1 -1
- repoauditor-0.4.6/docs/custom_plugin.md +454 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/index.md +1 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/usage.md +0 -1
- {repoauditor-0.4.4 → repoauditor-0.4.6}/mkdocs.yml +13 -3
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/EntryPoint.py +13 -5
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/EnableRequirementImpl.py +4 -1
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/ValueRequirementImpl.py +4 -1
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Requirement.py +3 -2
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/__init__.py +1 -1
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/EnableRulesetRequirementImpl_UnitTest.py +22 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/BlockMainlineForcePushesRule_UnitTest.py +1 -2
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/DismissStalePullRequestApprovalsRule_UnitTest.py +25 -7
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/EnsureStatusChecksRule_UnitTest.py +1 -2
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireApprovalMostRecentPushRule_UnitTest.py +26 -7
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireApprovalsRule_UnitTest.py +26 -7
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireCodeOwnerReviewRule_UnitTest.py +26 -7
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireConversationResolutionRule_UnitTest.py +26 -7
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequirePullRequests_UnitTest.py +1 -2
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireSignedCommits_UnitTest.py +1 -2
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireStatusChecksToPassRule_UnitTest.py +1 -2
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/RequireUpToDateBranchesRule_UnitTest.py +26 -7
- repoauditor-0.4.6/tests/Plugins/GitHubModule/StandardRequirements/StandardValueRequirementImpl_UnitTest.py +54 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/uv.lock +6 -3
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.copier-answers.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/CODEOWNERS +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/custom.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/actions/release_impl/action.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/pull_request_template.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/release_sources.yaml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/CICD.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/CICD_impl.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/EndToEnd.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.github/workflows/codeql.yml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.gitignore +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.pre-commit-config.yaml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/.python-version +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/CODE_OF_CONDUCT.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/GOVERNANCE.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/LICENSE +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/MAINTAINERS.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/README.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/SECURITY.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/default_config.yaml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/CONTRIBUTING.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/DEVELOPMENT.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/PAT.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/config_file.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/docs/troubleshooting.md +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/minisign_key.pub +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/pyproject.toml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/AutoGitSemVer.yaml +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/CommandLineProcessor.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Display.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/ExecuteModules.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Impl/ParallelSequentialProcessor.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Impl/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Module.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugin.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/CommunityStandardsQuery.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Impl/ExistsRequirementImpl.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Impl/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Module.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/CodeOfConduct.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/CodeOwners.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/Contributing.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/IssueTemplates.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/LicenseFile.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/PullRequestTemplates.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/ReadMe.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/SecurityPolicy.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/Requirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandards/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/CommunityStandardsPlugin.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionQuery.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/AllowDeletions.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/AllowMainlineForcePushes.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/DismissStalePullRequestApprovals.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/DoNotAllowBypassSettings.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/EnsureStatusChecks.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/Impl/ClassicEnableRequirementImpl.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/Impl/ClassicValueRequirementImpl.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/Impl/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireApprovalMostRecentPush.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireApprovals.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireCodeOwnerReview.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireConversationResolution.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireLinearHistory.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequirePullRequests.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireSignedCommits.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireStatusChecksToPass.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/RequireUpToDateBranches.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/ClassicBranchProtectionRequirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/DefaultBranchQuery.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/DefaultBranchRequirements/Protected.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/DefaultBranchRequirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/Common.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/EnableRulesetRequirementImpl.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Module.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetQuery.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/BlockMainlineForcePushes.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/DismissStalePullRequestApprovals.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/EnsureStatusChecks.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireApprovalMostRecentPush.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireApprovals.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireCodeOwnerReview.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireCodeScanningResults.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireConversationResolution.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireLinearHistory.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequirePullRequests.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireSignedCommits.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireStatusChecksToPass.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireSuccessfulDeployments.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RequireUpToDateBranches.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RestrictCreations.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RestrictDeletions.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/RestrictUpdates.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/RulesetRequirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardQuery.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/AutoMerge.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/DefaultBranch.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/DeleteHeadBranches.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/DependabotSecurityUpdates.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Description.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Impl/StandardEnableRequirementImpl.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Impl/StandardValueRequirementImpl.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Impl/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/License.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/MergeCommit.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/MergeCommitMessage.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/Private.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/RebaseMergeCommit.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SecretScanning.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SecretScanningPushProtection.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SquashCommitMerge.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SquashMergeCommitMessage.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SuggestUpdatingPullRequestBranches.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportDiscussions.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportIssues.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportProjects.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/SupportWikis.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/TemplateRepository.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/WebCommitSignoff.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/StandardRequirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHubBase/Module.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHubBase/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHubPlugin.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/Module.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/Requirements/Citation.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/Requirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/ScientificSoftwareQuery.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftware/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/ScientificSoftwarePlugin.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Query.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/py.typed +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/CommandLineProcessor_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/EntryPoint_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/ExecuteModules_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Module_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugin_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards/CommunityStandardsQuery_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards/CommunityStandards_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards/ExistsRequirementImpl_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/CommunityStandards_EndToEndTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubEnterprise_EndToEndTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionQuery_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/DismissStalePullRequestApprovals_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/EnsureStatusChecks_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireApprovalMostRecentPush_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireApprovals_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireCodeOwnerReview_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicBranchProtectionRequirements/RequireUpToDateBranches_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ClassicValueRequirementImpl_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/DefaultBranchQuery_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/DefaultBranchRequirements/Protected_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/GitHubModule_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/GitHubSession_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulesetRequirements/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/RulsesetQuery_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardQuery_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/DependabotSecurityUpdates_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/Description_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/License_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/MergeCommitMessage_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/Private_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/SecretScanningPushProtection_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/SecretScanning_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/StandardRequirements/SquashMergeCommitMessage_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/ValueRequrementImpl_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHubModule/fixtures.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/GitHub_EndToEndTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/ScientificSoftware/ScientificSoftwareQuery_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/ScientificSoftware_EndToEndTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/CommunityStandards_EndToEndTest.ambr +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/GitHubEnterprise_EndToEndTest.ambr +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/GitHub_EndToEndTest.ambr +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/__snapshots__/ScientificSoftware_EndToEndTest.ambr +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/conftest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Plugins/utilities.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Query_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/Requirement_UnitTest.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/__init__.py +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/__snapshots__/ExecuteModules_UnitTest.ambr +0 -0
- {repoauditor-0.4.4 → repoauditor-0.4.6}/tests/conftest.py +0 -0
|
@@ -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
|
|
@@ -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
|
-
-
|
|
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
|
|
22
|
+
# Extensions
|
|
22
23
|
markdown_extensions:
|
|
23
|
-
- pymdownx.
|
|
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(
|
|
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,
|
|
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(
|
|
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()
|
|
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,
|
{repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/EnableRequirementImpl.py
RENAMED
|
@@ -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
|
-
|
|
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
|
{repoauditor-0.4.4 → repoauditor-0.4.6}/src/RepoAuditor/Plugins/GitHub/Impl/ValueRequirementImpl.py
RENAMED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
|
@@ -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:
|