Files
audita/internal/cli/process_output.go

106 lines
3.9 KiB
Go
Raw Normal View History

package cli
import (
"context"
"errors"
"fmt"
"time"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/redaction"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/reporting"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/outcomes"
commandkit "go.maximumdirect.net/commandkit"
)
// finishProcess delivers the transcript and reports before emitting success information.
func finishProcess(inv commandkit.Invocation, input processInput, loaded *processInputs, prepared *preparedProcess, snapshot reporting.Snapshot, transcript []byte, primary error) commandkit.Outcome {
reporter := inv.Reporter()
secrets := loaded.Runtime.ConfiguredSecrets(inv.Context)
safe := redaction.New(secrets)
for _, warning := range prepared.Prepared.Warnings {
_ = reporter.Warn("Debug artifact: " + safe.String(warning.Error()))
}
completed := time.Now().UTC()
snapshot.RunID, snapshot.StartedAt, snapshot.CompletedAt = prepared.RunID, prepared.Started, &completed
snapshot.TranscriptPath, snapshot.OutputPath = input.Transcript, input.Output.Value
if primary == nil {
primary = inv.Context.Err()
}
snapshot.Status = reporting.StatusCompleted
if primary != nil {
snapshot = failedProcessingSnapshot(snapshot, primary, inv.Context.Err())
}
reports, err := renderReportBundle(snapshot, secrets...)
if err != nil {
if primary == nil {
primary = err
}
snapshot.Status = reporting.StatusFailed
}
failed := func(failures []diagnostics.FileFailure) diagnostics.ReportBundle {
result, bundleErr := renderReportBundle(failedOutputSnapshot(snapshot, failures), secrets...)
if bundleErr != nil {
return reports
}
return result
}
if canceled := inv.Context.Err(); primary == nil && canceled != nil {
primary = canceled
snapshot = failedProcessingSnapshot(snapshot, canceled, canceled)
reports = failed(nil)
}
result := prepared.Prepared.Output.Finish(primary, transcript, reports, failed)
if result != nil {
return commandkit.Failure(safe.String(result.Error())).WithCause(result)
}
_ = reporter.AtEnd().Info(reporting.CompletionLine(snapshot))
for _, path := range prepared.Prepared.Output.ReportPaths() {
_ = reporter.AtEnd().Info(fmt.Sprintf("Report: %s", safe.String(path)))
}
if prepared.Prepared.Debug != nil {
_ = reporter.AtEnd().Info(fmt.Sprintf("Diagnostics: %s", safe.String(prepared.Prepared.Debug.Path())))
}
return commandkit.Success()
}
func failedProcessingSnapshot(snapshot reporting.Snapshot, primary, contextErr error) reporting.Snapshot {
for _, record := range snapshot.Errors {
if record.Scope == "run" {
return failedOutputSnapshot(snapshot, nil)
}
}
record := reporting.ErrorRecord{Scope: "run", Reason: "processing_failure", Message: "Processing failed"}
if errors.Is(primary, context.Canceled) || (contextErr != nil && errors.Is(primary, contextErr)) {
record.Phase = "processing"
record.Reason = string(outcomes.ReasonCanceled)
record.Message = "Processing was canceled."
}
snapshot.Errors = append(snapshot.Errors, record)
return failedOutputSnapshot(snapshot, nil)
}
// failedOutputSnapshot derives the count from the same run records sent to each report.
func failedOutputSnapshot(snapshot reporting.Snapshot, failures []diagnostics.FileFailure) reporting.Snapshot {
snapshot.Status = reporting.StatusFailed
if len(failures) > 0 {
snapshot.Errors = append([]reporting.ErrorRecord(nil), snapshot.Errors...)
}
for _, failure := range failures {
record := reporting.ErrorRecord{Scope: "run", Reason: "output_failure", Message: "Required output delivery failed", OutputPath: failure.Path}
if failure.Kind == "report initialization" {
record.Phase = "initialization"
record.Message = "Required report initialization failed"
}
snapshot.Errors = append(snapshot.Errors, record)
}
count := 0
for _, record := range snapshot.Errors {
if record.Scope == "run" {
count++
}
}
snapshot.Summary.RunErrors = &count
return snapshot
}