Connect processing preflight and output reporting
This commit is contained in:
139
internal/cli/process_inputs.go
Normal file
139
internal/cli/process_inputs.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics"
|
||||
coreio "gitea.maximumdirect.net/eric/audita/v3/internal/core/io"
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/core/reporting"
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/preflight"
|
||||
"go.maximumdirect.net/ckconfig"
|
||||
commandkit "go.maximumdirect.net/commandkit"
|
||||
ck "go.maximumdirect.net/configkit"
|
||||
)
|
||||
|
||||
type processInputs struct {
|
||||
Configuration *ck.Result[config.Config]
|
||||
Runtime *llm.Runtime
|
||||
Source *schema.SourceTranscript
|
||||
RawTranscript []byte
|
||||
ProtectedPaths []string
|
||||
}
|
||||
|
||||
func loadProcessInputs(ctx context.Context, input processInput, mode commandkit.ReportingMode) (*processInputs, error) {
|
||||
if mode == commandkit.ReportingModeQuiet && input.Output.Value == "" {
|
||||
return nil, fmt.Errorf("quiet processing requires an output file or /dev/null")
|
||||
}
|
||||
selection := ckconfig.Selection{Config: input.Config, Overlay: input.Overlay, Overrides: input.Overrides}
|
||||
loaded, err := loadApplication(ctx, selection, struct{}{}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtime, err := llm.NewRuntime(ctx, loaded, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, err := coreio.ReadRequiredFile(input.Transcript, "transcript")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source, err := schema.ParseSourceTranscriptJSON(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protected := append([]string(nil), runtime.SourcePaths...)
|
||||
for _, entry := range loaded.Sources() {
|
||||
if entry.Kind == ck.SourceKindFile && entry.Path != "" {
|
||||
protected = append(protected, entry.Path)
|
||||
}
|
||||
}
|
||||
return &processInputs{Configuration: loaded, Runtime: runtime, Source: source, RawTranscript: raw, ProtectedPaths: protected}, nil
|
||||
}
|
||||
|
||||
// preparedProcess groups the output resources and the facts needed for reports.
|
||||
type preparedProcess struct {
|
||||
Prepared *preflight.Prepared
|
||||
RunID string
|
||||
Started time.Time
|
||||
}
|
||||
|
||||
func prepareProcess(ctx context.Context, input processInput, loaded *processInputs, stdout io.Writer) (*preparedProcess, error) {
|
||||
cfg := &loaded.Configuration.Config
|
||||
reportDir := ""
|
||||
reportDirSet := false
|
||||
for _, override := range input.Overrides {
|
||||
if override.Path.String() == "/reporting/stdout_dir" {
|
||||
reportDirSet = true
|
||||
}
|
||||
}
|
||||
if cfg.Reporting.Enabled {
|
||||
reportDir = cfg.Reporting.StdoutDir
|
||||
if input.Output.Value != "" && !reportDirSet {
|
||||
reportDir = filepath.Dir(input.Output.Value)
|
||||
}
|
||||
}
|
||||
runID, err := generateRunID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
started := time.Now().UTC()
|
||||
snapshot := reporting.Snapshot{RunID: runID, Status: reporting.StatusInProgress, StartedAt: started, TranscriptPath: input.Transcript, OutputPath: input.Output.Value}
|
||||
for _, item := range cfg.Pipeline.Stages {
|
||||
snapshot.StageNames = append(snapshot.StageNames, item.Module)
|
||||
}
|
||||
secrets := loaded.Runtime.ConfiguredSecrets(ctx)
|
||||
initial, err := renderReportBundle(snapshot, secrets...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshot.Status = reporting.StatusFailed
|
||||
failed, err := renderReportBundle(snapshot, secrets...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var debug *preflight.DebugInput
|
||||
var warnings []error
|
||||
if input.Debug.Value {
|
||||
var effective bytes.Buffer
|
||||
if err := loaded.Configuration.WriteEffective(&effective); err != nil {
|
||||
warnings = append(warnings, err)
|
||||
}
|
||||
debug = &preflight.DebugInput{WorkDir: cfg.Diagnostics.WorkDir, RunID: runID, Secrets: secrets, EffectiveConfig: effective.Bytes(), Metadata: diagnostics.InvocationMetadata{Operation: "process", TranscriptPath: input.Transcript, OutputPath: input.Output.Value, Overlay: input.Overlay.Value, ConfigVersion: &cfg.Version, RunID: runID, StartedAt: started}}
|
||||
}
|
||||
prepared, err := preflight.Prepare(ctx, preflight.Input{Config: cfg, Transcript: loaded.Source, Output: diagnostics.OutputSelection{InputTranscriptPath: input.Transcript, TranscriptPath: input.Output.Value, ReportDir: reportDir, ReportingEnabled: cfg.Reporting.Enabled, ProtectedPaths: loaded.ProtectedPaths, Stdout: stdout}, InitialReports: initial, FailedReports: failed, Debug: debug})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prepared.Warnings = append(warnings, prepared.Warnings...)
|
||||
return &preparedProcess{Prepared: prepared, RunID: runID, Started: started}, nil
|
||||
}
|
||||
|
||||
func generateRunID() (string, error) {
|
||||
var random [12]byte
|
||||
if _, err := rand.Read(random[:]); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "run-" + hex.EncodeToString(random[:]), nil
|
||||
}
|
||||
|
||||
func renderReportBundle(value reporting.Snapshot, secrets ...string) (diagnostics.ReportBundle, error) {
|
||||
errorsYAML, err := reporting.YAML(value.ErrorsDocument(), secrets...)
|
||||
if err != nil {
|
||||
return diagnostics.ReportBundle{}, err
|
||||
}
|
||||
recoveriesYAML, err := reporting.YAML(value.RecoveriesDocument(), secrets...)
|
||||
if err != nil {
|
||||
return diagnostics.ReportBundle{}, err
|
||||
}
|
||||
return diagnostics.ReportBundle{Errors: errorsYAML, Recoveries: recoveriesYAML, Markdown: reporting.Markdown(value, secrets...)}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user