package app import ( "fmt" "sort" "strings" "gitea.maximumdirect.net/eric/narratio/internal/artifacts" "gitea.maximumdirect.net/eric/narratio/internal/config" ) type artifactSelectionFlag struct { values []string } func (f *artifactSelectionFlag) String() string { return strings.Join(f.values, ",") } func (f *artifactSelectionFlag) Set(value string) error { f.values = append(f.values, value) return nil } func (f *artifactSelectionFlag) Normalize() ([]string, error) { if len(f.values) == 0 { return nil, nil } seen := map[string]struct{}{} out := make([]string, 0, len(f.values)) for _, raw := range f.values { for _, part := range strings.Split(raw, ",") { name := strings.TrimSpace(part) if name == "" { return nil, fmt.Errorf("artifact names must be non-empty") } if _, ok := seen[name]; ok { continue } seen[name] = struct{}{} out = append(out, name) } } sort.Strings(out) return out, nil } func validateSelectedArtifacts(cfg *config.Config, selected []string) error { _, err := resolveEffectiveArtifacts(cfg, selected) return err } func resolveEffectiveArtifacts(cfg *config.Config, selected []string) (artifacts.EffectiveArtifactSet, error) { if cfg == nil || cfg.Pipeline == nil || cfg.Pipeline.Scriptorium == nil { if len(selected) == 0 { return artifacts.ResolveEffectiveArtifactSet(nil, nil) } return artifacts.EffectiveArtifactSet{}, fmt.Errorf("--artifacts requires pipeline.scriptorium.artifacts to be configured") } configured := artifacts.ConfiguredArtifactDefinitions(cfg.Pipeline.Scriptorium.Artifacts) if len(selected) > 0 && len(configured) == 0 { return artifacts.EffectiveArtifactSet{}, fmt.Errorf("--artifacts requires at least one configured artifact in pipeline.scriptorium.artifacts") } effective, err := artifacts.ResolveEffectiveArtifactSet(configured, selected) if err != nil { if strings.Contains(err.Error(), "is not configured") { return artifacts.EffectiveArtifactSet{}, fmt.Errorf("--artifacts includes unknown artifact %q", selectedArtifactName(err)) } return artifacts.EffectiveArtifactSet{}, err } if err := validateEffectiveArtifactConfiguration(cfg.Pipeline.Scriptorium.Artifacts, effective); err != nil { return artifacts.EffectiveArtifactSet{}, err } return effective, nil } func selectedArtifactName(err error) string { message := err.Error() start := strings.Index(message, "\"") if start < 0 { return "" } end := strings.Index(message[start+1:], "\"") if end < 0 { return "" } return message[start+1 : start+1+end] } func validateEffectiveArtifactConfiguration(configured map[string]config.ScriptoriumArtifactConfig, effective artifacts.EffectiveArtifactSet) error { for _, name := range effective.Keys() { artifactCfg := configured[name] if strings.TrimSpace(artifactCfg.PromptID) == "" { return fmt.Errorf("pipeline.scriptorium.artifacts.%s.prompt_id is required when selected", name) } if strings.TrimSpace(artifactCfg.OutputPath) == "" { return fmt.Errorf("pipeline.scriptorium.artifacts.%s.output_path is required when selected", name) } } return nil }