Repository: helm/helm (go)

Issue:
Environment variable for max release history
Environment variable for setting the max history for an environment. This would remove need to set it on each upgrade with the `--history-max` flag. Similar to `TILLER_HISTORY_MAX` variable in Helm 2.

Output of `helm version`:
Helm 3


Pull request description:
Environment variable for setting the max history for an environment
<!--  Thanks for sending a pull request!  Here are some tips for you:
1. Make sure to read the Contributing Guide before submitting your PR: https://github.com/helm/helm/blob/master/CONTRIBUTING.md
2. If this PR closes another issue, add 'closes #<issue number>' somewhere in the PR summary. GitHub will automatically close that issue when this PR gets merged. Alternatively, adding 'refs #<issue number>' will not close the issue, but help provide the reviewer more context.-->

**What this PR does / why we need it**:
Environment variable for max release history

**Special notes for your reviewer**:

fix #8367

**If applicable**:
- [ ] this PR contains documentation
- [ ] this PR contains unit tests
- [ ] this PR has been tested for backwards compatibility


Patch:
diff --git a/cmd/helm/root.go b/cmd/helm/root.go
index 20dd1d93b..449009cb5 100644
--- a/cmd/helm/root.go
+++ b/cmd/helm/root.go
@@ -50,6 +50,7 @@ Environment variables:
 | $HELM_DATA_HOME                    | set an alternative location for storing Helm data.                                |
 | $HELM_DRIVER                       | set the backend storage driver. Values are: configmap, secret, memory, postgres   |
 | $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use.                      |
+| $HELM_MAX_HISTORY                  | set the maximum number of helm release history.                                   |
 | $HELM_NO_PLUGINS                   | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.                        |
 | $KUBECONFIG                        | set an alternative Kubernetes configuration file (default "~/.kube/config")       |
 
diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go
index c992e2f6b..dbddaa368 100644
--- a/cmd/helm/upgrade.go
+++ b/cmd/helm/upgrade.go
@@ -180,7 +180,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored")
 	f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
 	f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used")
-	f.IntVar(&client.MaxHistory, "history-max", 10, "limit the maximum number of revisions saved per release. Use 0 for no limit")
+	f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
 	f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
 	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
 	f.StringVar(&client.Description, "description", "", "add a custom description")
diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go
index d62f57a55..a9994f03d 100644
--- a/pkg/cli/environment.go
+++ b/pkg/cli/environment.go
@@ -33,6 +33,9 @@ import (
 	"helm.sh/helm/v3/pkg/helmpath"
 )
 
+// defaultMaxHistory sets the maximum number of releases to 0: unlimited
+const defaultMaxHistory = 10
+
 // EnvSettings describes all of the environment settings.
 type EnvSettings struct {
 	namespace string
@@ -56,11 +59,14 @@ type EnvSettings struct {
 	RepositoryCache string
 	// PluginsDirectory is the path to the plugins directory.
 	PluginsDirectory string
+	// MaxHistory is the max release history maintained.
+	MaxHistory int
 }
 
 func New() *EnvSettings {
 	env := &EnvSettings{
 		namespace:        os.Getenv("HELM_NAMESPACE"),
+		MaxHistory:       envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
 		KubeContext:      os.Getenv("HELM_KUBECONTEXT"),
 		KubeToken:        os.Getenv("HELM_KUBETOKEN"),
 		KubeAPIServer:    os.Getenv("HELM_KUBEAPISERVER"),
@@ -102,6 +108,18 @@ func envOr(name, def string) string {
 	return def
 }
 
+func envIntOr(name string, def int) int {
+	if name == "" {
+		return def
+	}
+	envVal := envOr(name, strconv.Itoa(def))
+	ret, err := strconv.Atoi(envVal)
+	if err != nil {
+		return def
+	}
+	return ret
+}
+
 func (s *EnvSettings) EnvVars() map[string]string {
 	envvars := map[string]string{
 		"HELM_BIN":               os.Args[0],
@@ -114,6 +132,7 @@ func (s *EnvSettings) EnvVars() map[string]string {
 		"HELM_REPOSITORY_CACHE":  s.RepositoryCache,
 		"HELM_REPOSITORY_CONFIG": s.RepositoryConfig,
 		"HELM_NAMESPACE":         s.Namespace(),
+		"HELM_MAX_HISTORY":       strconv.Itoa(s.MaxHistory),
 
 		// broken, these are populated from helm flags and not kubeconfig.
 		"HELM_KUBECONTEXT":   s.KubeContext,


Test patch:
diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go
index fadc2981e..3234a133b 100644
--- a/pkg/cli/environment_test.go
+++ b/pkg/cli/environment_test.go
@@ -35,29 +35,34 @@ func TestEnvSettings(t *testing.T) {
 		// expected values
 		ns, kcontext string
 		debug        bool
+		maxhistory   int
 	}{
 		{
-			name: "defaults",
-			ns:   "default",
+			name:       "defaults",
+			ns:         "default",
+			maxhistory: defaultMaxHistory,
 		},
 		{
-			name:  "with flags set",
-			args:  "--debug --namespace=myns",
-			ns:    "myns",
-			debug: true,
+			name:       "with flags set",
+			args:       "--debug --namespace=myns",
+			ns:         "myns",
+			debug:      true,
+			maxhistory: defaultMaxHistory,
 		},
 		{
-			name:    "with envvars set",
-			envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"},
-			ns:      "yourns",
-			debug:   true,
+			name:       "with envvars set",
+			envvars:    map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns", "HELM_MAX_HISTORY": "5"},
+			ns:         "yourns",
+			maxhistory: 5,
+			debug:      true,
 		},
 		{
-			name:    "with flags and envvars set",
-			args:    "--debug --namespace=myns",
-			envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"},
-			ns:      "myns",
-			debug:   true,
+			name:       "with flags and envvars set",
+			args:       "--debug --namespace=myns",
+			envvars:    map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"},
+			ns:         "myns",
+			debug:      true,
+			maxhistory: defaultMaxHistory,
 		},
 	}
 
@@ -84,6 +89,9 @@ func TestEnvSettings(t *testing.T) {
 			if settings.KubeContext != tt.kcontext {
 				t.Errorf("expected kube-context %q, got %q", tt.kcontext, settings.KubeContext)
 			}
+			if settings.MaxHistory != tt.maxhistory {
+				t.Errorf("expected maxHistory %d, got %d", tt.maxhistory, settings.MaxHistory)
+			}
 		})
 	}
 }