2026-10-11 01:06:21 +00:00
|
|
|
package ckconfig
|
2026-10-07 13:50:48 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
2026-10-26 03:16:02 +00:00
|
|
|
"io"
|
2026-10-07 13:50:48 +00:00
|
|
|
"strings"
|
|
|
|
|
|
2026-10-11 01:39:23 +00:00
|
|
|
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
2026-10-07 13:50:48 +00:00
|
|
|
"go.yaml.in/yaml/v4"
|
|
|
|
|
)
|
|
|
|
|
|
2026-10-11 01:39:23 +00:00
|
|
|
func runConfigValidation[I, S any](invocation commandkit.Invocation, plan *configOperationPlan[I, S], state S) commandkit.Outcome {
|
2026-10-26 03:16:02 +00:00
|
|
|
reporter := invocation.Reporter()
|
2026-10-07 13:50:48 +00:00
|
|
|
writtenRecord := false
|
2026-10-11 01:39:23 +00:00
|
|
|
for _, selected := range plan.scopes {
|
2026-10-07 16:47:28 +00:00
|
|
|
if err := invocation.Context.Err(); err != nil {
|
2026-10-26 03:16:02 +00:00
|
|
|
return configFailure("Configuration validation was canceled.", commandkit.Explanation{}, err)
|
2026-10-07 16:47:28 +00:00
|
|
|
}
|
2026-10-11 01:39:23 +00:00
|
|
|
if selected.scope.scope.Acquire == nil {
|
2026-10-26 03:16:02 +00:00
|
|
|
message := fmt.Sprintf("Configuration scope %q has no acquisition callback.", selected.scope.scope.Name)
|
|
|
|
|
return commandkit.Failure(message)
|
2026-10-11 01:39:23 +00:00
|
|
|
}
|
|
|
|
|
acquired, acquireErr := selected.scope.scope.Acquire(invocation.Context, Request{
|
|
|
|
|
Operation: Validate,
|
|
|
|
|
Selection: plan.selection,
|
|
|
|
|
}, selected.input, state)
|
2026-10-07 13:50:48 +00:00
|
|
|
if acquireErr != nil {
|
2026-10-26 03:16:02 +00:00
|
|
|
return configAcquisitionFailure(reporter, "validate", selected.scope.scope.Name, acquired, acquireErr)
|
2026-10-07 13:50:48 +00:00
|
|
|
}
|
2026-10-26 03:16:02 +00:00
|
|
|
queueConfigDiagnostics(reporter, acquired.Diagnostics)
|
2026-10-07 16:47:28 +00:00
|
|
|
if err := invocation.Context.Err(); err != nil {
|
2026-10-26 03:16:02 +00:00
|
|
|
return configFailure("Configuration validation was canceled.", acquired.Explanation, err)
|
2026-10-07 16:47:28 +00:00
|
|
|
}
|
|
|
|
|
|
2026-10-26 03:16:02 +00:00
|
|
|
err := reporter.RenderOutput(func(writer io.Writer) error {
|
|
|
|
|
record, err := validationSuccessRecord(selected.scope.scope.Name, plan.format)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if plan.format == "yaml" && !writtenRecord {
|
2026-10-26 03:20:17 +00:00
|
|
|
if _, err := io.WriteString(writer, "scopes:\n"); err != nil {
|
2026-10-26 03:16:02 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-26 03:20:17 +00:00
|
|
|
_, err = writer.Write(record)
|
|
|
|
|
return err
|
2026-10-26 03:16:02 +00:00
|
|
|
})
|
2026-10-07 13:50:48 +00:00
|
|
|
if err != nil {
|
2026-10-26 03:16:02 +00:00
|
|
|
if ctxErr := invocation.Context.Err(); ctxErr != nil {
|
|
|
|
|
return configFailure("Configuration validation was canceled.", acquired.Explanation, ctxErr)
|
2026-10-07 13:50:48 +00:00
|
|
|
}
|
2026-10-26 03:16:02 +00:00
|
|
|
return commandkit.Success()
|
2026-10-07 13:50:48 +00:00
|
|
|
}
|
|
|
|
|
writtenRecord = true
|
|
|
|
|
}
|
2026-10-26 03:16:02 +00:00
|
|
|
return commandkit.Success()
|
2026-10-07 13:50:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validationSuccessRecord(scope, format string) ([]byte, error) {
|
|
|
|
|
if format == "text" {
|
|
|
|
|
return []byte(scope + ": valid\n"), nil
|
|
|
|
|
}
|
|
|
|
|
encoded, err := yaml.Marshal(struct {
|
|
|
|
|
Name string `yaml:"name"`
|
|
|
|
|
Valid bool `yaml:"valid"`
|
|
|
|
|
}{Name: scope, Valid: true})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
lines := strings.Split(strings.TrimSuffix(string(encoded), "\n"), "\n")
|
|
|
|
|
var record bytes.Buffer
|
|
|
|
|
for index, line := range lines {
|
|
|
|
|
if index == 0 {
|
|
|
|
|
record.WriteString(" - ")
|
|
|
|
|
} else {
|
|
|
|
|
record.WriteString(" ")
|
|
|
|
|
}
|
|
|
|
|
record.WriteString(line)
|
|
|
|
|
record.WriteByte('\n')
|
|
|
|
|
}
|
|
|
|
|
return record.Bytes(), nil
|
|
|
|
|
}
|