Initial curated RepoPulse source from 2524ccc3c1c27739996dccb2b7992989f88cd26d
This commit is contained in:
commit
0adf8ef5d9
90 files changed
+14315
No files matched your search
@@ -0,0 +1,147 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .models import (
|
||||
DependencySignals,
|
||||
Finding,
|
||||
GateCheck,
|
||||
GateState,
|
||||
ReleaseGate,
|
||||
RepositoryMetrics,
|
||||
RepositoryPolicy,
|
||||
ScanCoverage,
|
||||
Severity,
|
||||
TestSignals,
|
||||
)
|
||||
|
||||
|
||||
def build_release_gate(
|
||||
*,
|
||||
health_score: int,
|
||||
findings: list[Finding],
|
||||
metrics: RepositoryMetrics,
|
||||
tests: TestSignals,
|
||||
dependencies: DependencySignals,
|
||||
coverage: ScanCoverage | None = None,
|
||||
policy: RepositoryPolicy | None = None,
|
||||
) -> ReleaseGate:
|
||||
policy = policy or RepositoryPolicy()
|
||||
active_findings = [item for item in findings if not item.suppressed]
|
||||
blockers = [item for item in active_findings if item.severity == Severity.BLOCKER]
|
||||
warnings = [item for item in active_findings if item.severity == Severity.WARNING]
|
||||
dependency_issues = dependencies.unbounded_dependencies + dependencies.unpinned_dependencies
|
||||
checks = [
|
||||
GateCheck(
|
||||
key="security",
|
||||
label="Security basics",
|
||||
state=GateState.BLOCK if len(blockers) > policy.max_blockers else GateState.PASS,
|
||||
detail=(
|
||||
f"{len(blockers)} blocker finding(s), policy allows {policy.max_blockers}"
|
||||
if blockers
|
||||
else "No blocker-level secret/environment findings"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="tests",
|
||||
label="Tests" + (" (required)" if policy.require_tests else ""),
|
||||
state=(
|
||||
GateState.BLOCK
|
||||
if policy.require_tests and not metrics.test_files
|
||||
else GateState.PASS
|
||||
if metrics.test_files
|
||||
else GateState.WARN
|
||||
),
|
||||
detail=(
|
||||
f"{metrics.test_files} test file(s) detected"
|
||||
if metrics.test_files
|
||||
else "Tests are required by repository policy"
|
||||
if policy.require_tests
|
||||
else "No test files detected"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="ci",
|
||||
label="Continuous integration" + (" (required)" if policy.require_ci else ""),
|
||||
state=(
|
||||
GateState.BLOCK
|
||||
if policy.require_ci and not tests.ci_validation_workflows
|
||||
else GateState.PASS
|
||||
if tests.ci_validation_workflows
|
||||
else GateState.WARN
|
||||
),
|
||||
detail=(
|
||||
f"{len(tests.ci_validation_workflows)} validation workflow(s) detected"
|
||||
if tests.ci_validation_workflows
|
||||
else f"{len(tests.ci_deploy_only_workflows)} deploy-only workflow(s) do not provide validation evidence"
|
||||
if tests.ci_deploy_only_workflows
|
||||
else "CI is required by repository policy"
|
||||
if policy.require_ci
|
||||
else "No validation workflow detected"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="coverage",
|
||||
label="Coverage evidence",
|
||||
state=(
|
||||
GateState.PASS
|
||||
if tests.coverage_evidence
|
||||
else GateState.WARN
|
||||
if metrics.test_files
|
||||
else GateState.PASS
|
||||
),
|
||||
detail=(
|
||||
f"{len(tests.coverage_evidence)} coverage evidence source(s) detected"
|
||||
if tests.coverage_evidence
|
||||
else "Tests exist but no coverage-producing command or retained coverage output was detected"
|
||||
if metrics.test_files
|
||||
else "No test suite is present to produce coverage evidence"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="analysis_coverage",
|
||||
label="Analysis coverage",
|
||||
state=GateState.WARN if coverage and coverage.truncated else GateState.PASS,
|
||||
detail=(
|
||||
"; ".join(coverage.notes) if coverage and coverage.truncated else "Analysis completed within configured limits"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="dependencies",
|
||||
label="Dependency hygiene",
|
||||
state=GateState.WARN if dependency_issues else GateState.PASS,
|
||||
detail=(
|
||||
f"{dependencies.unbounded_dependencies} unbounded and {dependencies.unpinned_dependencies} unpinned declaration(s)"
|
||||
if dependency_issues
|
||||
else f"{dependencies.declared_dependencies} dependency declaration(s), no unbounded or unpinned versions"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="health",
|
||||
label="Repository health",
|
||||
state=(
|
||||
GateState.PASS
|
||||
if health_score >= max(85, policy.min_health_score)
|
||||
else GateState.WARN
|
||||
if health_score >= max(65, policy.min_health_score)
|
||||
else GateState.BLOCK
|
||||
),
|
||||
detail=(
|
||||
f"Health score {health_score}/100"
|
||||
if not policy.min_health_score
|
||||
else f"Health score {health_score}/100; policy minimum {policy.min_health_score}"
|
||||
),
|
||||
),
|
||||
GateCheck(
|
||||
key="warnings",
|
||||
label="Engineering warnings",
|
||||
state=GateState.WARN if warnings else GateState.PASS,
|
||||
detail=f"{len(warnings)} warning finding(s)" if warnings else "No warning-level findings",
|
||||
),
|
||||
]
|
||||
|
||||
if any(check.state == GateState.BLOCK for check in checks):
|
||||
status = "BLOCKED"
|
||||
elif any(check.state == GateState.WARN for check in checks):
|
||||
status = "READY WITH WARNINGS"
|
||||
else:
|
||||
status = "READY"
|
||||
return ReleaseGate(status=status, checks=checks)
|
||||
Reference in new issue
Block a user