Apply explicit CLI configuration overrides

This commit is contained in:
2026-09-06 21:55:35 +00:00
parent d6e32d8807
commit d4ec8607d1
13 changed files with 1049 additions and 72 deletions

38
load.go
View File

@@ -20,7 +20,7 @@ type Definition[T any] struct {
// Options contains the application definition and per-load input selections.
// Imports enables explicit path and glob imports; Profile selects one fixed profile
// overlay. Explicit overrides remain unavailable until implemented.
// overlay; Overrides contains only values explicitly supplied by an application CLI.
type Options[T any] struct {
Definition Definition[T]
Imports bool
@@ -35,7 +35,9 @@ type FilesystemContext struct {
Root string
}
// Override describes one explicitly supplied application override.
// Override describes one explicitly supplied application override. Value uses
// parser-neutral built-in scalars, nil, []any, or map[string]any; Flag is a safe
// display name and must not contain a raw argument or value.
type Override struct {
Path Path
Value any
@@ -70,6 +72,7 @@ type Result[T any] struct {
diagnostics []Diagnostic
final *document.Node
resolution []resolutionSnapshot
overlays []overlayRecord
secrets map[string]bool
}
@@ -180,6 +183,29 @@ func loadWithSecretLookup[T any](source Source, lookup secretLookup, supplied ..
}
inputDocument = composition.root
}
if len(options.Overrides) != 0 {
if composition == nil {
if inputDocument == nil {
composition = &composedDocument{root: &document.Node{Kind: document.Mapping}, origins: make(map[string]Origin)}
} else {
composition, diagnostics = composeDocuments(inputDocument)
if len(diagnostics) != 0 {
accumulator.add(diagnostics...)
return nil, accumulator.loadError()
}
composition.inputBytes = parsed.bytes
composition.inputNodes = countInputNodes(inputDocument)
}
}
composition, accumulator.sources, diagnostics = applyOverrides(
composition, accumulator.sources, options.Overrides, options.Definition.Version, defaultImportLimits(),
)
if len(diagnostics) != 0 {
accumulator.add(diagnostics...)
return nil, accumulator.loadError()
}
inputDocument = composition.root
}
defaults := reflect.Zero(reflect.TypeFor[T]())
if options.Definition.Defaults != nil {
@@ -226,6 +252,10 @@ func loadWithSecretLookup[T any](source Source, lookup secretLookup, supplied ..
}
}
secrets := classifySecretPaths(schema, options.Definition.Secret, metadata, final)
var overlaySnapshots []overlayRecord
if composition != nil {
overlaySnapshots = cloneOverlayRecords(composition.overlays)
}
return &Result[T]{
Config: configuration,
metadata: metadata,
@@ -233,6 +263,7 @@ func loadWithSecretLookup[T any](source Source, lookup secretLookup, supplied ..
diagnostics: canonicalDiagnostics(accumulator.diagnostics),
final: document.Clone(final),
resolution: cloneResolutionSnapshots(resolutionSnapshots),
overlays: overlaySnapshots,
secrets: secrets,
}, nil
}
@@ -253,9 +284,6 @@ func selectOptions[T any](source Source, supplied []Options[T]) (Options[T], []D
if options.Filesystem != nil {
diagnostics = append(diagnostics, filesystemOptionDiagnostics(source, options.Filesystem)...)
}
if len(options.Overrides) != 0 {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsUnavailable, "configuration overrides are not available in this release boundary"))
}
if source.kind == sourceNone && options.Imports {
diagnostics = append(diagnostics, optionDiagnostic(CodeOptionsInvalid, "NoSource cannot enable imports"))
}