From bd37347a0633a38e68b913c45de98f09bbcb8293 Mon Sep 17 00:00:00 2001 From: MrJack <36191829+biagiopietro@users.noreply.github.com> Date: Sun, 7 Dec 2025 17:24:50 +0100 Subject: [PATCH 01/36] feat(plugin): add --version flag to plugin update command Add support for specifying a version constraint when updating plugins, matching the existing behavior of helm plugin install. Changes: - Add --version flag to plugin update command - Update VCSInstaller.Update() to resolve and checkout specified version - Update FindSource() to accept version parameter - Add TestVCSInstallerUpdateWithVersion test for version support Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/plugin/installer/installer.go | 4 +- internal/plugin/installer/vcs_installer.go | 18 +++++- .../plugin/installer/vcs_installer_test.go | 63 ++++++++++++++++++- pkg/cmd/plugin_update.go | 10 +-- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/internal/plugin/installer/installer.go b/internal/plugin/installer/installer.go index c7c1a8801..1f9839054 100644 --- a/internal/plugin/installer/installer.go +++ b/internal/plugin/installer/installer.go @@ -160,8 +160,8 @@ func NewForSource(source, version string) (installer Installer, err error) { } // FindSource determines the correct Installer for the given source. -func FindSource(location string) (Installer, error) { - installer, err := existingVCSRepo(location) +func FindSource(location string, version string) (Installer, error) { + installer, err := existingVCSRepo(location, version) if err != nil && err.Error() == "Cannot detect VCS" { slog.Warn("cannot get information about plugin source", "location", location, slog.Any("error", err)) return installer, errors.New("cannot get information about plugin source") diff --git a/internal/plugin/installer/vcs_installer.go b/internal/plugin/installer/vcs_installer.go index 3601ec7a8..1e858c9ce 100644 --- a/internal/plugin/installer/vcs_installer.go +++ b/internal/plugin/installer/vcs_installer.go @@ -38,14 +38,15 @@ type VCSInstaller struct { base } -func existingVCSRepo(location string) (Installer, error) { +func existingVCSRepo(location string, version string) (Installer, error) { repo, err := vcs.NewRepo("", location) if err != nil { return nil, err } i := &VCSInstaller{ - Repo: repo, - base: newBase(repo.Remote()), + Repo: repo, + Version: version, + base: newBase(repo.Remote()), } return i, nil } @@ -104,6 +105,17 @@ func (i *VCSInstaller) Update() error { if err := i.Repo.Update(); err != nil { return err } + + ref, err := i.solveVersion(i.Repo) + if err != nil { + return err + } + if ref != "" { + if err := i.setVersion(i.Repo, ref); err != nil { + return err + } + } + if !isPlugin(i.Repo.LocalPath()) { return ErrMissingMetadata } diff --git a/internal/plugin/installer/vcs_installer_test.go b/internal/plugin/installer/vcs_installer_test.go index d542a0f75..4a6b319cc 100644 --- a/internal/plugin/installer/vcs_installer_test.go +++ b/internal/plugin/installer/vcs_installer_test.go @@ -48,6 +48,7 @@ func (r *testRepo) UpdateVersion(version string) error { r.current = version return r.err } +func (r *testRepo) IsDirty() bool { return false } func TestVCSInstaller(t *testing.T) { ensure.HelmHome(t) @@ -96,7 +97,7 @@ func TestVCSInstaller(t *testing.T) { } // Testing FindSource method, expect error because plugin code is not a cloned repository - if _, err := FindSource(i.Path()); err == nil { + if _, err := FindSource(i.Path(), ""); err == nil { t.Fatalf("expected error for inability to find plugin source, got none") } else if err.Error() != "cannot get information about plugin source" { t.Fatalf("expected error for inability to find plugin source, got (%v)", err) @@ -158,7 +159,7 @@ func TestVCSInstallerUpdate(t *testing.T) { } // Test FindSource method for positive result - pluginInfo, err := FindSource(i.Path()) + pluginInfo, err := FindSource(i.Path(), "") if err != nil { t.Fatal(err) } @@ -187,3 +188,61 @@ func TestVCSInstallerUpdate(t *testing.T) { } } + +func TestVCSInstallerUpdateWithVersion(t *testing.T) { + ensure.HelmHome(t) + + if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { + t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) + } + + source := "https://github.com/adamreese/helm-env" + testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo-v1") + repo := &testRepo{ + local: testRepoPath, + remote: source, + tags: []string{"0.1.0", "0.1.1", "0.2.0"}, + } + + // First install without version + i, err := NewForSource(source, "") + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + vcsInstaller, ok := i.(*VCSInstaller) + if !ok { + t.Fatal("expected a VCSInstaller") + } + vcsInstaller.Repo = repo + + if err := Install(i); err != nil { + t.Fatal(err) + } + + // Now test update with specific version constraint + vcsInstaller.Version = "~0.1.0" + if err := Update(vcsInstaller); err != nil { + t.Fatal(err) + } + if repo.current != "0.1.1" { + t.Fatalf("expected version '0.1.1', got %q", repo.current) + } + + // Test update with different version constraint + vcsInstaller.Version = "0.2.0" + if err := Update(vcsInstaller); err != nil { + t.Fatal(err) + } + if repo.current != "0.2.0" { + t.Fatalf("expected version '0.2.0', got %q", repo.current) + } + + // Test update with non-existent version + vcsInstaller.Version = "0.3.0" + if err := Update(vcsInstaller); err == nil { + t.Fatal("expected error for version does not exist, got none") + } else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", "0.3.0", source) { + t.Fatalf("expected error for version does not exist, got (%v)", err) + } +} diff --git a/pkg/cmd/plugin_update.go b/pkg/cmd/plugin_update.go index c6d4b8530..c534f93e9 100644 --- a/pkg/cmd/plugin_update.go +++ b/pkg/cmd/plugin_update.go @@ -29,7 +29,8 @@ import ( ) type pluginUpdateOptions struct { - names []string + names []string + version string } func newPluginUpdateCmd(out io.Writer) *cobra.Command { @@ -49,6 +50,7 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command { return o.run(out) }, } + cmd.Flags().StringVar(&o.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed") return cmd } @@ -71,7 +73,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error { for _, name := range o.names { if found := findPlugin(plugins, name); found != nil { - if err := updatePlugin(found); err != nil { + if err := updatePlugin(found, o.version); err != nil { errorPlugins = append(errorPlugins, fmt.Errorf("failed to update plugin %s, got error (%v)", name, err)) } else { fmt.Fprintf(out, "Updated plugin: %s\n", name) @@ -86,7 +88,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error { return nil } -func updatePlugin(p plugin.Plugin) error { +func updatePlugin(p plugin.Plugin, version string) error { exactLocation, err := filepath.EvalSymlinks(p.Dir()) if err != nil { return err @@ -96,7 +98,7 @@ func updatePlugin(p plugin.Plugin) error { return err } - i, err := installer.FindSource(absExactLocation) + i, err := installer.FindSource(absExactLocation, version) if err != nil { return err } From c5b6eea1a0f4a713fae9e431fd44ef532c2148dc Mon Sep 17 00:00:00 2001 From: MrJack <36191829+biagiopietro@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:04:05 +0100 Subject: [PATCH 02/36] fix(action): enable server-side validation for dry-run=server When using --dry-run=server with --server-side=true, Helm now properly validates manifests against the Kubernetes API server. Previously, the dry-run would return early without calling the API, missing validation errors like unknown fields in the spec. This fix ensures that DryRunServer mode calls KubeClient.Create/Update with the dry-run option, matching the behavior of kubectl apply --dry-run=server. Fixes: helm/helm#31505 Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/action/install.go | 20 +++++++++++++ pkg/action/install_test.go | 40 ++++++++++++++++++++++++++ pkg/action/upgrade.go | 13 +++++++++ pkg/action/upgrade_test.go | 57 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) diff --git a/pkg/action/install.go b/pkg/action/install.go index 2f5910284..dc1735110 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -394,6 +394,26 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st // Bail out here if it is a dry run if isDryRun(i.DryRunStrategy) { + // For server-side dry-run, validate resources against the API server + if i.DryRunStrategy == DryRunServer && len(resources) > 0 { + if len(toBeAdopted) == 0 { + _, err = i.cfg.KubeClient.Create( + resources, + kube.ClientCreateOptionServerSideApply(i.ServerSideApply, false), + kube.ClientCreateOptionDryRun(true), + ) + } else { + _, err = i.cfg.KubeClient.Update( + toBeAdopted, + resources, + kube.ClientUpdateOptionServerSideApply(i.ServerSideApply, i.ForceConflicts), + kube.ClientUpdateOptionDryRun(true), + ) + } + if err != nil { + return rel, err + } + } rel.Info.Description = "Dry run complete" return rel, nil } diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 9f04f40d4..775870e55 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -412,6 +412,46 @@ func TestInstallRelease_DryRunClient(t *testing.T) { } } +func TestInstallRelease_DryRunServerValidation(t *testing.T) { + // Test that server-side dry-run actually calls the Kubernetes API for validation + is := assert.New(t) + + // Use a fixture that returns dummy resources so our code path is exercised + config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(false)) + + instAction := NewInstall(config) + instAction.Namespace = "spaced" + instAction.ReleaseName = "test-server-dry-run" + + // Set up the fake client to return an error on Create + expectedErr := errors.New("validation error: unknown field in spec") + config.KubeClient.(*kubefake.FailingKubeClient).CreateError = expectedErr + instAction.DryRunStrategy = DryRunServer + + vals := map[string]interface{}{} + _, err := instAction.Run(buildChart(withSampleTemplates()), vals) + + // The error from the API should be returned + is.Error(err) + is.Contains(err.Error(), "validation error") + + // Reset and test that client-side dry-run does NOT call the API + config2 := actionConfigFixtureWithDummyResources(t, createDummyResourceList(false)) + config2.KubeClient.(*kubefake.FailingKubeClient).CreateError = expectedErr + + instAction2 := NewInstall(config2) + instAction2.Namespace = "spaced" + instAction2.ReleaseName = "test-client-dry-run" + instAction2.DryRunStrategy = DryRunClient + + resi, err := instAction2.Run(buildChart(withSampleTemplates()), vals) + // Client-side dry-run should succeed since it doesn't call the API + is.NoError(err) + res, err := releaserToV1Release(resi) + is.NoError(err) + is.Equal(res.Info.Description, "Dry run complete") +} + func TestInstallRelease_DryRunHiddenSecret(t *testing.T) { is := assert.New(t) instAction := installAction(t) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 57a4a0272..0172d8160 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -392,6 +392,19 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR if isDryRun(u.DryRunStrategy) { u.cfg.Logger().Debug("dry run for release", "name", upgradedRelease.Name) + // For server-side dry-run, validate resources against the API server + if u.DryRunStrategy == DryRunServer { + _, err := u.cfg.KubeClient.Update( + current, + target, + kube.ClientUpdateOptionForceReplace(u.ForceReplace), + kube.ClientUpdateOptionServerSideApply(serverSideApply, u.ForceConflicts), + kube.ClientUpdateOptionDryRun(true), + ) + if err != nil { + return upgradedRelease, err + } + } if len(u.Description) > 0 { upgradedRelease.Info.Description = u.Description } else { diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go index e1eac3f9f..e649f1957 100644 --- a/pkg/action/upgrade_test.go +++ b/pkg/action/upgrade_test.go @@ -635,6 +635,63 @@ func TestUpgradeRelease_DryRun(t *testing.T) { req.Error(err) } +func TestUpgradeRelease_DryRunServerValidation(t *testing.T) { + // Test that server-side dry-run actually calls the Kubernetes API for validation + is := assert.New(t) + req := require.New(t) + + // Use a fixture that returns dummy resources so our code path is exercised + config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(true)) + + upAction := NewUpgrade(config) + upAction.Namespace = "spaced" + + // Create a previous release + rel := releaseStub() + rel.Name = "test-server-dry-run" + rel.Info.Status = common.StatusDeployed + req.NoError(upAction.cfg.Releases.Create(rel)) + + // Set up the fake client to return an error on Update + expectedErr := errors.New("validation error: unknown field in spec") + config.KubeClient.(*kubefake.FailingKubeClient).UpdateError = expectedErr + upAction.DryRunStrategy = DryRunServer + + vals := map[string]interface{}{} + ctx, done := context.WithCancel(t.Context()) + _, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals) + done() + + // The error from the API should be returned + is.Error(err) + is.Contains(err.Error(), "validation error") + + // Reset and test that client-side dry-run does NOT call the API + config2 := actionConfigFixtureWithDummyResources(t, createDummyResourceList(true)) + config2.KubeClient.(*kubefake.FailingKubeClient).UpdateError = expectedErr + + upAction2 := NewUpgrade(config2) + upAction2.Namespace = "spaced" + + // Create a previous release + rel2 := releaseStub() + rel2.Name = "test-client-dry-run" + rel2.Info.Status = common.StatusDeployed + req.NoError(upAction2.cfg.Releases.Create(rel2)) + + upAction2.DryRunStrategy = DryRunClient + + ctx, done = context.WithCancel(t.Context()) + resi, err := upAction2.RunWithContext(ctx, rel2.Name, buildChart(), vals) + done() + + // Client-side dry-run should succeed since it doesn't call the API + is.NoError(err) + res, err := releaserToV1Release(resi) + is.NoError(err) + is.Equal(res.Info.Description, "Dry run complete") +} + func TestGetUpgradeServerSideValue(t *testing.T) { tests := []struct { name string From 209569af01723522d328b79f9f77a99beecc4f86 Mon Sep 17 00:00:00 2001 From: MrJack <36191829+biagiopietro@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:54:56 +0100 Subject: [PATCH 03/36] Revert "feat(plugin): add --version flag to plugin update command" This reverts commit bd37347a0633a38e68b913c45de98f09bbcb8293. Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/plugin/installer/installer.go | 4 +- internal/plugin/installer/vcs_installer.go | 18 +----- .../plugin/installer/vcs_installer_test.go | 63 +------------------ pkg/cmd/plugin_update.go | 10 ++- 4 files changed, 11 insertions(+), 84 deletions(-) diff --git a/internal/plugin/installer/installer.go b/internal/plugin/installer/installer.go index 1f9839054..c7c1a8801 100644 --- a/internal/plugin/installer/installer.go +++ b/internal/plugin/installer/installer.go @@ -160,8 +160,8 @@ func NewForSource(source, version string) (installer Installer, err error) { } // FindSource determines the correct Installer for the given source. -func FindSource(location string, version string) (Installer, error) { - installer, err := existingVCSRepo(location, version) +func FindSource(location string) (Installer, error) { + installer, err := existingVCSRepo(location) if err != nil && err.Error() == "Cannot detect VCS" { slog.Warn("cannot get information about plugin source", "location", location, slog.Any("error", err)) return installer, errors.New("cannot get information about plugin source") diff --git a/internal/plugin/installer/vcs_installer.go b/internal/plugin/installer/vcs_installer.go index 1e858c9ce..3601ec7a8 100644 --- a/internal/plugin/installer/vcs_installer.go +++ b/internal/plugin/installer/vcs_installer.go @@ -38,15 +38,14 @@ type VCSInstaller struct { base } -func existingVCSRepo(location string, version string) (Installer, error) { +func existingVCSRepo(location string) (Installer, error) { repo, err := vcs.NewRepo("", location) if err != nil { return nil, err } i := &VCSInstaller{ - Repo: repo, - Version: version, - base: newBase(repo.Remote()), + Repo: repo, + base: newBase(repo.Remote()), } return i, nil } @@ -105,17 +104,6 @@ func (i *VCSInstaller) Update() error { if err := i.Repo.Update(); err != nil { return err } - - ref, err := i.solveVersion(i.Repo) - if err != nil { - return err - } - if ref != "" { - if err := i.setVersion(i.Repo, ref); err != nil { - return err - } - } - if !isPlugin(i.Repo.LocalPath()) { return ErrMissingMetadata } diff --git a/internal/plugin/installer/vcs_installer_test.go b/internal/plugin/installer/vcs_installer_test.go index 4a6b319cc..d542a0f75 100644 --- a/internal/plugin/installer/vcs_installer_test.go +++ b/internal/plugin/installer/vcs_installer_test.go @@ -48,7 +48,6 @@ func (r *testRepo) UpdateVersion(version string) error { r.current = version return r.err } -func (r *testRepo) IsDirty() bool { return false } func TestVCSInstaller(t *testing.T) { ensure.HelmHome(t) @@ -97,7 +96,7 @@ func TestVCSInstaller(t *testing.T) { } // Testing FindSource method, expect error because plugin code is not a cloned repository - if _, err := FindSource(i.Path(), ""); err == nil { + if _, err := FindSource(i.Path()); err == nil { t.Fatalf("expected error for inability to find plugin source, got none") } else if err.Error() != "cannot get information about plugin source" { t.Fatalf("expected error for inability to find plugin source, got (%v)", err) @@ -159,7 +158,7 @@ func TestVCSInstallerUpdate(t *testing.T) { } // Test FindSource method for positive result - pluginInfo, err := FindSource(i.Path(), "") + pluginInfo, err := FindSource(i.Path()) if err != nil { t.Fatal(err) } @@ -188,61 +187,3 @@ func TestVCSInstallerUpdate(t *testing.T) { } } - -func TestVCSInstallerUpdateWithVersion(t *testing.T) { - ensure.HelmHome(t) - - if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) - } - - source := "https://github.com/adamreese/helm-env" - testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo-v1") - repo := &testRepo{ - local: testRepoPath, - remote: source, - tags: []string{"0.1.0", "0.1.1", "0.2.0"}, - } - - // First install without version - i, err := NewForSource(source, "") - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - vcsInstaller, ok := i.(*VCSInstaller) - if !ok { - t.Fatal("expected a VCSInstaller") - } - vcsInstaller.Repo = repo - - if err := Install(i); err != nil { - t.Fatal(err) - } - - // Now test update with specific version constraint - vcsInstaller.Version = "~0.1.0" - if err := Update(vcsInstaller); err != nil { - t.Fatal(err) - } - if repo.current != "0.1.1" { - t.Fatalf("expected version '0.1.1', got %q", repo.current) - } - - // Test update with different version constraint - vcsInstaller.Version = "0.2.0" - if err := Update(vcsInstaller); err != nil { - t.Fatal(err) - } - if repo.current != "0.2.0" { - t.Fatalf("expected version '0.2.0', got %q", repo.current) - } - - // Test update with non-existent version - vcsInstaller.Version = "0.3.0" - if err := Update(vcsInstaller); err == nil { - t.Fatal("expected error for version does not exist, got none") - } else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", "0.3.0", source) { - t.Fatalf("expected error for version does not exist, got (%v)", err) - } -} diff --git a/pkg/cmd/plugin_update.go b/pkg/cmd/plugin_update.go index c534f93e9..c6d4b8530 100644 --- a/pkg/cmd/plugin_update.go +++ b/pkg/cmd/plugin_update.go @@ -29,8 +29,7 @@ import ( ) type pluginUpdateOptions struct { - names []string - version string + names []string } func newPluginUpdateCmd(out io.Writer) *cobra.Command { @@ -50,7 +49,6 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command { return o.run(out) }, } - cmd.Flags().StringVar(&o.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed") return cmd } @@ -73,7 +71,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error { for _, name := range o.names { if found := findPlugin(plugins, name); found != nil { - if err := updatePlugin(found, o.version); err != nil { + if err := updatePlugin(found); err != nil { errorPlugins = append(errorPlugins, fmt.Errorf("failed to update plugin %s, got error (%v)", name, err)) } else { fmt.Fprintf(out, "Updated plugin: %s\n", name) @@ -88,7 +86,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error { return nil } -func updatePlugin(p plugin.Plugin, version string) error { +func updatePlugin(p plugin.Plugin) error { exactLocation, err := filepath.EvalSymlinks(p.Dir()) if err != nil { return err @@ -98,7 +96,7 @@ func updatePlugin(p plugin.Plugin, version string) error { return err } - i, err := installer.FindSource(absExactLocation, version) + i, err := installer.FindSource(absExactLocation) if err != nil { return err } From 6495074826093bcd79ced58314d697a8b25f1d90 Mon Sep 17 00:00:00 2001 From: MrJack <36191829+biagiopietro@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:56:15 +0100 Subject: [PATCH 04/36] Revert "Revert "feat(plugin): add --version flag to plugin update command"" This reverts commit d1a7fc5fa19e490fb9562c8caf2e39a520b29840. Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/plugin/installer/installer.go | 4 +- internal/plugin/installer/vcs_installer.go | 18 +++++- .../plugin/installer/vcs_installer_test.go | 63 ++++++++++++++++++- pkg/cmd/plugin_update.go | 10 +-- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/internal/plugin/installer/installer.go b/internal/plugin/installer/installer.go index c7c1a8801..1f9839054 100644 --- a/internal/plugin/installer/installer.go +++ b/internal/plugin/installer/installer.go @@ -160,8 +160,8 @@ func NewForSource(source, version string) (installer Installer, err error) { } // FindSource determines the correct Installer for the given source. -func FindSource(location string) (Installer, error) { - installer, err := existingVCSRepo(location) +func FindSource(location string, version string) (Installer, error) { + installer, err := existingVCSRepo(location, version) if err != nil && err.Error() == "Cannot detect VCS" { slog.Warn("cannot get information about plugin source", "location", location, slog.Any("error", err)) return installer, errors.New("cannot get information about plugin source") diff --git a/internal/plugin/installer/vcs_installer.go b/internal/plugin/installer/vcs_installer.go index 3601ec7a8..1e858c9ce 100644 --- a/internal/plugin/installer/vcs_installer.go +++ b/internal/plugin/installer/vcs_installer.go @@ -38,14 +38,15 @@ type VCSInstaller struct { base } -func existingVCSRepo(location string) (Installer, error) { +func existingVCSRepo(location string, version string) (Installer, error) { repo, err := vcs.NewRepo("", location) if err != nil { return nil, err } i := &VCSInstaller{ - Repo: repo, - base: newBase(repo.Remote()), + Repo: repo, + Version: version, + base: newBase(repo.Remote()), } return i, nil } @@ -104,6 +105,17 @@ func (i *VCSInstaller) Update() error { if err := i.Repo.Update(); err != nil { return err } + + ref, err := i.solveVersion(i.Repo) + if err != nil { + return err + } + if ref != "" { + if err := i.setVersion(i.Repo, ref); err != nil { + return err + } + } + if !isPlugin(i.Repo.LocalPath()) { return ErrMissingMetadata } diff --git a/internal/plugin/installer/vcs_installer_test.go b/internal/plugin/installer/vcs_installer_test.go index d542a0f75..4a6b319cc 100644 --- a/internal/plugin/installer/vcs_installer_test.go +++ b/internal/plugin/installer/vcs_installer_test.go @@ -48,6 +48,7 @@ func (r *testRepo) UpdateVersion(version string) error { r.current = version return r.err } +func (r *testRepo) IsDirty() bool { return false } func TestVCSInstaller(t *testing.T) { ensure.HelmHome(t) @@ -96,7 +97,7 @@ func TestVCSInstaller(t *testing.T) { } // Testing FindSource method, expect error because plugin code is not a cloned repository - if _, err := FindSource(i.Path()); err == nil { + if _, err := FindSource(i.Path(), ""); err == nil { t.Fatalf("expected error for inability to find plugin source, got none") } else if err.Error() != "cannot get information about plugin source" { t.Fatalf("expected error for inability to find plugin source, got (%v)", err) @@ -158,7 +159,7 @@ func TestVCSInstallerUpdate(t *testing.T) { } // Test FindSource method for positive result - pluginInfo, err := FindSource(i.Path()) + pluginInfo, err := FindSource(i.Path(), "") if err != nil { t.Fatal(err) } @@ -187,3 +188,61 @@ func TestVCSInstallerUpdate(t *testing.T) { } } + +func TestVCSInstallerUpdateWithVersion(t *testing.T) { + ensure.HelmHome(t) + + if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { + t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) + } + + source := "https://github.com/adamreese/helm-env" + testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo-v1") + repo := &testRepo{ + local: testRepoPath, + remote: source, + tags: []string{"0.1.0", "0.1.1", "0.2.0"}, + } + + // First install without version + i, err := NewForSource(source, "") + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + vcsInstaller, ok := i.(*VCSInstaller) + if !ok { + t.Fatal("expected a VCSInstaller") + } + vcsInstaller.Repo = repo + + if err := Install(i); err != nil { + t.Fatal(err) + } + + // Now test update with specific version constraint + vcsInstaller.Version = "~0.1.0" + if err := Update(vcsInstaller); err != nil { + t.Fatal(err) + } + if repo.current != "0.1.1" { + t.Fatalf("expected version '0.1.1', got %q", repo.current) + } + + // Test update with different version constraint + vcsInstaller.Version = "0.2.0" + if err := Update(vcsInstaller); err != nil { + t.Fatal(err) + } + if repo.current != "0.2.0" { + t.Fatalf("expected version '0.2.0', got %q", repo.current) + } + + // Test update with non-existent version + vcsInstaller.Version = "0.3.0" + if err := Update(vcsInstaller); err == nil { + t.Fatal("expected error for version does not exist, got none") + } else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", "0.3.0", source) { + t.Fatalf("expected error for version does not exist, got (%v)", err) + } +} diff --git a/pkg/cmd/plugin_update.go b/pkg/cmd/plugin_update.go index c6d4b8530..c534f93e9 100644 --- a/pkg/cmd/plugin_update.go +++ b/pkg/cmd/plugin_update.go @@ -29,7 +29,8 @@ import ( ) type pluginUpdateOptions struct { - names []string + names []string + version string } func newPluginUpdateCmd(out io.Writer) *cobra.Command { @@ -49,6 +50,7 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command { return o.run(out) }, } + cmd.Flags().StringVar(&o.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed") return cmd } @@ -71,7 +73,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error { for _, name := range o.names { if found := findPlugin(plugins, name); found != nil { - if err := updatePlugin(found); err != nil { + if err := updatePlugin(found, o.version); err != nil { errorPlugins = append(errorPlugins, fmt.Errorf("failed to update plugin %s, got error (%v)", name, err)) } else { fmt.Fprintf(out, "Updated plugin: %s\n", name) @@ -86,7 +88,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error { return nil } -func updatePlugin(p plugin.Plugin) error { +func updatePlugin(p plugin.Plugin, version string) error { exactLocation, err := filepath.EvalSymlinks(p.Dir()) if err != nil { return err @@ -96,7 +98,7 @@ func updatePlugin(p plugin.Plugin) error { return err } - i, err := installer.FindSource(absExactLocation) + i, err := installer.FindSource(absExactLocation, version) if err != nil { return err } From aa29dd499566fd9239710be8dab38b01cdfad8fd Mon Sep 17 00:00:00 2001 From: MrJack <36191829+biagiopietro@users.noreply.github.com> Date: Tue, 16 Dec 2025 15:56:17 +0100 Subject: [PATCH 05/36] Revert "fix(action): enable server-side validation for dry-run=server" This reverts commit c5b6eea1a0f4a713fae9e431fd44ef532c2148dc. Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/action/install.go | 20 ------------- pkg/action/install_test.go | 40 -------------------------- pkg/action/upgrade.go | 13 --------- pkg/action/upgrade_test.go | 57 -------------------------------------- 4 files changed, 130 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index dc1735110..2f5910284 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -394,26 +394,6 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st // Bail out here if it is a dry run if isDryRun(i.DryRunStrategy) { - // For server-side dry-run, validate resources against the API server - if i.DryRunStrategy == DryRunServer && len(resources) > 0 { - if len(toBeAdopted) == 0 { - _, err = i.cfg.KubeClient.Create( - resources, - kube.ClientCreateOptionServerSideApply(i.ServerSideApply, false), - kube.ClientCreateOptionDryRun(true), - ) - } else { - _, err = i.cfg.KubeClient.Update( - toBeAdopted, - resources, - kube.ClientUpdateOptionServerSideApply(i.ServerSideApply, i.ForceConflicts), - kube.ClientUpdateOptionDryRun(true), - ) - } - if err != nil { - return rel, err - } - } rel.Info.Description = "Dry run complete" return rel, nil } diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 775870e55..9f04f40d4 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -412,46 +412,6 @@ func TestInstallRelease_DryRunClient(t *testing.T) { } } -func TestInstallRelease_DryRunServerValidation(t *testing.T) { - // Test that server-side dry-run actually calls the Kubernetes API for validation - is := assert.New(t) - - // Use a fixture that returns dummy resources so our code path is exercised - config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(false)) - - instAction := NewInstall(config) - instAction.Namespace = "spaced" - instAction.ReleaseName = "test-server-dry-run" - - // Set up the fake client to return an error on Create - expectedErr := errors.New("validation error: unknown field in spec") - config.KubeClient.(*kubefake.FailingKubeClient).CreateError = expectedErr - instAction.DryRunStrategy = DryRunServer - - vals := map[string]interface{}{} - _, err := instAction.Run(buildChart(withSampleTemplates()), vals) - - // The error from the API should be returned - is.Error(err) - is.Contains(err.Error(), "validation error") - - // Reset and test that client-side dry-run does NOT call the API - config2 := actionConfigFixtureWithDummyResources(t, createDummyResourceList(false)) - config2.KubeClient.(*kubefake.FailingKubeClient).CreateError = expectedErr - - instAction2 := NewInstall(config2) - instAction2.Namespace = "spaced" - instAction2.ReleaseName = "test-client-dry-run" - instAction2.DryRunStrategy = DryRunClient - - resi, err := instAction2.Run(buildChart(withSampleTemplates()), vals) - // Client-side dry-run should succeed since it doesn't call the API - is.NoError(err) - res, err := releaserToV1Release(resi) - is.NoError(err) - is.Equal(res.Info.Description, "Dry run complete") -} - func TestInstallRelease_DryRunHiddenSecret(t *testing.T) { is := assert.New(t) instAction := installAction(t) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 0172d8160..57a4a0272 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -392,19 +392,6 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR if isDryRun(u.DryRunStrategy) { u.cfg.Logger().Debug("dry run for release", "name", upgradedRelease.Name) - // For server-side dry-run, validate resources against the API server - if u.DryRunStrategy == DryRunServer { - _, err := u.cfg.KubeClient.Update( - current, - target, - kube.ClientUpdateOptionForceReplace(u.ForceReplace), - kube.ClientUpdateOptionServerSideApply(serverSideApply, u.ForceConflicts), - kube.ClientUpdateOptionDryRun(true), - ) - if err != nil { - return upgradedRelease, err - } - } if len(u.Description) > 0 { upgradedRelease.Info.Description = u.Description } else { diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go index e649f1957..e1eac3f9f 100644 --- a/pkg/action/upgrade_test.go +++ b/pkg/action/upgrade_test.go @@ -635,63 +635,6 @@ func TestUpgradeRelease_DryRun(t *testing.T) { req.Error(err) } -func TestUpgradeRelease_DryRunServerValidation(t *testing.T) { - // Test that server-side dry-run actually calls the Kubernetes API for validation - is := assert.New(t) - req := require.New(t) - - // Use a fixture that returns dummy resources so our code path is exercised - config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(true)) - - upAction := NewUpgrade(config) - upAction.Namespace = "spaced" - - // Create a previous release - rel := releaseStub() - rel.Name = "test-server-dry-run" - rel.Info.Status = common.StatusDeployed - req.NoError(upAction.cfg.Releases.Create(rel)) - - // Set up the fake client to return an error on Update - expectedErr := errors.New("validation error: unknown field in spec") - config.KubeClient.(*kubefake.FailingKubeClient).UpdateError = expectedErr - upAction.DryRunStrategy = DryRunServer - - vals := map[string]interface{}{} - ctx, done := context.WithCancel(t.Context()) - _, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals) - done() - - // The error from the API should be returned - is.Error(err) - is.Contains(err.Error(), "validation error") - - // Reset and test that client-side dry-run does NOT call the API - config2 := actionConfigFixtureWithDummyResources(t, createDummyResourceList(true)) - config2.KubeClient.(*kubefake.FailingKubeClient).UpdateError = expectedErr - - upAction2 := NewUpgrade(config2) - upAction2.Namespace = "spaced" - - // Create a previous release - rel2 := releaseStub() - rel2.Name = "test-client-dry-run" - rel2.Info.Status = common.StatusDeployed - req.NoError(upAction2.cfg.Releases.Create(rel2)) - - upAction2.DryRunStrategy = DryRunClient - - ctx, done = context.WithCancel(t.Context()) - resi, err := upAction2.RunWithContext(ctx, rel2.Name, buildChart(), vals) - done() - - // Client-side dry-run should succeed since it doesn't call the API - is.NoError(err) - res, err := releaserToV1Release(resi) - is.NoError(err) - is.Equal(res.Info.Description, "Dry run complete") -} - func TestGetUpgradeServerSideValue(t *testing.T) { tests := []struct { name string From 27f8e27f0b70a54dabb1823a4b3f696d06eba6b6 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Tue, 18 Nov 2025 13:11:23 +0100 Subject: [PATCH 06/36] fix: preserve vendor suffixes in KubeVersion.GitVersion Helm 3.19.0 introduced a regression where vendor-specific suffixes (e.g., -gke.1245000, -eks-4096722, +) are stripped from .Capabilities.KubeVersion.GitVersion, breaking charts that detect managed Kubernetes platforms. The root cause was using k8sversion.ParseGeneric().String() which intentionally discards vendor suffixes. The fix stores both the full version (with vendor suffix) and a normalized version. String() returns the normalized version for constraint checking (e.g., ">= 1.21.0"), while Version/GitVersion preserve the full string for template access. Fixes #31423 Related to #31063, #31078 Signed-off-by: Benoit Tigeot Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/action/action.go | 2 +- pkg/action/install_test.go | 2 +- pkg/chart/common/capabilities.go | 45 ++++++++++++++++++------- pkg/chart/common/capabilities_test.go | 47 ++++++++++++++++++++------- 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index 9555006be..273c55012 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -230,7 +230,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values common.Values, if ch.Metadata.KubeVersion != "" { if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) { - return hs, b, "", fmt.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) + return hs, b, "", fmt.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.Version) } } diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 9f04f40d4..374b318e1 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -572,7 +572,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) { vals = map[string]interface{}{} _, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals) is.Error(err) - is.Contains(err.Error(), "chart requires kubeVersion") + is.Contains(err.Error(), "chart requires kubeVersion: >=99.0.0 which is incompatible with Kubernetes v1.20.") } func TestInstallRelease_Wait(t *testing.T) { diff --git a/pkg/chart/common/capabilities.go b/pkg/chart/common/capabilities.go index 355c3978a..9953abaf5 100644 --- a/pkg/chart/common/capabilities.go +++ b/pkg/chart/common/capabilities.go @@ -19,6 +19,7 @@ import ( "fmt" "slices" "strconv" + "strings" "k8s.io/client-go/kubernetes/scheme" @@ -39,11 +40,13 @@ var ( DefaultVersionSet = allKnownVersions() // DefaultCapabilities is the default set of capabilities. + version = fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor) DefaultCapabilities = &Capabilities{ KubeVersion: KubeVersion{ - Version: fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor), - Major: k8sVersionMajor, - Minor: k8sVersionMinor, + Version: version, + normalizedVersion: version, + Major: k8sVersionMajor, + Minor: k8sVersionMinor, }, APIVersions: DefaultVersionSet, HelmVersion: helmversion.Get(), @@ -70,15 +73,22 @@ func (capabilities *Capabilities) Copy() *Capabilities { // KubeVersion is the Kubernetes version. type KubeVersion struct { - Version string // Kubernetes version - Major string // Kubernetes major version - Minor string // Kubernetes minor version + Version string // Full version (e.g., v1.33.4-gke.1245000) + normalizedVersion string // Normalized for constraint checking (e.g., v1.33.4) + Major string // Kubernetes major version + Minor string // Kubernetes minor version } -// String implements fmt.Stringer -func (kv *KubeVersion) String() string { return kv.Version } +// String implements fmt.Stringer. +// Returns the normalized version used for constraint checking. +func (kv *KubeVersion) String() string { + if kv.normalizedVersion != "" { + return kv.normalizedVersion + } + return kv.Version +} -// GitVersion returns the Kubernetes version string. +// GitVersion returns the full Kubernetes version string. // // Deprecated: use KubeVersion.Version. func (kv *KubeVersion) GitVersion() string { return kv.Version } @@ -91,10 +101,21 @@ func ParseKubeVersion(version string) (*KubeVersion, error) { if err != nil { return nil, err } + + // Preserve original input (e.g., v1.33.4-gke.1245000) + gitVersion := version + if !strings.HasPrefix(version, "v") { + gitVersion = "v" + version + } + + // Normalize for constraint checking (strips all suffixes) + normalizedVer := "v" + sv.String() + return &KubeVersion{ - Version: "v" + sv.String(), - Major: strconv.FormatUint(uint64(sv.Major()), 10), - Minor: strconv.FormatUint(uint64(sv.Minor()), 10), + Version: gitVersion, + normalizedVersion: normalizedVer, + Major: strconv.FormatUint(uint64(sv.Major()), 10), + Minor: strconv.FormatUint(uint64(sv.Minor()), 10), }, nil } diff --git a/pkg/chart/common/capabilities_test.go b/pkg/chart/common/capabilities_test.go index 716b903aa..f29c2c752 100644 --- a/pkg/chart/common/capabilities_test.go +++ b/pkg/chart/common/capabilities_test.go @@ -83,18 +83,41 @@ func TestParseKubeVersion(t *testing.T) { } } -func TestParseKubeVersionSuffix(t *testing.T) { - kv, err := ParseKubeVersion("v1.28+") - if err != nil { - t.Errorf("Expected v1.28+ to parse successfully") - } - if kv.Version != "v1.28" { - t.Errorf("Expected parsed KubeVersion.Version to be v1.28, got %q", kv.String()) - } - if kv.Major != "1" { - t.Errorf("Expected parsed KubeVersion.Major to be 1, got %q", kv.Major) +func TestParseKubeVersionWithVendorSuffixes(t *testing.T) { + tests := []struct { + name string + input string + wantVer string + wantString string + wantMajor string + wantMinor string + }{ + {"GKE vendor suffix", "v1.33.4-gke.1245000", "v1.33.4-gke.1245000", "v1.33.4", "1", "33"}, + {"GKE without v", "1.30.2-gke.1587003", "v1.30.2-gke.1587003", "v1.30.2", "1", "30"}, + {"EKS trailing +", "v1.28+", "v1.28+", "v1.28", "1", "28"}, + {"EKS + without v", "1.28+", "v1.28+", "v1.28", "1", "28"}, + {"Standard version", "v1.31.0", "v1.31.0", "v1.31.0", "1", "31"}, + {"Standard without v", "1.29.0", "v1.29.0", "v1.29.0", "1", "29"}, } - if kv.Minor != "28" { - t.Errorf("Expected parsed KubeVersion.Minor to be 28, got %q", kv.Minor) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kv, err := ParseKubeVersion(tt.input) + if err != nil { + t.Fatalf("ParseKubeVersion() error = %v", err) + } + if kv.Version != tt.wantVer { + t.Errorf("Version = %q, want %q", kv.Version, tt.wantVer) + } + if kv.String() != tt.wantString { + t.Errorf("String() = %q, want %q", kv.String(), tt.wantString) + } + if kv.Major != tt.wantMajor { + t.Errorf("Major = %q, want %q", kv.Major, tt.wantMajor) + } + if kv.Minor != tt.wantMinor { + t.Errorf("Minor = %q, want %q", kv.Minor, tt.wantMinor) + } + }) } } From e89e351fb84cdbe0d948c44669f21b3c034ba085 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Thu, 20 Nov 2025 20:46:46 +0100 Subject: [PATCH 07/36] fix: prevent reporting fallback on version when none specified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/helm/helm/issues/31548 ``` ❯ helm pull rancher/longhorn-crd --version 106.2.0+up1.8.2 --destination /tmp/ level=WARN msg="unable to find exact version; falling back to closest available version" chart=longhorn-crd requested=106.2.0+up1.8.2 selected=106.2.0+up1.8.1 ❯ bin/helm show chart brigade/brigade apiVersion: v1 appVersion: v1.5.0 dependencies: - condition: kashti.enabled name: kashti repository: https://brigadecore.github.io/charts version: 0.7.0 - condition: brigade-github-app.enabled name: brigade-github-app repository: https://brigadecore.github.io/charts version: 0.8.0 - alias: gw condition: gw.enabled name: brigade-github-oauth repository: https://brigadecore.github.io/charts version: 0.4.0 description: Brigade provides event-driven scripting of Kubernetes pipelines. name: brigade version: 1.10.0 ``` Signed-off-by: Benoit Tigeot Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/repo/v1/index.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index d77d70a7f..7969d64e9 100644 --- a/pkg/repo/v1/index.go +++ b/pkg/repo/v1/index.go @@ -215,7 +215,9 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) { } if constraint.Check(test) { - slog.Warn("unable to find exact version; falling back to closest available version", "chart", name, "requested", version, "selected", ver.Version) + if len(version) != 0 { + slog.Warn("unable to find exact version requested; falling back to closest available version", "chart", name, "requested", version, "selected", ver.Version) + } return ver, nil } } From 06bb9cda55e7c25bdfe72a0df157dc0fba6fa753 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 9 Dec 2025 14:08:34 -0500 Subject: [PATCH 08/36] Update the govulncheck.yml to run on change Signed-off-by: Matt Farina Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- .github/workflows/govulncheck.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index a3088ad75..59fc695a0 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -3,6 +3,7 @@ on: push: paths: - go.sum + - .github/workflows/govulncheck.yml schedule: - cron: "0 0 * * *" From 35ee0930b84356ac62fff27d6b71022daab4e933 Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 9 Dec 2025 13:52:50 -0500 Subject: [PATCH 09/36] Fix govulncheck in CI govulncheck is having trouble checking out the source due to multiple Authorization headers. The fix for this is to not persist the credentials. Signed-off-by: Matt Farina Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- .github/workflows/govulncheck.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 59fc695a0..992283d1a 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1 + with: + persist-credentials: false - name: Add variables to environment file run: cat ".github/env" >> "$GITHUB_ENV" - name: Setup Go From 101913949e990d89f8327d22eb93b4eebb96501e Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Tue, 9 Dec 2025 15:47:56 -0500 Subject: [PATCH 10/36] Run the vulnerability check on PR that change the file Signed-off-by: Matt Farina Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- .github/workflows/govulncheck.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 992283d1a..e8f2560e3 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -4,6 +4,10 @@ on: paths: - go.sum - .github/workflows/govulncheck.yml + pull_request: + paths: + - go.sum + - .github/workflows/govulncheck.yml schedule: - cron: "0 0 * * *" From f3d2786ecaf6c0012da246a65a0dd9e942d7db68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 20:04:53 +0000 Subject: [PATCH 11/36] chore(deps): bump golang.org/x/term from 0.37.0 to 0.38.0 Bumps [golang.org/x/term](https://github.com/golang/term) from 0.37.0 to 0.38.0. - [Commits](https://github.com/golang/term/compare/v0.37.0...v0.38.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-version: 0.38.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 0561bcc44..f437ae8cf 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/tetratelabs/wazero v1.10.1 go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.45.0 - golang.org/x/term v0.37.0 + golang.org/x/term v0.38.0 golang.org/x/text v0.31.0 gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.34.2 @@ -166,7 +166,7 @@ require ( golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/sys v0.39.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect diff --git a/go.sum b/go.sum index cf09e6b3c..3b88d8749 100644 --- a/go.sum +++ b/go.sum @@ -454,8 +454,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -463,8 +463,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= From 550c27dff6f23fa47f518f2b7bc4665f02bdbf2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:28:40 +0000 Subject: [PATCH 12/36] chore(deps): bump github.com/spf13/cobra from 1.10.1 to 1.10.2 Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.10.1 to 1.10.2. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.10.1...v1.10.2) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-version: 1.10.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f437ae8cf..ccbbda0ab 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/opencontainers/image-spec v1.1.1 github.com/rubenv/sql-migrate v1.8.0 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 github.com/tetratelabs/wazero v1.10.1 diff --git a/go.sum b/go.sum index 3b88d8749..363d716b3 100644 --- a/go.sum +++ b/go.sum @@ -304,8 +304,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= From def85941f9f2e2ac3bc49ee6bac4dbcad7aaa899 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:07:22 +0000 Subject: [PATCH 13/36] chore(deps): bump github.com/rubenv/sql-migrate from 1.8.0 to 1.8.1 Bumps [github.com/rubenv/sql-migrate](https://github.com/rubenv/sql-migrate) from 1.8.0 to 1.8.1. - [Commits](https://github.com/rubenv/sql-migrate/compare/v1.8.0...v1.8.1) --- updated-dependencies: - dependency-name: github.com/rubenv/sql-migrate dependency-version: 1.8.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ccbbda0ab..9c540049a 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/moby/term v0.5.2 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 - github.com/rubenv/sql-migrate v1.8.0 + github.com/rubenv/sql-migrate v1.8.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 363d716b3..f04ca40b3 100644 --- a/go.sum +++ b/go.sum @@ -289,8 +289,8 @@ github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0 github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rubenv/sql-migrate v1.8.0 h1:dXnYiJk9k3wetp7GfQbKJcPHjVJL6YK19tKj8t2Ns0o= -github.com/rubenv/sql-migrate v1.8.0/go.mod h1:F2bGFBwCU+pnmbtNYDeKvSuvL6lBVtXDXUUv5t+u1qw= +github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0= +github.com/rubenv/sql-migrate v1.8.1/go.mod h1:BTIKBORjzyxZDS6dzoiw6eAFYJ1iNlGAtjn4LGeVjS8= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= From 9fde0317ed2472a54d4c32f1ff126f78d1910ecb Mon Sep 17 00:00:00 2001 From: Matt Farina Date: Thu, 11 Dec 2025 10:30:22 -0500 Subject: [PATCH 14/36] Use latest patch release of Go in releases GitHub Actions has a cache of tools, like Go, and it does not update this cache when a new version comes out. It can take more than a week for a new version to be available. This change forces the action to check if a newer version is available than is in the cache. Closes #31634 Signed-off-by: Matt Farina Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34b244086..cf8595742 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,6 +31,7 @@ jobs: uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # pin@6.1.0 with: go-version: '${{ env.GOLANG_VERSION }}' + check-latest: true - name: Run unit tests run: make test-coverage - name: Build Helm Binaries From c497bcf9083ae704bac8ce2c61fa7f181bd5cc6d Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Thu, 11 Dec 2025 10:42:04 -0700 Subject: [PATCH 15/36] feat: move TerryHowe triage to maintainers Signed-off-by: Terry Howe Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- OWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index 761cf76a3..17dd3997c 100644 --- a/OWNERS +++ b/OWNERS @@ -7,9 +7,9 @@ maintainers: - sabre1041 - scottrigby - technosophos + - TerryHowe triage: - banjoh - - TerryHowe - yxxhero - zonggen - z4ce From 86821ebd415a7f971695cdc47a22121f97c0b584 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:17:55 +0000 Subject: [PATCH 16/36] chore(deps): bump golang.org/x/text from 0.31.0 to 0.32.0 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.31.0 to 0.32.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.31.0...v0.32.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-version: 0.32.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 9c540049a..06dd6f32d 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.45.0 golang.org/x/term v0.38.0 - golang.org/x/text v0.31.0 + golang.org/x/text v0.32.0 gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.34.2 k8s.io/apiextensions-apiserver v0.34.2 @@ -162,13 +162,13 @@ require ( go.opentelemetry.io/otel/trace v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/mod v0.29.0 // indirect + golang.org/x/mod v0.30.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.18.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/tools v0.39.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/grpc v1.72.1 // indirect diff --git a/go.sum b/go.sum index f04ca40b3..da00cbd1d 100644 --- a/go.sum +++ b/go.sum @@ -404,8 +404,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -433,8 +433,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -472,8 +472,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -484,8 +484,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 0da30de892f790eefc82f0dc744b111e2db90f95 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Mon, 22 May 2023 11:05:32 +0200 Subject: [PATCH 17/36] Make test scripts run without /bin/bash The test scripts hardcoded #!/bin/bash while they are not really requiring bash. Use the more portable #!/usr/bin/env sh instead, so that they use the default shell. Signed-off-by: Tom Wieczorek Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/plugin/installer/http_installer_test.go | 4 ++-- internal/plugin/installer/local_installer_test.go | 2 +- internal/plugin/testdata/plugdir/good/hello-legacy/hello.sh | 4 ++-- internal/plugin/testdata/plugdir/good/hello-v1/hello.sh | 4 ++-- pkg/cmd/testdata/helmhome/helm/plugins/args/args.sh | 4 ++-- pkg/cmd/testdata/helmhome/helm/plugins/exitwith/exitwith.sh | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/plugin/installer/http_installer_test.go b/internal/plugin/installer/http_installer_test.go index be40b1b90..7f7e6cef6 100644 --- a/internal/plugin/installer/http_installer_test.go +++ b/internal/plugin/installer/http_installer_test.go @@ -368,7 +368,7 @@ func TestExtractWithNestedDirectories(t *testing.T) { }{ {"plugin.yaml", "plugin metadata", 0600, tar.TypeReg}, {"bin/", "", 0755, tar.TypeDir}, - {"bin/plugin", "#!/bin/bash\necho plugin", 0755, tar.TypeReg}, + {"bin/plugin", "#!/usr/bin/env sh\necho plugin", 0755, tar.TypeReg}, {"docs/", "", 0755, tar.TypeDir}, {"docs/README.md", "readme content", 0644, tar.TypeReg}, {"docs/examples/", "", 0755, tar.TypeDir}, @@ -531,7 +531,7 @@ func TestExtractPluginInSubdirectory(t *testing.T) { {"my-plugin/", "", 0755, tar.TypeDir}, {"my-plugin/plugin.yaml", "name: my-plugin\nversion: 1.0.0\nusage: test\ndescription: test plugin\ncommand: $HELM_PLUGIN_DIR/bin/my-plugin", 0644, tar.TypeReg}, {"my-plugin/bin/", "", 0755, tar.TypeDir}, - {"my-plugin/bin/my-plugin", "#!/bin/bash\necho test", 0755, tar.TypeReg}, + {"my-plugin/bin/my-plugin", "#!/usr/bin/env sh\necho test", 0755, tar.TypeReg}, } for _, file := range files { diff --git a/internal/plugin/installer/local_installer_test.go b/internal/plugin/installer/local_installer_test.go index 2decb695f..3ee8ab6d0 100644 --- a/internal/plugin/installer/local_installer_test.go +++ b/internal/plugin/installer/local_installer_test.go @@ -87,7 +87,7 @@ func TestLocalInstallerTarball(t *testing.T) { Mode int64 }{ {"test-plugin/plugin.yaml", "name: test-plugin\napiVersion: v1\ntype: cli/v1\nruntime: subprocess\nversion: 1.0.0\nconfig:\n shortHelp: test\n longHelp: test\nruntimeConfig:\n platformCommand:\n - command: echo", 0644}, - {"test-plugin/bin/test-plugin", "#!/bin/bash\necho test", 0755}, + {"test-plugin/bin/test-plugin", "#!/usr/bin/env sh\necho test", 0755}, } for _, file := range files { diff --git a/internal/plugin/testdata/plugdir/good/hello-legacy/hello.sh b/internal/plugin/testdata/plugdir/good/hello-legacy/hello.sh index dcfd58876..4f20796ef 100755 --- a/internal/plugin/testdata/plugdir/good/hello-legacy/hello.sh +++ b/internal/plugin/testdata/plugdir/good/hello-legacy/hello.sh @@ -1,9 +1,9 @@ -#!/bin/bash +#!/usr/bin/env sh echo "Hello from a Helm plugin" echo "PARAMS" -echo $* +echo "$@" $HELM_BIN ls --all diff --git a/internal/plugin/testdata/plugdir/good/hello-v1/hello.sh b/internal/plugin/testdata/plugdir/good/hello-v1/hello.sh index dcfd58876..4f20796ef 100755 --- a/internal/plugin/testdata/plugdir/good/hello-v1/hello.sh +++ b/internal/plugin/testdata/plugdir/good/hello-v1/hello.sh @@ -1,9 +1,9 @@ -#!/bin/bash +#!/usr/bin/env sh echo "Hello from a Helm plugin" echo "PARAMS" -echo $* +echo "$@" $HELM_BIN ls --all diff --git a/pkg/cmd/testdata/helmhome/helm/plugins/args/args.sh b/pkg/cmd/testdata/helmhome/helm/plugins/args/args.sh index 678b4eff5..6c62be8b9 100755 --- a/pkg/cmd/testdata/helmhome/helm/plugins/args/args.sh +++ b/pkg/cmd/testdata/helmhome/helm/plugins/args/args.sh @@ -1,2 +1,2 @@ -#!/bin/bash -echo $* +#!/usr/bin/env sh +echo "$@" diff --git a/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/exitwith.sh b/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/exitwith.sh index ec8469657..9cf68da68 100755 --- a/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/exitwith.sh +++ b/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/exitwith.sh @@ -1,2 +1,2 @@ -#!/bin/bash -exit $* +#!/usr/bin/env sh +exit "$1" From d45eb6ea081c29dd423fb6ac1c0adeac2badae67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:54:28 +0000 Subject: [PATCH 18/36] chore(deps): bump golang.org/x/crypto from 0.45.0 to 0.46.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.45.0 to 0.46.0. - [Commits](https://github.com/golang/crypto/compare/v0.45.0...v0.46.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.46.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 06dd6f32d..b344fa516 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/tetratelabs/wazero v1.10.1 go.yaml.in/yaml/v3 v3.0.4 - golang.org/x/crypto v0.45.0 + golang.org/x/crypto v0.46.0 golang.org/x/term v0.38.0 golang.org/x/text v0.32.0 gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index da00cbd1d..4de45a3da 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= From e79d37e738f910d3cf3c8ef5088c847f067ff0e2 Mon Sep 17 00:00:00 2001 From: Mads Jensen Date: Mon, 8 Dec 2025 19:52:01 +0100 Subject: [PATCH 19/36] Enable the sloglint linter Signed-off-by: Mads Jensen Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- .golangci.yml | 1 + internal/chart/v3/util/dependencies.go | 7 +++- internal/plugin/installer/installer.go | 10 ++++-- pkg/action/action.go | 7 +++- pkg/action/upgrade.go | 6 +++- pkg/chart/v2/util/dependencies.go | 6 +++- pkg/cmd/search_repo.go | 2 +- pkg/engine/lookup_func.go | 12 +++++-- pkg/ignore/rules.go | 10 ++++-- pkg/kube/client.go | 46 ++++++++++++++++++++++---- pkg/kube/wait.go | 17 +++++++--- pkg/storage/driver/cfgmaps.go | 14 +++++--- pkg/storage/driver/secrets.go | 11 ++++-- pkg/storage/driver/sql.go | 45 +++++++++++++++++-------- pkg/storage/storage.go | 2 +- 15 files changed, 152 insertions(+), 44 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 236dadf7b..baa3d96e7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -26,6 +26,7 @@ linters: - misspell - nakedret - revive + - sloglint - staticcheck - thelper - unused diff --git a/internal/chart/v3/util/dependencies.go b/internal/chart/v3/util/dependencies.go index cd7a8b78c..4ef9e6961 100644 --- a/internal/chart/v3/util/dependencies.go +++ b/internal/chart/v3/util/dependencies.go @@ -280,7 +280,12 @@ func processImportValues(c *chart.Chart, merge bool) error { // get child table vv, err := cvals.Table(r.Name + "." + child) if err != nil { - slog.Warn("ImportValues missing table from chart", "chart", r.Name, slog.Any("error", err)) + slog.Warn( + "ImportValues missing table from chart", + slog.String("chart", "chart"), + slog.String("name", r.Name), + slog.Any("error", err), + ) continue } // create value map from child to be merged into parent diff --git a/internal/plugin/installer/installer.go b/internal/plugin/installer/installer.go index 1f9839054..3522a11e9 100644 --- a/internal/plugin/installer/installer.go +++ b/internal/plugin/installer/installer.go @@ -80,7 +80,7 @@ func InstallWithOptions(i Installer, opts Options) (*VerificationResult, error) return nil, err } if _, pathErr := os.Stat(i.Path()); !os.IsNotExist(pathErr) { - slog.Warn("plugin already exists", "path", i.Path(), slog.Any("error", pathErr)) + slog.Warn("plugin already exists", slog.String("path", i.Path()), slog.Any("error", pathErr)) return nil, errors.New("plugin already exists") } @@ -132,7 +132,7 @@ func InstallWithOptions(i Installer, opts Options) (*VerificationResult, error) // Update updates a plugin. func Update(i Installer) error { if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) { - slog.Warn("plugin does not exist", "path", i.Path(), slog.Any("error", pathErr)) + slog.Warn("plugin does not exist", slog.String("path", i.Path()), slog.Any("error", pathErr)) return errors.New("plugin does not exist") } return i.Update() @@ -163,7 +163,11 @@ func NewForSource(source, version string) (installer Installer, err error) { func FindSource(location string, version string) (Installer, error) { installer, err := existingVCSRepo(location, version) if err != nil && err.Error() == "Cannot detect VCS" { - slog.Warn("cannot get information about plugin source", "location", location, slog.Any("error", err)) + slog.Warn( + "cannot get information about plugin source", + slog.String("location", location), + slog.Any("error", err), + ) return installer, errors.New("cannot get information about plugin source") } return installer, err diff --git a/pkg/action/action.go b/pkg/action/action.go index 273c55012..c2a27940f 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -502,7 +502,12 @@ func GetVersionSet(client discovery.ServerResourcesInterface) (common.VersionSet // recordRelease with an update operation in case reuse has been set. func (cfg *Configuration) recordRelease(r *release.Release) { if err := cfg.Releases.Update(r); err != nil { - cfg.Logger().Warn("failed to update release", "name", r.Name, "revision", r.Version, slog.Any("error", err)) + cfg.Logger().Warn( + "failed to update release", + slog.String("name", r.Name), + slog.Int("revision", r.Version), + slog.Any("error", err), + ) } } diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 57a4a0272..13d28fd4d 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -515,7 +515,11 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, err error) (*release.Release, error) { msg := fmt.Sprintf("Upgrade %q failed: %s", rel.Name, err) - u.cfg.Logger().Warn("upgrade failed", "name", rel.Name, slog.Any("error", err)) + u.cfg.Logger().Warn( + "upgrade failed", + slog.String("name", rel.Name), + slog.Any("error", err), + ) rel.Info.Status = rcommon.StatusFailed rel.Info.Description = msg diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index 294b782f8..c7bb6621e 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -280,7 +280,11 @@ func processImportValues(c *chart.Chart, merge bool) error { // get child table vv, err := cvals.Table(r.Name + "." + child) if err != nil { - slog.Warn("ImportValues missing table from chart", "chart", r.Name, slog.Any("error", err)) + slog.Warn( + "ImportValues missing table from chart", + slog.String("chart", r.Name), + slog.Any("error", err), + ) continue } // create value map from child to be merged into parent diff --git a/pkg/cmd/search_repo.go b/pkg/cmd/search_repo.go index 07345a48f..d54e85d91 100644 --- a/pkg/cmd/search_repo.go +++ b/pkg/cmd/search_repo.go @@ -190,7 +190,7 @@ func (o *searchRepoOptions) buildIndex() (*search.Index, error) { f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) ind, err := repo.LoadIndexFile(f) if err != nil { - slog.Warn("repo is corrupt or missing", "repo", n, slog.Any("error", err)) + slog.Warn("repo is corrupt or missing", slog.String("repo", n), slog.Any("error", err)) continue } diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go index 18ed2b63b..c6ad8d252 100644 --- a/pkg/engine/lookup_func.go +++ b/pkg/engine/lookup_func.go @@ -98,7 +98,11 @@ func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) gvk := schema.FromAPIVersionAndKind(apiversion, kind) apiRes, err := getAPIResourceForGVK(gvk, config) if err != nil { - slog.Error("unable to get apiresource", "groupVersionKind", gvk.String(), slog.Any("error", err)) + slog.Error( + "unable to get apiresource", + slog.String("groupVersionKind", gvk.String()), + slog.Any("error", err), + ) return nil, false, fmt.Errorf("unable to get apiresource from unstructured: %s: %w", gvk.String(), err) } gvr := schema.GroupVersionResource{ @@ -124,7 +128,11 @@ func getAPIResourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (met } resList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) if err != nil { - slog.Error("unable to retrieve resource list", "GroupVersion", gvk.GroupVersion().String(), slog.Any("error", err)) + slog.Error( + "unable to retrieve resource list", + slog.String("GroupVersion", gvk.GroupVersion().String()), + slog.Any("error", err), + ) return res, err } for _, resource := range resList.APIResources { diff --git a/pkg/ignore/rules.go b/pkg/ignore/rules.go index 3511c2d40..a8160da2a 100644 --- a/pkg/ignore/rules.go +++ b/pkg/ignore/rules.go @@ -176,7 +176,7 @@ func (r *Rules) parseRule(rule string) error { rule = after ok, err := filepath.Match(rule, n) if err != nil { - slog.Error("failed to compile", "rule", rule, slog.Any("error", err)) + slog.Error("failed to compile", slog.String("rule", rule), slog.Any("error", err)) return false } return ok @@ -186,7 +186,11 @@ func (r *Rules) parseRule(rule string) error { p.match = func(n string, _ os.FileInfo) bool { ok, err := filepath.Match(rule, n) if err != nil { - slog.Error("failed to compile", "rule", rule, slog.Any("error", err)) + slog.Error( + "failed to compile", + slog.String("rule", rule), + slog.Any("error", err), + ) return false } return ok @@ -198,7 +202,7 @@ func (r *Rules) parseRule(rule string) error { n = filepath.Base(n) ok, err := filepath.Match(rule, n) if err != nil { - slog.Error("failed to compile", "rule", rule, slog.Any("error", err)) + slog.Error("failed to compile", slog.String("rule", rule), slog.Any("error", err)) return false } return ok diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 47a226000..be98dac31 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -563,7 +563,12 @@ func (c *Client) update(originals, targets ResourceList, createApplyFunc CreateA } kind := target.Mapping.GroupVersionKind.Kind - c.Logger().Debug("created a new resource", "namespace", target.Namespace, "name", target.Name, "kind", kind) + c.Logger().Debug( + "created a new resource", + slog.String("namespace", target.Namespace), + slog.String("name", target.Name), + slog.String("kind", kind), + ) return nil } @@ -594,19 +599,37 @@ func (c *Client) update(originals, targets ResourceList, createApplyFunc CreateA c.Logger().Debug("deleting resource", "namespace", info.Namespace, "name", info.Name, "kind", info.Mapping.GroupVersionKind.Kind) if err := info.Get(); err != nil { - c.Logger().Debug("unable to get object", "namespace", info.Namespace, "name", info.Name, "kind", info.Mapping.GroupVersionKind.Kind, slog.Any("error", err)) + c.Logger().Debug( + "unable to get object", + slog.String("namespace", info.Namespace), + slog.String("name", info.Name), + slog.String("kind", info.Mapping.GroupVersionKind.Kind), + slog.Any("error", err), + ) continue } annotations, err := metadataAccessor.Annotations(info.Object) if err != nil { - c.Logger().Debug("unable to get annotations", "namespace", info.Namespace, "name", info.Name, "kind", info.Mapping.GroupVersionKind.Kind, slog.Any("error", err)) + c.Logger().Debug( + "unable to get annotations", + slog.String("namespace", info.Namespace), + slog.String("name", info.Name), + slog.String("kind", info.Mapping.GroupVersionKind.Kind), + slog.Any("error", err), + ) } if annotations != nil && annotations[ResourcePolicyAnno] == KeepPolicy { c.Logger().Debug("skipping delete due to annotation", "namespace", info.Namespace, "name", info.Name, "kind", info.Mapping.GroupVersionKind.Kind, "annotation", ResourcePolicyAnno, "value", KeepPolicy) continue } if err := deleteResource(info, metav1.DeletePropagationBackground); err != nil { - c.Logger().Debug("failed to delete resource", "namespace", info.Namespace, "name", info.Name, "kind", info.Mapping.GroupVersionKind.Kind, slog.Any("error", err)) + c.Logger().Debug( + "failed to delete resource", + slog.String("namespace", info.Namespace), + slog.String("name", info.Name), + slog.String("kind", info.Mapping.GroupVersionKind.Kind), + slog.Any("error", err), + ) if !apierrors.IsNotFound(err) { updateErrors = append(updateErrors, fmt.Errorf("failed to delete resource %s: %w", info.Name, err)) } @@ -760,7 +783,13 @@ func (c *Client) Update(originals, targets ResourceList, options ...ClientUpdate slog.String("fieldValidationDirective", string(updateOptions.fieldValidationDirective))) return func(original, target *resource.Info) error { if err := replaceResource(target, updateOptions.fieldValidationDirective); err != nil { - c.Logger().Debug("error replacing the resource", "namespace", target.Namespace, "name", target.Name, "kind", target.Mapping.GroupVersionKind.Kind, slog.Any("error", err)) + c.Logger().With( + slog.String("namespace", target.Namespace), + slog.String("name", target.Name), + slog.String("gvk", target.Mapping.GroupVersionKind.String()), + ).Debug( + "error replacing the resource", slog.Any("error", err), + ) return err } @@ -829,7 +858,12 @@ func (c *Client) Delete(resources ResourceList, policy metav1.DeletionPropagatio err := deleteResource(target, policy) if err == nil || apierrors.IsNotFound(err) { if err != nil { - c.Logger().Debug("ignoring delete failure", "namespace", target.Namespace, "name", target.Name, "kind", target.Mapping.GroupVersionKind.Kind, slog.Any("error", err)) + c.Logger().Debug( + "ignoring delete failure", + slog.String("namespace", target.Namespace), + slog.String("name", target.Name), + slog.String("kind", target.Mapping.GroupVersionKind.Kind), + slog.Any("error", err)) } mtx.Lock() defer mtx.Unlock() diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index f776ae471..354915b29 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -102,11 +102,20 @@ func (hw *legacyWaiter) isRetryableError(err error, resource *resource.Info) boo if err == nil { return false } - slog.Debug("error received when checking resource status", "resource", resource.Name, slog.Any("error", err)) + slog.Debug( + "error received when checking resource status", + slog.String("resource", resource.Name), + slog.Any("error", err), + ) if ev, ok := err.(*apierrors.StatusError); ok { statusCode := ev.Status().Code retryable := hw.isRetryableHTTPStatusCode(statusCode) - slog.Debug("status code received", "resource", resource.Name, "statusCode", statusCode, "retryable", retryable) + slog.Debug( + "status code received", + slog.String("resource", resource.Name), + slog.Int("statusCode", int(statusCode)), + slog.Bool("retryable", retryable), + ) return retryable } slog.Debug("retryable error assumed", "resource", resource.Name) @@ -137,9 +146,9 @@ func (hw *legacyWaiter) WaitForDelete(deleted ResourceList, timeout time.Duratio elapsed := time.Since(startTime).Round(time.Second) if err != nil { - slog.Debug("wait for resources failed", "elapsed", elapsed, slog.Any("error", err)) + slog.Debug("wait for resources failed", slog.Duration("elapsed", elapsed), slog.Any("error", err)) } else { - slog.Debug("wait for resources succeeded", "elapsed", elapsed) + slog.Debug("wait for resources succeeded", slog.Duration("elapsed", elapsed)) } return err diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 5af432d8a..f82ade5e9 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -75,13 +75,13 @@ func (cfgmaps *ConfigMaps) Get(key string) (release.Releaser, error) { return nil, ErrReleaseNotFound } - cfgmaps.Logger().Debug("failed to get release", "key", key, slog.Any("error", err)) + cfgmaps.Logger().Debug("failed to get release", slog.String("key", key), slog.Any("error", err)) return nil, err } // found the configmap, decode the base64 data string r, err := decodeRelease(obj.Data["release"]) if err != nil { - cfgmaps.Logger().Debug("failed to decode data", "key", key, slog.Any("error", err)) + cfgmaps.Logger().Debug("failed to decode data", slog.String("key", key), slog.Any("error", err)) return nil, err } r.Labels = filterSystemLabels(obj.Labels) @@ -109,7 +109,7 @@ func (cfgmaps *ConfigMaps) List(filter func(release.Releaser) bool) ([]release.R for _, item := range list.Items { rls, err := decodeRelease(item.Data["release"]) if err != nil { - cfgmaps.Logger().Debug("failed to decode release", "item", item, slog.Any("error", err)) + cfgmaps.Logger().Debug("failed to decode release", slog.Any("item", item), slog.Any("error", err)) continue } @@ -181,7 +181,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls release.Releaser) error { // create a new configmap to hold the release obj, err := newConfigMapsObject(key, rel, lbs) if err != nil { - cfgmaps.Logger().Debug("failed to encode release", "name", rac.Name(), slog.Any("error", err)) + cfgmaps.Logger().Debug("failed to encode release", slog.String("name", rac.Name()), slog.Any("error", err)) return err } // push the configmap object out into the kubiverse @@ -214,7 +214,11 @@ func (cfgmaps *ConfigMaps) Update(key string, rel release.Releaser) error { // create a new configmap object to hold the release obj, err := newConfigMapsObject(key, rls, lbs) if err != nil { - cfgmaps.Logger().Debug("failed to encode release", "name", rls.Name, slog.Any("error", err)) + cfgmaps.Logger().Debug( + "failed to encode release", + slog.String("name", rls.Name), + slog.Any("error", err), + ) return err } // push the configmap object out into the kubiverse diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 85f3497e7..a73f3cf05 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -103,7 +103,10 @@ func (secrets *Secrets) List(filter func(release.Releaser) bool) ([]release.Rele for _, item := range list.Items { rls, err := decodeRelease(string(item.Data["release"])) if err != nil { - secrets.Logger().Debug("list failed to decode release", "key", item.Name, slog.Any("error", err)) + secrets.Logger().Debug( + "list failed to decode release", slog.String("key", item.Name), + slog.Any("error", err), + ) continue } @@ -142,7 +145,11 @@ func (secrets *Secrets) Query(labels map[string]string) ([]release.Releaser, err for _, item := range list.Items { rls, err := decodeRelease(string(item.Data["release"])) if err != nil { - secrets.Logger().Debug("failed to decode release", "key", item.Name, slog.Any("error", err)) + secrets.Logger().Debug( + "failed to decode release", + slog.String("key", item.Name), + slog.Any("error", err), + ) continue } rls.Labels = item.Labels diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index b6ea3916d..21d9f6679 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -319,18 +319,23 @@ func (s *SQL) Get(key string) (release.Releaser, error) { // Get will return an error if the result is empty if err := s.db.Get(&record, query, args...); err != nil { - s.Logger().Debug("got SQL error when getting release", "key", key, slog.Any("error", err)) + s.Logger().Debug("got SQL error when getting release", slog.String("key", key), slog.Any("error", err)) return nil, ErrReleaseNotFound } release, err := decodeRelease(record.Body) if err != nil { - s.Logger().Debug("failed to decode data", "key", key, slog.Any("error", err)) + s.Logger().Debug("failed to decode data", slog.String("key", key), slog.Any("error", err)) return nil, err } if release.Labels, err = s.getReleaseCustomLabels(key, s.namespace); err != nil { - s.Logger().Debug("failed to get release custom labels", "namespace", s.namespace, "key", key, slog.Any("error", err)) + s.Logger().Debug( + "failed to get release custom labels", + slog.String("namespace", s.namespace), + slog.String("key", key), + slog.Any("error", err), + ) return nil, err } @@ -365,12 +370,17 @@ func (s *SQL) List(filter func(release.Releaser) bool) ([]release.Releaser, erro for _, record := range records { release, err := decodeRelease(record.Body) if err != nil { - s.Logger().Debug("failed to decode release", "record", record, slog.Any("error", err)) + s.Logger().Debug("failed to decode release", slog.Any("record", record), slog.Any("error", err)) continue } if release.Labels, err = s.getReleaseCustomLabels(record.Key, record.Namespace); err != nil { - s.Logger().Debug("failed to get release custom labels", "namespace", record.Namespace, "key", record.Key, slog.Any("error", err)) + s.Logger().Debug( + "failed to get release custom labels", + slog.String("namespace", record.Namespace), + slog.String("key", record.Key), + slog.Any("error", err), + ) return nil, err } maps.Copy(release.Labels, getReleaseSystemLabels(release)) @@ -429,12 +439,17 @@ func (s *SQL) Query(labels map[string]string) ([]release.Releaser, error) { for _, record := range records { release, err := decodeRelease(record.Body) if err != nil { - s.Logger().Debug("failed to decode release", "record", record, slog.Any("error", err)) + s.Logger().Debug("failed to decode release", slog.Any("record", record), slog.Any("error", err)) continue } if release.Labels, err = s.getReleaseCustomLabels(record.Key, record.Namespace); err != nil { - s.Logger().Debug("failed to get release custom labels", "namespace", record.Namespace, "key", record.Key, slog.Any("error", err)) + s.Logger().Debug( + "failed to get release custom labels", + slog.String("namespace", record.Namespace), + slog.String("key", record.Key), + slog.Any("error", err), + ) return nil, err } @@ -518,11 +533,11 @@ func (s *SQL) Create(key string, rel release.Releaser) error { var record SQLReleaseWrapper if err := transaction.Get(&record, selectQuery, args...); err == nil { - s.Logger().Debug("release already exists", "key", key) + s.Logger().Debug("release already exists", slog.String("key", key)) return ErrReleaseExists } - s.Logger().Debug("failed to store release in SQL database", "key", key, slog.Any("error", err)) + s.Logger().Debug("failed to store release in SQL database", slog.String("key", key), slog.Any("error", err)) return err } @@ -596,7 +611,7 @@ func (s *SQL) Update(key string, rel release.Releaser) error { } if _, err := s.db.Exec(query, args...); err != nil { - s.Logger().Debug("failed to update release in SQL database", "key", key, slog.Any("error", err)) + s.Logger().Debug("failed to update release in SQL database", slog.String("key", key), slog.Any("error", err)) return err } @@ -625,13 +640,13 @@ func (s *SQL) Delete(key string) (release.Releaser, error) { var record SQLReleaseWrapper err = transaction.Get(&record, selectQuery, args...) if err != nil { - s.Logger().Debug("release not found", "key", key, slog.Any("error", err)) + s.Logger().Debug("release not found", slog.String("key", key), slog.Any("error", err)) return nil, ErrReleaseNotFound } release, err := decodeRelease(record.Body) if err != nil { - s.Logger().Debug("failed to decode release", "key", key, slog.Any("error", err)) + s.Logger().Debug("failed to decode release", slog.String("key", key), slog.Any("error", err)) transaction.Rollback() return nil, err } @@ -654,7 +669,11 @@ func (s *SQL) Delete(key string) (release.Releaser, error) { } if release.Labels, err = s.getReleaseCustomLabels(key, s.namespace); err != nil { - s.Logger().Debug("failed to get release custom labels", "namespace", s.namespace, "key", key, slog.Any("error", err)) + s.Logger().Debug( + "failed to get release custom labels", + slog.String("namespace", s.namespace), + slog.String("key", key), + slog.Any("error", err)) return nil, err } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 07dc12c7b..d6c41635b 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -293,7 +293,7 @@ func (s *Storage) deleteReleaseVersion(name string, version int) error { key := makeKey(name, version) _, err := s.Delete(name, version) if err != nil { - s.Logger().Debug("error pruning release", "key", key, slog.Any("error", err)) + s.Logger().Debug("error pruning release", slog.String("key", key), slog.Any("error", err)) return err } return nil From 3868cbb1bdbdd7d1c2711d0367f63893cb6eee96 Mon Sep 17 00:00:00 2001 From: wangjingcun Date: Fri, 12 Dec 2025 12:01:35 +0800 Subject: [PATCH 20/36] chore: fix some comments to improve readability Signed-off-by: wangjingcun Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/chart/v3/lint/rules/deprecations.go | 2 +- internal/chart/v3/loader/load.go | 2 +- pkg/action/install.go | 4 ++-- pkg/chart/v2/lint/rules/deprecations.go | 2 +- pkg/chart/v2/loader/load.go | 2 +- pkg/kube/client.go | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/chart/v3/lint/rules/deprecations.go b/internal/chart/v3/lint/rules/deprecations.go index 6f86bdbbd..b088dda2b 100644 --- a/internal/chart/v3/lint/rules/deprecations.go +++ b/internal/chart/v3/lint/rules/deprecations.go @@ -36,7 +36,7 @@ var ( k8sVersionMinor = "20" ) -// deprecatedAPIError indicates than an API is deprecated in Kubernetes +// deprecatedAPIError indicates that an API is deprecated in Kubernetes type deprecatedAPIError struct { Deprecated string Message string diff --git a/internal/chart/v3/loader/load.go b/internal/chart/v3/loader/load.go index 1c5b4cad1..9d657fb95 100644 --- a/internal/chart/v3/loader/load.go +++ b/internal/chart/v3/loader/load.go @@ -181,7 +181,7 @@ func LoadFiles(files []*archive.BufferedFile) (*chart.Chart, error) { // LoadValues loads values from a reader. // // The reader is expected to contain one or more YAML documents, the values of which are merged. -// And the values can be either a chart's default values or a user-supplied values. +// And the values can be either a chart's default values or user-supplied values. func LoadValues(data io.Reader) (map[string]interface{}, error) { values := map[string]interface{}{} reader := utilyaml.NewYAMLReader(bufio.NewReader(data)) diff --git a/pkg/action/install.go b/pkg/action/install.go index 2f5910284..ecf3ea340 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -62,8 +62,8 @@ import ( "helm.sh/helm/v4/pkg/storage/driver" ) -// notesFileSuffix that we want to treat special. It goes through the templating engine -// but it's not a yaml file (resource) hence can't have hooks, etc. And the user actually +// notesFileSuffix that we want to treat specially. It goes through the templating engine +// but it's not a YAML file (resource) hence can't have hooks, etc. And the user actually // wants to see this file after rendering in the status command. However, it must be a suffix // since there can be filepath in front of it. const notesFileSuffix = "NOTES.txt" diff --git a/pkg/chart/v2/lint/rules/deprecations.go b/pkg/chart/v2/lint/rules/deprecations.go index 6eba316bc..34bd361bf 100644 --- a/pkg/chart/v2/lint/rules/deprecations.go +++ b/pkg/chart/v2/lint/rules/deprecations.go @@ -36,7 +36,7 @@ var ( k8sVersionMinor = "20" ) -// deprecatedAPIError indicates than an API is deprecated in Kubernetes +// deprecatedAPIError indicates that an API is deprecated in Kubernetes type deprecatedAPIError struct { Deprecated string Message string diff --git a/pkg/chart/v2/loader/load.go b/pkg/chart/v2/loader/load.go index ba3a9b6bc..6fdaa4f7d 100644 --- a/pkg/chart/v2/loader/load.go +++ b/pkg/chart/v2/loader/load.go @@ -208,7 +208,7 @@ func LoadFiles(files []*archive.BufferedFile) (*chart.Chart, error) { // LoadValues loads values from a reader. // // The reader is expected to contain one or more YAML documents, the values of which are merged. -// And the values can be either a chart's default values or a user-supplied values. +// And the values can be either a chart's default values or user-supplied values. func LoadValues(data io.Reader) (map[string]interface{}, error) { values := map[string]interface{}{} reader := utilyaml.NewYAMLReader(bufio.NewReader(data)) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index be98dac31..6e09fceac 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -271,7 +271,7 @@ func ClientCreateOptionDryRun(dryRun bool) ClientCreateOption { } } -// ClientCreateOptionFieldValidationDirective specifies show API operations validate object's schema +// ClientCreateOptionFieldValidationDirective specifies how API operations validate object's schema // - For client-side apply: this is ignored // - For server-side apply: the directive is sent to the server to perform the validation // @@ -704,7 +704,7 @@ func ClientUpdateOptionDryRun(dryRun bool) ClientUpdateOption { } } -// ClientUpdateOptionFieldValidationDirective specifies show API operations validate object's schema +// ClientUpdateOptionFieldValidationDirective specifies how API operations validate object's schema // - For client-side apply: this is ignored // - For server-side apply: the directive is sent to the server to perform the validation // From e6f8de3970f3375d20ab516c55f4ed8d3f9a0840 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:54:38 +0000 Subject: [PATCH 21/36] chore(deps): bump the k8s-io group with 7 updates Bumps the k8s-io group with 7 updates: | Package | From | To | | --- | --- | --- | | [k8s.io/api](https://github.com/kubernetes/api) | `0.34.2` | `0.34.3` | | [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) | `0.34.2` | `0.34.3` | | [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) | `0.34.2` | `0.34.3` | | [k8s.io/apiserver](https://github.com/kubernetes/apiserver) | `0.34.2` | `0.34.3` | | [k8s.io/cli-runtime](https://github.com/kubernetes/cli-runtime) | `0.34.2` | `0.34.3` | | [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.34.2` | `0.34.3` | | [k8s.io/kubectl](https://github.com/kubernetes/kubectl) | `0.34.2` | `0.34.3` | Updates `k8s.io/api` from 0.34.2 to 0.34.3 - [Commits](https://github.com/kubernetes/api/compare/v0.34.2...v0.34.3) Updates `k8s.io/apiextensions-apiserver` from 0.34.2 to 0.34.3 - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.34.2...v0.34.3) Updates `k8s.io/apimachinery` from 0.34.2 to 0.34.3 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.34.2...v0.34.3) Updates `k8s.io/apiserver` from 0.34.2 to 0.34.3 - [Commits](https://github.com/kubernetes/apiserver/compare/v0.34.2...v0.34.3) Updates `k8s.io/cli-runtime` from 0.34.2 to 0.34.3 - [Commits](https://github.com/kubernetes/cli-runtime/compare/v0.34.2...v0.34.3) Updates `k8s.io/client-go` from 0.34.2 to 0.34.3 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.34.2...v0.34.3) Updates `k8s.io/kubectl` from 0.34.2 to 0.34.3 - [Commits](https://github.com/kubernetes/kubectl/compare/v0.34.2...v0.34.3) --- updated-dependencies: - dependency-name: k8s.io/api dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/apiextensions-apiserver dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/apimachinery dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/apiserver dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/cli-runtime dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/client-go dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/kubectl dependency-version: 0.34.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index b344fa516..3316bd925 100644 --- a/go.mod +++ b/go.mod @@ -39,14 +39,14 @@ require ( golang.org/x/term v0.38.0 golang.org/x/text v0.32.0 gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.34.2 - k8s.io/apiextensions-apiserver v0.34.2 - k8s.io/apimachinery v0.34.2 - k8s.io/apiserver v0.34.2 - k8s.io/cli-runtime v0.34.2 - k8s.io/client-go v0.34.2 + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/apiserver v0.34.3 + k8s.io/cli-runtime v0.34.3 + k8s.io/client-go v0.34.3 k8s.io/klog/v2 v2.130.1 - k8s.io/kubectl v0.34.2 + k8s.io/kubectl v0.34.3 oras.land/oras-go/v2 v2.6.0 sigs.k8s.io/controller-runtime v0.22.4 sigs.k8s.io/kustomize/kyaml v0.21.0 @@ -176,7 +176,7 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/component-base v0.34.2 // indirect + k8s.io/component-base v0.34.3 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/go.sum b/go.sum index 4de45a3da..db38a021b 100644 --- a/go.sum +++ b/go.sum @@ -512,26 +512,26 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= -k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= -k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE= -k8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI= -k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= -k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= -k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= -k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo= +k8s.io/apiserver v0.34.3/go.mod h1:QPnnahMO5C2m3lm6fPW3+JmyQbvHZQ8uudAu/493P2w= +k8s.io/cli-runtime v0.34.3 h1:YRyMhiwX0dT9lmG0AtZDaeG33Nkxgt9OlCTZhRXj9SI= +k8s.io/cli-runtime v0.34.3/go.mod h1:GVwL1L5uaGEgM7eGeKjaTG2j3u134JgG4dAI6jQKhMc= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= +k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= -k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= +k8s.io/kubectl v0.34.3 h1:vpM6//153gh5gvsYHXWHVJ4l4xmN5QFwTSmlfd8icm8= +k8s.io/kubectl v0.34.3/go.mod h1:zZQHtIZoUqTP1bAnPzq/3W1jfc0NeOeunFgcswrfg1c= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= From 6cd444296686bae91d159a511681f64e07aab14c Mon Sep 17 00:00:00 2001 From: Calvin Bui <3604363+calvinbui@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:56:17 +1100 Subject: [PATCH 22/36] Use length check for MetaDependencies instead of nil comparison Signed-off-by: Calvin Bui <3604363+calvinbui@users.noreply.github.com> Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/action/package.go | 2 +- pkg/cmd/install.go | 2 +- pkg/cmd/upgrade.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/action/package.go b/pkg/action/package.go index 963883707..0ab49538c 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -103,7 +103,7 @@ func (p *Package) Run(path string, _ map[string]interface{}) (string, error) { ch.Metadata.AppVersion = p.AppVersion } - if reqs := ac.MetaDependencies(); reqs != nil { + if reqs := ac.MetaDependencies(); len(reqs) > 0 { if err := CheckDependencies(ch, reqs); err != nil { return "", err } diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go index e5d311394..d36cd9e34 100644 --- a/pkg/cmd/install.go +++ b/pkg/cmd/install.go @@ -275,7 +275,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options slog.Warn("this chart is deprecated") } - if req := ac.MetaDependencies(); req != nil { + if req := ac.MetaDependencies(); len(req) > 0 { // If CheckDependencies returns an error, we have unfulfilled dependencies. // As of Helm 2.4.0, this is treated as a stopping condition: // https://github.com/helm/helm/issues/2209 diff --git a/pkg/cmd/upgrade.go b/pkg/cmd/upgrade.go index 92f130f60..bcb4b048f 100644 --- a/pkg/cmd/upgrade.go +++ b/pkg/cmd/upgrade.go @@ -200,7 +200,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { if err != nil { return err } - if req := ac.MetaDependencies(); req != nil { + if req := ac.MetaDependencies(); len(req) > 0 { if err := action.CheckDependencies(ch, req); err != nil { err = fmt.Errorf("an error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies: %w", err) if client.DependencyUpdate { From 04498e89b9487496b49816ba38189ce5a698d0a0 Mon Sep 17 00:00:00 2001 From: zyfy29 Date: Thu, 28 Aug 2025 09:25:15 +0900 Subject: [PATCH 23/36] chore: delete unused var in installer.go Signed-off-by: zyfy29 Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/plugin/installer/installer.go | 4 ---- pkg/cmd/plugin_install.go | 2 -- pkg/cmd/plugin_update.go | 1 - 3 files changed, 7 deletions(-) diff --git a/internal/plugin/installer/installer.go b/internal/plugin/installer/installer.go index 3522a11e9..33bcdd6be 100644 --- a/internal/plugin/installer/installer.go +++ b/internal/plugin/installer/installer.go @@ -31,9 +31,6 @@ import ( // ErrMissingMetadata indicates that plugin.yaml is missing. var ErrMissingMetadata = errors.New("plugin metadata (plugin.yaml) missing") -// Debug enables verbose output. -var Debug bool - // Options contains options for plugin installation. type Options struct { // Verify enables signature verification before installation @@ -41,7 +38,6 @@ type Options struct { // Keyring is the path to the keyring for verification Keyring string } - // Installer provides an interface for installing helm client plugins. type Installer interface { // Install adds a plugin. diff --git a/pkg/cmd/plugin_install.go b/pkg/cmd/plugin_install.go index a45c4a3de..efa9b466c 100644 --- a/pkg/cmd/plugin_install.go +++ b/pkg/cmd/plugin_install.go @@ -119,8 +119,6 @@ func (o *pluginInstallOptions) newInstallerForSource() (installer.Installer, err } func (o *pluginInstallOptions) run(out io.Writer) error { - installer.Debug = settings.Debug - i, err := o.newInstallerForSource() if err != nil { return err diff --git a/pkg/cmd/plugin_update.go b/pkg/cmd/plugin_update.go index c534f93e9..86edc917e 100644 --- a/pkg/cmd/plugin_update.go +++ b/pkg/cmd/plugin_update.go @@ -63,7 +63,6 @@ func (o *pluginUpdateOptions) complete(args []string) error { } func (o *pluginUpdateOptions) run(out io.Writer) error { - installer.Debug = settings.Debug slog.Debug("loading installed plugins", "path", settings.PluginsDirectory) plugins, err := plugin.LoadAll(settings.PluginsDirectory) if err != nil { From 84708937bfbfa76df83fa0282b1d9163d267f060 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Thu, 27 Nov 2025 08:55:42 +0100 Subject: [PATCH 24/36] Inform we use a different golangci-lint version than the CI Signed-off-by: Benoit Tigeot Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index d7422bf23..25b8a56f6 100644 --- a/Makefile +++ b/Makefile @@ -129,6 +129,13 @@ test-coverage: .PHONY: test-style test-style: + @EXPECTED_VERSION=$$(grep GOLANGCI_LINT_VERSION .github/env | cut -d= -f2); \ + ACTUAL_VERSION=$$(golangci-lint --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1); \ + if [ "v$$ACTUAL_VERSION" != "$$EXPECTED_VERSION" ]; then \ + echo "Warning: golangci-lint version is v$$ACTUAL_VERSION (expected $$EXPECTED_VERSION from CI)"; \ + echo "To install the correct version, run:"; \ + echo " curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b \$$(go env GOPATH)/bin $$EXPECTED_VERSION"; \ + fi golangci-lint run ./... @scripts/validate-license.sh From bd1d2fff751bebea53b4ff265f20dcc78ea670ee Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Mon, 13 Nov 2023 21:21:43 +0100 Subject: [PATCH 25/36] fix(uninstall): supersede deployed releases This ensures that when `helm uninstall` is run with `--keep-history` any release in a `deployed` state other than the last release (e.g. due to a failed upgrade) is being marked as `superseded`. As a by-effect, running `helm upgrade` on a release which has been uninstalled after an upgrade failure now no longer works. But instead fails with a `"" has no deployed releases` error. Which is the (likely) intended behavior, and prevents other side-effects like rolling back to a release version which happened before the uninstall if `--atomic` (or `--rollback-on-failure`) was provided. Signed-off-by: Hidde Beydals Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/action/uninstall.go | 19 +++++++++++++++++++ ...ninstall-keep-history-earlier-deployed.txt | 1 + pkg/cmd/uninstall_test.go | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 pkg/cmd/testdata/output/uninstall-keep-history-earlier-deployed.txt diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index d5474490c..efbc72fef 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -188,6 +188,25 @@ func (u *Uninstall) Run(name string) (*releasei.UninstallReleaseResponse, error) u.cfg.Logger().Debug("uninstall: Failed to store updated release", slog.Any("error", err)) } + // Supersede all previous deployments, see issue #12556 (which is a + // variation on #2941). + deployed, err := u.cfg.Releases.DeployedAll(name) + if err != nil && !errors.Is(err, driver.ErrNoDeployedReleases) { + return nil, err + } + for _, reli := range deployed { + rel, err := releaserToV1Release(reli) + if err != nil { + return nil, err + } + + u.cfg.Logger().Debug("superseding previous deployment", "version", rel.Version) + rel.Info.Status = common.StatusSuperseded + if err := u.cfg.Releases.Update(rel); err != nil { + u.cfg.Logger().Debug("uninstall: Failed to store updated release", slog.Any("error", err)) + } + } + if len(errs) > 0 { return res, fmt.Errorf("uninstallation completed with %d error(s): %w", len(errs), joinErrors(errs, "; ")) } diff --git a/pkg/cmd/testdata/output/uninstall-keep-history-earlier-deployed.txt b/pkg/cmd/testdata/output/uninstall-keep-history-earlier-deployed.txt new file mode 100644 index 000000000..f5454b88d --- /dev/null +++ b/pkg/cmd/testdata/output/uninstall-keep-history-earlier-deployed.txt @@ -0,0 +1 @@ +release "aeneas" uninstalled diff --git a/pkg/cmd/uninstall_test.go b/pkg/cmd/uninstall_test.go index 1123f449b..ce436e68c 100644 --- a/pkg/cmd/uninstall_test.go +++ b/pkg/cmd/uninstall_test.go @@ -19,6 +19,7 @@ package cmd import ( "testing" + "helm.sh/helm/v4/pkg/release/common" release "helm.sh/helm/v4/pkg/release/v1" ) @@ -57,6 +58,15 @@ func TestUninstall(t *testing.T) { golden: "output/uninstall-keep-history.txt", rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, }, + { + name: "keep history with earlier deployed release", + cmd: "uninstall aeneas --keep-history", + golden: "output/uninstall-keep-history-earlier-deployed.txt", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{Name: "aeneas", Version: 1, Status: common.StatusDeployed}), + release.Mock(&release.MockReleaseOptions{Name: "aeneas", Version: 2, Status: common.StatusFailed}), + }, + }, { name: "wait", cmd: "uninstall aeneas --wait", From f19747c4b5e8b0067975d65aedc2674212551258 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Mon, 13 Nov 2023 22:02:07 +0100 Subject: [PATCH 26/36] fix(rollback): `errors.Is` instead of string comp Signed-off-by: Hidde Beydals Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/action/rollback.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index cd160dfec..4cdb2d33b 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -18,8 +18,8 @@ package action import ( "bytes" + "errors" "fmt" - "strings" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,6 +28,7 @@ import ( "helm.sh/helm/v4/pkg/kube" "helm.sh/helm/v4/pkg/release/common" release "helm.sh/helm/v4/pkg/release/v1" + "helm.sh/helm/v4/pkg/storage/driver" ) // Rollback is the action for rolling back to a given release. @@ -278,7 +279,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas } deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name) - if err != nil && !strings.Contains(err.Error(), "has no deployed releases") { + if err != nil && !errors.Is(err, driver.ErrNoDeployedReleases) { return nil, err } // Supersede all previous deployments, see issue #2941. From 79caff55ddb4a5471e020df6c98159d7391a9384 Mon Sep 17 00:00:00 2001 From: Evans Mungai Date: Thu, 11 Dec 2025 14:45:31 +0000 Subject: [PATCH 27/36] fix(upgrade): pass --server-side flag to install when using upgrade --install When running `helm upgrade --install` on a non-existent release, the --server-side flag was not being passed to the install action. This caused the install to always use server-side apply (the default), ignoring --server-side=false. Copy ServerSideApply and ForceConflicts from the upgrade client to the install client when falling back to install. Fixes #31627 Signed-off-by: Evans Mungai Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/cmd/upgrade.go | 2 ++ pkg/cmd/upgrade_test.go | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/pkg/cmd/upgrade.go b/pkg/cmd/upgrade.go index bcb4b048f..918d6f5b8 100644 --- a/pkg/cmd/upgrade.go +++ b/pkg/cmd/upgrade.go @@ -153,6 +153,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { instClient.EnableDNS = client.EnableDNS instClient.HideSecret = client.HideSecret instClient.TakeOwnership = client.TakeOwnership + instClient.ForceConflicts = client.ForceConflicts + instClient.ServerSideApply = client.ServerSideApply != "false" if isReleaseUninstalled(versions) { instClient.Replace = true diff --git a/pkg/cmd/upgrade_test.go b/pkg/cmd/upgrade_test.go index 8729be0ec..0ae1e3561 100644 --- a/pkg/cmd/upgrade_test.go +++ b/pkg/cmd/upgrade_test.go @@ -605,3 +605,58 @@ func TestUpgradeWithDryRun(t *testing.T) { t.Error("expected error when --hide-secret used without --dry-run") } } + +func TestUpgradeInstallServerSideApply(t *testing.T) { + _, _, chartPath := prepareMockRelease(t, "ssa-test") + + defer resetEnv()() + + tests := []struct { + name string + serverSideFlag string + expectedApplyMethod string + }{ + { + name: "upgrade --install with --server-side=false uses client-side apply", + serverSideFlag: "--server-side=false", + expectedApplyMethod: "csa", + }, + { + name: "upgrade --install with --server-side=true uses server-side apply", + serverSideFlag: "--server-side=true", + expectedApplyMethod: "ssa", + }, + { + name: "upgrade --install with --server-side=auto uses server-side apply (default for new install)", + serverSideFlag: "--server-side=auto", + expectedApplyMethod: "ssa", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + store := storageFixture() + releaseName := fmt.Sprintf("ssa-test-%s", tt.expectedApplyMethod) + + cmd := fmt.Sprintf("upgrade %s --install %s '%s'", releaseName, tt.serverSideFlag, chartPath) + _, _, err := executeActionCommandC(store, cmd) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + rel, err := store.Get(releaseName, 1) + if err != nil { + t.Fatalf("unexpected error getting release: %v", err) + } + + relV1, err := releaserToV1Release(rel) + if err != nil { + t.Fatalf("unexpected error converting release: %v", err) + } + + if relV1.ApplyMethod != tt.expectedApplyMethod { + t.Errorf("expected ApplyMethod %q, got %q", tt.expectedApplyMethod, relV1.ApplyMethod) + } + }) + } +} From 6cc85a67f0b08a1b5c512886644a89ded687b005 Mon Sep 17 00:00:00 2001 From: Mads Jensen Date: Sun, 14 Dec 2025 08:54:11 +0100 Subject: [PATCH 28/36] Use errors.Is to check for io.EOF and gzip.ErrHeader In GoLang, using the == operator to check for a certain error will not unwrap the error chain, and therefore may hide the problem. Signed-off-by: Mads Jensen Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/chart/v3/lint/rules/crds.go | 2 +- internal/chart/v3/lint/rules/template.go | 2 +- internal/chart/v3/loader/archive.go | 2 +- internal/chart/v3/loader/load.go | 2 +- internal/chart/v3/loader/load_test.go | 3 ++- internal/chart/v3/util/save_test.go | 3 ++- internal/plugin/installer/extractor.go | 2 +- internal/plugin/installer/oci_installer.go | 3 ++- internal/plugin/sign.go | 2 +- pkg/chart/loader/archive/archive.go | 2 +- pkg/chart/loader/load.go | 6 +++--- pkg/chart/v2/lint/rules/crds.go | 2 +- pkg/chart/v2/lint/rules/template.go | 2 +- pkg/chart/v2/loader/archive.go | 4 ++-- pkg/chart/v2/loader/load.go | 2 +- pkg/chart/v2/loader/load_test.go | 3 ++- pkg/chart/v2/util/save_test.go | 3 ++- pkg/strvals/literal_parser.go | 5 +++-- pkg/strvals/parser.go | 2 +- 19 files changed, 29 insertions(+), 23 deletions(-) diff --git a/internal/chart/v3/lint/rules/crds.go b/internal/chart/v3/lint/rules/crds.go index 735573624..deedeb0f2 100644 --- a/internal/chart/v3/lint/rules/crds.go +++ b/internal/chart/v3/lint/rules/crds.go @@ -70,7 +70,7 @@ func Crds(linter *support.Linter) { var yamlStruct *k8sYamlStruct err := decoder.Decode(&yamlStruct) - if err == io.EOF { + if errors.Is(err, io.EOF) { break } diff --git a/internal/chart/v3/lint/rules/template.go b/internal/chart/v3/lint/rules/template.go index 204966364..38e602b7e 100644 --- a/internal/chart/v3/lint/rules/template.go +++ b/internal/chart/v3/lint/rules/template.go @@ -150,7 +150,7 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string var yamlStruct *k8sYamlStruct err := decoder.Decode(&yamlStruct) - if err == io.EOF { + if errors.Is(err, io.EOF) { break } diff --git a/internal/chart/v3/loader/archive.go b/internal/chart/v3/loader/archive.go index 358c2ce4d..a9d4faf8f 100644 --- a/internal/chart/v3/loader/archive.go +++ b/internal/chart/v3/loader/archive.go @@ -56,7 +56,7 @@ func LoadFile(name string) (*chart.Chart, error) { c, err := LoadArchive(raw) if err != nil { - if err == gzip.ErrHeader { + if errors.Is(err, gzip.ErrHeader) { return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err) } } diff --git a/internal/chart/v3/loader/load.go b/internal/chart/v3/loader/load.go index 9d657fb95..373c4659f 100644 --- a/internal/chart/v3/loader/load.go +++ b/internal/chart/v3/loader/load.go @@ -189,7 +189,7 @@ func LoadValues(data io.Reader) (map[string]interface{}, error) { currentMap := map[string]interface{}{} raw, err := reader.Read() if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { break } return nil, fmt.Errorf("error reading yaml document: %w", err) diff --git a/internal/chart/v3/loader/load_test.go b/internal/chart/v3/loader/load_test.go index f91005bf6..12403f9c2 100644 --- a/internal/chart/v3/loader/load_test.go +++ b/internal/chart/v3/loader/load_test.go @@ -20,6 +20,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "errors" "io" "log" "os" @@ -116,7 +117,7 @@ func TestBomTestData(t *testing.T) { tr := tar.NewReader(unzipped) for { file, err := tr.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/internal/chart/v3/util/save_test.go b/internal/chart/v3/util/save_test.go index 62625919b..7a42a76af 100644 --- a/internal/chart/v3/util/save_test.go +++ b/internal/chart/v3/util/save_test.go @@ -21,6 +21,7 @@ import ( "bytes" "compress/gzip" "crypto/sha256" + "errors" "fmt" "io" "os" @@ -201,7 +202,7 @@ func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) { headers := []*tar.Header{} for { hd, err := tr.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } diff --git a/internal/plugin/installer/extractor.go b/internal/plugin/installer/extractor.go index 71efebc67..b753dfbca 100644 --- a/internal/plugin/installer/extractor.go +++ b/internal/plugin/installer/extractor.go @@ -140,7 +140,7 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error { tarReader := tar.NewReader(uncompressedStream) for { header, err := tarReader.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/internal/plugin/installer/oci_installer.go b/internal/plugin/installer/oci_installer.go index afbb42ca5..67f99b6f8 100644 --- a/internal/plugin/installer/oci_installer.go +++ b/internal/plugin/installer/oci_installer.go @@ -19,6 +19,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "errors" "fmt" "io" "log/slog" @@ -214,7 +215,7 @@ func extractTar(r io.Reader, targetDir string) error { for { header, err := tarReader.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/internal/plugin/sign.go b/internal/plugin/sign.go index 6b8aafd3e..6ddf113a2 100644 --- a/internal/plugin/sign.go +++ b/internal/plugin/sign.go @@ -63,7 +63,7 @@ func ExtractTgzPluginMetadata(r io.Reader) (*Metadata, error) { tr := tar.NewReader(gzr) for { header, err := tr.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/pkg/chart/loader/archive/archive.go b/pkg/chart/loader/archive/archive.go index c6875db3f..e98f5c333 100644 --- a/pkg/chart/loader/archive/archive.go +++ b/pkg/chart/loader/archive/archive.go @@ -68,7 +68,7 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { for { b := bytes.NewBuffer(nil) hd, err := tr.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go index dbc5d3004..3fd381825 100644 --- a/pkg/chart/loader/load.go +++ b/pkg/chart/loader/load.go @@ -131,8 +131,8 @@ func LoadFile(name string) (chart.Charter, error) { files, err := archive.LoadArchiveFiles(raw) if err != nil { - if err == gzip.ErrHeader { - return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err) + if errors.Is(err, gzip.ErrHeader) { + return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %w)", name, err) } return nil, errors.New("unable to load chart archive") } @@ -163,7 +163,7 @@ func LoadArchive(in io.Reader) (chart.Charter, error) { files, err := archive.LoadArchiveFiles(in) if err != nil { - if err == gzip.ErrHeader { + if errors.Is(err, gzip.ErrHeader) { return nil, fmt.Errorf("stream does not appear to be a valid chart file (details: %w)", err) } return nil, fmt.Errorf("unable to load chart archive: %w", err) diff --git a/pkg/chart/v2/lint/rules/crds.go b/pkg/chart/v2/lint/rules/crds.go index faef7dcf9..4bb4d370b 100644 --- a/pkg/chart/v2/lint/rules/crds.go +++ b/pkg/chart/v2/lint/rules/crds.go @@ -70,7 +70,7 @@ func Crds(linter *support.Linter) { var yamlStruct *k8sYamlStruct err := decoder.Decode(&yamlStruct) - if err == io.EOF { + if errors.Is(err, io.EOF) { break } diff --git a/pkg/chart/v2/lint/rules/template.go b/pkg/chart/v2/lint/rules/template.go index 0c633dc1a..43665aa3a 100644 --- a/pkg/chart/v2/lint/rules/template.go +++ b/pkg/chart/v2/lint/rules/template.go @@ -180,7 +180,7 @@ func (t *templateLinter) Lint() { var yamlStruct *k8sYamlStruct err := decoder.Decode(&yamlStruct) - if err == io.EOF { + if errors.Is(err, io.EOF) { break } diff --git a/pkg/chart/v2/loader/archive.go b/pkg/chart/v2/loader/archive.go index f6ed0e84f..c6885e125 100644 --- a/pkg/chart/v2/loader/archive.go +++ b/pkg/chart/v2/loader/archive.go @@ -56,8 +56,8 @@ func LoadFile(name string) (*chart.Chart, error) { c, err := LoadArchive(raw) if err != nil { - if err == gzip.ErrHeader { - return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err) + if errors.Is(err, gzip.ErrHeader) { + return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %w)", name, err) } } return c, err diff --git a/pkg/chart/v2/loader/load.go b/pkg/chart/v2/loader/load.go index 6fdaa4f7d..d466e247c 100644 --- a/pkg/chart/v2/loader/load.go +++ b/pkg/chart/v2/loader/load.go @@ -216,7 +216,7 @@ func LoadValues(data io.Reader) (map[string]interface{}, error) { currentMap := map[string]interface{}{} raw, err := reader.Read() if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { break } return nil, fmt.Errorf("error reading yaml document: %w", err) diff --git a/pkg/chart/v2/loader/load_test.go b/pkg/chart/v2/loader/load_test.go index ee0be5b18..397745dd6 100644 --- a/pkg/chart/v2/loader/load_test.go +++ b/pkg/chart/v2/loader/load_test.go @@ -20,6 +20,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "errors" "io" "log" "os" @@ -116,7 +117,7 @@ func TestBomTestData(t *testing.T) { tr := tar.NewReader(unzipped) for { file, err := tr.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/pkg/chart/v2/util/save_test.go b/pkg/chart/v2/util/save_test.go index e317d1c09..6d4e2c8cd 100644 --- a/pkg/chart/v2/util/save_test.go +++ b/pkg/chart/v2/util/save_test.go @@ -21,6 +21,7 @@ import ( "bytes" "compress/gzip" "crypto/sha256" + "errors" "fmt" "io" "os" @@ -205,7 +206,7 @@ func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) { headers := []*tar.Header{} for { hd, err := tr.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } diff --git a/pkg/strvals/literal_parser.go b/pkg/strvals/literal_parser.go index d34e5e854..d5d4c25b4 100644 --- a/pkg/strvals/literal_parser.go +++ b/pkg/strvals/literal_parser.go @@ -17,6 +17,7 @@ package strvals import ( "bytes" + "errors" "fmt" "io" "strconv" @@ -66,7 +67,7 @@ func (t *literalParser) parse() error { if err == nil { continue } - if err == io.EOF { + if errors.Is(err, io.EOF) { return nil } return err @@ -183,7 +184,7 @@ func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([] case lastRune == '=': value, err := t.val() - if err != nil && err != io.EOF { + if err != nil && !errors.Is(err, io.EOF) { return list, err } return setIndex(list, i, string(value)) diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 86e349f37..8eb761dce 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -161,7 +161,7 @@ func (t *parser) parse() error { if err == nil { continue } - if err == io.EOF { + if errors.Is(err, io.EOF) { return nil } return err From c7b5ec5617b8e55380c1fb98ad12c30458381ca9 Mon Sep 17 00:00:00 2001 From: George Jenkins Date: Sun, 23 Nov 2025 10:49:13 -0800 Subject: [PATCH 29/36] Bump required go version (`go.mod` version) Explicit PR to bump the version, as needed by: https://github.com/helm/helm/pull/31215 Signed-off-by: George Jenkins Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3316bd925..1dba4ea66 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module helm.sh/helm/v4 -go 1.24.0 +go 1.25.0 require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 From dae93a4f02b28665c3bc51abcefa7e707e040a57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 05:07:55 +0000 Subject: [PATCH 30/36] chore(deps): bump github.com/fluxcd/cli-utils Bumps [github.com/fluxcd/cli-utils](https://github.com/fluxcd/cli-utils) from 0.36.0-flux.14 to 0.36.0-flux.15. - [Commits](https://github.com/fluxcd/cli-utils/compare/v0.36.0-flux.14...v0.36.0-flux.15) --- updated-dependencies: - dependency-name: github.com/fluxcd/cli-utils dependency-version: 0.36.0-flux.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 1dba4ea66..652b07919 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/evanphx/json-patch/v5 v5.9.11 github.com/extism/go-sdk v1.7.1 github.com/fatih/color v1.18.0 - github.com/fluxcd/cli-utils v0.36.0-flux.14 + github.com/fluxcd/cli-utils v0.36.0-flux.15 github.com/foxcpp/go-mockdns v1.1.0 github.com/gobwas/glob v0.2.3 github.com/gofrs/flock v0.13.0 @@ -120,7 +120,7 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/onsi/gomega v1.37.0 // indirect + github.com/onsi/gomega v1.38.2 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -172,7 +172,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/grpc v1.72.1 // indirect - google.golang.org/protobuf v1.36.6 // indirect + google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index db38a021b..59796f68c 100644 --- a/go.sum +++ b/go.sum @@ -93,8 +93,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fluxcd/cli-utils v0.36.0-flux.14 h1:I//AMVUXTc+M04UtIXArMXQZCazGMwfemodV1j/yG8c= -github.com/fluxcd/cli-utils v0.36.0-flux.14/go.mod h1:uDo7BYOfbdmk/asnHuI0IQPl6u0FCgcN54AHDu3Y5As= +github.com/fluxcd/cli-utils v0.36.0-flux.15 h1:Et5QLnIpRjj+oZtM9gEybkAaoNsjysHq0y1253Ai94Y= +github.com/fluxcd/cli-utils v0.36.0-flux.15/go.mod h1:AqRUmWIfNE7cdL6NWSGF0bAlypGs+9x5UQ2qOtlEzv4= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -244,10 +244,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/ginkgo/v2 v2.25.2 h1:hepmgwx1D+llZleKQDMEvy8vIlCxMGt7W5ZxDjIEhsw= +github.com/onsi/ginkgo/v2 v2.25.2/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -496,8 +496,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From f865d7fc741d9e0a94a4a1d13006e77d1774f801 Mon Sep 17 00:00:00 2001 From: George Jenkins Date: Sun, 14 Dec 2025 20:42:29 -0800 Subject: [PATCH 31/36] Fix `TestConcurrencyDownloadIndex` typo Signed-off-by: George Jenkins Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/repo/v1/chartrepo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/repo/v1/chartrepo_test.go b/pkg/repo/v1/chartrepo_test.go index a707cf36d..353ab62d6 100644 --- a/pkg/repo/v1/chartrepo_test.go +++ b/pkg/repo/v1/chartrepo_test.go @@ -94,7 +94,7 @@ func TestIndexCustomSchemeDownload(t *testing.T) { } } -func TestConcurrenyDownloadIndex(t *testing.T) { +func TestConcurrencyDownloadIndex(t *testing.T) { srv, err := startLocalServerForTests(nil) if err != nil { t.Fatal(err) From 3d03f0a15122f624a054a3ce47ca58ae54b6e727 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 21:06:25 +0000 Subject: [PATCH 32/36] chore(deps): bump actions/upload-artifact from 4.6.2 to 6.0.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 6.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/ea165f8d65b6e75b540449e92b4886f43607fa02...b7c566a772e6b6bfb58ed0dc250532a479d7789f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- .github/workflows/scorecards.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 3e475edc0..514a649cb 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -55,7 +55,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif From d85df3309fd24135af258959334706c3c079482d Mon Sep 17 00:00:00 2001 From: Deepak Date: Tue, 16 Dec 2025 11:35:11 +0530 Subject: [PATCH 33/36] fix(doc): Update default wait strategy fixes: #31652 Default wait strategy is hookOnly, so updated the docs to reflect the same Signed-off-by: Deepak Chethan Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/cmd/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/flags.go b/pkg/cmd/flags.go index 939523ffd..02981420e 100644 --- a/pkg/cmd/flags.go +++ b/pkg/cmd/flags.go @@ -59,7 +59,7 @@ func AddWaitFlag(cmd *cobra.Command, wait *kube.WaitStrategy) { cmd.Flags().Var( newWaitValue(kube.HookOnlyStrategy, wait), "wait", - "if specified, wait until resources are ready (up to --timeout). Values: 'watcher' (default), 'hookOnly', and 'legacy'.", + "if specified, wait until resources are ready (up to --timeout). Values: 'hookOnly' (default), 'watcher', and 'legacy'.", ) // Sets the strategy to use the watcher strategy if `--wait` is used without an argument cmd.Flags().Lookup("wait").NoOptDefVal = string(kube.StatusWatcherStrategy) From 6bfefe1503e988b58053e0ad7bdf2be200a824c1 Mon Sep 17 00:00:00 2001 From: Deepak Chethan Date: Wed, 17 Dec 2025 14:41:37 +0530 Subject: [PATCH 34/36] doc: update based on review suggestion Co-authored-by: Terry Howe Signed-off-by: Deepak Chethan Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/cmd/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/flags.go b/pkg/cmd/flags.go index 02981420e..251bfa032 100644 --- a/pkg/cmd/flags.go +++ b/pkg/cmd/flags.go @@ -59,7 +59,7 @@ func AddWaitFlag(cmd *cobra.Command, wait *kube.WaitStrategy) { cmd.Flags().Var( newWaitValue(kube.HookOnlyStrategy, wait), "wait", - "if specified, wait until resources are ready (up to --timeout). Values: 'hookOnly' (default), 'watcher', and 'legacy'.", + "if specified, wait until resources are ready (up to --timeout). Values: 'watcher', 'hookOnly', and 'legacy'.", ) // Sets the strategy to use the watcher strategy if `--wait` is used without an argument cmd.Flags().Lookup("wait").NoOptDefVal = string(kube.StatusWatcherStrategy) From e393266b8cb2e04c257699faaff2b18e81b9b087 Mon Sep 17 00:00:00 2001 From: Paul Van Laer Date: Thu, 30 Oct 2025 18:00:08 +0100 Subject: [PATCH 35/36] feat(repo): add --no-headers option to 'helm repo list' This adds a --no-headers flag to the 'helm repo list' command, allowing users to suppress table headers in the output. Useful for scripting and automation. Signed-off-by: Paul Van Laer Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- pkg/cmd/repo_list.go | 17 +++++++++++++---- pkg/cmd/repo_list_test.go | 6 ++++++ .../testdata/output/repo-list-no-headers.txt | 3 +++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 pkg/cmd/testdata/output/repo-list-no-headers.txt diff --git a/pkg/cmd/repo_list.go b/pkg/cmd/repo_list.go index 10b4442a0..60f4cb6c6 100644 --- a/pkg/cmd/repo_list.go +++ b/pkg/cmd/repo_list.go @@ -30,6 +30,7 @@ import ( func newRepoListCmd(out io.Writer) *cobra.Command { var outfmt output.Format + var noHeaders bool cmd := &cobra.Command{ Use: "list", Aliases: []string{"ls"}, @@ -46,12 +47,17 @@ func newRepoListCmd(out io.Writer) *cobra.Command { return nil } - return outfmt.Write(out, &repoListWriter{f.Repositories}) + w := &repoListWriter{ + repos: f.Repositories, + noHeaders: noHeaders, + } + + return outfmt.Write(out, w) }, } + cmd.Flags().BoolVar(&noHeaders, "no-headers", false, "suppress headers in the output") bindOutputFlag(cmd, &outfmt) - return cmd } @@ -61,12 +67,15 @@ type repositoryElement struct { } type repoListWriter struct { - repos []*repo.Entry + repos []*repo.Entry + noHeaders bool } func (r *repoListWriter) WriteTable(out io.Writer) error { table := uitable.New() - table.AddRow("NAME", "URL") + if !r.noHeaders { + table.AddRow("NAME", "URL") + } for _, re := range r.repos { table.AddRow(re.Name, re.URL) } diff --git a/pkg/cmd/repo_list_test.go b/pkg/cmd/repo_list_test.go index 2f6a9e4ad..94cdf3969 100644 --- a/pkg/cmd/repo_list_test.go +++ b/pkg/cmd/repo_list_test.go @@ -48,6 +48,12 @@ func TestRepoList(t *testing.T) { golden: "output/repo-list.txt", wantError: false, }, + { + name: "list without headers", + cmd: fmt.Sprintf("repo list --repository-config %s --repository-cache %s --no-headers", repoFile2, rootDir), + golden: "output/repo-list-no-headers.txt", + wantError: false, + }, } runTestCmd(t, tests) diff --git a/pkg/cmd/testdata/output/repo-list-no-headers.txt b/pkg/cmd/testdata/output/repo-list-no-headers.txt new file mode 100644 index 000000000..13491aeb2 --- /dev/null +++ b/pkg/cmd/testdata/output/repo-list-no-headers.txt @@ -0,0 +1,3 @@ +charts https://charts.helm.sh/stable +firstexample http://firstexample.com +secondexample http://secondexample.com From 951ae096aeb8eac66725f4f8cc2a98e36fc2453d Mon Sep 17 00:00:00 2001 From: Evans Mungai Date: Thu, 30 Oct 2025 21:08:08 +0000 Subject: [PATCH 36/36] chore: increase logging package test coverage Signed-off-by: Evans Mungai Signed-off-by: MrJack <36191829+biagiopietro@users.noreply.github.com> --- internal/logging/logging.go | 3 + internal/logging/logging_test.go | 258 +++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) diff --git a/internal/logging/logging.go b/internal/logging/logging.go index b8faf859e..674e2db34 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -36,6 +36,9 @@ type DebugCheckHandler struct { // Enabled implements slog.Handler.Enabled func (h *DebugCheckHandler) Enabled(_ context.Context, level slog.Level) bool { if level == slog.LevelDebug { + if h.debugEnabled == nil { + return false + } return h.debugEnabled() } return true // Always log other levels diff --git a/internal/logging/logging_test.go b/internal/logging/logging_test.go index 75e6c4025..d22a47a31 100644 --- a/internal/logging/logging_test.go +++ b/internal/logging/logging_test.go @@ -18,8 +18,10 @@ package logging import ( "bytes" + "context" "log/slog" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -113,3 +115,259 @@ func TestLogHolder_InterfaceCompliance(t *testing.T) { assert.Equal(t, handler, logger.Handler()) }) } + +func TestDebugCheckHandler_Enabled(t *testing.T) { + t.Run("returns debugEnabled function result for debug level", func(t *testing.T) { + // Test with debug enabled + debugEnabled := func() bool { return true } + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: debugEnabled, + } + + assert.True(t, handler.Enabled(t.Context(), slog.LevelDebug)) + }) + + t.Run("returns false for debug level when debug disabled", func(t *testing.T) { + // Test with debug disabled + debugEnabled := func() bool { return false } + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: debugEnabled, + } + + assert.False(t, handler.Enabled(t.Context(), slog.LevelDebug)) + }) + + t.Run("always returns true for non-debug levels", func(t *testing.T) { + debugEnabled := func() bool { return false } // Debug disabled + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: debugEnabled, + } + + // Even with debug disabled, other levels should always be enabled + assert.True(t, handler.Enabled(t.Context(), slog.LevelInfo)) + assert.True(t, handler.Enabled(t.Context(), slog.LevelWarn)) + assert.True(t, handler.Enabled(t.Context(), slog.LevelError)) + }) + + t.Run("calls debugEnabled function dynamically", func(t *testing.T) { + callCount := 0 + debugEnabled := func() bool { + callCount++ + return callCount%2 == 1 // Alternates between true and false + } + + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: debugEnabled, + } + + // First call should return true + assert.True(t, handler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 1, callCount) + + // Second call should return false + assert.False(t, handler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 2, callCount) + + // Third call should return true again + assert.True(t, handler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 3, callCount) + }) +} + +func TestDebugCheckHandler_Handle(t *testing.T) { + t.Run("delegates to underlying handler", func(t *testing.T) { + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: func() bool { return true }, + } + + record := slog.NewRecord(time.Now(), slog.LevelInfo, "test message", 0) + err := handler.Handle(t.Context(), record) + + assert.NoError(t, err) + assert.Contains(t, buf.String(), "test message") + }) + + t.Run("handles context correctly", func(t *testing.T) { + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: func() bool { return true }, + } + + type testKey string + ctx := context.WithValue(t.Context(), testKey("test"), "value") + record := slog.NewRecord(time.Now(), slog.LevelInfo, "context test", 0) + err := handler.Handle(ctx, record) + + assert.NoError(t, err) + assert.Contains(t, buf.String(), "context test") + }) +} + +func TestDebugCheckHandler_WithAttrs(t *testing.T) { + t.Run("returns new DebugCheckHandler with attributes", func(t *testing.T) { + logger := NewLogger(func() bool { return true }) + handler := logger.Handler() + newHandler := handler.WithAttrs([]slog.Attr{ + slog.String("key1", "value1"), + slog.Int("key2", 42), + }) + + // Should return a DebugCheckHandler + debugHandler, ok := newHandler.(*DebugCheckHandler) + assert.True(t, ok) + assert.NotNil(t, debugHandler) + + // Should preserve the debugEnabled function + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelDebug)) + + // Should have the attributes applied to the underlying handler + assert.NotEqual(t, handler, debugHandler.handler) + }) + + t.Run("preserves debugEnabled function", func(t *testing.T) { + callCount := 0 + debugEnabled := func() bool { + callCount++ + return callCount%2 == 1 + } + + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: debugEnabled, + } + + attrs := []slog.Attr{slog.String("test", "value")} + newHandler := handler.WithAttrs(attrs) + + // The new handler should use the same debugEnabled function + assert.True(t, newHandler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 1, callCount) + + assert.False(t, newHandler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 2, callCount) + }) +} + +func TestDebugCheckHandler_WithGroup(t *testing.T) { + t.Run("returns new DebugCheckHandler with group", func(t *testing.T) { + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: func() bool { return true }, + } + + newHandler := handler.WithGroup("testgroup") + + // Should return a DebugCheckHandler + debugHandler, ok := newHandler.(*DebugCheckHandler) + assert.True(t, ok) + assert.NotNil(t, debugHandler) + + // Should preserve the debugEnabled function + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelDebug)) + + // Should have the group applied to the underlying handler + assert.NotEqual(t, handler.handler, debugHandler.handler) + }) + + t.Run("preserves debugEnabled function", func(t *testing.T) { + callCount := 0 + debugEnabled := func() bool { + callCount++ + return callCount%2 == 1 + } + + buf := &bytes.Buffer{} + baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}) + handler := &DebugCheckHandler{ + handler: baseHandler, + debugEnabled: debugEnabled, + } + + newHandler := handler.WithGroup("testgroup") + + // The new handler should use the same debugEnabled function + assert.True(t, newHandler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 1, callCount) + + assert.False(t, newHandler.Enabled(t.Context(), slog.LevelDebug)) + assert.Equal(t, 2, callCount) + }) +} + +func TestDebugCheckHandler_Integration(t *testing.T) { + t.Run("works with NewLogger function", func(t *testing.T) { + debugEnabled := func() bool { return true } + logger := NewLogger(debugEnabled) + + assert.NotNil(t, logger) + + // The logger should have a DebugCheckHandler + handler := logger.Handler() + debugHandler, ok := handler.(*DebugCheckHandler) + assert.True(t, ok) + + // Should enable debug when debugEnabled returns true + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelDebug)) + + // Should enable other levels regardless + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelInfo)) + }) + + t.Run("dynamic debug checking works in practice", func(t *testing.T) { + debugState := false + debugEnabled := func() bool { return debugState } + + logger := NewLogger(debugEnabled) + + // Initially debug should be disabled + assert.False(t, logger.Handler().(*DebugCheckHandler).Enabled(t.Context(), slog.LevelDebug)) + + // Enable debug + debugState = true + assert.True(t, logger.Handler().(*DebugCheckHandler).Enabled(t.Context(), slog.LevelDebug)) + + // Disable debug again + debugState = false + assert.False(t, logger.Handler().(*DebugCheckHandler).Enabled(t.Context(), slog.LevelDebug)) + }) + + t.Run("handles nil debugEnabled function", func(t *testing.T) { + logger := NewLogger(nil) + + assert.NotNil(t, logger) + + // The logger should have a DebugCheckHandler + handler := logger.Handler() + debugHandler, ok := handler.(*DebugCheckHandler) + assert.True(t, ok) + + // When debugEnabled is nil, debug level should be disabled (default behavior) + assert.False(t, debugHandler.Enabled(t.Context(), slog.LevelDebug)) + + // Other levels should always be enabled + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelInfo)) + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelWarn)) + assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelError)) + }) +}