Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions internal/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,17 @@ func (e *Engine) WriteLeaf(basepath string, tier store.Tier, env string, vals ma
// store so it is never lost when the leaf file is absent (e.g. fresh clone).
func (e *Engine) writeLeaf(basepath string, tier store.Tier, env string, vals, seedIgnored map[string]string) error {
full := filepath.Join(e.Root, leaf.Path(basepath, tier, env))
ignoredNames := e.Store.IgnoredNames(basepath, env, tier)

// Gather ignored variables to preserve: from the on-disk file first, then
// falling back to the seed for any ignored name not present on disk.
var ignored map[string]string
if names := e.Store.IgnoredNames(basepath, env, tier); len(names) > 0 {
if len(ignoredNames) > 0 {
existing, ok, err := e.ReadLeaf(basepath, tier, env)
if err != nil {
return err
}
for _, k := range names {
for _, k := range ignoredNames {
var (
v string
present bool
Expand All @@ -252,11 +253,16 @@ func (e *Engine) writeLeaf(basepath string, tier store.Tier, env string, vals, s
}
}

// Never emit an ignored variable as a managed one.
if len(ignored) > 0 {
// Never emit an ignored variable as a managed one, even when there is no
// preserved ignored value to write.
if len(ignoredNames) > 0 {
ignoredSet := make(map[string]struct{}, len(ignoredNames))
for _, k := range ignoredNames {
ignoredSet[k] = struct{}{}
}
managed := make(map[string]string, len(vals))
for k, v := range vals {
if _, ok := ignored[k]; !ok {
if _, ok := ignoredSet[k]; !ok {
managed[k] = v
}
}
Expand Down
27 changes: 27 additions & 0 deletions internal/engine/ignore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,33 @@ func TestIgnore_PreservesLocalManagedEdits(t *testing.T) {
}
}

// TestWriteLeaf_DoesNotEmitIgnoredAsManaged verifies that an ignore marker is
// enough to suppress a managed value, even when there is no ignored value to
// preserve in the trailing block.
func TestWriteLeaf_DoesNotEmitIgnoredAsManaged(t *testing.T) {
dir, _ := newRepo(t)
e := openIn(t, dir)
if err := e.Init(nil, pass); err != nil {
t.Fatal(err)
}
cell := Cell{"apps/api", "_", store.Buildtime}
e.setIgnoreMarker(cell, "SECRET")

if err := e.writeLeaf(cell.Path, cell.Tier, cell.Env, map[string]string{
"KEEP": "1",
"SECRET": "2",
}, nil); err != nil {
t.Fatal(err)
}
body := readFile(t, filepath.Join(dir, "apps/api/.env"))
if strings.Contains(body, "SECRET=") {
t.Fatalf("ignored key emitted as managed:\n%s", body)
}
if !strings.Contains(body, "KEEP=1") {
t.Fatalf("managed key missing:\n%s", body)
}
}

// TestIgnore_EvictsExisting verifies that ignoring an already-stored variable
// drops it from the store and base but keeps it in the leaf's ignored block.
func TestIgnore_EvictsExisting(t *testing.T) {
Expand Down
Loading