Files
commandkit/integrations/ckconfig/sources.go

187 lines
7.0 KiB
Go
Raw Permalink Normal View History

package ckconfig
2026-10-07 14:05:26 +00:00
import (
"bytes"
"fmt"
2026-10-11 01:39:23 +00:00
"io"
2026-10-07 14:05:26 +00:00
"strconv"
2026-10-11 01:39:23 +00:00
commandkit "gitea.maximumdirect.net/eric/CommandKit"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
"go.yaml.in/yaml/v4"
)
2026-10-07 14:05:26 +00:00
type sourceReport struct {
Scope string `yaml:"scope"`
Sources []sourceDTO `yaml:"sources"`
}
type sourceDTO struct {
ID string `yaml:"id"`
Label string `yaml:"label"`
Path string `yaml:"path"`
Destination string `yaml:"destination"`
Placement string `yaml:"placement"`
DestinationUnresolved bool `yaml:"destination_unresolved"`
Layer string `yaml:"layer"`
Kind string `yaml:"kind"`
UnderlayIndex int `yaml:"underlay_index"`
ImportedFrom *locationDTO `yaml:"imported_from"`
VersionCheck *sourceVersionCheckDTO `yaml:"version_check"`
2026-10-07 14:05:26 +00:00
}
type locationDTO struct {
SourceID string `yaml:"source_id"`
Line int `yaml:"line"`
Column int `yaml:"column"`
}
type sourceVersionCheckDTO struct {
Path string `yaml:"path"`
Version versionDTO `yaml:"version"`
Location *locationDTO `yaml:"location"`
Redacted bool `yaml:"redacted"`
}
type versionDTO struct {
Present bool `yaml:"present"`
Null bool `yaml:"null"`
Text string `yaml:"text"`
}
2026-10-11 01:39:23 +00:00
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
if err := invocation.Context.Err(); err != nil {
2026-10-11 01:39:23 +00:00
return configOperationFailure(err, nil, false)
}
2026-10-11 01:39:23 +00:00
if selected.scope.scope.Acquire == nil {
return configOperationFailure(fmt.Errorf("configuration scope %q has no acquisition callback", name), nil, false)
2026-10-07 14:05:26 +00:00
}
2026-10-11 01:39:23 +00:00
acquired, acquireErr := selected.scope.scope.Acquire(invocation.Context, Request{
Operation: Sources,
Selection: plan.selection,
}, selected.input, state)
diagnostics := append([]commandkit.Diagnostic(nil), acquired.Diagnostics...)
2026-10-07 14:05:26 +00:00
if acquireErr != nil {
2026-10-11 01:39:23 +00:00
return configOperationFailure(acquireErr, diagnostics, false)
2026-10-07 14:05:26 +00:00
}
if err := invocation.Context.Err(); err != nil {
2026-10-11 01:39:23 +00:00
return configOperationFailure(err, diagnostics, false)
}
2026-10-07 14:05:26 +00:00
if acquired.Value.Sources == nil {
2026-10-11 01:39:23 +00:00
return configOperationFailure(fmt.Errorf("configuration scope %q did not provide a source reader", name), diagnostics, false)
2026-10-07 14:05:26 +00:00
}
2026-10-11 01:39:23 +00:00
2026-10-07 14:05:26 +00:00
sources := acquired.Value.Sources.Sources()
document := sourceReport{Scope: name, Sources: make([]sourceDTO, 0, len(sources))}
for _, source := range sources {
placement := "mapping"
if source.PlacementMode == configkit.PlacementModeSequenceItem {
placement = "sequence_item"
}
2026-10-07 14:05:26 +00:00
item := sourceDTO{
ID: source.ID,
Label: source.Label,
Path: source.Path,
Destination: source.Destination.String(),
Placement: placement,
DestinationUnresolved: source.DestinationUnresolved,
Layer: string(source.Layer),
Kind: string(source.Kind),
UnderlayIndex: source.UnderlayIndex,
2026-10-07 14:05:26 +00:00
}
if source.ImportedFrom != nil {
item.ImportedFrom = &locationDTO{SourceID: source.ImportedFrom.SourceID, Line: source.ImportedFrom.Line, Column: source.ImportedFrom.Column}
}
if source.VersionCheck != nil {
check := source.VersionCheck
item.VersionCheck = &sourceVersionCheckDTO{
Path: check.Path.String(),
Version: versionDTO{
Present: check.Version.Present,
Null: check.Version.Null,
Text: check.Version.Text,
},
Redacted: check.Redacted,
}
if check.Location != nil {
item.VersionCheck.Location = &locationDTO{SourceID: check.Location.SourceID, Line: check.Location.Line, Column: check.Location.Column}
}
}
2026-10-07 14:05:26 +00:00
document.Sources = append(document.Sources, item)
}
var report bytes.Buffer
2026-10-11 01:39:23 +00:00
if plan.format == "yaml" {
2026-10-07 14:05:26 +00:00
if err := encodeYAML(&report, document); err != nil {
2026-10-11 01:39:23 +00:00
return configOperationFailure(err, diagnostics, false)
2026-10-07 14:05:26 +00:00
}
} else {
writeSourceReportText(&report, document)
}
if err := invocation.Context.Err(); err != nil {
2026-10-11 01:39:23 +00:00
return configOperationFailure(err, diagnostics, false)
}
2026-10-11 01:39:23 +00:00
if err := writeConfigReport(invocation.Streams.Out, report.Bytes()); err != nil {
return configDeliveryFailure(err, diagnostics, true)
2026-10-07 14:05:26 +00:00
}
2026-10-11 01:39:23 +00:00
outcome := commandkit.Success()
2026-10-07 14:05:26 +00:00
outcome.Diagnostics = diagnostics
return outcome
}
func writeSourceReportText(writer *bytes.Buffer, document sourceReport) {
_, _ = fmt.Fprintf(writer, "Scope: %s\n", strconv.Quote(document.Scope))
if len(document.Sources) == 0 {
_, _ = writer.WriteString("Sources: []\n")
return
}
_, _ = writer.WriteString("Sources:\n")
for _, source := range document.Sources {
_, _ = fmt.Fprintf(writer, " - id: %s\n", strconv.Quote(source.ID))
_, _ = fmt.Fprintf(writer, " label: %s\n", strconv.Quote(source.Label))
_, _ = fmt.Fprintf(writer, " path: %s\n", strconv.Quote(source.Path))
_, _ = fmt.Fprintf(writer, " destination: %s\n", strconv.Quote(source.Destination))
_, _ = fmt.Fprintf(writer, " placement: %s\n", strconv.Quote(source.Placement))
_, _ = fmt.Fprintf(writer, " destination_unresolved: %t\n", source.DestinationUnresolved)
2026-10-07 14:05:26 +00:00
_, _ = fmt.Fprintf(writer, " layer: %s\n", strconv.Quote(source.Layer))
_, _ = fmt.Fprintf(writer, " kind: %s\n", strconv.Quote(source.Kind))
_, _ = fmt.Fprintf(writer, " underlay_index: %d\n", source.UnderlayIndex)
if source.ImportedFrom == nil {
_, _ = writer.WriteString(" imported_from: null\n")
} else {
_, _ = fmt.Fprintf(writer, " imported_from: {source_id: %s, line: %d, column: %d}\n",
strconv.Quote(source.ImportedFrom.SourceID), source.ImportedFrom.Line, source.ImportedFrom.Column)
}
if source.VersionCheck == nil {
_, _ = writer.WriteString(" version_check: null\n")
continue
}
check := source.VersionCheck
_, _ = fmt.Fprintf(writer, " version_check:\n path: %s\n", strconv.Quote(check.Path))
_, _ = fmt.Fprintf(writer, " version:\n present: %t\n null: %t\n text: %s\n",
check.Version.Present, check.Version.Null, strconv.Quote(check.Version.Text))
if check.Location == nil {
_, _ = writer.WriteString(" location: null\n")
} else {
_, _ = fmt.Fprintf(writer, " location: {source_id: %s, line: %d, column: %d}\n",
strconv.Quote(check.Location.SourceID), check.Location.Line, check.Location.Column)
}
_, _ = fmt.Fprintf(writer, " redacted: %t\n", check.Redacted)
2026-10-07 14:05:26 +00:00
}
}
2026-10-11 01:39:23 +00:00
func encodeYAML(writer io.Writer, document any) error {
encoder := yaml.NewEncoder(writer)
encoder.SetIndent(2)
return encoder.Encode(document)
}
func locationReports(locations []configkit.Location) []locationDTO {
report := make([]locationDTO, 0, len(locations))
for _, location := range locations {
report = append(report, locationDTO{SourceID: location.SourceID, Line: location.Line, Column: location.Column})
}
return report
2026-10-07 14:05:26 +00:00
}