From f9b5c2a043609cbc4c673dd0428adf5662b2ccbc Mon Sep 17 00:00:00 2001 From: yxxhero Date: Sun, 15 Mar 2026 15:55:57 +0800 Subject: [PATCH] fix: helmfile list now reflects version from helmfile.lock The list command now resolves locked dependencies before returning release information, ensuring the version field reflects the pinned version from helmfile.lock when present. Fixes #1953 Signed-off-by: yxxhero --- pkg/app/app.go | 14 +++++---- pkg/app/app_list_test.go | 65 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index cdc31745..06a41f1a 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -4,6 +4,7 @@ import ( "bytes" goContext "context" "fmt" + "maps" "os" "path/filepath" "sort" @@ -701,14 +702,17 @@ func (a *App) ListReleases(c ListConfigProvider) error { func (a *App) list(run *Run) ([]*HelmRelease, error) { var releases []*HelmRelease - for _, r := range run.state.Releases { + resolvedState, err := run.state.ResolveDeps() + if err != nil { + return nil, fmt.Errorf("unable to resolve dependencies: %w", err) + } + + for _, r := range resolvedState.Releases { labels := "" if r.Labels == nil { r.Labels = map[string]string{} } - for k, v := range run.state.CommonLabels { - r.Labels[k] = v - } + maps.Copy(r.Labels, resolvedState.CommonLabels) var keys []string for k := range r.Labels { @@ -722,7 +726,7 @@ func (a *App) list(run *Run) ([]*HelmRelease, error) { } labels = strings.Trim(labels, ",") - enabled, err := state.ConditionEnabled(r, run.state.Values()) + enabled, err := state.ConditionEnabled(r, resolvedState.Values()) if err != nil { return nil, err } diff --git a/pkg/app/app_list_test.go b/pkg/app/app_list_test.go index f8084682..cb0beea5 100644 --- a/pkg/app/app_list_test.go +++ b/pkg/app/app_list_test.go @@ -2,6 +2,7 @@ package app import ( "bytes" + "encoding/json" "os" "testing" @@ -301,3 +302,67 @@ func TestListWithJSONOutput(t *testing.T) { testListWithJSONOutput(t, configImpl{skipCharts: true}) }) } + +func TestListWithLockFileVersion(t *testing.T) { + files := map[string]string{ + "/path/to/helmfile.yaml": ` +repositories: +- name: bitnami + url: https://charts.bitnami.com/bitnami + +releases: +- name: redis + namespace: default + chart: bitnami/redis + version: ">=1.0.0" +`, + "/path/to/helmfile.lock": `version: v0.0.0 +dependencies: +- name: redis + repository: https://charts.bitnami.com/bitnami + version: 17.0.7 +digest: sha256:abc123 +generated: "2024-01-01T00:00:00Z" +`, + } + + stdout := os.Stdout + defer func() { os.Stdout = stdout }() + + var buffer bytes.Buffer + syncWriter := testhelper.NewSyncWriter(&buffer) + logger := helmexec.NewLogger(syncWriter, "debug") + + valsRuntime, err := vals.New(vals.Options{CacheSize: 32}) + if err != nil { + t.Fatalf("unexpected error creating vals runtime: %v", err) + } + + app := appWithFs(&App{ + OverrideHelmBinary: DefaultHelmBinary, + fs: ffs.DefaultFileSystem(), + OverrideKubeContext: "default", + DisableKubeVersionAutoDetection: true, + Env: "default", + Logger: logger, + valsRuntime: valsRuntime, + }, files) + + expectNoCallsToHelm(app) + + out, err := testutil.CaptureStdout(func() { + err := app.ListReleases(configImpl{skipCharts: true, output: "json"}) + assert.Nil(t, err) + }) + assert.NoError(t, err) + + var releases []HelmRelease + if err := json.Unmarshal([]byte(out), &releases); err != nil { + t.Fatalf("failed to parse JSON output: %v", err) + } + + assert.Len(t, releases, 1, "expected 1 release") + assert.Equal(t, "redis", releases[0].Name) + assert.Equal(t, "bitnami/redis", releases[0].Chart) + assert.Equal(t, "17.0.7", releases[0].Version, "expected version from helmfile.lock") +}