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 }