Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd730d5c99 |
@@ -7,6 +7,8 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const directiveKey = "configkit"
|
||||||
|
|
||||||
type importDeclaration struct {
|
type importDeclaration struct {
|
||||||
path string
|
path string
|
||||||
glob string
|
glob string
|
||||||
@@ -19,11 +21,11 @@ func splitImportDirectives(root *document.Node) (*document.Node, []importDeclara
|
|||||||
var declarations []importDeclaration
|
var declarations []importDeclaration
|
||||||
directiveIndex := -1
|
directiveIndex := -1
|
||||||
for index, entry := range body.Mapping {
|
for index, entry := range body.Mapping {
|
||||||
if entry.Key != "_configkit" {
|
if entry.Key != directiveKey {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
directiveIndex = index
|
directiveIndex = index
|
||||||
parsed, diagnostics := parseImportDirective(entry.Value, Keys("_configkit"))
|
parsed, diagnostics := parseImportDirective(entry.Value, Keys(directiveKey))
|
||||||
if len(diagnostics) != 0 {
|
if len(diagnostics) != 0 {
|
||||||
return nil, nil, diagnostics
|
return nil, nil, diagnostics
|
||||||
}
|
}
|
||||||
@@ -102,7 +104,7 @@ func nestedDirectiveDiagnostics(node *document.Node, current Path) []Diagnostic
|
|||||||
if node.Kind == document.Mapping {
|
if node.Kind == document.Mapping {
|
||||||
for _, entry := range node.Mapping {
|
for _, entry := range node.Mapping {
|
||||||
childPath := current.Key(entry.Key)
|
childPath := current.Key(entry.Key)
|
||||||
if entry.Key == "_configkit" {
|
if entry.Key == directiveKey {
|
||||||
location := publicLocation(entry.KeyLocation)
|
location := publicLocation(entry.KeyLocation)
|
||||||
return []Diagnostic{{Code: CodeImportInvalid, Severity: SeverityError, Path: childPath, Message: "configuration directives are permitted only at the document root", Primary: &location}}
|
return []Diagnostic{{Code: CodeImportInvalid, Severity: SeverityError, Path: childPath, Message: "configuration directives are permitted only at the document root", Primary: &location}}
|
||||||
}
|
}
|
||||||
@@ -183,7 +185,7 @@ func profileDirectiveDiagnostics(node *document.Node, current Path) []Diagnostic
|
|||||||
if node.Kind == document.Mapping {
|
if node.Kind == document.Mapping {
|
||||||
for _, entry := range node.Mapping {
|
for _, entry := range node.Mapping {
|
||||||
childPath := current.Key(entry.Key)
|
childPath := current.Key(entry.Key)
|
||||||
if entry.Key == "_configkit" {
|
if entry.Key == directiveKey {
|
||||||
location := publicLocation(entry.KeyLocation)
|
location := publicLocation(entry.KeyLocation)
|
||||||
return []Diagnostic{{
|
return []Diagnostic{{
|
||||||
Code: CodeProfileDirective, Severity: SeverityError, Path: childPath,
|
Code: CodeProfileDirective, Severity: SeverityError, Path: childPath,
|
||||||
@@ -209,7 +211,7 @@ func disabledDirectiveDiagnostics(node *document.Node, path Path) []Diagnostic {
|
|||||||
if node.Kind == document.Mapping {
|
if node.Kind == document.Mapping {
|
||||||
for _, entry := range node.Mapping {
|
for _, entry := range node.Mapping {
|
||||||
childPath := path.Key(entry.Key)
|
childPath := path.Key(entry.Key)
|
||||||
if entry.Key == "_configkit" {
|
if entry.Key == directiveKey {
|
||||||
location := Location{SourceID: entry.KeyLocation.SourceID, Line: entry.KeyLocation.Line, Column: entry.KeyLocation.Column}
|
location := Location{SourceID: entry.KeyLocation.SourceID, Line: entry.KeyLocation.Line, Column: entry.KeyLocation.Column}
|
||||||
diagnostics = append(diagnostics, Diagnostic{
|
diagnostics = append(diagnostics, Diagnostic{
|
||||||
Code: CodeImportDisabled, Severity: SeverityError, Path: childPath,
|
Code: CodeImportDisabled, Severity: SeverityError, Path: childPath,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ semantic validator, explicit path and glob imports, one selected profile, and
|
|||||||
filesystem context. It also accepts explicit CLI override records, static secret
|
filesystem context. It also accepts explicit CLI override records, static secret
|
||||||
tags, and a dynamic path predicate. `NoSource` rejects imports, active profiles,
|
tags, and a dynamic path predicate. `NoSource` rejects imports, active profiles,
|
||||||
and filesystem context as invalid combinations but accepts overrides. Input
|
and filesystem context as invalid combinations but accepts overrides. Input
|
||||||
containing a reserved `_configkit` mapping key is rejected while imports are
|
containing a reserved `configkit` mapping key is rejected while imports are
|
||||||
disabled.
|
disabled.
|
||||||
|
|
||||||
Every source, options, schema, version, defaults, decode, resolution, or validation
|
Every source, options, schema, version, defaults, decode, resolution, or validation
|
||||||
@@ -419,7 +419,7 @@ origin data; history retains the displaced definitions and identifies the parent
|
|||||||
replacement. Surviving profile paths have profile origins, while recursively shared
|
replacement. Surviving profile paths have profile origins, while recursively shared
|
||||||
mapping containers retain their contributing locations.
|
mapping containers retain their contributing locations.
|
||||||
|
|
||||||
A profile may not contain `_configkit` anywhere, contribute the configured schema
|
A profile may not contain `configkit` anywhere, contribute the configured schema
|
||||||
version, or replace an ancestor of that version with a scalar or null. These rules
|
version, or replace an ancestor of that version with a scalar or null. These rules
|
||||||
apply even if the supplied version text would be unchanged. Profiles cannot mask a
|
apply even if the supplied version text would be unchanged. Profiles cannot mask a
|
||||||
base parse, composition, or version failure because those checks run first.
|
base parse, composition, or version failure because those checks run first.
|
||||||
@@ -452,7 +452,7 @@ All records are detached and validated before any is applied. Root and explicit
|
|||||||
sequence-index paths are invalid; parsed numeric tokens remain mapping keys. A path
|
sequence-index paths are invalid; parsed numeric tokens remain mapping keys. A path
|
||||||
may create missing mapping ancestors but cannot traverse an existing scalar, null,
|
may create missing mapping ancestors but cannot traverse an existing scalar, null,
|
||||||
or sequence. Duplicate and ancestor/descendant-overlapping records are rejected.
|
or sequence. Duplicate and ancestor/descendant-overlapping records are rejected.
|
||||||
Neither paths nor values may create `_configkit`, and no record may overlap the
|
Neither paths nor values may create `configkit`, and no record may overlap the
|
||||||
configured schema-version path. Unknown fields and effective type mismatches remain
|
configured schema-version path. Unknown fields and effective type mismatches remain
|
||||||
ordinary strict-decoder errors.
|
ordinary strict-decoder errors.
|
||||||
|
|
||||||
@@ -475,12 +475,18 @@ zero.
|
|||||||
## Explicit imports
|
## Explicit imports
|
||||||
|
|
||||||
Imports are opt-in. With `Options.Imports` enabled, a document may reserve its root
|
Imports are opt-in. With `Options.Imports` enabled, a document may reserve its root
|
||||||
`_configkit` mapping for an `imports` sequence. ConfigKit removes that mapping before
|
`configkit` mapping for an `imports` sequence. ConfigKit removes that mapping before
|
||||||
application decoding. Each sequence item is a mapping with exactly one nonempty
|
application decoding. Each sequence item is a mapping with exactly one nonempty
|
||||||
string `path` or `glob` plus an optional boolean `optional`; all other shapes and
|
string `path` or `glob` plus an optional boolean `optional`; all other shapes and
|
||||||
fields are `import.invalid`. A `_configkit` key below the root is invalid. With
|
fields are `import.invalid`. A `configkit` key below the root is invalid. With
|
||||||
imports disabled, any occurrence remains `import.disabled`.
|
imports disabled, any occurrence remains `import.disabled`.
|
||||||
|
|
||||||
|
The directive key is `configkit`, not the `_configkit` spelling used in v0.1.0.
|
||||||
|
There is no legacy alias: `_configkit` is ordinary application data, subject to
|
||||||
|
normal schema validation, and does not trigger imports. When upgrading, rename
|
||||||
|
directive keys in root files and imported fragments, and rename any application
|
||||||
|
fields previously named `configkit` to avoid the newly reserved key.
|
||||||
|
|
||||||
Path declarations use slash-separated relative paths. Absolute paths and network
|
Path declarations use slash-separated relative paths. Absolute paths and network
|
||||||
URLs are invalid. Each path resolves from its declaring file's canonical directory,
|
URLs are invalid. Each path resolves from its declaring file's canonical directory,
|
||||||
and files are traversed depth-first in declaration order: a file contributes its own
|
and files are traversed depth-first in declaration order: a file contributes its own
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Base Policy Version:** 0.1.0
|
**Base Policy Version:** 0.1.0
|
||||||
|
|
||||||
**Last Updated:** 2026-09-06
|
**Last Updated:** 2026-09-07
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@@ -220,5 +220,12 @@ restating or redefining its requirements.
|
|||||||
|
|
||||||
## Policy Exceptions
|
## Policy Exceptions
|
||||||
|
|
||||||
None. Candidate selection, publication, and post-publication verification remain
|
For v0.1.1 only, the maintainer approved renaming the reserved `_configkit` key to
|
||||||
mandatory steps in the [release procedure](../release.md), not policy exceptions.
|
`configkit` as an error-correcting point release because there are no current
|
||||||
|
consumers. This intentionally changes the v0.1.0 configuration contract without a
|
||||||
|
minor-version increment. The release note must explain the renamed directive,
|
||||||
|
the lack of a legacy alias, and the newly reserved application key. This exception
|
||||||
|
does not apply to subsequent releases or waive any validation or publication gate.
|
||||||
|
|
||||||
|
Candidate selection, publication, and post-publication verification remain
|
||||||
|
mandatory steps in the [release procedure](../release.md).
|
||||||
|
|||||||
43
docs/releases/v0.1.1.md
Normal file
43
docs/releases/v0.1.1.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# ConfigKit v0.1.1
|
||||||
|
|
||||||
|
This release corrects the spelling of ConfigKit's reserved configuration key.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
The reserved directive key is now `configkit` instead of `_configkit`. Import
|
||||||
|
declarations continue to use the same `imports` sequence and path/glob syntax.
|
||||||
|
See the [configuration contract](../config.md#explicit-imports).
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
This is an intentional configuration compatibility change, approved as a one-time
|
||||||
|
[patch-release exception](../policy/release.md#policy-exceptions) because there are
|
||||||
|
no current consumers. `_configkit` is no longer a directive or a reserved key; it
|
||||||
|
is ordinary application data and is subject to normal schema validation. There is
|
||||||
|
no legacy alias. `configkit` is now reserved, including in nested mappings, profiles,
|
||||||
|
and override paths and values.
|
||||||
|
|
||||||
|
The Go API, diagnostic codes, Go minimum version, and supported platforms are
|
||||||
|
unchanged. The private YAML dependency remains pinned to
|
||||||
|
`go.yaml.in/yaml/v4 v4.0.0-rc.6`, confirmed as the newest available v4 release
|
||||||
|
during candidate preparation.
|
||||||
|
|
||||||
|
## Upgrade
|
||||||
|
|
||||||
|
Rename `_configkit` directive keys to `configkit` in root configuration files and
|
||||||
|
imported fragments. Rename any application-owned `configkit` fields to avoid the
|
||||||
|
newly reserved key. Update diagnostic-path expectations to the new spelling.
|
||||||
|
Files retaining `_configkit` will not load their declared imports.
|
||||||
|
|
||||||
|
Consumers can select this release with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get gitea.maximumdirect.net/eric/ConfigKit@v0.1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Renamed the directive key consistently across imports, profiles, and overrides.
|
||||||
|
- Updated current configuration documentation and the composed consumer fixture.
|
||||||
|
- Updated behavioral tests and added coverage proving the old spelling is not an
|
||||||
|
import alias.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
_configkit:
|
configkit:
|
||||||
imports:
|
imports:
|
||||||
- path: artifacts.yml
|
- path: artifacts.yml
|
||||||
poll_interval: 1m
|
poll_interval: 1m
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type explanationConcurrency struct {
|
|||||||
|
|
||||||
func TestExplainShowsDefaultBaseAndEffectiveProfileValue(t *testing.T) {
|
func TestExplainShowsDefaultBaseAndEffectiveProfileValue(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "config.yml", "_configkit:\n imports: [{path: transcription.yml}]\n")
|
mainPath := writeImportFile(t, directory, "config.yml", "configkit:\n imports: [{path: transcription.yml}]\n")
|
||||||
writeImportFile(t, directory, "transcription.yml", "transcription:\n concurrency: 8\n")
|
writeImportFile(t, directory, "transcription.yml", "transcription:\n concurrency: 8\n")
|
||||||
writeImportFile(t, directory, filepath.Join("profiles", "testing.yml"), "transcription:\n concurrency: 1\n")
|
writeImportFile(t, directory, filepath.Join("profiles", "testing.yml"), "transcription:\n concurrency: 1\n")
|
||||||
result, err := Load[explanationConcurrency](File(mainPath), Options[explanationConcurrency]{
|
result, err := Load[explanationConcurrency](File(mainPath), Options[explanationConcurrency]{
|
||||||
|
|||||||
10
glob_test.go
10
glob_test.go
@@ -10,13 +10,13 @@ import (
|
|||||||
|
|
||||||
func TestLoadGlobImportsUseLexicalDeclarationOrderAndNestedDFS(t *testing.T) {
|
func TestLoadGlobImportsUseLexicalDeclarationOrderAndNestedDFS(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- glob: fragments/*.yml
|
- glob: fragments/*.yml
|
||||||
- path: after.yml
|
- path: after.yml
|
||||||
items: {root: root}
|
items: {root: root}
|
||||||
`)
|
`)
|
||||||
aPath := writeImportFile(t, directory, "fragments/a.yml", `_configkit:
|
aPath := writeImportFile(t, directory, "fragments/a.yml", `configkit:
|
||||||
imports: [{path: nested/child.yml}]
|
imports: [{path: nested/child.yml}]
|
||||||
items: {alpha: alpha}
|
items: {alpha: alpha}
|
||||||
`)
|
`)
|
||||||
@@ -56,7 +56,7 @@ items: {alpha: alpha}
|
|||||||
|
|
||||||
func TestLoadGlobImportsSupportEscapesClassesAndExplicitConfD(t *testing.T) {
|
func TestLoadGlobImportsSupportEscapesClassesAndExplicitConfD(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- glob: 'conf.d/literal\*.yml'
|
- glob: 'conf.d/literal\*.yml'
|
||||||
- glob: 'conf.d/item-[ab].yml'
|
- glob: 'conf.d/item-[ab].yml'
|
||||||
@@ -126,7 +126,7 @@ func TestLoadGlobImportsPreserveIdentityAndFileRules(t *testing.T) {
|
|||||||
t.Run("overlapping declarations", func(t *testing.T) {
|
t.Run("overlapping declarations", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
writeImportFile(t, directory, "conf/value.yml", "items: {value: value}\n")
|
writeImportFile(t, directory, "conf/value.yml", "items: {value: value}\n")
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- glob: conf/*.yml
|
- glob: conf/*.yml
|
||||||
- glob: conf/value.*
|
- glob: conf/value.*
|
||||||
@@ -269,5 +269,5 @@ func writeGlobRoot(t *testing.T, directory, pattern string, optional bool) strin
|
|||||||
if optional {
|
if optional {
|
||||||
optionalLine = "\n optional: true"
|
optionalLine = "\n optional: true"
|
||||||
}
|
}
|
||||||
return writeImportFile(t, directory, "main.yml", "_configkit:\n imports:\n - glob: '"+pattern+"'"+optionalLine+"\n")
|
return writeImportFile(t, directory, "main.yml", "configkit:\n imports:\n - glob: '"+pattern+"'"+optionalLine+"\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,37 @@ type importTestConfig struct {
|
|||||||
Items map[string]string `yaml:"items"`
|
Items map[string]string `yaml:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadFormerDirectiveKeyIsApplicationData(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
Legacy struct {
|
||||||
|
Imports []struct {
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
} `yaml:"imports"`
|
||||||
|
} `yaml:"_configkit"`
|
||||||
|
}
|
||||||
|
for _, imports := range []bool{false, true} {
|
||||||
|
directory := t.TempDir()
|
||||||
|
mainPath := writeImportFile(t, directory, "main.yml", "_configkit:\n imports:\n - path: nonexistent.yml\n")
|
||||||
|
result, err := Load[config](File(mainPath), Options[config]{Imports: imports})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Imports=%v: %v", imports, err)
|
||||||
|
}
|
||||||
|
if len(result.Config.Legacy.Imports) != 1 || result.Config.Legacy.Imports[0].Path != "nonexistent.yml" || len(result.Sources()) != 1 {
|
||||||
|
t.Fatalf("Imports=%v: former directive was not retained as application data: %#v", imports, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadTraversesPathImportsDepthFirstAndComposesOrigins(t *testing.T) {
|
func TestLoadTraversesPathImportsDepthFirstAndComposesOrigins(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- path: first.yml
|
- path: first.yml
|
||||||
- path: second.yml
|
- path: second.yml
|
||||||
settings:
|
settings:
|
||||||
root: root
|
root: root
|
||||||
`)
|
`)
|
||||||
firstPath := writeImportFile(t, directory, "first.yml", `_configkit:
|
firstPath := writeImportFile(t, directory, "first.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- path: nested/third.yml
|
- path: nested/third.yml
|
||||||
settings:
|
settings:
|
||||||
@@ -71,7 +92,7 @@ settings:
|
|||||||
func TestLoadRejectsImportConflictsAndFragmentVersions(t *testing.T) {
|
func TestLoadRejectsImportConflictsAndFragmentVersions(t *testing.T) {
|
||||||
t.Run("all duplicate claimants", func(t *testing.T) {
|
t.Run("all duplicate claimants", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- path: one.yml
|
- path: one.yml
|
||||||
- path: two.yml
|
- path: two.yml
|
||||||
@@ -97,7 +118,7 @@ func TestLoadRejectsImportConflictsAndFragmentVersions(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit:\n imports:\n - path: fragment.yml\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit:\n imports:\n - path: fragment.yml\n")
|
||||||
writeImportFile(t, directory, "fragment.yml", test.fragment)
|
writeImportFile(t, directory, "fragment.yml", test.fragment)
|
||||||
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true, Definition: Definition[importTestConfig]{Version: versionRule}}, CodeImportVersion)
|
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true, Definition: Definition[importTestConfig]{Version: versionRule}}, CodeImportVersion)
|
||||||
diagnostic := loadError.Diagnostics()[0]
|
diagnostic := loadError.Diagnostics()[0]
|
||||||
@@ -114,15 +135,15 @@ func TestLoadImportDirectiveValidationAndTemporaryGlobGate(t *testing.T) {
|
|||||||
body string
|
body string
|
||||||
code Code
|
code Code
|
||||||
}{
|
}{
|
||||||
{name: "directive kind", body: "_configkit: []\n", code: CodeImportInvalid},
|
{name: "directive kind", body: "configkit: []\n", code: CodeImportInvalid},
|
||||||
{name: "unknown directive field", body: "_configkit: {unknown: true}\n", code: CodeImportInvalid},
|
{name: "unknown directive field", body: "configkit: {unknown: true}\n", code: CodeImportInvalid},
|
||||||
{name: "imports kind", body: "_configkit: {imports: {}}\n", code: CodeImportInvalid},
|
{name: "imports kind", body: "configkit: {imports: {}}\n", code: CodeImportInvalid},
|
||||||
{name: "declaration kind", body: "_configkit: {imports: [file.yml]}\n", code: CodeImportInvalid},
|
{name: "declaration kind", body: "configkit: {imports: [file.yml]}\n", code: CodeImportInvalid},
|
||||||
{name: "missing target", body: "_configkit: {imports: [{optional: true}]}\n", code: CodeImportInvalid},
|
{name: "missing target", body: "configkit: {imports: [{optional: true}]}\n", code: CodeImportInvalid},
|
||||||
{name: "two targets", body: "_configkit: {imports: [{path: a.yml, glob: '*.yml'}]}\n", code: CodeImportInvalid},
|
{name: "two targets", body: "configkit: {imports: [{path: a.yml, glob: '*.yml'}]}\n", code: CodeImportInvalid},
|
||||||
{name: "unknown declaration field", body: "_configkit: {imports: [{path: a.yml, extra: true}]}\n", code: CodeImportInvalid},
|
{name: "unknown declaration field", body: "configkit: {imports: [{path: a.yml, extra: true}]}\n", code: CodeImportInvalid},
|
||||||
{name: "optional kind", body: "_configkit: {imports: [{path: a.yml, optional: 'yes'}]}\n", code: CodeImportInvalid},
|
{name: "optional kind", body: "configkit: {imports: [{path: a.yml, optional: 'yes'}]}\n", code: CodeImportInvalid},
|
||||||
{name: "nested directive", body: "nested: {_configkit: {}}\n", code: CodeImportInvalid},
|
{name: "nested directive", body: "nested: {configkit: {}}\n", code: CodeImportInvalid},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
@@ -136,7 +157,7 @@ func TestLoadImportDirectiveValidationAndTemporaryGlobGate(t *testing.T) {
|
|||||||
func TestLoadImportMissingContainmentAndRegularFileRules(t *testing.T) {
|
func TestLoadImportMissingContainmentAndRegularFileRules(t *testing.T) {
|
||||||
t.Run("optional missing", func(t *testing.T) {
|
t.Run("optional missing", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports:
|
imports:
|
||||||
- path: missing.yml
|
- path: missing.yml
|
||||||
optional: true
|
optional: true
|
||||||
@@ -172,7 +193,7 @@ settings: {root: retained}
|
|||||||
if test.setup != nil {
|
if test.setup != nil {
|
||||||
test.setup(t, directory)
|
test.setup(t, directory)
|
||||||
}
|
}
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit:\n imports:\n - path: \""+test.path+"\"\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit:\n imports:\n - path: \""+test.path+"\"\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, test.code)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, test.code)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -181,9 +202,9 @@ settings: {root: retained}
|
|||||||
func TestLoadImportIdentityReportsCyclesAndRepeatedRoutes(t *testing.T) {
|
func TestLoadImportIdentityReportsCyclesAndRepeatedRoutes(t *testing.T) {
|
||||||
t.Run("cycle", func(t *testing.T) {
|
t.Run("cycle", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit:\n imports: [{path: a.yml}]\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit:\n imports: [{path: a.yml}]\n")
|
||||||
writeImportFile(t, directory, "a.yml", "_configkit:\n imports: [{path: b.yml}]\n")
|
writeImportFile(t, directory, "a.yml", "configkit:\n imports: [{path: b.yml}]\n")
|
||||||
writeImportFile(t, directory, "b.yml", "_configkit:\n imports: [{path: main.yml}]\n")
|
writeImportFile(t, directory, "b.yml", "configkit:\n imports: [{path: main.yml}]\n")
|
||||||
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportCycle)
|
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportCycle)
|
||||||
diagnostic := loadError.Diagnostics()[0]
|
diagnostic := loadError.Diagnostics()[0]
|
||||||
if diagnostic.Primary == nil || len(diagnostic.Related) != 2 || !strings.Contains(diagnostic.Message, "main.yml") || !strings.Contains(diagnostic.Message, "a.yml") || !strings.Contains(diagnostic.Message, "b.yml") {
|
if diagnostic.Primary == nil || len(diagnostic.Related) != 2 || !strings.Contains(diagnostic.Message, "main.yml") || !strings.Contains(diagnostic.Message, "a.yml") || !strings.Contains(diagnostic.Message, "b.yml") {
|
||||||
@@ -193,9 +214,9 @@ func TestLoadImportIdentityReportsCyclesAndRepeatedRoutes(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("diamond", func(t *testing.T) {
|
t.Run("diamond", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit:\n imports: [{path: left.yml}, {path: right.yml}]\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit:\n imports: [{path: left.yml}, {path: right.yml}]\n")
|
||||||
writeImportFile(t, directory, "left.yml", "_configkit:\n imports: [{path: shared.yml}]\n")
|
writeImportFile(t, directory, "left.yml", "configkit:\n imports: [{path: shared.yml}]\n")
|
||||||
writeImportFile(t, directory, "right.yml", "_configkit:\n imports: [{path: shared.yml}]\n")
|
writeImportFile(t, directory, "right.yml", "configkit:\n imports: [{path: shared.yml}]\n")
|
||||||
writeImportFile(t, directory, "shared.yml", "settings: {root: shared}\n")
|
writeImportFile(t, directory, "shared.yml", "settings: {root: shared}\n")
|
||||||
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportRepeated)
|
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportRepeated)
|
||||||
diagnostic := loadError.Diagnostics()[0]
|
diagnostic := loadError.Diagnostics()[0]
|
||||||
@@ -216,7 +237,7 @@ func TestLoadImportIdentityReportsCyclesAndRepeatedRoutes(t *testing.T) {
|
|||||||
if err := os.Symlink("target.yml", filepath.Join(directory, "second-link.yml")); err != nil {
|
if err := os.Symlink("target.yml", filepath.Join(directory, "second-link.yml")); err != nil {
|
||||||
t.Skipf("symlinks unavailable: %v", err)
|
t.Skipf("symlinks unavailable: %v", err)
|
||||||
}
|
}
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: first-link.yml}, {path: second-link.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: first-link.yml}, {path: second-link.yml}]}\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportRepeated)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportRepeated)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -231,7 +252,7 @@ func TestLoadImportsCanonicalSymlinksAndKeepsHardLinksDistinct(t *testing.T) {
|
|||||||
if err := os.Symlink(target, filepath.Join(directory, "link.yml")); err != nil {
|
if err := os.Symlink(target, filepath.Join(directory, "link.yml")); err != nil {
|
||||||
t.Skipf("symlinks unavailable: %v", err)
|
t.Skipf("symlinks unavailable: %v", err)
|
||||||
}
|
}
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: link.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: link.yml}]}\n")
|
||||||
result, err := Load[importTestConfig](File(mainPath), Options[importTestConfig]{Imports: true})
|
result, err := Load[importTestConfig](File(mainPath), Options[importTestConfig]{Imports: true})
|
||||||
if err != nil || result.Config.Settings.First != "linked" || result.Sources()[1].Path != canonicalTestPath(t, target) {
|
if err != nil || result.Config.Settings.First != "linked" || result.Sources()[1].Path != canonicalTestPath(t, target) {
|
||||||
t.Fatalf("symlink result = %#v, %v", result, err)
|
t.Fatalf("symlink result = %#v, %v", result, err)
|
||||||
@@ -245,7 +266,7 @@ func TestLoadImportsCanonicalSymlinksAndKeepsHardLinksDistinct(t *testing.T) {
|
|||||||
if err := os.Symlink(target, filepath.Join(root, "escape.yml")); err != nil {
|
if err := os.Symlink(target, filepath.Join(root, "escape.yml")); err != nil {
|
||||||
t.Skipf("symlinks unavailable: %v", err)
|
t.Skipf("symlinks unavailable: %v", err)
|
||||||
}
|
}
|
||||||
mainPath := writeImportFile(t, root, "main.yml", "_configkit: {imports: [{path: escape.yml}]}\n")
|
mainPath := writeImportFile(t, root, "main.yml", "configkit: {imports: [{path: escape.yml}]}\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportOutsideRoot)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportOutsideRoot)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -255,7 +276,7 @@ func TestLoadImportsCanonicalSymlinksAndKeepsHardLinksDistinct(t *testing.T) {
|
|||||||
if err := os.Link(one, filepath.Join(directory, "two.yml")); err != nil {
|
if err := os.Link(one, filepath.Join(directory, "two.yml")); err != nil {
|
||||||
t.Skipf("hard links unavailable: %v", err)
|
t.Skipf("hard links unavailable: %v", err)
|
||||||
}
|
}
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: one.yml}, {path: two.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: one.yml}, {path: two.yml}]}\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeComposeConflict)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeComposeConflict)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -264,7 +285,7 @@ func TestLoadImportsCanonicalSymlinksAndKeepsHardLinksDistinct(t *testing.T) {
|
|||||||
if err := os.Symlink("loop.yml", filepath.Join(directory, "loop.yml")); err != nil {
|
if err := os.Symlink("loop.yml", filepath.Join(directory, "loop.yml")); err != nil {
|
||||||
t.Skipf("symlinks unavailable: %v", err)
|
t.Skipf("symlinks unavailable: %v", err)
|
||||||
}
|
}
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: loop.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: loop.yml}]}\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportInvalid)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportInvalid)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -272,7 +293,7 @@ func TestLoadImportsCanonicalSymlinksAndKeepsHardLinksDistinct(t *testing.T) {
|
|||||||
func TestLoadByteAndReaderImportsRequireExplicitFilesystemContext(t *testing.T) {
|
func TestLoadByteAndReaderImportsRequireExplicitFilesystemContext(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
writeImportFile(t, directory, "fragment.yml", "settings: {first: imported}\n")
|
writeImportFile(t, directory, "fragment.yml", "settings: {first: imported}\n")
|
||||||
input := []byte("_configkit: {imports: [{path: fragment.yml}]}\n")
|
input := []byte("configkit: {imports: [{path: fragment.yml}]}\n")
|
||||||
context := &FilesystemContext{BaseDir: directory, Root: directory}
|
context := &FilesystemContext{BaseDir: directory, Root: directory}
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -295,7 +316,7 @@ func TestLoadByteAndReaderImportsRequireExplicitFilesystemContext(t *testing.T)
|
|||||||
|
|
||||||
func TestFileImportRootCanContainTheSelectedFileDirectory(t *testing.T) {
|
func TestFileImportRootCanContainTheSelectedFileDirectory(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
mainPath := writeImportFile(t, root, "configs/main.yml", "_configkit: {imports: [{path: ../shared.yml}]}\n")
|
mainPath := writeImportFile(t, root, "configs/main.yml", "configkit: {imports: [{path: ../shared.yml}]}\n")
|
||||||
writeImportFile(t, root, "shared.yml", "settings: {root: shared}\n")
|
writeImportFile(t, root, "shared.yml", "settings: {root: shared}\n")
|
||||||
result, err := Load[importTestConfig](File(mainPath), Options[importTestConfig]{
|
result, err := Load[importTestConfig](File(mainPath), Options[importTestConfig]{
|
||||||
Imports: true, Filesystem: &FilesystemContext{Root: root},
|
Imports: true, Filesystem: &FilesystemContext{Root: root},
|
||||||
@@ -309,7 +330,7 @@ func TestFileImportRootCanContainTheSelectedFileDirectory(t *testing.T) {
|
|||||||
func TestImportedParseAndDecodeDiagnosticsKeepTheirSource(t *testing.T) {
|
func TestImportedParseAndDecodeDiagnosticsKeepTheirSource(t *testing.T) {
|
||||||
t.Run("parse", func(t *testing.T) {
|
t.Run("parse", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: broken.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: broken.yml}]}\n")
|
||||||
writeImportFile(t, directory, "broken.yml", "settings: [\n")
|
writeImportFile(t, directory, "broken.yml", "settings: [\n")
|
||||||
loadError := requireAnyLoadError(t, File(mainPath), Options[importTestConfig]{Imports: true})
|
loadError := requireAnyLoadError(t, File(mainPath), Options[importTestConfig]{Imports: true})
|
||||||
diagnostics := loadError.Diagnostics()
|
diagnostics := loadError.Diagnostics()
|
||||||
@@ -320,7 +341,7 @@ func TestImportedParseAndDecodeDiagnosticsKeepTheirSource(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("decode", func(t *testing.T) {
|
t.Run("decode", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: unknown.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: unknown.yml}]}\n")
|
||||||
writeImportFile(t, directory, "unknown.yml", "unknown: value\n")
|
writeImportFile(t, directory, "unknown.yml", "unknown: value\n")
|
||||||
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeDecodeUnknownField)
|
loadError := requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeDecodeUnknownField)
|
||||||
diagnostic := loadError.Diagnostics()[0]
|
diagnostic := loadError.Diagnostics()[0]
|
||||||
@@ -332,7 +353,7 @@ func TestImportedParseAndDecodeDiagnosticsKeepTheirSource(t *testing.T) {
|
|||||||
|
|
||||||
func TestOptionalImportDoesNotSuppressUnsafeOrUnreadableTargets(t *testing.T) {
|
func TestOptionalImportDoesNotSuppressUnsafeOrUnreadableTargets(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "outside.yml", "_configkit: {imports: [{path: ../missing.yml, optional: true}]}\n")
|
mainPath := writeImportFile(t, directory, "outside.yml", "configkit: {imports: [{path: ../missing.yml, optional: true}]}\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportOutsideRoot)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeImportOutsideRoot)
|
||||||
|
|
||||||
locked := writeImportFile(t, directory, "locked.yml", "settings: {root: locked}\n")
|
locked := writeImportFile(t, directory, "locked.yml", "settings: {root: locked}\n")
|
||||||
@@ -344,13 +365,13 @@ func TestOptionalImportDoesNotSuppressUnsafeOrUnreadableTargets(t *testing.T) {
|
|||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
t.Skip("filesystem does not enforce the unreadable-file fixture")
|
t.Skip("filesystem does not enforce the unreadable-file fixture")
|
||||||
}
|
}
|
||||||
mainPath = writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: locked.yml, optional: true}]}\n")
|
mainPath = writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: locked.yml, optional: true}]}\n")
|
||||||
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeSourcePermission)
|
requireLoadCode(t, File(mainPath), Options[importTestConfig]{Imports: true}, CodeSourcePermission)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportTraversalEnforcesPrivateBudgets(t *testing.T) {
|
func TestImportTraversalEnforcesPrivateBudgets(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: one.yml}, {path: two.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: one.yml}, {path: two.yml}]}\n")
|
||||||
writeImportFile(t, directory, "one.yml", "settings: {first: one}\n")
|
writeImportFile(t, directory, "one.yml", "settings: {first: one}\n")
|
||||||
writeImportFile(t, directory, "two.yml", "settings: {second: two}\n")
|
writeImportFile(t, directory, "two.yml", "settings: {second: two}\n")
|
||||||
main := parseImportMain(t, mainPath)
|
main := parseImportMain(t, mainPath)
|
||||||
@@ -370,7 +391,7 @@ func TestImportTraversalEnforcesPrivateBudgets(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
writeImportFile(t, directory, "one.yml", "_configkit: {imports: [{path: nested.yml}]}\n")
|
writeImportFile(t, directory, "one.yml", "configkit: {imports: [{path: nested.yml}]}\n")
|
||||||
writeImportFile(t, directory, "nested.yml", "settings: {nested: nested}\n")
|
writeImportFile(t, directory, "nested.yml", "settings: {nested: nested}\n")
|
||||||
main = parseImportMain(t, mainPath)
|
main = parseImportMain(t, mainPath)
|
||||||
_, _, diagnostics := loadImportedDocuments(File(mainPath), nil, main, nil, importLimits{depth: 1, sources: 8, bytes: maxTotalBytes, nodes: maxDocumentNodes})
|
_, _, diagnostics := loadImportedDocuments(File(mainPath), nil, main, nil, importLimits{depth: 1, sources: 8, bytes: maxTotalBytes, nodes: maxDocumentNodes})
|
||||||
@@ -381,7 +402,7 @@ func TestImportTraversalEnforcesPrivateBudgets(t *testing.T) {
|
|||||||
|
|
||||||
func TestImportTraversalAcceptsExactPrivateBudgets(t *testing.T) {
|
func TestImportTraversalAcceptsExactPrivateBudgets(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: imported.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: imported.yml}]}\n")
|
||||||
importedPath := writeImportFile(t, directory, "imported.yml", "settings: {first: imported}\n")
|
importedPath := writeImportFile(t, directory, "imported.yml", "settings: {first: imported}\n")
|
||||||
main := parseImportMain(t, mainPath)
|
main := parseImportMain(t, mainPath)
|
||||||
imported := parseImportMain(t, importedPath)
|
imported := parseImportMain(t, importedPath)
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ func TestLoadRejectsUnavailableOptionsAndDisabledDirectives(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := Load[config](Bytes("directive.yml", []byte("nested:\n _configkit: {}\n")))
|
result, err := Load[config](Bytes("directive.yml", []byte("nested:\n configkit: {}\n")))
|
||||||
if result != nil || err == nil {
|
if result != nil || err == nil {
|
||||||
t.Fatalf("result, err = %#v, %v", result, err)
|
t.Fatalf("result, err = %#v, %v", result, err)
|
||||||
}
|
}
|
||||||
@@ -249,7 +249,7 @@ func TestLoadRejectsUnavailableOptionsAndDisabledDirectives(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
diagnostic := loadError.Diagnostics()[0]
|
diagnostic := loadError.Diagnostics()[0]
|
||||||
if diagnostic.Code != CodeImportDisabled || diagnostic.Path.String() != "/nested/_configkit" || diagnostic.Primary == nil || diagnostic.Primary.Line != 2 {
|
if diagnostic.Code != CodeImportDisabled || diagnostic.Path.String() != "/nested/configkit" || diagnostic.Primary == nil || diagnostic.Primary.Line != 2 {
|
||||||
t.Fatalf("diagnostic = %#v", diagnostic)
|
t.Fatalf("diagnostic = %#v", diagnostic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ func overlappingOverride(previous []Override, valid []bool, path Path) int {
|
|||||||
|
|
||||||
func pathContainsDirective(path Path) bool {
|
func pathContainsDirective(path Path) bool {
|
||||||
for _, segment := range path.segments {
|
for _, segment := range path.segments {
|
||||||
if segment.key == "_configkit" {
|
if segment.key == directiveKey {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ func overrideDirectivePath(node *document.Node, path Path) (Path, bool) {
|
|||||||
if node.Kind == document.Mapping {
|
if node.Kind == document.Mapping {
|
||||||
for _, entry := range node.Mapping {
|
for _, entry := range node.Mapping {
|
||||||
child := path.Key(entry.Key)
|
child := path.Key(entry.Key)
|
||||||
if entry.Key == "_configkit" {
|
if entry.Key == directiveKey {
|
||||||
return child, true
|
return child, true
|
||||||
}
|
}
|
||||||
if found, ok := overrideDirectivePath(entry.Value, child); ok {
|
if found, ok := overrideDirectivePath(entry.Value, child); ok {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type overrideLifecycleConfig struct {
|
|||||||
|
|
||||||
func TestOverridesRunAtHighestInputPrecedenceBeforeResolutionAndValidation(t *testing.T) {
|
func TestOverridesRunAtHighestInputPrecedenceBeforeResolutionAndValidation(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit:\n imports: [{path: base.yml}]\nversion: v1\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit:\n imports: [{path: base.yml}]\nversion: v1\n")
|
||||||
writeImportFile(t, directory, "base.yml", `settings:
|
writeImportFile(t, directory, "base.yml", `settings:
|
||||||
target: base
|
target: base
|
||||||
count: 4
|
count: 4
|
||||||
@@ -187,8 +187,8 @@ func TestOverrideValidationRejectsPathsOverlapValuesAndProtectedTargets(t *testi
|
|||||||
{name: "nonfinite", overrides: []Override{{Path: Keys("scalar"), Value: math.Inf(1), Flag: "--scalar"}}, code: CodeOverrideInvalidValue},
|
{name: "nonfinite", overrides: []Override{{Path: Keys("scalar"), Value: math.Inf(1), Flag: "--scalar"}}, code: CodeOverrideInvalidValue},
|
||||||
{name: "depth", overrides: []Override{{Path: Keys("nested"), Value: deep, Flag: "--nested"}}, code: CodeOverrideLimitExceeded},
|
{name: "depth", overrides: []Override{{Path: Keys("nested"), Value: deep, Flag: "--nested"}}, code: CodeOverrideLimitExceeded},
|
||||||
{name: "record bytes", overrides: []Override{{Path: Keys("scalar"), Value: strings.Repeat("x", maxSourceBytes+1), Flag: "--scalar"}}, code: CodeOverrideLimitExceeded},
|
{name: "record bytes", overrides: []Override{{Path: Keys("scalar"), Value: strings.Repeat("x", maxSourceBytes+1), Flag: "--scalar"}}, code: CodeOverrideLimitExceeded},
|
||||||
{name: "directive path", overrides: []Override{{Path: Keys("_configkit", "x"), Value: true, Flag: "--directive"}}, code: CodeOverrideDirective},
|
{name: "directive path", overrides: []Override{{Path: Keys("configkit", "x"), Value: true, Flag: "--directive"}}, code: CodeOverrideDirective},
|
||||||
{name: "directive value", overrides: []Override{{Path: Keys("nested"), Value: map[string]any{"_configkit": map[string]any{}}, Flag: "--nested"}}, code: CodeOverrideDirective},
|
{name: "directive value", overrides: []Override{{Path: Keys("nested"), Value: map[string]any{"configkit": map[string]any{}}, Flag: "--nested"}}, code: CodeOverrideDirective},
|
||||||
{name: "version", overrides: []Override{{Path: Keys("version"), Value: "v2", Flag: "--version"}}, code: CodeOverrideVersion},
|
{name: "version", overrides: []Override{{Path: Keys("version"), Value: "v2", Flag: "--version"}}, code: CodeOverrideVersion},
|
||||||
{name: "version ancestor", overrides: []Override{{Path: Keys("version"), Value: map[string]any{"child": "v2"}, Flag: "--version"}}, code: CodeOverrideVersion},
|
{name: "version ancestor", overrides: []Override{{Path: Keys("version"), Value: map[string]any{"child": "v2"}, Flag: "--version"}}, code: CodeOverrideVersion},
|
||||||
{name: "label", overrides: []Override{{Path: Keys("scalar"), Value: "x", Flag: "\nCANARY"}}, code: CodeOverrideInvalidLabel},
|
{name: "label", overrides: []Override{{Path: Keys("scalar"), Value: "x", Flag: "\nCANARY"}}, code: CodeOverrideInvalidLabel},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type profileTestConfig struct {
|
|||||||
|
|
||||||
func TestLoadAppliesSelectedProfileAfterBaseAndBeforeDefaults(t *testing.T) {
|
func TestLoadAppliesSelectedProfileAfterBaseAndBeforeDefaults(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports: [{path: base.yml}]
|
imports: [{path: base.yml}]
|
||||||
settings:
|
settings:
|
||||||
kept: main
|
kept: main
|
||||||
@@ -133,8 +133,8 @@ func TestLoadProfileRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
|
|||||||
profile string
|
profile string
|
||||||
code Code
|
code Code
|
||||||
}{
|
}{
|
||||||
{name: "root directive", profile: "_configkit: {}\n", code: CodeProfileDirective},
|
{name: "root directive", profile: "configkit: {}\n", code: CodeProfileDirective},
|
||||||
{name: "nested directive", profile: "settings: {_configkit: {}}\n", code: CodeProfileDirective},
|
{name: "nested directive", profile: "settings: {configkit: {}}\n", code: CodeProfileDirective},
|
||||||
{name: "equal version", profile: "schema: {version: v1}\n", code: CodeProfileVersion},
|
{name: "equal version", profile: "schema: {version: v1}\n", code: CodeProfileVersion},
|
||||||
{name: "ancestor scalar", profile: "schema: null\n", code: CodeProfileVersion},
|
{name: "ancestor scalar", profile: "schema: null\n", code: CodeProfileVersion},
|
||||||
} {
|
} {
|
||||||
@@ -158,7 +158,7 @@ func TestLoadProfileRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("profile aliases imported base", func(t *testing.T) {
|
t.Run("profile aliases imported base", func(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", "_configkit: {imports: [{path: profiles/testing.yml}]}\n")
|
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: profiles/testing.yml}]}\n")
|
||||||
writeImportFile(t, directory, "profiles/testing.yml", "settings: {changed: value}\n")
|
writeImportFile(t, directory, "profiles/testing.yml", "settings: {changed: value}\n")
|
||||||
requireProfileLoadCode(t, File(mainPath), Options[profileTestConfig]{Imports: true, Profile: "testing"}, CodeProfileRepeated)
|
requireProfileLoadCode(t, File(mainPath), Options[profileTestConfig]{Imports: true, Profile: "testing"}, CodeProfileRepeated)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type layeredConfig struct {
|
|||||||
|
|
||||||
func TestLayeredProvenanceAgreesWithResolvedSnapshot(t *testing.T) {
|
func TestLayeredProvenanceAgreesWithResolvedSnapshot(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
mainPath := writeImportFile(t, directory, "main.yml", `_configkit:
|
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
|
||||||
imports: [{path: imported.yml}]
|
imports: [{path: imported.yml}]
|
||||||
shared: {equal: same}
|
shared: {equal: same}
|
||||||
values: [1, 2]
|
values: [1, 2]
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func TestNonregularSourcesDoNotBlock(t *testing.T) {
|
|||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
input := "value: 1\n"
|
input := "value: 1\n"
|
||||||
if mode == "import" {
|
if mode == "import" {
|
||||||
input = "_configkit:\n imports:\n - path: pipe.yml\n"
|
input = "configkit:\n imports:\n - path: pipe.yml\n"
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(filepath.Join(root, "config.yml"), []byte(input), 0600); err != nil {
|
if err := os.WriteFile(filepath.Join(root, "config.yml"), []byte(input), 0600); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user