From 2e8f0b70cc6d94e10708dd5c6f9b596f7633bf24 Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Fri, 20 Feb 2026 18:19:12 +0100 Subject: [PATCH 1/7] fix(pkg): respect subchart conditions when dependency uses an alias When a dependency declared an alias, the nested path entry in the coalesced values snapshot (e.g. cvals["midChart"]["leafChart"]) was built by an ancestor's CoalesceValues call that still used the original chart name as the map key. As a result, the subchart's own default values were coalesced under the original name only, leaving the alias-keyed nested entry incomplete. Condition lookups that navigated via the accumulated path therefore returned ErrNoValue instead of the chart's default, causing disabled-by-default dependencies to be incorrectly rendered. Fix by coalescing the correctly-computed top-level entry (cvals[alias], produced at the current level after alias renaming) into the stale nested entry before recursing. The nested entry retains priority so explicit ancestor overrides are preserved; only missing defaults are filled in. Signed-off-by: Julian Siebert --- pkg/chart/v2/util/dependencies.go | 11 +++++++ pkg/chart/v2/util/dependencies_test.go | 29 +++++++++++++++++++ .../alias-condition-nested/Chart.yaml | 9 ++++++ .../charts/mid/Chart.yaml | 9 ++++++ .../charts/mid/charts/leaf/Chart.yaml | 8 +++++ .../mid/charts/leaf/charts/util/Chart.yaml | 3 ++ .../mid/charts/leaf/charts/util/values.yaml | 1 + .../charts/mid/charts/leaf/values.yaml | 3 ++ .../charts/mid/values.yaml | 2 ++ .../alias-condition-nested/values.yaml | 2 ++ 10 files changed, 77 insertions(+) create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/Chart.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/Chart.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/values.yaml create mode 100644 pkg/chart/v2/util/testdata/alias-condition-nested/values.yaml diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index aa242e0ca..65524c66f 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -214,6 +214,17 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { + // cvals[path][alias] may be stale when an alias is used, because ancestor + // CoalesceValues calls used the original chart name. Fill in missing keys from the + // correctly-coalesced top-level entry, keeping the nested entry authoritative. + if path != "" { + if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { + if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { + nested, _ := pt[t.Metadata.Name].(map[string]interface{}) + pt[t.Metadata.Name] = util.CoalesceTables(nested, top) + } + } + } subpath := path + t.Metadata.Name + "." if err := processDependencyEnabled(t, cvals, subpath); err != nil { return err diff --git a/pkg/chart/v2/util/dependencies_test.go b/pkg/chart/v2/util/dependencies_test.go index c817b0b89..0cc1c8967 100644 --- a/pkg/chart/v2/util/dependencies_test.go +++ b/pkg/chart/v2/util/dependencies_test.go @@ -134,6 +134,35 @@ func TestDependencyEnabled(t *testing.T) { } } +// TestDependencyEnabledAliasNestedCondition tests that a condition defaulting to false in a +// leaf chart's values.yaml is respected when the leaf chart is accessed through multiple +// levels of aliased dependencies. +func TestDependencyEnabledAliasNestedCondition(t *testing.T) { + // Chart structure: + // parentchart -> mid (alias: midchart) -> leaf (alias: leafchart) -> util (condition: util.enabled) + // + // leaf/values.yaml sets util.enabled: false. + // No user-provided values override this. + // Expected: util is NOT included in the output. + c := loadChart(t, "testdata/alias-condition-nested") + if err := processDependencyEnabled(c, c.Values, ""); err != nil { + t.Fatalf("error processing enabled dependencies: %v", err) + } + + names := extractChartNames(c) + expected := []string{"parentchart", "parentchart.midchart", "parentchart.midchart.leafchart"} + sort.Strings(expected) + + if len(names) != len(expected) { + t.Fatalf("slice lengths do not match: got %v, expected %v", names, expected) + } + for i := range names { + if names[i] != expected[i] { + t.Fatalf("slice values do not match: got %v, expected %v", names, expected) + } + } +} + // extractChartNames recursively searches chart dependencies returning all charts found func extractChartNames(c *chart.Chart) []string { var out []string diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/Chart.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/Chart.yaml new file mode 100644 index 000000000..fc50e2468 --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +name: parentchart +version: 0.1.0 +dependencies: + - name: mid + repository: http://localhost:10191 + version: 0.1.0 + alias: midchart + condition: midchart.enabled diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/Chart.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/Chart.yaml new file mode 100644 index 000000000..9348b5fd2 --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +name: mid +version: 0.1.0 +dependencies: + - name: leaf + repository: http://localhost:10191 + version: 0.1.0 + alias: leafchart + condition: leafchart.enabled diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml new file mode 100644 index 000000000..28f1f26fe --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: leaf +version: 0.1.0 +dependencies: + - name: util + repository: http://localhost:10191 + version: 0.1.0 + condition: util.enabled diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml new file mode 100644 index 000000000..a57a4988d --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: util +version: 0.1.0 diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml @@ -0,0 +1 @@ +{} diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml new file mode 100644 index 000000000..54d7c9524 --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml @@ -0,0 +1,3 @@ +# util is disabled by default in this chart's values +util: + enabled: false diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/values.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/values.yaml new file mode 100644 index 000000000..de12ce852 --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/charts/mid/values.yaml @@ -0,0 +1,2 @@ +leafchart: + enabled: true diff --git a/pkg/chart/v2/util/testdata/alias-condition-nested/values.yaml b/pkg/chart/v2/util/testdata/alias-condition-nested/values.yaml new file mode 100644 index 000000000..4ef046569 --- /dev/null +++ b/pkg/chart/v2/util/testdata/alias-condition-nested/values.yaml @@ -0,0 +1,2 @@ +midchart: + enabled: true From 19fcdd6f15b6acbe4c3302e872364a042f1eb962 Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Thu, 26 Feb 2026 10:45:11 +0100 Subject: [PATCH 2/7] fix(pkg): respect subchart conditions when dependency uses an alias in v3 & add logging Signed-off-by: Julian Siebert --- internal/chart/v3/util/dependencies.go | 15 +++++++++++ internal/chart/v3/util/dependencies_test.go | 26 +++++++++++++++++++ .../alias-condition-nested/Chart.yaml | 9 +++++++ .../charts/mid/Chart.yaml | 9 +++++++ .../charts/mid/charts/leaf/Chart.yaml | 8 ++++++ .../mid/charts/leaf/charts/util/Chart.yaml | 3 +++ .../mid/charts/leaf/charts/util/values.yaml | 1 + .../charts/mid/charts/leaf/values.yaml | 3 +++ .../charts/mid/values.yaml | 2 ++ .../alias-condition-nested/values.yaml | 2 ++ pkg/chart/v2/util/dependencies.go | 8 ++++-- 11 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/Chart.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/Chart.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/values.yaml create mode 100644 internal/chart/v3/util/testdata/alias-condition-nested/values.yaml diff --git a/internal/chart/v3/util/dependencies.go b/internal/chart/v3/util/dependencies.go index 9c4d8e80f..91427091a 100644 --- a/internal/chart/v3/util/dependencies.go +++ b/internal/chart/v3/util/dependencies.go @@ -214,6 +214,21 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { + // cvals[path][alias] may be stale when an alias is used, because ancestor + // CoalesceValues calls used the original chart name. Fill in missing keys from the + // correctly-coalesced top-level entry, keeping the nested entry authoritative. + if path != "" { + if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { + if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { + if v, ok := pt[t.Metadata.Name]; ok && !istable(v) { + slog.Warn("skipping nested path update: value is not a table", "path", path+t.Metadata.Name) + } else { + nested, _ := v.(map[string]interface{}) + pt[t.Metadata.Name] = util.CoalesceTables(nested, top) + } + } + } + } subpath := path + t.Metadata.Name + "." if err := processDependencyEnabled(t, cvals, subpath); err != nil { return err diff --git a/internal/chart/v3/util/dependencies_test.go b/internal/chart/v3/util/dependencies_test.go index 3c5bb96f7..8b3d458e8 100644 --- a/internal/chart/v3/util/dependencies_test.go +++ b/internal/chart/v3/util/dependencies_test.go @@ -552,6 +552,32 @@ func validateDependencyTree(t *testing.T, c *chart.Chart) { } } +func TestDependencyEnabledAliasNestedCondition(t *testing.T) { + // Chart structure: + // parentchart -> mid (alias: midchart) -> leaf (alias: leafchart) -> util (condition: util.enabled) + // + // leaf/values.yaml sets util.enabled: false. + // No user-provided values override this. + // Expected: util is NOT included in the output. + c := loadChart(t, "testdata/alias-condition-nested") + if err := processDependencyEnabled(c, c.Values, ""); err != nil { + t.Fatalf("error processing enabled dependencies: %v", err) + } + + names := extractChartNames(c) + expected := []string{"parentchart", "parentchart.midchart", "parentchart.midchart.leafchart"} + sort.Strings(expected) + + if len(names) != len(expected) { + t.Fatalf("slice lengths do not match: got %v, expected %v", names, expected) + } + for i := range names { + if names[i] != expected[i] { + t.Fatalf("slice values do not match: got %v, expected %v", names, expected) + } + } +} + func TestChartWithDependencyAliasedTwiceAndDoublyReferencedSubDependency(t *testing.T) { c := loadChart(t, "testdata/chart-with-dependency-aliased-twice") diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/Chart.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/Chart.yaml new file mode 100644 index 000000000..8cab888a3 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v3 +name: parentchart +version: 0.1.0 +dependencies: + - name: mid + repository: http://localhost:10191 + version: 0.1.0 + alias: midchart + condition: midchart.enabled diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/Chart.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/Chart.yaml new file mode 100644 index 000000000..9009efc75 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v3 +name: mid +version: 0.1.0 +dependencies: + - name: leaf + repository: http://localhost:10191 + version: 0.1.0 + alias: leafchart + condition: leafchart.enabled diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml new file mode 100644 index 000000000..8477bef5f --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v3 +name: leaf +version: 0.1.0 +dependencies: + - name: util + repository: http://localhost:10191 + version: 0.1.0 + condition: util.enabled diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml new file mode 100644 index 000000000..b9ddc5115 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v3 +name: util +version: 0.1.0 diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/charts/util/values.yaml @@ -0,0 +1 @@ +{} diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml new file mode 100644 index 000000000..54d7c9524 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/charts/leaf/values.yaml @@ -0,0 +1,3 @@ +# util is disabled by default in this chart's values +util: + enabled: false diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/values.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/values.yaml new file mode 100644 index 000000000..de12ce852 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/charts/mid/values.yaml @@ -0,0 +1,2 @@ +leafchart: + enabled: true diff --git a/internal/chart/v3/util/testdata/alias-condition-nested/values.yaml b/internal/chart/v3/util/testdata/alias-condition-nested/values.yaml new file mode 100644 index 000000000..4ef046569 --- /dev/null +++ b/internal/chart/v3/util/testdata/alias-condition-nested/values.yaml @@ -0,0 +1,2 @@ +midchart: + enabled: true diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index 65524c66f..727d0dcf0 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -220,8 +220,12 @@ Loop: if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { - nested, _ := pt[t.Metadata.Name].(map[string]interface{}) - pt[t.Metadata.Name] = util.CoalesceTables(nested, top) + if v, ok := pt[t.Metadata.Name]; ok && !istable(v) { + slog.Warn("skipping nested path update: value is not a table", "path", path+t.Metadata.Name) + } else { + nested, _ := v.(map[string]interface{}) + pt[t.Metadata.Name] = util.CoalesceTables(nested, top) + } } } } From 91db4bc9e29908faa3c49411717eb98fe9137c0a Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Thu, 26 Feb 2026 11:36:06 +0100 Subject: [PATCH 3/7] fix(pkg): add testcases and doc Signed-off-by: Julian Siebert --- internal/chart/v3/util/dependencies.go | 9 +++-- internal/chart/v3/util/dependencies_test.go | 41 +++++++++++++++++++++ pkg/chart/v2/util/dependencies.go | 9 +++-- pkg/chart/v2/util/dependencies_test.go | 41 +++++++++++++++++++++ 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/internal/chart/v3/util/dependencies.go b/internal/chart/v3/util/dependencies.go index 91427091a..063e4fab1 100644 --- a/internal/chart/v3/util/dependencies.go +++ b/internal/chart/v3/util/dependencies.go @@ -214,9 +214,12 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { - // cvals[path][alias] may be stale when an alias is used, because ancestor - // CoalesceValues calls used the original chart name. Fill in missing keys from the - // correctly-coalesced top-level entry, keeping the nested entry authoritative. + // When a dependency uses an alias, cvals[path][alias] may be missing keys + // that were coalesced into cvals[originalName] by ancestor CoalesceValues + // calls (which used the original chart name, not the alias). To correct + // this, we backfill any missing keys from the top-level entry into the + // nested path entry. The nested entry is authoritative: if a key exists + // in both, the nested (alias-keyed) value wins. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { diff --git a/internal/chart/v3/util/dependencies_test.go b/internal/chart/v3/util/dependencies_test.go index 8b3d458e8..6ca87cf4d 100644 --- a/internal/chart/v3/util/dependencies_test.go +++ b/internal/chart/v3/util/dependencies_test.go @@ -578,6 +578,47 @@ func TestDependencyEnabledAliasNestedCondition(t *testing.T) { } } +// TestDependencyEnabledAliasNestedConditionEnabled tests that overriding util.enabled=true +// through the parent chart's values correctly includes the dependency even when the leaf +// chart's default disables it. This is the complementary positive case to +// TestDependencyEnabledAliasNestedCondition. +func TestDependencyEnabledAliasNestedConditionEnabled(t *testing.T) { + // Chart structure: + // parentchart -> mid (alias: midchart) -> leaf (alias: leafchart) -> util (condition: util.enabled) + // + // leaf/values.yaml sets util.enabled: false by default. + // User-provided values override util.enabled=true via the full alias path. + // Expected: util IS included in the output. + c := loadChart(t, "testdata/alias-condition-nested") + vals := map[string]interface{}{ + "midchart": map[string]interface{}{ + "enabled": true, + "leafchart": map[string]interface{}{ + "enabled": true, + "util": map[string]interface{}{ + "enabled": true, + }, + }, + }, + } + if err := processDependencyEnabled(c, vals, ""); err != nil { + t.Fatalf("error processing enabled dependencies: %v", err) + } + + names := extractChartNames(c) + expected := []string{"parentchart", "parentchart.midchart", "parentchart.midchart.leafchart", "parentchart.midchart.leafchart.util"} + sort.Strings(expected) + + if len(names) != len(expected) { + t.Fatalf("slice lengths do not match: got %v, expected %v", names, expected) + } + for i := range names { + if names[i] != expected[i] { + t.Fatalf("slice values do not match: got %v, expected %v", names, expected) + } + } +} + func TestChartWithDependencyAliasedTwiceAndDoublyReferencedSubDependency(t *testing.T) { c := loadChart(t, "testdata/chart-with-dependency-aliased-twice") diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index 727d0dcf0..38ca554b3 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -214,9 +214,12 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { - // cvals[path][alias] may be stale when an alias is used, because ancestor - // CoalesceValues calls used the original chart name. Fill in missing keys from the - // correctly-coalesced top-level entry, keeping the nested entry authoritative. + // When a dependency uses an alias, cvals[path][alias] may be missing keys + // that were coalesced into cvals[originalName] by ancestor CoalesceValues + // calls (which used the original chart name, not the alias). To correct + // this, we backfill any missing keys from the top-level entry into the + // nested path entry. The nested entry is authoritative: if a key exists + // in both, the nested (alias-keyed) value wins. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { diff --git a/pkg/chart/v2/util/dependencies_test.go b/pkg/chart/v2/util/dependencies_test.go index 0cc1c8967..23d4b8c32 100644 --- a/pkg/chart/v2/util/dependencies_test.go +++ b/pkg/chart/v2/util/dependencies_test.go @@ -163,6 +163,47 @@ func TestDependencyEnabledAliasNestedCondition(t *testing.T) { } } +// TestDependencyEnabledAliasNestedConditionEnabled tests that overriding util.enabled=true +// through the parent chart's values correctly includes the dependency even when the leaf +// chart's default disables it. This is the complementary positive case to +// TestDependencyEnabledAliasNestedCondition. +func TestDependencyEnabledAliasNestedConditionEnabled(t *testing.T) { + // Chart structure: + // parentchart -> mid (alias: midchart) -> leaf (alias: leafchart) -> util (condition: util.enabled) + // + // leaf/values.yaml sets util.enabled: false by default. + // User-provided values override util.enabled=true via the full alias path. + // Expected: util IS included in the output. + c := loadChart(t, "testdata/alias-condition-nested") + vals := map[string]interface{}{ + "midchart": map[string]interface{}{ + "enabled": true, + "leafchart": map[string]interface{}{ + "enabled": true, + "util": map[string]interface{}{ + "enabled": true, + }, + }, + }, + } + if err := processDependencyEnabled(c, vals, ""); err != nil { + t.Fatalf("error processing enabled dependencies: %v", err) + } + + names := extractChartNames(c) + expected := []string{"parentchart", "parentchart.midchart", "parentchart.midchart.leafchart", "parentchart.midchart.leafchart.util"} + sort.Strings(expected) + + if len(names) != len(expected) { + t.Fatalf("slice lengths do not match: got %v, expected %v", names, expected) + } + for i := range names { + if names[i] != expected[i] { + t.Fatalf("slice values do not match: got %v, expected %v", names, expected) + } + } +} + // extractChartNames recursively searches chart dependencies returning all charts found func extractChartNames(c *chart.Chart) []string { var out []string From 40adeadf113fccbfa1d0d94b43abbaf0e57c4e1f Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Tue, 30 Jun 2026 09:21:02 +0200 Subject: [PATCH 4/7] fix(pkg): fix linting Signed-off-by: Julian Siebert --- internal/chart/v3/util/dependencies_test.go | 8 ++++---- pkg/chart/v2/util/dependencies.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/chart/v3/util/dependencies_test.go b/internal/chart/v3/util/dependencies_test.go index 92621487c..d133d9d63 100644 --- a/internal/chart/v3/util/dependencies_test.go +++ b/internal/chart/v3/util/dependencies_test.go @@ -589,12 +589,12 @@ func TestDependencyEnabledAliasNestedConditionEnabled(t *testing.T) { // User-provided values override util.enabled=true via the full alias path. // Expected: util IS included in the output. c := loadChart(t, "testdata/alias-condition-nested") - vals := map[string]interface{}{ - "midchart": map[string]interface{}{ + vals := map[string]any{ + "midchart": map[string]any{ "enabled": true, - "leafchart": map[string]interface{}{ + "leafchart": map[string]any{ "enabled": true, - "util": map[string]interface{}{ + "util": map[string]any{ "enabled": true, }, }, diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index dd17a6730..f00a5ecd1 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -223,11 +223,11 @@ Loop: // in both, the nested (alias-keyed) value wins. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { - if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { + if top, ok := cvals[t.Metadata.Name].(map[string]any); ok { if v, ok := pt[t.Metadata.Name]; ok && !istable(v) { slog.Warn("skipping nested path update: value is not a table", "path", path+t.Metadata.Name) } else { - nested, _ := v.(map[string]interface{}) + nested, _ := v.(map[string]any) pt[t.Metadata.Name] = util.CoalesceTables(nested, top) } } From 6a00fee7abef93379a4c8a4fac83597b2ac9d7bc Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Tue, 30 Jun 2026 09:40:12 +0200 Subject: [PATCH 5/7] Apply Copilot suggestion to avoid confusion in doc Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Julian Siebert --- internal/chart/v3/util/dependencies.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/chart/v3/util/dependencies.go b/internal/chart/v3/util/dependencies.go index e28288663..dd778d1d3 100644 --- a/internal/chart/v3/util/dependencies.go +++ b/internal/chart/v3/util/dependencies.go @@ -216,11 +216,11 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { // When a dependency uses an alias, cvals[path][alias] may be missing keys - // that were coalesced into cvals[originalName] by ancestor CoalesceValues - // calls (which used the original chart name, not the alias). To correct - // this, we backfill any missing keys from the top-level entry into the - // nested path entry. The nested entry is authoritative: if a key exists - // in both, the nested (alias-keyed) value wins. + // that were instead placed in cvals[t.Metadata.Name] at the current values root by + // earlier CoalesceValues calls (which coalesce against the full values tree, not the + // path-scoped subtable). Backfill any missing keys into the path-scoped entry so + // condition checks see chart defaults; path-scoped values remain authoritative and + // win on conflicts. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { From 5bd8aa1943776a42bd55b511682dd50a1ba11c61 Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Tue, 30 Jun 2026 09:49:41 +0200 Subject: [PATCH 6/7] Apply Copilot suggestion to avoid confusion in doc Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Julian Siebert --- pkg/chart/v2/util/dependencies.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index f00a5ecd1..e92eb2348 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -215,11 +215,11 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { - // When a dependency uses an alias, cvals[path][alias] may be missing keys - // that were coalesced into cvals[originalName] by ancestor CoalesceValues - // calls (which used the original chart name, not the alias). To correct - // this, we backfill any missing keys from the top-level entry into the - // nested path entry. The nested entry is authoritative: if a key exists + // When processing nested dependencies, CoalesceValues is invoked with the full values tree, + // so defaults for this dependency can be coalesced into cvals[t.Metadata.Name] at the + // current values root instead of into the path-scoped table (under `path`). + // Backfill any missing keys from the root entry into the path-scoped entry so condition + // checks see chart defaults. // in both, the nested (alias-keyed) value wins. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { From ab717bd6d7c7e701a28779912c9828f41eab47b6 Mon Sep 17 00:00:00 2001 From: Julian Siebert Date: Tue, 30 Jun 2026 11:29:54 +0200 Subject: [PATCH 7/7] fix(pkg): add nil-checks Signed-off-by: Julian Siebert --- internal/chart/v3/util/dependencies.go | 7 +++++-- pkg/chart/v2/util/dependencies.go | 3 +++ pkg/chart/v2/util/dependencies_test.go | 8 ++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/internal/chart/v3/util/dependencies.go b/internal/chart/v3/util/dependencies.go index dd778d1d3..48584a6f9 100644 --- a/internal/chart/v3/util/dependencies.go +++ b/internal/chart/v3/util/dependencies.go @@ -223,11 +223,14 @@ Loop: // win on conflicts. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { - if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { + if top, ok := cvals[t.Metadata.Name].(map[string]any); ok { if v, ok := pt[t.Metadata.Name]; ok && !istable(v) { slog.Warn("skipping nested path update: value is not a table", "path", path+t.Metadata.Name) } else { - nested, _ := v.(map[string]interface{}) + nested, _ := v.(map[string]any) + if nested == nil { + nested = map[string]any{} + } pt[t.Metadata.Name] = util.CoalesceTables(nested, top) } } diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index e92eb2348..38d1897cb 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -228,6 +228,9 @@ Loop: slog.Warn("skipping nested path update: value is not a table", "path", path+t.Metadata.Name) } else { nested, _ := v.(map[string]any) + if nested == nil { + nested = map[string]any{} + } pt[t.Metadata.Name] = util.CoalesceTables(nested, top) } } diff --git a/pkg/chart/v2/util/dependencies_test.go b/pkg/chart/v2/util/dependencies_test.go index da7e05778..0b60e0716 100644 --- a/pkg/chart/v2/util/dependencies_test.go +++ b/pkg/chart/v2/util/dependencies_test.go @@ -175,12 +175,12 @@ func TestDependencyEnabledAliasNestedConditionEnabled(t *testing.T) { // User-provided values override util.enabled=true via the full alias path. // Expected: util IS included in the output. c := loadChart(t, "testdata/alias-condition-nested") - vals := map[string]interface{}{ - "midchart": map[string]interface{}{ + vals := map[string]any{ + "midchart": map[string]any{ "enabled": true, - "leafchart": map[string]interface{}{ + "leafchart": map[string]any{ "enabled": true, - "util": map[string]interface{}{ + "util": map[string]any{ "enabled": true, }, },