Add effective artifact catalog command

This commit is contained in:
2026-09-07 04:46:20 +00:00
parent 63a86c43f4
commit 2b97ef3722
7 changed files with 415 additions and 12 deletions

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"sort"
"strconv"
"strings"
"gitea.maximumdirect.net/eric/narratio/internal/config"
@@ -36,7 +37,7 @@ func Config(ctx context.Context, args []string, out io.Writer) error {
func configWithDependencies(ctx context.Context, args []string, out io.Writer, deps commandDependencies) error {
deps = normalizeCommandDependencies(deps)
if len(args) == 0 {
return fmt.Errorf("config: expected subcommand: validate|show|sources|diff")
return fmt.Errorf("config: expected subcommand: validate|show|sources|artifacts|diff")
}
if args[0] == "--help" || args[0] == "-h" {
writeConfigCommandUsage(out)
@@ -49,6 +50,8 @@ func configWithDependencies(ctx context.Context, args []string, out io.Writer, d
return configShowWithDependencies(ctx, args[1:], out, deps)
case "sources":
return configSourcesWithDependencies(ctx, args[1:], out, deps)
case "artifacts":
return configArtifactsWithDependencies(ctx, args[1:], out, deps)
case "diff":
return configDiffWithDependencies(ctx, args[1:], out, deps)
default:
@@ -140,6 +143,70 @@ func configSourcesWithDependencies(_ context.Context, args []string, out io.Writ
return nil
}
// ConfigArtifacts validates and reports the effective concrete artifact catalog
// without loading session state or runtime collaborators.
func ConfigArtifacts(ctx context.Context, args []string, out io.Writer) error {
return configArtifactsWithDependencies(ctx, args, out, commandDependencies{})
}
func configArtifactsWithDependencies(_ context.Context, args []string, out io.Writer, deps commandDependencies) error {
flags, err := parseInspectionFlags("config artifacts", args, out)
if err != nil {
if errors.Is(err, flag.ErrHelp) {
return nil
}
return err
}
resolved, err := resolveInspectionConfigWithDependencies(flags, deps)
if err != nil {
return fmt.Errorf("config artifacts: %w", err)
}
records, err := config.EffectiveArtifactCatalog(resolved.Pipeline, resolved.Party)
if err != nil {
return fmt.Errorf("config artifacts: %w", err)
}
if _, err := fmt.Fprintln(out, "key\tsource\tdefinitions\tenabled\toutput\tdependencies\tfamily\tcharacter\tfamily_source\tparty_source"); err != nil {
return err
}
for _, record := range records {
fields := []string{
record.Key,
record.SourceID,
strings.Join(normalizedCatalogPaths(record.DefinitionSources), ","),
strconv.FormatBool(record.Enabled),
record.OutputPath,
strings.Join(record.Dependencies, ","),
record.FamilyKey,
record.CharacterID,
config.NormalizedConfigurationPath(record.FamilySource),
config.NormalizedConfigurationPath(record.PartySource),
}
for index := range fields {
fields[index] = escapeCatalogField(fields[index])
}
if _, err := fmt.Fprintln(out, strings.Join(fields, "\t")); err != nil {
return err
}
}
return nil
}
func normalizedCatalogPaths(paths []string) []string {
result := make([]string, len(paths))
for index, path := range paths {
result[index] = config.NormalizedConfigurationPath(path)
}
sort.Strings(result)
return result
}
func escapeCatalogField(value string) string {
value = strings.ReplaceAll(value, "\\", "\\\\")
value = strings.ReplaceAll(value, "\t", "\\t")
value = strings.ReplaceAll(value, "\r", "\\r")
return strings.ReplaceAll(value, "\n", "\\n")
}
func parseInspectionFlags(command string, args []string, out io.Writer) (inspectionFlags, error) {
fs := flag.NewFlagSet(command, flag.ContinueOnError)
fs.SetOutput(out)
@@ -256,9 +323,9 @@ func inspectionProfile(pipeline *config.PipelineConfig) string {
}
func writeConfigCommandUsage(out io.Writer) {
fmt.Fprintln(out, "Usage: narratio config <validate|show|sources|diff>")
fmt.Fprintln(out, "Usage: narratio config <validate|show|sources|artifacts|diff>")
fmt.Fprintln(out)
fmt.Fprintln(out, "Use config validate to check an effective pipeline, config show to print normalized YAML, config sources to report ownership, or config diff to compare two profiles.")
fmt.Fprintln(out, "Use config validate to check an effective pipeline, config show to print normalized YAML, config sources to report ownership, config artifacts to list concrete artifacts, or config diff to compare two profiles.")
}
func writeInspectionSourceHeader(out io.Writer, resolved *inspectionConfig) error {