2026-05-02 10:37:38 -05:00
|
|
|
package config
|
|
|
|
|
|
2026-05-02 10:43:45 -05:00
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
2026-05-22 19:05:23 -05:00
|
|
|
|
2026-05-23 13:20:28 +00:00
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/pathsafe"
|
2026-05-02 10:43:45 -05:00
|
|
|
)
|
2026-05-02 10:37:38 -05:00
|
|
|
|
2026-05-02 10:43:45 -05:00
|
|
|
// Validate checks resolved configuration for required fields and parseable durations.
|
|
|
|
|
func Validate(cfg *Config) error {
|
|
|
|
|
if cfg == nil {
|
|
|
|
|
return fmt.Errorf("config is nil")
|
|
|
|
|
}
|
|
|
|
|
if cfg.Pipeline == nil {
|
|
|
|
|
return fmt.Errorf("pipeline config is required")
|
|
|
|
|
}
|
2026-05-20 20:41:28 -05:00
|
|
|
if cfg.Campaign == nil {
|
|
|
|
|
return fmt.Errorf("campaign config is required")
|
|
|
|
|
}
|
2026-05-02 10:43:45 -05:00
|
|
|
if cfg.Session == nil {
|
|
|
|
|
return fmt.Errorf("session config is required")
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 14:43:25 +00:00
|
|
|
if err := ValidatePipelineConfig(cfg.Pipeline); err != nil {
|
2026-05-02 11:44:15 -05:00
|
|
|
return fmt.Errorf("pipeline config %q invalid: %w", shortName(cfg.PipelinePath, "pipeline.yml"), err)
|
2026-05-02 10:43:45 -05:00
|
|
|
}
|
2026-08-30 14:43:25 +00:00
|
|
|
if err := ValidateCampaignConfig(cfg.Campaign); err != nil {
|
2026-05-20 20:41:28 -05:00
|
|
|
return fmt.Errorf("campaign config %q invalid: %w", shortName(cfg.CampaignPath, "campaign.yml"), err)
|
|
|
|
|
}
|
2026-05-02 10:43:45 -05:00
|
|
|
if err := validateSession(cfg.Session); err != nil {
|
2026-05-02 11:44:15 -05:00
|
|
|
return fmt.Errorf("session config %q invalid: %w", shortName(cfg.SessionPath, "session.yml"), err)
|
2026-05-02 10:43:45 -05:00
|
|
|
}
|
2026-08-30 14:01:51 +00:00
|
|
|
if err := validateResolvedPartyInputs(cfg); err != nil {
|
|
|
|
|
return fmt.Errorf("campaign/session config invalid: %w", err)
|
|
|
|
|
}
|
2026-08-29 14:55:24 +00:00
|
|
|
if err := validateCrossConfig(cfg.Pipeline, cfg.Session, cfg.StableInputs); err != nil {
|
2026-05-16 14:11:59 +00:00
|
|
|
return fmt.Errorf("pipeline/session config invalid: %w", err)
|
|
|
|
|
}
|
2026-05-02 10:43:45 -05:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 14:43:25 +00:00
|
|
|
// ValidatePipelineConfig validates a loaded, defaulted pipeline without
|
|
|
|
|
// requiring session configuration. Callers that require party-driven artifact
|
|
|
|
|
// expansion must resolve a campaign first through LoadPipelineCampaign.
|
|
|
|
|
func ValidatePipelineConfig(cfg *PipelineConfig) error {
|
|
|
|
|
if cfg == nil {
|
|
|
|
|
return fmt.Errorf("pipeline config is required")
|
|
|
|
|
}
|
|
|
|
|
return validatePipeline(cfg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ValidateCampaignConfig validates a loaded campaign without requiring a
|
|
|
|
|
// session. Party parsing and canonical-party checks remain owned by
|
|
|
|
|
// LoadPipelineCampaign, which has the campaign source path available.
|
|
|
|
|
func ValidateCampaignConfig(cfg *CampaignConfig) error {
|
|
|
|
|
return validateCampaign(cfg)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 10:43:45 -05:00
|
|
|
func validatePipeline(cfg *PipelineConfig) error {
|
|
|
|
|
if strings.TrimSpace(cfg.Workspace.Root) == "" {
|
|
|
|
|
return fmt.Errorf("pipeline.workspace.root is required")
|
|
|
|
|
}
|
2026-08-10 17:35:52 +00:00
|
|
|
if campaignID := cfg.Campaigns.DefaultCampaignID; campaignID != "" {
|
|
|
|
|
if err := pathsafe.ValidateOpaqueSegment(campaignID); err != nil {
|
|
|
|
|
return fmt.Errorf("pipeline.campaigns.default_campaign_id: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-16 14:11:59 +00:00
|
|
|
if err := validateStorage(cfg.Storage); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := validateSpool(cfg.Spool); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-05-21 22:22:08 -05:00
|
|
|
if err := validateCache(cfg.Cache); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-08-09 23:30:32 +00:00
|
|
|
if err := validateNotarius(cfg.Notarius, cfg.Scriptorium); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err := validatePublish(cfg.Publish, cfg.Scriptorium, cfg.Notarius); err != nil {
|
2026-05-16 14:11:59 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2026-09-05 15:09:57 +00:00
|
|
|
if err := validateCleanupPublishPolicy(cfg); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-05-03 15:23:47 -05:00
|
|
|
if err := validateWhisperX(cfg.WhisperX); err != nil {
|
2026-05-02 10:43:45 -05:00
|
|
|
return err
|
|
|
|
|
}
|
2026-05-03 19:13:50 -05:00
|
|
|
if err := validateSeriatim(cfg.Seriatim); err != nil {
|
2026-05-02 10:43:45 -05:00
|
|
|
return err
|
|
|
|
|
}
|
2026-05-04 07:48:59 -05:00
|
|
|
if err := validateAudita(cfg.Audita); err != nil {
|
2026-05-02 10:43:45 -05:00
|
|
|
return err
|
|
|
|
|
}
|
2026-05-10 21:30:35 +00:00
|
|
|
if err := validateNormalize(cfg.Normalize); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-05-08 16:54:17 +00:00
|
|
|
if err := validateTrim(cfg.Trim); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-05-25 00:28:01 +00:00
|
|
|
if err := validateRender(cfg.Render); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-08-09 23:30:32 +00:00
|
|
|
if err := validateScriptorium(cfg.Scriptorium, cfg.Notarius); err != nil {
|
2026-05-06 21:12:22 +00:00
|
|
|
return err
|
|
|
|
|
}
|
2026-08-10 21:41:00 +00:00
|
|
|
if err := validateNotification(cfg.Notification); err != nil {
|
2026-05-02 10:43:45 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|