package app import ( "context" "errors" "flag" "fmt" "io" "gitea.maximumdirect.net/eric/narratio/internal/config" ) // Run executes the pipeline plan and persists manifest state. func Run(ctx context.Context, args []string, out io.Writer) error { request, err := parseBoundedRunRequest("run", args, out) if err != nil { if errors.Is(err, flag.ErrHelp) { return nil } return err } flags := request.Config loaded, err := loadCommandConfig(ctx, flags.pipelinePath, flags.campaignPath, flags.campaignFilePath, flags.sessionPath, flags.sessionOptions()) if err != nil { return fmt.Errorf("run: %w", err) } defer func() { _ = loaded.Close() }() cfg := loaded.Config if err := config.Validate(cfg); err != nil { return fmt.Errorf("run: %w", err) } selectedArtifacts, err := normalizeArtifactSelection(cfg, request.SelectedArtifacts) if err != nil { return fmt.Errorf("run: %w", err) } effectiveArtifacts, err := resolveEffectiveArtifacts(cfg, selectedArtifacts) if err != nil { return fmt.Errorf("run: %w", err) } summary, err := executeStagesFn(ctx, cfg, request.Plan, RunOptions{ Force: request.Force, SelectedArtifacts: selectedArtifacts, EffectiveArtifacts: effectiveArtifacts, }) if err != nil { return fmt.Errorf("run: %w", err) } _, err = fmt.Fprintf( out, "narratio run: session %s; executed=%d skipped=%d; manifest=%s; %s\n", summary.SessionID, len(summary.Executed), len(summary.Skipped), summary.ManifestPath, effectiveConfigSummary(cfg), ) return err }