Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
78 lines
2.5 KiB
C#
78 lines
2.5 KiB
C#
using System;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
|
|
namespace StellaOps.Policy;
|
|
|
|
public sealed record PolicyDiagnosticsReport(
|
|
string Version,
|
|
int RuleCount,
|
|
int ErrorCount,
|
|
int WarningCount,
|
|
DateTimeOffset GeneratedAt,
|
|
ImmutableArray<PolicyIssue> Issues,
|
|
ImmutableArray<string> Recommendations);
|
|
|
|
public static class PolicyDiagnostics
|
|
{
|
|
public static PolicyDiagnosticsReport Create(PolicyBindingResult bindingResult, TimeProvider? timeProvider = null)
|
|
{
|
|
if (bindingResult is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(bindingResult));
|
|
}
|
|
|
|
var time = (timeProvider ?? TimeProvider.System).GetUtcNow();
|
|
var errorCount = bindingResult.Issues.Count(static issue => issue.Severity == PolicyIssueSeverity.Error);
|
|
var warningCount = bindingResult.Issues.Count(static issue => issue.Severity == PolicyIssueSeverity.Warning);
|
|
|
|
var recommendations = BuildRecommendations(bindingResult.Document, errorCount, warningCount);
|
|
|
|
return new PolicyDiagnosticsReport(
|
|
bindingResult.Document.Version,
|
|
bindingResult.Document.Rules.Length,
|
|
errorCount,
|
|
warningCount,
|
|
time,
|
|
bindingResult.Issues,
|
|
recommendations);
|
|
}
|
|
|
|
private static ImmutableArray<string> BuildRecommendations(PolicyDocument document, int errorCount, int warningCount)
|
|
{
|
|
var messages = ImmutableArray.CreateBuilder<string>();
|
|
|
|
if (errorCount > 0)
|
|
{
|
|
messages.Add("Resolve policy errors before promoting the revision; fallback rules may be applied while errors remain.");
|
|
}
|
|
|
|
if (warningCount > 0)
|
|
{
|
|
messages.Add("Review policy warnings and ensure intentional overrides are documented.");
|
|
}
|
|
|
|
if (document.Rules.Length == 0)
|
|
{
|
|
messages.Add("Add at least one policy rule to enforce gating logic.");
|
|
}
|
|
|
|
var quietRules = document.Rules
|
|
.Where(static rule => rule.Action.Quiet)
|
|
.Select(static rule => rule.Name)
|
|
.ToArray();
|
|
|
|
if (quietRules.Length > 0)
|
|
{
|
|
messages.Add($"Quiet rules detected ({string.Join(", ", quietRules)}); verify scoring behaviour aligns with expectations.");
|
|
}
|
|
|
|
if (messages.Count == 0)
|
|
{
|
|
messages.Add("Policy validated successfully; no additional action required.");
|
|
}
|
|
|
|
return messages.ToImmutable();
|
|
}
|
|
}
|