From eafbd32489ebc48787a95268d4b3bfc60c519db6 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 27 Oct 2026 15:09:04 +0000 Subject: [PATCH] Clarify search and secret bootstrap names --- discovery.go | 4 ++-- docs/roadmap/implementation.md | 4 ++-- examples/consumers/consumers_test.go | 2 +- examples/consumers/sequence_catalog.go | 2 +- facade_candidate_test.go | 4 ++-- facade_discovery_test.go | 20 ++++++++++---------- secrets.go | 13 +++++++------ 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/discovery.go b/discovery.go index a3f003d..351affc 100644 --- a/discovery.go +++ b/discovery.go @@ -52,8 +52,8 @@ func SearchDirectory(directory string, supplied ...DirectorySearchOptions) Searc return Search{value: engine.SearchDirectory(directory, options...)} } -// Paths describes literal host-file candidates in the supplied order. -func Paths(paths ...string) Search { return Search{value: engine.Paths(paths...)} } +// SearchPaths describes literal host-file candidates in the supplied order. +func SearchPaths(paths ...string) Search { return Search{value: engine.Paths(paths...)} } // Searches concatenates descriptions in order and snapshots their entries. func Searches(searches ...Search) Search { diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 472a973..6cb6829 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # Pre-v1 Public API Cleanup Implementation Plan -Status: in progress; Stages 1–4 are complete and Stages 5–11 are pending. +Status: in progress; Stages 1–5 are complete and Stages 6–11 are pending. Baseline: `07e4ae5d9236935389e81b207d4a8f00543fafce`. Audience: a Luna coding agent executing the stages sequentially. @@ -225,7 +225,7 @@ ordinary/layered loading. ## Stage 5 — Clarify Discovery And Environment Bootstrap Names -Status: pending. Implements N1 and D7. +Status: complete. Implements N1 and D7. 1. In root [discovery.go](../../discovery.go), rename `Paths` to `SearchPaths`. Forward the same literal candidates to existing engine machinery. Migrate diff --git a/examples/consumers/consumers_test.go b/examples/consumers/consumers_test.go index 70cd12c..e47873d 100644 --- a/examples/consumers/consumers_test.go +++ b/examples/consumers/consumers_test.go @@ -82,7 +82,7 @@ func Example_weatherStartup() { defer os.RemoveAll(directory) _ = os.WriteFile(filepath.Join(directory, blockedName), []byte("unused-example-value\n"), 0o600) _ = os.WriteFile(filepath.Join(directory, tokenName), []byte("disposable-example-credential\n"), 0o600) - report, bootstrapErr := configkit.LoadSecretsDir(directory) + report, bootstrapErr := configkit.LoadSecretsIntoEnvironment(directory) var reportOutput bytes.Buffer _, reportWriteErr := report.WriteTo(&reportOutput) diff --git a/examples/consumers/sequence_catalog.go b/examples/consumers/sequence_catalog.go index 4604c5c..0174a2b 100644 --- a/examples/consumers/sequence_catalog.go +++ b/examples/consumers/sequence_catalog.go @@ -43,7 +43,7 @@ func LoadSequenceCatalog(mainPath, overlayName, runLabel string) (*configkit.Res filesystem := configkit.NewFilesystem("sequence catalog", os.DirFS(root)) filesystemContext := &configkit.FilesystemContext{BaseDir: ".", Root: ".", FS: &filesystem} mainSearch := configkit.SearchConfig("", configkit.Searches( - configkit.Paths(absoluteMain), + configkit.SearchPaths(absoluteMain), configkit.SearchDirectory(filepath.Join(root, "fallback"), configkit.DirectorySearchOptions{ Filenames: []configkit.FilenameGroup{configkit.CustomFilenames(filepath.Base(absoluteMain))}, }), diff --git a/facade_candidate_test.go b/facade_candidate_test.go index 0934816..a992e79 100644 --- a/facade_candidate_test.go +++ b/facade_candidate_test.go @@ -140,7 +140,7 @@ func TestFacadeSecretsBootstrapUsesFacadeRecords(t *testing.T) { t.Fatal(err) } - report, err := configkit.LoadSecretsDir(directory) + report, err := configkit.LoadSecretsIntoEnvironment(directory) if err != nil { t.Fatal(err) } @@ -164,7 +164,7 @@ func TestFacadeSecretsBootstrapUsesFacadeRecords(t *testing.T) { t.Fatalf("report rendering = %q, %v", rendered.String(), err) } - failedReport, err := configkit.LoadSecretsDir("") + failedReport, err := configkit.LoadSecretsIntoEnvironment("") var secretsError *configkit.SecretsError if failedReport == nil || !errors.As(err, &secretsError) || len(secretsError.Diagnostics()) != 1 || secretsError.Diagnostics()[0].Code != configkit.CodeSecretsInvalidPath { t.Fatalf("bootstrap failure = %#v / %#v", failedReport, err) diff --git a/facade_discovery_test.go b/facade_discovery_test.go index 78f3fc0..8201a58 100644 --- a/facade_discovery_test.go +++ b/facade_discovery_test.go @@ -93,14 +93,14 @@ func TestFacadeSearchOrderingRequirednessAndSnapshots(t *testing.T) { names := []string{"config.yml"} groups := []FilenameGroup{CustomFilenames(names...)} directoryOptions := []DirectorySearchOptions{{Filenames: groups}} - search := Searches(Paths(paths...), SearchDirectory(root, directoryOptions...), Paths(laterPath)) + search := Searches(SearchPaths(paths...), SearchDirectory(root, directoryOptions...), SearchPaths(laterPath)) source := SearchConfig("", search, SearchOptions{Required: true}) paths[0] = laterPath names[0] = "later.yml" groups[0] = CustomFilenames("later.yml") directoryOptions[0].Filenames[0] = CustomFilenames("later.yml") - search = Paths(laterPath) + search = SearchPaths(laterPath) result, err := Load[facadeDiscoveryConfig](source) if err != nil || result.Config.Value != "selected" { @@ -110,7 +110,7 @@ func TestFacadeSearchOrderingRequirednessAndSnapshots(t *testing.T) { t.Fatalf("ordered search sources = %#v", sources) } - _, err = Load[facadeDiscoveryConfig](SearchConfig("", Paths(filepath.Join(root, "missing.yml")), SearchOptions{Required: true})) + _, err = Load[facadeDiscoveryConfig](SearchConfig("", SearchPaths(filepath.Join(root, "missing.yml")), SearchOptions{Required: true})) var loadErr *LoadError if !errors.As(err, &loadErr) { t.Fatalf("required exhausted search error = %v, want *LoadError", err) @@ -131,7 +131,7 @@ func TestFacadeSearchWorksForLayeredMainAndUnderlay(t *testing.T) { mainPath := writeFacadeImportFile(t, root, "main.yml", "settings: {main: value}\n") underlayPath := writeFacadeImportFile(t, root, "underlay/base.yml", "settings: {underlay: value}\n") result, err := LoadLayered( - Input{Source: SearchConfig("", Paths(mainPath))}, + Input{Source: SearchConfig("", SearchPaths(mainPath))}, LayeredOptions[facadeDiscoveryConfig]{Underlays: []Underlay{{Inputs: []Input{{ Source: SearchConfig("", SearchDirectory(filepath.Dir(underlayPath), DirectorySearchOptions{ Filenames: []FilenameGroup{CustomFilenames("base.yml")}, @@ -157,7 +157,7 @@ func TestFacadeSearchSelectedAndAbsentUseRootOnlyContext(t *testing.T) { filesystem := &FilesystemContext{Root: root} options := Options[facadeDiscoveryConfig]{Overlay: "testing", Filesystem: filesystem} - selected, err := Load(SearchConfig("", Paths(mainPath)), options) + selected, err := Load(SearchConfig("", SearchPaths(mainPath)), options) if err != nil || selected.Config.Value != "overlay" { t.Fatalf("selected search with root-only context = %#v, %v", selected, err) } @@ -171,7 +171,7 @@ func TestFacadeSearchSelectedAndAbsentUseRootOnlyContext(t *testing.T) { t.Fatalf("selected file base context = %#v, %v", mainContext, ok) } - missing := SearchConfig("", Paths(filepath.Join(root, "missing.yml"))) + missing := SearchConfig("", SearchPaths(filepath.Join(root, "missing.yml"))) searchedAbsent, err := Load(missing, options) if err != nil { t.Fatalf("exhausted search with root-only overlay context: %v", err) @@ -201,7 +201,7 @@ func TestFacadeSearchRejectsSelectedContextWithoutFallback(t *testing.T) { inside := writeFacadeImportFile(t, root, "inside.yml", "value: fallback\n") outsideRoot := t.TempDir() outside := writeFacadeImportFile(t, outsideRoot, "outside.yml", "value: selected\n") - search := SearchConfig("", Paths(outside, inside)) + search := SearchConfig("", SearchPaths(outside, inside)) _, err := Load(search, Options[facadeDiscoveryConfig]{Filesystem: &FilesystemContext{Root: root}}) var loadErr *LoadError @@ -228,7 +228,7 @@ func TestFacadeSearchRunsCallbacksOnceAndKeepsInspectionWithSelectedFile(t *test selectedPath := writeFacadeImportFile(t, root, "selected.yml", "value: selected\n") writeFacadeImportFile(t, root, "later.yml", "value: later\n") defaultsCalls, resolveCalls, validateCalls := 0, 0, 0 - result, err := Load(SearchConfig("", Paths(selectedPath, filepath.Join(root, "later.yml"))), Options[facadeDiscoveryLifecycleConfig]{ + result, err := Load(SearchConfig("", SearchPaths(selectedPath, filepath.Join(root, "later.yml"))), Options[facadeDiscoveryLifecycleConfig]{ Definition: Definition[facadeDiscoveryLifecycleConfig]{ Defaults: func() facadeDiscoveryLifecycleConfig { defaultsCalls++ @@ -272,7 +272,7 @@ func TestFacadeSearchSequenceItemAndAbsentInputPlacement(t *testing.T) { Destination: Keys("items"), PlacementMode: PlacementModeSequenceItem, } - selected, err := Load(SearchConfig("", Paths(selectedPath)), options) + selected, err := Load(SearchConfig("", SearchPaths(selectedPath)), options) if err != nil || len(selected.Config.Items) != 1 || selected.Config.Items[0].Name != "selected" { t.Fatalf("selected sequence item = %#v, %v", selected, err) } @@ -283,7 +283,7 @@ func TestFacadeSearchSequenceItemAndAbsentInputPlacement(t *testing.T) { defaults := func() facadeDiscoveryPlacementConfig { return facadeDiscoveryPlacementConfig{Items: []facadeDiscoveryPlacementItem{}} } - absent, err := Load(SearchConfig("", Paths(filepath.Join(root, "missing.yml"))), Options[facadeDiscoveryPlacementConfig]{ + absent, err := Load(SearchConfig("", SearchPaths(filepath.Join(root, "missing.yml"))), Options[facadeDiscoveryPlacementConfig]{ Definition: Definition[facadeDiscoveryPlacementConfig]{Defaults: defaults}, Destination: options.Destination, PlacementMode: options.PlacementMode, diff --git a/secrets.go b/secrets.go index d9a1108..d80f066 100644 --- a/secrets.go +++ b/secrets.go @@ -8,7 +8,7 @@ import ( ) // SecretFileStatus describes the outcome for one file considered by -// LoadSecretsDir. +// LoadSecretsIntoEnvironment. type SecretFileStatus string const ( @@ -85,11 +85,12 @@ func (e *SecretsError) Diagnostics() []Diagnostic { return result } -// LoadSecretsDir explicitly loads immediate regular files from directory into -// previously unset process environment variables. Applications should call it -// once during startup, before concurrent work; Load never calls it. -// Dot-prefixed entries are ignored and omitted from the report. -func LoadSecretsDir(directory string) (*SecretsReport, error) { +// LoadSecretsIntoEnvironment explicitly loads immediate regular files into the +// process environment, preserving variables that are already set, including +// empty values. Dot-prefixed entries are ignored and omitted from the report. +// Load never calls it. The report accompanies any error, and an error does not +// roll back values already applied. +func LoadSecretsIntoEnvironment(directory string) (*SecretsReport, error) { report, err := engine.LoadSecretsDir(directory) result := &SecretsReport{value: report} if err == nil {