Migrate ConfigKit reports to the reporter
This commit is contained in:
@@ -3,41 +3,44 @@ package ckconfig
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
||||
)
|
||||
|
||||
func runConfigDiff[I, S any](invocation commandkit.Invocation, plan *configOperationPlan[I, S], state S) commandkit.Outcome {
|
||||
if plan.comparison == nil || plan.comparison.scope == nil {
|
||||
return configOperationFailure(errors.New("configuration comparison was not prepared"), nil, false)
|
||||
cause := errors.New("configuration comparison was not prepared")
|
||||
return configFailure("Configuration comparison was not prepared.", commandkit.Explanation{}, cause)
|
||||
}
|
||||
selected := plan.comparison
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, nil, false)
|
||||
return configFailure("Configuration comparison was canceled.", commandkit.Explanation{}, err)
|
||||
}
|
||||
compare := selected.scope.scope.Compare
|
||||
if compare == nil {
|
||||
return configOperationFailure(fmt.Errorf("configuration scope %q has no comparison callback", selected.scope.scope.Name), nil, false)
|
||||
cause := fmt.Errorf("configuration scope %q has no comparison callback", selected.scope.scope.Name)
|
||||
return configFailure("Configuration scope does not support comparison.", commandkit.Explanation{}, cause)
|
||||
}
|
||||
acquired, err := compare(invocation.Context, selected.request, state)
|
||||
diagnostics := append([]commandkit.Diagnostic(nil), acquired.Diagnostics...)
|
||||
reporter := invocation.Reporter()
|
||||
queueConfigDiagnostics(reporter, acquired.Diagnostics)
|
||||
if err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
return configFailure(fmt.Sprintf("Unable to compare configuration scope %q.", selected.scope.scope.Name), acquired.Explanation, err)
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
return configFailure("Configuration comparison was canceled.", acquired.Explanation, err)
|
||||
}
|
||||
report, err := renderDiffReport(acquired.Value)
|
||||
if err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
err = reporter.RenderOutput(func(writer io.Writer) error {
|
||||
report, err := renderDiffReport(acquired.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(report)
|
||||
return err
|
||||
})
|
||||
if err != nil && invocation.Context.Err() != nil {
|
||||
return configFailure("Configuration comparison was canceled.", acquired.Explanation, invocation.Context.Err())
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
}
|
||||
if err := writeConfigReport(invocation.Streams.Out, report); err != nil {
|
||||
return configDeliveryFailure(err, diagnostics, true)
|
||||
}
|
||||
outcome := commandkit.Success()
|
||||
outcome.Diagnostics = diagnostics
|
||||
return outcome
|
||||
return commandkit.Success()
|
||||
}
|
||||
|
||||
@@ -101,21 +101,30 @@ func TestConfigDiffRunsComparisonOnlyScopeAndRejectsUnsupportedScopeBeforeWork(t
|
||||
|
||||
func TestConfigDiffProviderFailureRetainsDiagnosticsAndCause(t *testing.T) {
|
||||
wantErr := errors.New("provider unavailable")
|
||||
diagnostic := commandkit.Diagnostic{Severity: commandkit.SeverityError, Write: func(writer io.Writer) error {
|
||||
diagnostic := commandkit.Diagnostic{Severity: commandkit.SeverityWarning, Write: func(writer io.Writer) error {
|
||||
_, err := io.WriteString(writer, "provider warning\n")
|
||||
return err
|
||||
}}
|
||||
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{{
|
||||
Name: "app", Operations: []Operation{Diff},
|
||||
Compare: func(context.Context, ComparisonRequest[struct{}], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
|
||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: []commandkit.Diagnostic{diagnostic}}, fmt.Errorf("comparison setup: %w", wantErr)
|
||||
return commandkit.Acquired[[]configkit.Difference]{
|
||||
Diagnostics: []commandkit.Diagnostic{diagnostic},
|
||||
Explanation: commandkit.Explanation{Details: []commandkit.Detail{func(writer io.Writer) error {
|
||||
_, err := io.WriteString(writer, "native comparison context\n")
|
||||
return err
|
||||
}}},
|
||||
}, fmt.Errorf("comparison setup: %w", wantErr)
|
||||
},
|
||||
}}}))
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"config", "diff"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, wantErr) || outcome.ReportIncomplete || outcome.DeliveryError != nil || output.Len() != 0 || len(outcome.Diagnostics) < 1 || outcome.Diagnostics[0].Severity != commandkit.SeverityError {
|
||||
var output, diagnosticOutput bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"config", "diff", "--verbose"}, commandkit.Streams{Out: &output, Err: &diagnosticOutput})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, wantErr) || output.Len() != 0 {
|
||||
t.Fatalf("provider failure outcome/output = %#v / %q", outcome, output.String())
|
||||
}
|
||||
if got := diagnosticOutput.String(); !strings.Contains(got, "provider warning") || !strings.Contains(got, "native comparison context") || strings.Index(got, "provider warning") > strings.Index(got, "native comparison context") || strings.Contains(got, wantErr.Error()) {
|
||||
t.Fatalf("provider messages and terminal context = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDiffReportPreparationFailureWritesNoReport(t *testing.T) {
|
||||
@@ -128,8 +137,8 @@ func TestConfigDiffReportPreparationFailureWritesNoReport(t *testing.T) {
|
||||
},
|
||||
}}}))
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"config", "diff"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.ReportIncomplete || outcome.DeliveryError != nil || output.Len() != 0 {
|
||||
outcome := app.Execute(context.Background(), []string{"config", "diff", "--quiet"}, commandkit.Streams{Out: failingOutput{err: errors.New("quiet destination should not be called")}, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Cause != nil || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete || outcome.Presentation.Output.RenderError == nil || output.Len() != 0 {
|
||||
t.Fatalf("report preparation failure outcome/output = %#v / %q", outcome, output.String())
|
||||
}
|
||||
}
|
||||
@@ -146,8 +155,8 @@ func TestConfigDiffCancellationAfterAcquisitionRetainsDiagnosticsWithoutWriting(
|
||||
}}}))
|
||||
defer cancel()
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(ctx, []string{"config", "diff"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || outcome.ReportIncomplete || output.Len() != 0 || len(outcome.Diagnostics) < 1 || outcome.Diagnostics[0].Severity != commandkit.SeverityWarning {
|
||||
outcome := app.Execute(ctx, []string{"config", "diff", "--verbose"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || output.Len() != 0 {
|
||||
t.Fatalf("cancelled comparison outcome/output = %#v / %q", outcome, output.String())
|
||||
}
|
||||
}
|
||||
@@ -170,13 +179,36 @@ func TestConfigDiffOutputErrorsCarryIncompleteDeliveryOutcome(t *testing.T) {
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
outcome := app.Execute(context.Background(), []string{"config", "diff"}, commandkit.Streams{Out: test.writer, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.DeliveryError, test.want) || !errors.Is(outcome.Cause, test.want) || !outcome.ReportIncomplete {
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Cause != nil || !errors.Is(outcome.Presentation.Output.WriteError, test.want) || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete {
|
||||
t.Fatalf("delivery failure outcome = %#v", outcome)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDiffDiagnosticDestinationFailureIsPresentationEvidence(t *testing.T) {
|
||||
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{{
|
||||
Name: "app", Operations: []Operation{Diff},
|
||||
Compare: func(context.Context, ComparisonRequest[struct{}], struct{}) (commandkit.Acquired[[]configkit.Difference], error) {
|
||||
return commandkit.Acquired[[]configkit.Difference]{
|
||||
Diagnostics: []commandkit.Diagnostic{{Severity: commandkit.SeverityWarning, Write: func(writer io.Writer) error {
|
||||
_, err := io.WriteString(writer, "provider warning\n")
|
||||
return err
|
||||
}}},
|
||||
}, nil
|
||||
},
|
||||
}}}))
|
||||
writeErr := errors.New("diagnostic destination failed")
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"config", "diff", "--verbose"}, commandkit.Streams{Out: &output, Err: failingOutput{err: writeErr}})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Cause != nil || !errors.Is(outcome.Presentation.Diagnostics.WriteError, writeErr) || outcome.Presentation.Diagnostics.State != commandkit.DeliveryIncomplete {
|
||||
t.Fatalf("diagnostic delivery failure outcome = %#v", outcome)
|
||||
}
|
||||
if output.Len() == 0 {
|
||||
t.Fatal("healthy result destination did not receive the diff report")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigDiffRepeatedInvocationsPrepareFreshInputsAndState(t *testing.T) {
|
||||
type input struct {
|
||||
ID int
|
||||
|
||||
@@ -2,7 +2,6 @@ package ckconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
@@ -54,70 +53,59 @@ type secretReferenceDTO struct {
|
||||
func runConfigExplanation[I, S any](invocation commandkit.Invocation, plan *configOperationPlan[I, S], state S) commandkit.Outcome {
|
||||
selected := plan.scopes[0]
|
||||
name := selected.scope.scope.Name
|
||||
reporter := invocation.Reporter()
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, nil, false)
|
||||
return configFailure("Configuration explanation was canceled.", commandkit.Explanation{}, err)
|
||||
}
|
||||
if selected.scope.scope.Acquire == nil {
|
||||
return configOperationFailure(fmt.Errorf("configuration scope %q has no acquisition callback", name), nil, false)
|
||||
cause := fmt.Errorf("configuration scope %q has no acquisition callback", name)
|
||||
return configFailure(fmt.Sprintf("Unable to explain configuration scope %q.", name), commandkit.Explanation{}, cause)
|
||||
}
|
||||
acquired, acquireErr := selected.scope.scope.Acquire(invocation.Context, Request{
|
||||
Operation: Explain,
|
||||
Selection: plan.selection,
|
||||
}, selected.input, state)
|
||||
diagnostics := append([]commandkit.Diagnostic(nil), acquired.Diagnostics...)
|
||||
queueConfigDiagnostics(reporter, acquired.Diagnostics)
|
||||
if acquireErr != nil {
|
||||
return configOperationFailure(acquireErr, diagnostics, false)
|
||||
return configFailure(fmt.Sprintf("Unable to explain configuration scope %q.", name), acquired.Explanation, acquireErr)
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
return configFailure("Configuration explanation was canceled.", acquired.Explanation, err)
|
||||
}
|
||||
if acquired.Value.Explanation == nil {
|
||||
return configOperationFailure(fmt.Errorf("configuration scope %q did not provide an explanation reader", name), diagnostics, false)
|
||||
cause := fmt.Errorf("configuration scope %q did not provide an explanation reader", name)
|
||||
return configFailure("Configuration scope did not provide explanation data.", acquired.Explanation, cause)
|
||||
}
|
||||
|
||||
explanation, found := acquired.Value.Explanation.Explain(plan.path)
|
||||
if !found {
|
||||
return configOperationFailure(fmt.Errorf("configuration path %q is not known in scope %q", plan.path.String(), name), diagnostics, false)
|
||||
cause := fmt.Errorf("configuration path %q is not known in scope %q", plan.path.String(), name)
|
||||
return configFailure("The requested configuration path is not available.", acquired.Explanation, cause)
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
return configFailure("Configuration explanation was canceled.", acquired.Explanation, err)
|
||||
}
|
||||
if plan.format != "yaml" {
|
||||
return writeNativeExplanation(invocation.Streams.Out, acquired.Value.Explanation, plan.path, diagnostics)
|
||||
}
|
||||
var report bytes.Buffer
|
||||
if err := encodeYAML(&report, makeExplanationReport(name, explanation)); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
}
|
||||
if err := writeConfigReport(invocation.Streams.Out, report.Bytes()); err != nil {
|
||||
return configDeliveryFailure(err, diagnostics, true)
|
||||
}
|
||||
outcome := commandkit.Success()
|
||||
outcome.Diagnostics = diagnostics
|
||||
return outcome
|
||||
}
|
||||
|
||||
func writeNativeExplanation(writer io.Writer, reader ExplanationReader, path configkit.Path, diagnostics []commandkit.Diagnostic) commandkit.Outcome {
|
||||
checked := &checkedWriter{writer: writer}
|
||||
renderErr := reader.WriteExplanation(checked, path)
|
||||
if checked.failure != nil {
|
||||
if renderErr == nil {
|
||||
renderErr = checked.failure
|
||||
} else {
|
||||
renderErr = errors.Join(renderErr, checked.failure)
|
||||
err := reporter.RenderOutput(func(writer io.Writer) error {
|
||||
return acquired.Value.Explanation.WriteExplanation(writer, plan.path)
|
||||
})
|
||||
if err != nil && invocation.Context.Err() != nil {
|
||||
return configFailure("Configuration explanation was canceled.", acquired.Explanation, invocation.Context.Err())
|
||||
}
|
||||
return commandkit.Success()
|
||||
}
|
||||
if renderErr != nil {
|
||||
outcome := configOperationFailure(renderErr, diagnostics, true)
|
||||
outcome.DeliveryError = checked.failure
|
||||
return outcome
|
||||
err := reporter.RenderOutput(func(writer io.Writer) error {
|
||||
var report bytes.Buffer
|
||||
if err := encodeYAML(&report, makeExplanationReport(name, explanation)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := writer.Write(report.Bytes())
|
||||
return err
|
||||
})
|
||||
if err != nil && invocation.Context.Err() != nil {
|
||||
return configFailure("Configuration explanation was canceled.", acquired.Explanation, invocation.Context.Err())
|
||||
}
|
||||
outcome := commandkit.Success()
|
||||
outcome.Diagnostics = diagnostics
|
||||
return outcome
|
||||
return commandkit.Success()
|
||||
}
|
||||
|
||||
func makeExplanationReport(scope string, explanation configkit.Explanation) explanationReport {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ckconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
|
||||
commandkit "gitea.maximumdirect.net/eric/CommandKit"
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
type scopeMetadata struct {
|
||||
@@ -265,19 +265,18 @@ func newConfigScopesCommand[I, S any](prepared *preparedSuite[I, S]) commandkit.
|
||||
)},
|
||||
CheckInput: func(input scopeInventoryInput) error { return checkConfigFormat(input.Format) },
|
||||
Run: func(invocation commandkit.Invocation, input scopeInventoryInput) commandkit.Outcome {
|
||||
var output []byte
|
||||
var err error
|
||||
if input.Format == "yaml" {
|
||||
output, err = yaml.Marshal(prepared.inventory)
|
||||
} else {
|
||||
output = []byte(formatScopeInventory(prepared.inventory))
|
||||
}
|
||||
if err != nil {
|
||||
return commandkit.Failure(err)
|
||||
}
|
||||
if err := writeConfigReport(invocation.Streams.Out, output); err != nil {
|
||||
return incompleteConfigReport(err)
|
||||
}
|
||||
_ = invocation.Reporter().RenderOutput(func(writer io.Writer) error {
|
||||
var report bytes.Buffer
|
||||
if input.Format == "yaml" {
|
||||
if err := encodeYAML(&report, prepared.inventory); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
_, _ = report.WriteString(formatScopeInventory(prepared.inventory))
|
||||
}
|
||||
_, err := writer.Write(report.Bytes())
|
||||
return err
|
||||
})
|
||||
return commandkit.Success()
|
||||
},
|
||||
}
|
||||
@@ -314,21 +313,3 @@ func checkConfigFormat(format string) error {
|
||||
}
|
||||
|
||||
func parseConfigString(value string) (string, error) { return value, nil }
|
||||
|
||||
func writeConfigReport(writer io.Writer, content []byte) error {
|
||||
written, err := writer.Write(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if written != len(content) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func incompleteConfigReport(err error) commandkit.Outcome {
|
||||
outcome := commandkit.Failure(err)
|
||||
outcome.ReportIncomplete = true
|
||||
outcome.DeliveryError = err
|
||||
return outcome
|
||||
}
|
||||
|
||||
@@ -472,13 +472,28 @@ func TestExplainParsesBeforeAcquisitionAndPreservesNativeSafeHistory(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportWriteFailureTracksIncompleteDeliveryAndCancellation(t *testing.T) {
|
||||
func TestExplainTracksNativeRenderFailureWithoutReplacingOperationCause(t *testing.T) {
|
||||
renderErr := errors.New("native explanation renderer failed")
|
||||
reader := &failingNativeExplanation{err: renderErr}
|
||||
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{{
|
||||
Name: "app", Operations: []Operation{Explain}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
||||
return commandkit.Acquired[View]{Value: View{Explanation: reader}}, nil
|
||||
},
|
||||
}}}))
|
||||
var output bytes.Buffer
|
||||
outcome := app.Execute(context.Background(), []string{"config", "explain", "--path", "/token"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Cause != nil || !errors.Is(outcome.Presentation.Output.RenderError, renderErr) || output.String() != "partial native explanation\n" {
|
||||
t.Fatalf("native explanation failure outcome/output = %#v / %q", outcome, output.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportWriteFailureIsRecordedByReporterAndPreservesCancellation(t *testing.T) {
|
||||
writeErr := errors.New("output failed")
|
||||
app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{{Name: "app", Operations: []Operation{Sources}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) {
|
||||
return commandkit.Acquired[View]{Value: View{Sources: &sourceInventory{values: []configkit.SourceInfo{}}}}, nil
|
||||
}}}}))
|
||||
outcome := app.Execute(context.Background(), []string{"config", "sources"}, commandkit.Streams{Out: failingOutput{err: writeErr}, Err: io.Discard})
|
||||
if !errors.Is(outcome.Cause, writeErr) || outcome.DeliveryError != writeErr || !outcome.ReportIncomplete {
|
||||
if outcome.Kind != commandkit.OutcomeOperational || outcome.Cause != nil || !errors.Is(outcome.Presentation.Output.WriteError, writeErr) || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete {
|
||||
t.Fatalf("source delivery failure outcome = %#v", outcome)
|
||||
}
|
||||
|
||||
@@ -531,6 +546,19 @@ type nativeExplanation struct {
|
||||
found bool
|
||||
}
|
||||
|
||||
type failingNativeExplanation struct{ err error }
|
||||
|
||||
func (reader *failingNativeExplanation) Explain(configkit.Path) (configkit.Explanation, bool) {
|
||||
return configkit.Explanation{}, true
|
||||
}
|
||||
|
||||
func (reader *failingNativeExplanation) WriteExplanation(writer io.Writer, _ configkit.Path) error {
|
||||
if _, err := io.WriteString(writer, "partial native explanation\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
return reader.err
|
||||
}
|
||||
|
||||
func (reader *nativeExplanation) Explain(path configkit.Path) (configkit.Explanation, bool) {
|
||||
reader.path = path
|
||||
return reader.value, reader.found
|
||||
|
||||
@@ -70,14 +70,14 @@ func runConfigShow[I, S any](invocation commandkit.Invocation, plan *configOpera
|
||||
}
|
||||
framed, hasDirective := frameEffectiveYAML(document.name, native.Bytes())
|
||||
if index > 0 && hasDirective {
|
||||
if err := writeConfigReport(writer, []byte("...\n")); err != nil {
|
||||
if _, err := io.WriteString(writer, "...\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if index < len(documents)-1 && len(framed) > 0 && framed[len(framed)-1] != '\n' {
|
||||
framed = append(framed, '\n')
|
||||
}
|
||||
if err := writeConfigReport(writer, framed); err != nil {
|
||||
if _, err := writer.Write(framed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,25 +52,28 @@ type versionDTO struct {
|
||||
func runConfigSources[I, S any](invocation commandkit.Invocation, plan *configOperationPlan[I, S], state S) commandkit.Outcome {
|
||||
selected := plan.scopes[0]
|
||||
name := selected.scope.scope.Name
|
||||
reporter := invocation.Reporter()
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, nil, false)
|
||||
return configFailure("Configuration source listing was canceled.", commandkit.Explanation{}, err)
|
||||
}
|
||||
if selected.scope.scope.Acquire == nil {
|
||||
return configOperationFailure(fmt.Errorf("configuration scope %q has no acquisition callback", name), nil, false)
|
||||
cause := fmt.Errorf("configuration scope %q has no acquisition callback", name)
|
||||
return configFailure(fmt.Sprintf("Unable to list sources for configuration scope %q.", name), commandkit.Explanation{}, cause)
|
||||
}
|
||||
acquired, acquireErr := selected.scope.scope.Acquire(invocation.Context, Request{
|
||||
Operation: Sources,
|
||||
Selection: plan.selection,
|
||||
}, selected.input, state)
|
||||
diagnostics := append([]commandkit.Diagnostic(nil), acquired.Diagnostics...)
|
||||
queueConfigDiagnostics(reporter, acquired.Diagnostics)
|
||||
if acquireErr != nil {
|
||||
return configOperationFailure(acquireErr, diagnostics, false)
|
||||
return configFailure(fmt.Sprintf("Unable to list sources for configuration scope %q.", name), acquired.Explanation, acquireErr)
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
return configFailure("Configuration source listing was canceled.", acquired.Explanation, err)
|
||||
}
|
||||
if acquired.Value.Sources == nil {
|
||||
return configOperationFailure(fmt.Errorf("configuration scope %q did not provide a source reader", name), diagnostics, false)
|
||||
cause := fmt.Errorf("configuration scope %q did not provide a source reader", name)
|
||||
return configFailure("Configuration scope did not provide source information.", acquired.Explanation, cause)
|
||||
}
|
||||
|
||||
sources := acquired.Value.Sources.Sources()
|
||||
@@ -111,23 +114,25 @@ func runConfigSources[I, S any](invocation commandkit.Invocation, plan *configOp
|
||||
}
|
||||
document.Sources = append(document.Sources, item)
|
||||
}
|
||||
var report bytes.Buffer
|
||||
if plan.format == "yaml" {
|
||||
if err := encodeYAML(&report, document); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
}
|
||||
} else {
|
||||
writeSourceReportText(&report, document)
|
||||
}
|
||||
if err := invocation.Context.Err(); err != nil {
|
||||
return configOperationFailure(err, diagnostics, false)
|
||||
return configFailure("Configuration source listing was canceled.", acquired.Explanation, err)
|
||||
}
|
||||
if err := writeConfigReport(invocation.Streams.Out, report.Bytes()); err != nil {
|
||||
return configDeliveryFailure(err, diagnostics, true)
|
||||
err := reporter.RenderOutput(func(writer io.Writer) error {
|
||||
var report bytes.Buffer
|
||||
if plan.format == "yaml" {
|
||||
if err := encodeYAML(&report, document); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
writeSourceReportText(&report, document)
|
||||
}
|
||||
_, err := writer.Write(report.Bytes())
|
||||
return err
|
||||
})
|
||||
if err != nil && invocation.Context.Err() != nil {
|
||||
return configFailure("Configuration source listing was canceled.", acquired.Explanation, invocation.Context.Err())
|
||||
}
|
||||
outcome := commandkit.Success()
|
||||
outcome.Diagnostics = diagnostics
|
||||
return outcome
|
||||
return commandkit.Success()
|
||||
}
|
||||
|
||||
func writeSourceReportText(writer *bytes.Buffer, document sourceReport) {
|
||||
|
||||
@@ -39,11 +39,12 @@ func runConfigValidation[I, S any](invocation commandkit.Invocation, plan *confi
|
||||
return err
|
||||
}
|
||||
if plan.format == "yaml" && !writtenRecord {
|
||||
if err := writeConfigReport(writer, []byte("scopes:\n")); err != nil {
|
||||
if _, err := io.WriteString(writer, "scopes:\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return writeConfigReport(writer, record)
|
||||
_, err = writer.Write(record)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
if ctxErr := invocation.Context.Err(); ctxErr != nil {
|
||||
|
||||
Reference in New Issue
Block a user