Prepare ConfigKit validation and listing commands
This commit is contained in:
@@ -451,6 +451,8 @@ multi-error reporting, and profile selection. Use the real ConfigKit boundary.
|
||||
|
||||
## Stage 9 — Prepare Validation And Pipeline Listing Commands
|
||||
|
||||
**Status:** Completed 2026-09-13.
|
||||
|
||||
**Goal:** Finish the read-only command implementations before the shared
|
||||
production cutover.
|
||||
|
||||
|
||||
100
internal/cli/configkit_commands.go
Normal file
100
internal/cli/configkit_commands.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/config"
|
||||
)
|
||||
|
||||
func runConfigKitValidate(args []string, stdout, stderr io.Writer, opts Options) int {
|
||||
fs := flag.NewFlagSet("config validate", flag.ContinueOnError)
|
||||
fs.SetOutput(io.Discard)
|
||||
configPath := fs.String("config", "", "config file path")
|
||||
profile := fs.String("config-profile", "", "configuration profile")
|
||||
pipelineID := fs.String("pipeline", "", "pipeline ID")
|
||||
onlyRaw := fs.String("only", "", "comma-separated artifact lanes")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
fmt.Fprintf(stderr, "notarius: unexpected argument %q\n", fs.Arg(0))
|
||||
return 2
|
||||
}
|
||||
if strings.TrimSpace(*onlyRaw) != "" && strings.TrimSpace(*pipelineID) == "" {
|
||||
fmt.Fprintln(stderr, "notarius: --only requires --pipeline")
|
||||
return 2
|
||||
}
|
||||
only, err := parseOnly(*onlyRaw)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
loaded, path, err := loadConfigKit(*configPath, *profile, nil, opts)
|
||||
if err != nil {
|
||||
writeConfigLoadError(stderr, err)
|
||||
return 1
|
||||
}
|
||||
if strings.TrimSpace(*pipelineID) == "" {
|
||||
fmt.Fprintf(stdout, "config %q is valid\n", path)
|
||||
return 0
|
||||
}
|
||||
catalog, err := effectiveCatalog(opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
effective, err := loaded.Config.Resolve(config.ResolveInput{PipelineID: *pipelineID, Only: only, Catalog: catalog})
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
if err := validateExplicitPromptKitProfiles(context.Background(), effective.Config, effectiveLLMProfileIDs(effective.ResolvedPipeline), opts.promptKitAssets); err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
fmt.Fprintf(stdout, "config %q is valid for pipeline %q\n", path, strings.TrimSpace(*pipelineID))
|
||||
return 0
|
||||
}
|
||||
|
||||
func runConfigKitPipelinesList(args []string, stdout, stderr io.Writer, opts Options) int {
|
||||
fs := flag.NewFlagSet("pipelines list", flag.ContinueOnError)
|
||||
fs.SetOutput(io.Discard)
|
||||
configPath := fs.String("config", "", "config file path")
|
||||
profile := fs.String("config-profile", "", "configuration profile")
|
||||
jsonOutput := fs.Bool("json", false, "write JSON output")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: %v\n", err)
|
||||
return 2
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
fmt.Fprintf(stderr, "notarius: unexpected argument %q\n", fs.Arg(0))
|
||||
return 2
|
||||
}
|
||||
loaded, _, err := loadConfigKit(*configPath, *profile, nil, opts)
|
||||
if err != nil {
|
||||
writeConfigLoadError(stderr, err)
|
||||
return 1
|
||||
}
|
||||
ids := sortedPipelineIDs(loaded.Config)
|
||||
if *jsonOutput {
|
||||
payload, err := json.Marshal(struct {
|
||||
Pipelines []string `json:"pipelines"`
|
||||
}{Pipelines: ids})
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "notarius: marshal pipeline list: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
fmt.Fprintf(stdout, "%s\n", payload)
|
||||
return 0
|
||||
}
|
||||
for _, id := range ids {
|
||||
fmt.Fprintln(stdout, id)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
35
internal/cli/configkit_commands_test.go
Normal file
35
internal/cli/configkit_commands_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigKitReadOnlyCommandsLoadImportsAndProfiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
root := t.TempDir()
|
||||
path := filepath.Join(root, "notarius.yml")
|
||||
writeConfigKitFile(t, filepath.Join(root, "fragment.yml"), "pipelines:\n imported:\n input: {module: input}\n")
|
||||
writeConfigKitFile(t, path, "version: 5\nconfigkit:\n imports:\n - path: fragment.yml\npipelines:\n base:\n input: {module: input}\n")
|
||||
var stdout, stderr bytes.Buffer
|
||||
if code := runConfigKitPipelinesList([]string{"--config", path}, &stdout, &stderr, commandContractOptions(t)); code != 0 || stdout.String() != "base\nimported\n" {
|
||||
t.Fatalf("list = %d, %q, %q", code, stdout.String(), stderr.String())
|
||||
}
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
if code := runConfigKitValidate([]string{"--config", path}, &stdout, &stderr, commandContractOptions(t)); code != 0 || !strings.Contains(stdout.String(), "is valid") {
|
||||
t.Fatalf("validate = %d, %q, %q", code, stdout.String(), stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigKitReadOnlyCommandsRejectVersionFour(t *testing.T) {
|
||||
t.Parallel()
|
||||
path := filepath.Join(t.TempDir(), "notarius.yml")
|
||||
writeConfigKitFile(t, path, "version: 4\n")
|
||||
var stdout, stderr bytes.Buffer
|
||||
if code := runConfigKitPipelinesList([]string{"--config", path}, &stdout, &stderr, commandContractOptions(t)); code != 1 || !strings.Contains(stderr.String(), "version") {
|
||||
t.Fatalf("list = %d, %q", code, stderr.String())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user