103 lines
3.9 KiB
Go
103 lines
3.9 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
)
|
|
|
|
// SessionValidate performs a read-only session preflight.
|
|
func SessionValidate(ctx context.Context, args []string, out io.Writer) error {
|
|
fs := flag.NewFlagSet("session validate", flag.ContinueOnError)
|
|
fs.SetOutput(io.Discard)
|
|
var flags commonConfigFlags
|
|
addCommonConfigFlags(fs, &flags)
|
|
if err := parseSessionAwareFlags("session validate", fs, args, &flags.sessionID); err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(flags.sessionID) == "" {
|
|
return fmt.Errorf("session validate: session_id is required")
|
|
}
|
|
|
|
findings := []finding{}
|
|
loaded, err := loadCommandConfig(ctx, flags.pipelinePath, flags.campaignPath, flags.campaignFilePath, flags.sessionPath, flags.sessionOptions())
|
|
if err != nil {
|
|
findings = append(findings, errorFinding("config", err.Error()))
|
|
return renderFindings(out, "", "", findings)
|
|
}
|
|
defer func() { _ = loaded.Close() }()
|
|
cfg := loaded.Config
|
|
if err := config.Validate(cfg); err != nil {
|
|
findings = append(findings, errorFinding("config", err.Error()))
|
|
} else {
|
|
findings = append(findings, okFinding("config", "resolved pipeline, campaign, and session config"))
|
|
}
|
|
findings = append(findings, okFinding("session", fmt.Sprintf("session source: %s", sessionSourceSummary(cfg))))
|
|
|
|
paths := artifacts.NewLocalStore(cfg.Pipeline.Workspace.Root).SessionPathsFor(cfg.Session.Campaign, cfg.Session.SessionID)
|
|
findings = append(findings, validateStableInputFindings(cfg)...)
|
|
findings = append(findings, validateLocalAudioFindings(cfg)...)
|
|
|
|
store, storeErr := objectStoreIfConfigured(ctx, cfg)
|
|
if storeErr != nil {
|
|
findings = append(findings, errorFinding("storage", storeErr.Error()))
|
|
}
|
|
if cfg.Session.Inputs.AudioS3 != nil {
|
|
if storeErr != nil {
|
|
findings = append(findings, errorFinding("audio", "remote audio cannot be checked because storage is unavailable"))
|
|
} else {
|
|
findings = append(findings, validateRemoteAudioFinding(ctx, cfg, store))
|
|
}
|
|
}
|
|
|
|
effective, effectiveErr := resolveEffectiveArtifacts(cfg, nil)
|
|
if effectiveErr != nil {
|
|
findings = append(findings, errorFinding("config", effectiveErr.Error()))
|
|
return renderFindings(out, cfg.Session.Campaign, cfg.Session.SessionID, findings)
|
|
}
|
|
requirements := artifacts.CollectPreviousArtifactRequirements(configuredScriptoriumArtifacts(cfg), effective)
|
|
previous := inspectPreviousArtifactReadiness(ctx, cfg, store, requirements)
|
|
if len(previous.Requirements) == 0 {
|
|
findings = append(findings, okFinding("previous", "no previous-session artifacts required"))
|
|
} else if previous.Err != nil {
|
|
findings = append(findings, errorFinding("previous", previous.Err.Error()))
|
|
} else {
|
|
for _, req := range previous.Requirements {
|
|
if !req.Required && previousRequirementSkipped(previous.SkippedMissing, req.Name) {
|
|
findings = append(findings, okFinding("previous", fmt.Sprintf("%s required=false unavailable", req.Name)))
|
|
continue
|
|
}
|
|
findings = append(findings, okFinding("previous", fmt.Sprintf("%s required=%t", req.Name, req.Required)))
|
|
}
|
|
}
|
|
|
|
locks := inspectEffectiveLocks(ctx, cfg, store)
|
|
if locks.Err != nil {
|
|
findings = append(findings, errorFinding("locks", locks.Err.Error()))
|
|
} else if len(locks.Locks.All) == 0 {
|
|
findings = append(findings, okFinding("locks", "no effective publish locks"))
|
|
} else {
|
|
for _, lock := range locks.Locks.All {
|
|
findings = append(findings, warnFinding("locks", fmt.Sprintf("%s locked: %s", lock.Source, strings.TrimSpace(lock.Reason))))
|
|
}
|
|
}
|
|
if paths.ManifestPath != "" {
|
|
findings = append(findings, infoFinding("workspace", "manifest path: "+paths.ManifestPath))
|
|
}
|
|
return renderFindings(out, cfg.Session.Campaign, cfg.Session.SessionID, findings)
|
|
}
|
|
|
|
func previousRequirementSkipped(values []string, name string) bool {
|
|
for _, value := range values {
|
|
if value == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|