Connect processing preflight and output reporting

This commit is contained in:
2026-11-10 17:12:28 +00:00
parent 852e79405f
commit 199c1d1dbe
10 changed files with 634 additions and 328 deletions

View File

@@ -0,0 +1,76 @@
// Package preflight checks resolved processing inputs and prepares output resources.
package preflight
import (
"context"
"fmt"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
)
// Input contains already acquired application and transcript inputs.
type Input struct {
Config *config.Config
Transcript *schema.SourceTranscript
Output diagnostics.OutputSelection
InitialReports diagnostics.ReportBundle
FailedReports diagnostics.ReportBundle
Debug *DebugInput
}
type DebugInput struct {
WorkDir string
RunID string
Metadata diagnostics.InvocationMetadata
EffectiveConfig []byte
Secrets []string
}
type Prepared struct {
Output *diagnostics.OutputSession
Debug *diagnostics.RunDirectory
Warnings []error
}
// Prepare validates invocation inputs and collisions before acquiring outputs.
func Prepare(ctx context.Context, input Input) (*Prepared, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
if input.Config == nil {
return nil, fmt.Errorf("application configuration is required")
}
if input.Transcript == nil {
return nil, fmt.Errorf("transcript is required")
}
session, err := diagnostics.SelectOutput(input.Output)
if err != nil {
return nil, err
}
if err := ctx.Err(); err != nil {
return nil, err
}
if err := session.Prepare(); err != nil {
return nil, err
}
if err := ctx.Err(); err != nil {
_ = session.Close()
return nil, err
}
if err := session.Initialize(input.InitialReports, input.FailedReports); err != nil {
return nil, err
}
if err := ctx.Err(); err != nil {
return nil, session.Finish(err, nil, input.FailedReports, func([]diagnostics.FileFailure) diagnostics.ReportBundle { return input.FailedReports })
}
result := &Prepared{Output: session}
if input.Debug != nil {
result.Debug, result.Warnings = session.PrepareDebug(input.Debug.WorkDir, input.Debug.RunID, input.Debug.Metadata, input.Debug.EffectiveConfig, input.Debug.Secrets...)
}
if err := ctx.Err(); err != nil {
return nil, session.Finish(err, nil, input.FailedReports, func([]diagnostics.FileFailure) diagnostics.ReportBundle { return input.FailedReports })
}
return result, nil
}

View File

@@ -0,0 +1,48 @@
package preflight
import (
"context"
"os"
"path/filepath"
"testing"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
)
func TestReadinessAndCollisionPrecedeOutputPreparation(t *testing.T) {
dir := t.TempDir()
output := filepath.Join(dir, "output.json")
if _, err := Prepare(context.Background(), Input{Config: &config.Config{}, Output: diagnostics.OutputSelection{TranscriptPath: output}}); err == nil {
t.Fatal("missing transcript accepted")
}
if _, err := os.Stat(output); !os.IsNotExist(err) {
t.Fatalf("output created: %v", err)
}
if _, err := Prepare(context.Background(), Input{Config: &config.Config{}, Transcript: &schema.SourceTranscript{}, Output: diagnostics.OutputSelection{TranscriptPath: output, ProtectedPaths: []string{output}}}); err == nil {
t.Fatal("collision accepted")
}
entries, err := os.ReadDir(dir)
if err != nil || len(entries) != 0 {
t.Fatalf("prepared before check: %v %v", entries, err)
}
}
func TestOptionalDebugFailureDoesNotPreventPreparation(t *testing.T) {
dir := t.TempDir()
blocked := filepath.Join(dir, "file")
if err := os.WriteFile(blocked, []byte("x"), 0600); err != nil {
t.Fatal(err)
}
result, err := Prepare(context.Background(), Input{Config: &config.Config{}, Transcript: &schema.SourceTranscript{}, Output: diagnostics.OutputSelection{TranscriptPath: filepath.Join(dir, "output")}, Debug: &DebugInput{WorkDir: blocked, RunID: "run"}})
if err != nil {
t.Fatal(err)
}
if len(result.Warnings) == 0 || result.Debug != nil {
t.Fatalf("debug=%v warnings=%v", result.Debug, result.Warnings)
}
if err := result.Output.Close(); err != nil {
t.Fatal(err)
}
}