From a02a598c33d951ddb4f1d2fd261bdcbca5607af0 Mon Sep 17 00:00:00 2001
From: Adam Reese <adam@reese.io>
Date: Mon, 14 May 2018 09:23:21 -0700
Subject: [PATCH] ref(tests): simplify cmd test setup/teardown

Ensure proper cleanup of `HELM_HOME` and `HELM_DEBUG`
---
 Makefile                                      |   2 +-
 cmd/helm/create.go                            |  10 +-
 cmd/helm/create_test.go                       |  59 ++----
 cmd/helm/delete_test.go                       |   4 +-
 cmd/helm/dependency_build.go                  |   7 +-
 cmd/helm/dependency_build_test.go             |  14 +-
 cmd/helm/dependency_test.go                   |   4 +-
 cmd/helm/dependency_update_test.go            |  33 +---
 cmd/helm/fetch_test.go                        |  18 +-
 cmd/helm/get_hooks_test.go                    |   4 +-
 cmd/helm/get_manifest_test.go                 |   4 +-
 cmd/helm/get_test.go                          |   4 +-
 cmd/helm/get_values_test.go                   |   4 +-
 cmd/helm/helm.go                              |  79 --------
 cmd/helm/helm_test.go                         | 177 +++++++-----------
 cmd/helm/history_test.go                      |   4 +-
 cmd/helm/init_test.go                         |   8 +-
 cmd/helm/install_test.go                      |   4 +-
 cmd/helm/list_test.go                         |   4 +-
 cmd/helm/package_test.go                      |  87 +++------
 cmd/helm/plugin_test.go                       |   7 +-
 cmd/helm/release_testing_test.go              |   4 +-
 cmd/helm/repo_add_test.go                     |  37 ++--
 cmd/helm/repo_index_test.go                   |   7 +-
 cmd/helm/repo_remove_test.go                  |  17 +-
 cmd/helm/repo_update_test.go                  |  32 +---
 cmd/helm/rollback_test.go                     |   4 +-
 cmd/helm/root.go                              | 102 ++++++++++
 cmd/helm/root_test.go                         |  93 +++++++++
 cmd/helm/search_test.go                       |  35 ++--
 cmd/helm/status_test.go                       |   4 +-
 .../output/upgrade-with-install-timeout.txt   |  44 -----
 .../testdata/output/upgrade-with-install.txt  |  44 -----
 .../output/upgrade-with-reset-values.txt      |  44 -----
 .../output/upgrade-with-reset-values2.txt     |  44 -----
 .../testdata/output/upgrade-with-timeout.txt  |  44 -----
 .../testdata/output/upgrade-with-wait.txt     |  44 -----
 cmd/helm/testdata/output/upgrade.txt          |  44 -----
 cmd/helm/upgrade_test.go                      |  29 ++-
 cmd/helm/version_test.go                      |   4 +-
 internal/test/test.go                         |   4 +-
 pkg/helm/environment/environment_test.go      |   7 +-
 42 files changed, 413 insertions(+), 811 deletions(-)
 create mode 100644 cmd/helm/root.go
 create mode 100644 cmd/helm/root_test.go

diff --git a/Makefile b/Makefile
index 32bef5c6d..394a4a2e0 100644
--- a/Makefile
+++ b/Makefile
@@ -74,7 +74,7 @@ test: test-unit
 test-unit:
 	@echo
 	@echo "==> Running unit tests <=="
-	HELM_HOME=/no/such/dir $(GO) test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS)
+	HELM_HOME=/no_such_dir $(GO) test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS)
 
 .PHONY: test-style
 test-style:
diff --git a/cmd/helm/create.go b/cmd/helm/create.go
index 39ec211aa..6a099454a 100644
--- a/cmd/helm/create.go
+++ b/cmd/helm/create.go
@@ -26,7 +26,6 @@ import (
 
 	"k8s.io/helm/pkg/chartutil"
 	"k8s.io/helm/pkg/hapi/chart"
-	"k8s.io/helm/pkg/helm/helmpath"
 )
 
 const createDesc = `
@@ -51,11 +50,7 @@ will be overwritten, but other files will be left alone.
 
 type createOptions struct {
 	starter string // --starter
-
-	// args
-	name string
-
-	home helmpath.Home
+	name    string
 }
 
 func newCreateCmd(out io.Writer) *cobra.Command {
@@ -66,7 +61,6 @@ func newCreateCmd(out io.Writer) *cobra.Command {
 		Short: "create a new chart with the given name",
 		Long:  createDesc,
 		RunE: func(cmd *cobra.Command, args []string) error {
-			o.home = settings.Home
 			if len(args) == 0 {
 				return errors.New("the name of the new chart is required")
 			}
@@ -93,7 +87,7 @@ func (o *createOptions) run(out io.Writer) error {
 
 	if o.starter != "" {
 		// Create from the starter
-		lstarter := filepath.Join(o.home.Starters(), o.starter)
+		lstarter := filepath.Join(settings.Home.Starters(), o.starter)
 		return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter)
 	}
 
diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go
index bf3fdc342..5ec69f678 100644
--- a/cmd/helm/create_test.go
+++ b/cmd/helm/create_test.go
@@ -28,23 +28,10 @@ import (
 )
 
 func TestCreateCmd(t *testing.T) {
-	cname := "testchart"
-	// Make a temp dir
-	tdir, err := ioutil.TempDir("", "helm-create-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tdir)
+	tdir := testTempDir(t)
+	defer testChdir(t, tdir)()
 
-	// CD into it
-	pwd, err := os.Getwd()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := os.Chdir(tdir); err != nil {
-		t.Fatal(err)
-	}
-	defer os.Chdir(pwd)
+	cname := "testchart"
 
 	// Run a create
 	if _, err := executeCommand(nil, "create "+cname); err != nil {
@@ -73,28 +60,17 @@ func TestCreateCmd(t *testing.T) {
 }
 
 func TestCreateStarterCmd(t *testing.T) {
+	defer resetEnv()()
+
 	cname := "testchart"
 	// Make a temp dir
-	tdir, err := ioutil.TempDir("", "helm-create-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tdir)
-
-	thome, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(thome.String())
-		cleanup()
-	}()
+	tdir := testTempDir(t)
 
-	settings.Home = thome
+	hh := testHelmHome(t)
+	settings.Home = hh
 
 	// Create a starter.
-	starterchart := filepath.Join(thome.String(), "starters")
+	starterchart := hh.Starters()
 	os.Mkdir(starterchart, 0755)
 	if dest, err := chartutil.Create(&chart.Metadata{Name: "starterchart"}, starterchart); err != nil {
 		t.Fatalf("Could not create chart: %s", err)
@@ -106,18 +82,10 @@ func TestCreateStarterCmd(t *testing.T) {
 		t.Fatalf("Could not write template: %s", err)
 	}
 
-	// CD into it
-	pwd, err := os.Getwd()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if err := os.Chdir(tdir); err != nil {
-		t.Fatal(err)
-	}
-	defer os.Chdir(pwd)
+	defer testChdir(t, tdir)()
 
 	// Run a create
-	if _, err := executeCommand(nil, fmt.Sprintf("--home=%s create --starter=starterchart %s", thome, cname)); err != nil {
+	if _, err := executeCommand(nil, fmt.Sprintf("--home=%s create --starter=starterchart %s", hh, cname)); err != nil {
 		t.Errorf("Failed to run create: %s", err)
 		return
 	}
@@ -149,9 +117,8 @@ func TestCreateStarterCmd(t *testing.T) {
 	for _, tpl := range c.Templates {
 		if tpl.Name == "templates/foo.tpl" {
 			found = true
-			data := tpl.Data
-			if string(data) != "test" {
-				t.Errorf("Expected template 'test', got %q", string(data))
+			if data := string(tpl.Data); data != "test" {
+				t.Errorf("Expected template 'test', got %q", data)
 			}
 		}
 	}
diff --git a/cmd/helm/delete_test.go b/cmd/helm/delete_test.go
index fffaccdd6..a391d995f 100644
--- a/cmd/helm/delete_test.go
+++ b/cmd/helm/delete_test.go
@@ -27,7 +27,7 @@ func TestDelete(t *testing.T) {
 
 	rels := []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})}
 
-	tests := []releaseCase{
+	tests := []cmdTestCase{
 		{
 			name:   "basic delete",
 			cmd:    "delete aeneas",
@@ -59,5 +59,5 @@ func TestDelete(t *testing.T) {
 			wantError: true,
 		},
 	}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/dependency_build.go b/cmd/helm/dependency_build.go
index 69ff3637d..7a9942087 100644
--- a/cmd/helm/dependency_build.go
+++ b/cmd/helm/dependency_build.go
@@ -22,7 +22,6 @@ import (
 
 	"k8s.io/helm/pkg/downloader"
 	"k8s.io/helm/pkg/getter"
-	"k8s.io/helm/pkg/helm/helmpath"
 )
 
 const dependencyBuildDesc = `
@@ -40,10 +39,7 @@ type dependencyBuildOptions struct {
 	keyring string // --keyring
 	verify  bool   // --verify
 
-	// args
 	chartpath string
-
-	helmhome helmpath.Home
 }
 
 func newDependencyBuildCmd(out io.Writer) *cobra.Command {
@@ -56,7 +52,6 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
 		Short: "rebuild the charts/ directory based on the requirements.lock file",
 		Long:  dependencyBuildDesc,
 		RunE: func(cmd *cobra.Command, args []string) error {
-			o.helmhome = settings.Home
 			if len(args) > 0 {
 				o.chartpath = args[0]
 			}
@@ -75,7 +70,7 @@ func (o *dependencyBuildOptions) run(out io.Writer) error {
 	man := &downloader.Manager{
 		Out:       out,
 		ChartPath: o.chartpath,
-		HelmHome:  o.helmhome,
+		HelmHome:  settings.Home,
 		Keyring:   o.keyring,
 		Getters:   getter.All(settings),
 	}
diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go
index b313f5c2c..234fc9b30 100644
--- a/cmd/helm/dependency_build_test.go
+++ b/cmd/helm/dependency_build_test.go
@@ -27,22 +27,14 @@ import (
 )
 
 func TestDependencyBuildCmd(t *testing.T) {
-	hh, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(hh.String())
-		cleanup()
-	}()
+	defer resetEnv()()
 
+	hh := testHelmHome(t)
 	settings.Home = hh
 
 	srv := repotest.NewServer(hh.String())
 	defer srv.Stop()
-	_, err = srv.CopyCharts("testdata/testcharts/*.tgz")
-	if err != nil {
+	if _, err := srv.CopyCharts("testdata/testcharts/*.tgz"); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/cmd/helm/dependency_test.go b/cmd/helm/dependency_test.go
index 27e64f28b..98dcab1a6 100644
--- a/cmd/helm/dependency_test.go
+++ b/cmd/helm/dependency_test.go
@@ -20,7 +20,7 @@ import (
 )
 
 func TestDependencyListCmd(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:      "No such chart",
 		cmd:       "dependency list /no/such/chart",
 		golden:    "output/dependency-list-no-chart.txt",
@@ -38,5 +38,5 @@ func TestDependencyListCmd(t *testing.T) {
 		cmd:    "dependency list testdata/testcharts/reqtest-0.1.0.tgz",
 		golden: "output/dependency-list-archive.txt",
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go
index f8d0537bc..212106af4 100644
--- a/cmd/helm/dependency_update_test.go
+++ b/cmd/helm/dependency_update_test.go
@@ -35,16 +35,9 @@ import (
 )
 
 func TestDependencyUpdateCmd(t *testing.T) {
-	hh, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(hh.String())
-		cleanup()
-	}()
+	defer resetEnv()()
 
+	hh := testHelmHome(t)
 	settings.Home = hh
 
 	srv := repotest.NewServer(hh.String())
@@ -125,16 +118,9 @@ func TestDependencyUpdateCmd(t *testing.T) {
 }
 
 func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
-	hh, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(hh.String())
-		cleanup()
-	}()
+	defer resetEnv()()
 
+	hh := testHelmHome(t)
 	settings.Home = hh
 
 	srv := repotest.NewServer(hh.String())
@@ -163,16 +149,9 @@ func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
 }
 
 func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
-	hh, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(hh.String())
-		cleanup()
-	}()
+	defer resetEnv()()
 
+	hh := testHelmHome(t)
 	settings.Home = hh
 
 	srv := repotest.NewServer(hh.String())
diff --git a/cmd/helm/fetch_test.go b/cmd/helm/fetch_test.go
index 66b688a41..86c0c5327 100644
--- a/cmd/helm/fetch_test.go
+++ b/cmd/helm/fetch_test.go
@@ -28,20 +28,14 @@ import (
 )
 
 func TestFetchCmd(t *testing.T) {
-	hh, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(hh.String())
-		cleanup()
-	}()
-	srv := repotest.NewServer(hh.String())
-	defer srv.Stop()
+	defer resetEnv()()
 
+	hh := testHelmHome(t)
 	settings.Home = hh
 
+	srv := repotest.NewServer(hh.String())
+	defer srv.Stop()
+
 	// all flags will get "--home=TMDIR -d outdir" appended.
 	tests := []struct {
 		name         string
@@ -131,7 +125,7 @@ func TestFetchCmd(t *testing.T) {
 	}
 
 	for _, tt := range tests {
-		outdir := filepath.Join(hh.String(), "testout")
+		outdir := hh.Path("testout")
 		os.RemoveAll(outdir)
 		os.Mkdir(outdir, 0755)
 
diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go
index 1af2fdbd3..001c826b6 100644
--- a/cmd/helm/get_hooks_test.go
+++ b/cmd/helm/get_hooks_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 func TestGetHooks(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "get hooks with release",
 		cmd:    "get hooks aeneas",
 		golden: "output/get-hooks.txt",
@@ -35,5 +35,5 @@ func TestGetHooks(t *testing.T) {
 		golden:    "output/get-hooks-no-args.txt",
 		wantError: true,
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go
index 08160a71b..da29aee1d 100644
--- a/cmd/helm/get_manifest_test.go
+++ b/cmd/helm/get_manifest_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 func TestGetManifest(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "get manifest with release",
 		cmd:    "get manifest juno",
 		golden: "output/get-manifest.txt",
@@ -35,5 +35,5 @@ func TestGetManifest(t *testing.T) {
 		golden:    "output/get-manifest-no-args.txt",
 		wantError: true,
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go
index 9a00bb356..1df178aa8 100644
--- a/cmd/helm/get_test.go
+++ b/cmd/helm/get_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 func TestGetCmd(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "get with a release",
 		cmd:    "get thomas-guide",
 		golden: "output/get-release.txt",
@@ -35,5 +35,5 @@ func TestGetCmd(t *testing.T) {
 		golden:    "output/get-no-args.txt",
 		wantError: true,
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go
index bcef9c432..a9e81dbce 100644
--- a/cmd/helm/get_values_test.go
+++ b/cmd/helm/get_values_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 func TestGetValuesCmd(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "get values with a release",
 		cmd:    "get values thomas-guide",
 		golden: "output/get-values.txt",
@@ -35,5 +35,5 @@ func TestGetValuesCmd(t *testing.T) {
 		golden:    "output/get-values-args.txt",
 		wantError: true,
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go
index 1451fac37..1e58be85f 100644
--- a/cmd/helm/helm.go
+++ b/cmd/helm/helm.go
@@ -18,14 +18,12 @@ package main // import "k8s.io/helm/cmd/helm"
 
 import (
 	"fmt"
-	"io"
 	"log"
 	"os"
 	"strings"
 	"sync"
 
 	"github.com/pkg/errors"
-	"github.com/spf13/cobra"
 	// Import to initialize client auth plugins.
 	_ "k8s.io/client-go/plugin/pkg/client/auth"
 	"k8s.io/client-go/tools/clientcmd"
@@ -42,83 +40,6 @@ var (
 	configOnce sync.Once
 )
 
-var globalUsage = `The Kubernetes package manager
-
-To begin working with Helm, run the 'helm init' command:
-
-	$ helm init
-
-This will set up any necessary local configuration.
-
-Common actions from this point include:
-
-- helm search:    search for charts
-- helm fetch:     download a chart to your local directory to view
-- helm install:   upload the chart to Kubernetes
-- helm list:      list releases of charts
-
-Environment:
-  $HELM_HOME          set an alternative location for Helm files. By default, these are stored in ~/.helm
-  $HELM_NO_PLUGINS    disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
-  $KUBECONFIG         set an alternative Kubernetes configuration file (default "~/.kube/config")
-`
-
-func newRootCmd(c helm.Interface, out io.Writer, args []string) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:          "helm",
-		Short:        "The Helm package manager for Kubernetes.",
-		Long:         globalUsage,
-		SilenceUsage: true,
-	}
-	flags := cmd.PersistentFlags()
-
-	settings.AddFlags(flags)
-
-	cmd.AddCommand(
-		// chart commands
-		newCreateCmd(out),
-		newDependencyCmd(out),
-		newFetchCmd(out),
-		newInspectCmd(out),
-		newLintCmd(out),
-		newPackageCmd(out),
-		newRepoCmd(out),
-		newSearchCmd(out),
-		newVerifyCmd(out),
-
-		// release commands
-		newDeleteCmd(c, out),
-		newGetCmd(c, out),
-		newHistoryCmd(c, out),
-		newInstallCmd(c, out),
-		newListCmd(c, out),
-		newReleaseTestCmd(c, out),
-		newRollbackCmd(c, out),
-		newStatusCmd(c, out),
-		newUpgradeCmd(c, out),
-
-		newCompletionCmd(out),
-		newHomeCmd(out),
-		newInitCmd(out),
-		newPluginCmd(out),
-		newTemplateCmd(out),
-		newVersionCmd(out),
-
-		// Hidden documentation generator command: 'helm docs'
-		newDocsCmd(out),
-	)
-
-	flags.Parse(args)
-
-	// set defaults from environment
-	settings.Init(flags)
-
-	// Find and add plugins
-	loadPlugins(cmd, out)
-
-	return cmd
-}
-
 func init() {
 	log.SetFlags(log.Lshortfile)
 }
diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go
index 46cfeddb8..f1d7563af 100644
--- a/cmd/helm/helm_test.go
+++ b/cmd/helm/helm_test.go
@@ -20,12 +20,10 @@ import (
 	"bytes"
 	"io/ioutil"
 	"os"
-	"path/filepath"
 	"strings"
 	"testing"
 
 	shellwords "github.com/mattn/go-shellwords"
-	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"k8s.io/helm/internal/test"
@@ -35,30 +33,40 @@ import (
 	"k8s.io/helm/pkg/repo"
 )
 
-func executeCommand(c helm.Interface, cmd string) (string, error) {
-	_, output, err := executeCommandC(c, cmd)
-	return output, err
-}
+// base temp directory
+var testingDir string
 
-func executeCommandC(client helm.Interface, cmd string) (*cobra.Command, string, error) {
-	args, err := shellwords.Parse(cmd)
+func init() {
+	var err error
+	testingDir, err = ioutil.TempDir(testingDir, "helm")
 	if err != nil {
-		return nil, "", err
+		panic(err)
 	}
-	buf := new(bytes.Buffer)
-	root := newRootCmd(client, buf, args)
-	root.SetOutput(buf)
-	root.SetArgs(args)
+}
 
-	c, err := root.ExecuteC()
+func TestMain(m *testing.M) {
+	os.Unsetenv("HELM_HOME")
 
-	return c, buf.String(), err
+	exitCode := m.Run()
+	os.RemoveAll(testingDir)
+	os.Exit(exitCode)
 }
 
-func testReleaseCmd(t *testing.T, tests []releaseCase) {
+func testTempDir(t *testing.T) string {
+	t.Helper()
+	d, err := ioutil.TempDir(testingDir, "helm")
+	if err != nil {
+		t.Fatal(err)
+	}
+	return d
+}
+
+func runTestCmd(t *testing.T, tests []cmdTestCase) {
 	t.Helper()
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			defer resetEnv()()
+
 			c := &helm.FakeClient{
 				Rels:          tt.rels,
 				TestRunStatus: tt.testRunStatus,
@@ -74,8 +82,8 @@ func testReleaseCmd(t *testing.T, tests []releaseCase) {
 	}
 }
 
-// releaseCase describes a test case that works with releases.
-type releaseCase struct {
+// cmdTestCase describes a test case that works with releases.
+type cmdTestCase struct {
 	name      string
 	cmd       string
 	golden    string
@@ -85,29 +93,28 @@ type releaseCase struct {
 	testRunStatus map[string]release.TestRunStatus
 }
 
-// tempHelmHome sets up a Helm Home in a temp dir.
-//
-// This does not clean up the directory. You must do that yourself.
-// You  must also set helmHome yourself.
-func tempHelmHome(t *testing.T) (helmpath.Home, error) {
-	oldhome := settings.Home
-	dir, err := ioutil.TempDir("", "helm_home-")
+func executeCommand(c helm.Interface, cmd string) (string, error) {
+	_, output, err := executeCommandC(c, cmd)
+	return output, err
+}
+
+func executeCommandC(client helm.Interface, cmd string) (*cobra.Command, string, error) {
+	args, err := shellwords.Parse(cmd)
 	if err != nil {
-		return helmpath.Home("n/"), err
+		return nil, "", err
 	}
+	buf := new(bytes.Buffer)
+	root := newRootCmd(client, buf, args)
+	root.SetOutput(buf)
+	root.SetArgs(args)
 
-	settings.Home = helmpath.Home(dir)
-	if err := ensureTestHome(t, settings.Home); err != nil {
-		return helmpath.Home("n/"), err
-	}
-	settings.Home = oldhome
-	return helmpath.Home(dir), nil
+	c, err := root.ExecuteC()
+
+	return c, buf.String(), err
 }
 
 // ensureTestHome creates a home directory like ensureHome, but without remote references.
-//
-// t is used only for logging.
-func ensureTestHome(t *testing.T, home helmpath.Home) error {
+func ensureTestHome(t *testing.T, home helmpath.Home) {
 	t.Helper()
 	for _, p := range []string{
 		home.String(),
@@ -117,7 +124,7 @@ func ensureTestHome(t *testing.T, home helmpath.Home) error {
 		home.Starters(),
 	} {
 		if err := os.MkdirAll(p, 0755); err != nil {
-			return errors.Wrapf(err, "could not create %s", p)
+			t.Fatal(err)
 		}
 	}
 
@@ -130,96 +137,30 @@ func ensureTestHome(t *testing.T, home helmpath.Home) error {
 			Cache: "charts-index.yaml",
 		})
 		if err := rf.WriteFile(repoFile, 0644); err != nil {
-			return err
+			t.Fatal(err)
 		}
 	}
 	if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate {
 		t.Log("Updating repository file format...")
 		if err := r.WriteFile(repoFile, 0644); err != nil {
-			return err
+			t.Fatal(err)
 		}
 	}
-
 	t.Logf("$HELM_HOME has been configured at %s.\n", home)
-	return nil
-
 }
 
-func TestRootCmd(t *testing.T) {
-	cleanup := resetEnv()
-	defer cleanup()
-
-	tests := []struct {
-		name, args, home string
-		envars           map[string]string
-	}{
-		{
-			name: "defaults",
-			args: "home",
-			home: filepath.Join(os.Getenv("HOME"), "/.helm"),
-		},
-		{
-			name: "with --home set",
-			args: "--home /foo",
-			home: "/foo",
-		},
-		{
-			name: "subcommands with --home set",
-			args: "home --home /foo",
-			home: "/foo",
-		},
-		{
-			name:   "with $HELM_HOME set",
-			args:   "home",
-			envars: map[string]string{"HELM_HOME": "/bar"},
-			home:   "/bar",
-		},
-		{
-			name:   "subcommands with $HELM_HOME set",
-			args:   "home",
-			envars: map[string]string{"HELM_HOME": "/bar"},
-			home:   "/bar",
-		},
-		{
-			name:   "with $HELM_HOME and --home set",
-			args:   "home --home /foo",
-			envars: map[string]string{"HELM_HOME": "/bar"},
-			home:   "/foo",
-		},
-	}
-
-	// ensure not set locally
-	os.Unsetenv("HELM_HOME")
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			defer os.Unsetenv("HELM_HOME")
-
-			for k, v := range tt.envars {
-				os.Setenv(k, v)
-			}
-
-			cmd, _, err := executeCommandC(nil, tt.args)
-			if err != nil {
-				t.Fatalf("unexpected error: %s", err)
-			}
-
-			if settings.Home.String() != tt.home {
-				t.Errorf("expected home %q, got %q", tt.home, settings.Home)
-			}
-			homeFlag := cmd.Flag("home").Value.String()
-			homeFlag = os.ExpandEnv(homeFlag)
-			if homeFlag != tt.home {
-				t.Errorf("expected home %q, got %q", tt.home, homeFlag)
-			}
-		})
-	}
+// testHelmHome sets up a Helm Home in a temp dir.
+func testHelmHome(t *testing.T) helmpath.Home {
+	t.Helper()
+	dir := helmpath.Home(testTempDir(t))
+	ensureTestHome(t, dir)
+	return dir
 }
 
 func resetEnv() func() {
-	origSettings := settings
-	origEnv := os.Environ()
+	origSettings, origEnv := settings, os.Environ()
 	return func() {
+		os.Clearenv()
 		settings = origSettings
 		for _, pair := range origEnv {
 			kv := strings.SplitN(pair, "=", 2)
@@ -227,3 +168,15 @@ func resetEnv() func() {
 		}
 	}
 }
+
+func testChdir(t *testing.T, dir string) func() {
+	t.Helper()
+	old, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := os.Chdir(dir); err != nil {
+		t.Fatal(err)
+	}
+	return func() { os.Chdir(old) }
+}
diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go
index 84fcfe691..3d7786e04 100644
--- a/cmd/helm/history_test.go
+++ b/cmd/helm/history_test.go
@@ -32,7 +32,7 @@ func TestHistoryCmd(t *testing.T) {
 		})
 	}
 
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name: "get history for release",
 		cmd:  "history angry-bird",
 		rels: []*rpb.Release{
@@ -67,5 +67,5 @@ func TestHistoryCmd(t *testing.T) {
 		},
 		golden: "output/history.json",
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go
index 5ee148bae..5f6724b28 100644
--- a/cmd/helm/init_test.go
+++ b/cmd/helm/init_test.go
@@ -18,7 +18,6 @@ package main
 
 import (
 	"bytes"
-	"io/ioutil"
 	"os"
 	"testing"
 
@@ -26,14 +25,9 @@ import (
 )
 
 func TestEnsureHome(t *testing.T) {
-	home, err := ioutil.TempDir("", "helm_home")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(home)
+	hh := helmpath.Home(testTempDir(t))
 
 	b := bytes.NewBuffer(nil)
-	hh := helmpath.Home(home)
 	settings.Home = hh
 	if err := ensureDirectories(hh, b); err != nil {
 		t.Error(err)
diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go
index 397805345..298edb5f2 100644
--- a/cmd/helm/install_test.go
+++ b/cmd/helm/install_test.go
@@ -23,7 +23,7 @@ import (
 )
 
 func TestInstall(t *testing.T) {
-	tests := []releaseCase{
+	tests := []cmdTestCase{
 		// Install, base case
 		{
 			name:   "basic install",
@@ -120,7 +120,7 @@ func TestInstall(t *testing.T) {
 		},
 	}
 
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
 
 type nameTemplateTestCase struct {
diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go
index efffaf070..2a331eb01 100644
--- a/cmd/helm/list_test.go
+++ b/cmd/helm/list_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 func TestListCmd(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name: "with a release",
 		cmd:  "list",
 		rels: []*release.Release{
@@ -111,5 +111,5 @@ func TestListCmd(t *testing.T) {
 		golden: "output/list-with-old-releases.txt",
 	}}
 
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go
index ead7c7fb1..d7f0e0f9d 100644
--- a/cmd/helm/package_test.go
+++ b/cmd/helm/package_test.go
@@ -53,6 +53,7 @@ func TestSetVersion(t *testing.T) {
 }
 
 func TestPackage(t *testing.T) {
+	defer resetEnv()()
 
 	tests := []struct {
 		name    string
@@ -132,32 +133,20 @@ func TestPackage(t *testing.T) {
 		},
 	}
 
-	// Because these tests are destructive, we run them in a tempdir.
 	origDir, err := os.Getwd()
 	if err != nil {
 		t.Fatal(err)
 	}
-	tmp, err := ioutil.TempDir("", "helm-package-test-")
-	if err != nil {
-		t.Fatal(err)
-	}
+	tmp := testTempDir(t)
 
 	t.Logf("Running tests in %s", tmp)
-	if err := os.Chdir(tmp); err != nil {
-		t.Fatal(err)
-	}
+	defer testChdir(t, tmp)()
 
 	if err := os.Mkdir("toot", 0777); err != nil {
 		t.Fatal(err)
 	}
 
 	ensureTestHome(t, helmpath.Home(tmp))
-	cleanup := resetEnv()
-	defer func() {
-		os.Chdir(origDir)
-		os.RemoveAll(tmp)
-		cleanup()
-	}()
 
 	settings.Home = helmpath.Home(tmp)
 
@@ -210,22 +199,14 @@ func TestPackage(t *testing.T) {
 }
 
 func TestSetAppVersion(t *testing.T) {
+	defer resetEnv()()
+
 	var ch *chart.Chart
 	expectedAppVersion := "app-version-foo"
-	tmp, _ := ioutil.TempDir("", "helm-package-app-version-")
+	tmp := testTempDir(t)
 
-	thome, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(tmp)
-		os.RemoveAll(thome.String())
-		cleanup()
-	}()
-
-	settings.Home = helmpath.Home(thome)
+	hh := testHelmHome(t)
+	settings.Home = helmpath.Home(hh)
 
 	c := newPackageCmd(&bytes.Buffer{})
 	flags := map[string]string{
@@ -233,8 +214,7 @@ func TestSetAppVersion(t *testing.T) {
 		"app-version": expectedAppVersion,
 	}
 	setFlags(c, flags)
-	err = c.RunE(c, []string{"testdata/testcharts/alpine"})
-	if err != nil {
+	if err := c.RunE(c, []string{"testdata/testcharts/alpine"}); err != nil {
 		t.Errorf("unexpected error %q", err)
 	}
 
@@ -244,7 +224,7 @@ func TestSetAppVersion(t *testing.T) {
 	} else if fi.Size() == 0 {
 		t.Errorf("file %q has zero bytes.", chartPath)
 	}
-	ch, err = chartutil.Load(chartPath)
+	ch, err := chartutil.Load(chartPath)
 	if err != nil {
 		t.Errorf("unexpected error loading packaged chart: %v", err)
 	}
@@ -254,6 +234,8 @@ func TestSetAppVersion(t *testing.T) {
 }
 
 func TestPackageValues(t *testing.T) {
+	defer resetEnv()()
+
 	testCases := []struct {
 		desc               string
 		args               []string
@@ -288,26 +270,13 @@ func TestPackageValues(t *testing.T) {
 		},
 	}
 
-	thome, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(thome.String())
-		cleanup()
-	}()
-
-	settings.Home = thome
+	hh := testHelmHome(t)
+	settings.Home = hh
 
 	for _, tc := range testCases {
 		var files []string
 		for _, contents := range tc.valuefilesContents {
-			f, err := createValuesFile(contents)
-			if err != nil {
-				t.Errorf("%q unexpected error creating temporary values file: %q", tc.desc, err)
-			}
-			defer os.RemoveAll(filepath.Dir(f))
+			f := createValuesFile(t, contents)
 			files = append(files, f)
 		}
 		valueFiles := strings.Join(files, ",")
@@ -322,11 +291,7 @@ func TestPackageValues(t *testing.T) {
 }
 
 func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) {
-	outputDir, err := ioutil.TempDir("", "helm-package")
-	if err != nil {
-		t.Errorf("unexpected error creating temporary output directory: %q", err)
-	}
-	defer os.RemoveAll(outputDir)
+	outputDir := testTempDir(t)
 
 	if len(flags) == 0 {
 		flags = make(map[string]string)
@@ -339,16 +304,14 @@ func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[str
 
 	cmd := newPackageCmd(&bytes.Buffer{})
 	setFlags(cmd, flags)
-	err = cmd.RunE(cmd, args)
-	if err != nil {
+	if err := cmd.RunE(cmd, args); err != nil {
 		t.Errorf("unexpected error: %q", err)
 	}
 
 	outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
 	verifyOutputChartExists(t, outputFile)
 
-	var actual chartutil.Values
-	actual, err = getChartValues(outputFile)
+	actual, err := getChartValues(outputFile)
 	if err != nil {
 		t.Errorf("unexpected error extracting chart values: %q", err)
 	}
@@ -356,19 +319,15 @@ func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[str
 	verifyValues(t, actual, expected)
 }
 
-func createValuesFile(data string) (string, error) {
-	outputDir, err := ioutil.TempDir("", "values-file")
-	if err != nil {
-		return "", err
-	}
+func createValuesFile(t *testing.T, data string) string {
+	outputDir := testTempDir(t)
 
 	outputFile := filepath.Join(outputDir, "values.yaml")
-	if err = ioutil.WriteFile(outputFile, []byte(data), 0755); err != nil {
-		os.RemoveAll(outputFile)
-		return "", err
+	if err := ioutil.WriteFile(outputFile, []byte(data), 0755); err != nil {
+		t.Fatalf("err: %s", err)
 	}
 
-	return outputFile, nil
+	return outputFile
 }
 
 func getChartValues(chartPath string) (chartutil.Values, error) {
diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go
index 707616f5a..32af2cd21 100644
--- a/cmd/helm/plugin_test.go
+++ b/cmd/helm/plugin_test.go
@@ -62,8 +62,7 @@ func TestManuallyProcessArgs(t *testing.T) {
 }
 
 func TestLoadPlugins(t *testing.T) {
-	cleanup := resetEnv()
-	defer cleanup()
+	defer resetEnv()()
 
 	settings.Home = "testdata/helmhome"
 
@@ -133,8 +132,7 @@ func TestLoadPlugins(t *testing.T) {
 }
 
 func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
-	cleanup := resetEnv()
-	defer cleanup()
+	defer resetEnv()()
 
 	settings.Home = "testdata/helmhome"
 
@@ -151,6 +149,7 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
 }
 
 func TestSetupEnv(t *testing.T) {
+	defer resetEnv()()
 	name := "pequod"
 	settings.Home = helmpath.Home("testdata/helmhome")
 	base := filepath.Join(settings.Home.Plugins(), name)
diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go
index 6213cda33..4f7392b93 100644
--- a/cmd/helm/release_testing_test.go
+++ b/cmd/helm/release_testing_test.go
@@ -23,7 +23,7 @@ import (
 )
 
 func TestReleaseTesting(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:          "basic test",
 		cmd:           "test example-release",
 		testRunStatus: map[string]release.TestRunStatus{"PASSED: green lights everywhere": release.TestRunSuccess},
@@ -62,5 +62,5 @@ func TestReleaseTesting(t *testing.T) {
 			"PASSED: feel free to party again":            release.TestRunSuccess},
 		wantError: true,
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go
index 4f4f041da..406d9c756 100644
--- a/cmd/helm/repo_add_test.go
+++ b/cmd/helm/repo_add_test.go
@@ -26,50 +26,43 @@ import (
 )
 
 func TestRepoAddCmd(t *testing.T) {
-	srv, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
+	defer resetEnv()()
+
+	srv, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	cleanup := resetEnv()
 	defer func() {
 		srv.Stop()
-		os.RemoveAll(thome.String())
-		cleanup()
+		os.RemoveAll(hh.String())
 	}()
-	if err := ensureTestHome(t, thome); err != nil {
-		t.Fatal(err)
-	}
-
-	settings.Home = thome
+	ensureTestHome(t, hh)
+	settings.Home = hh
 
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "add a repository",
-		cmd:    fmt.Sprintf("repo add test-name %s --home %s", srv.URL(), thome),
+		cmd:    fmt.Sprintf("repo add test-name %s --home %s", srv.URL(), hh),
 		golden: "output/repo-add.txt",
 	}}
 
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
 
 func TestRepoAdd(t *testing.T) {
-	ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
+	defer resetEnv()()
+
+	ts, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	cleanup := resetEnv()
-	hh := thome
 	defer func() {
 		ts.Stop()
-		os.RemoveAll(thome.String())
-		cleanup()
+		os.RemoveAll(hh.String())
 	}()
-	if err := ensureTestHome(t, hh); err != nil {
-		t.Fatal(err)
-	}
-
-	settings.Home = thome
+	ensureTestHome(t, hh)
+	settings.Home = hh
 
 	const testRepoName = "test-name"
 
diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go
index 4d6313f6c..026e162f3 100644
--- a/cmd/helm/repo_index_test.go
+++ b/cmd/helm/repo_index_test.go
@@ -19,7 +19,6 @@ package main
 import (
 	"bytes"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"testing"
@@ -29,11 +28,7 @@ import (
 
 func TestRepoIndexCmd(t *testing.T) {
 
-	dir, err := ioutil.TempDir("", "helm-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(dir)
+	dir := testTempDir(t)
 
 	comp := filepath.Join(dir, "compressedchart-0.1.0.tgz")
 	if err := linkOrCopy("testdata/testcharts/compressedchart-0.1.0.tgz", comp); err != nil {
diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go
index 7b7ad5a5d..340af3ef8 100644
--- a/cmd/helm/repo_remove_test.go
+++ b/cmd/helm/repo_remove_test.go
@@ -22,29 +22,24 @@ import (
 	"strings"
 	"testing"
 
-	"k8s.io/helm/pkg/helm/helmpath"
 	"k8s.io/helm/pkg/repo"
 	"k8s.io/helm/pkg/repo/repotest"
 )
 
 func TestRepoRemove(t *testing.T) {
-	ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
+	defer resetEnv()()
+
+	ts, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	hh := helmpath.Home(thome)
-	cleanup := resetEnv()
 	defer func() {
 		ts.Stop()
-		os.RemoveAll(thome.String())
-		cleanup()
+		os.RemoveAll(hh.String())
 	}()
-	if err := ensureTestHome(t, hh); err != nil {
-		t.Fatal(err)
-	}
-
-	settings.Home = thome
+	ensureTestHome(t, hh)
+	settings.Home = hh
 
 	const testRepoName = "test-name"
 
diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go
index 582113314..b84cd7a2d 100644
--- a/cmd/helm/repo_update_test.go
+++ b/cmd/helm/repo_update_test.go
@@ -30,18 +30,10 @@ import (
 )
 
 func TestUpdateCmd(t *testing.T) {
-	thome, err := tempHelmHome(t)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	cleanup := resetEnv()
-	defer func() {
-		os.RemoveAll(thome.String())
-		cleanup()
-	}()
+	defer resetEnv()()
 
-	settings.Home = thome
+	hh := testHelmHome(t)
+	settings.Home = hh
 
 	out := bytes.NewBuffer(nil)
 	// Instead of using the HTTP updater, we provide our own for this test.
@@ -53,7 +45,7 @@ func TestUpdateCmd(t *testing.T) {
 	}
 	o := &repoUpdateOptions{
 		update: updater,
-		home:   helmpath.Home(thome),
+		home:   helmpath.Home(hh),
 	}
 	if err := o.run(out); err != nil {
 		t.Fatal(err)
@@ -65,23 +57,19 @@ func TestUpdateCmd(t *testing.T) {
 }
 
 func TestUpdateCharts(t *testing.T) {
-	ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
+	defer resetEnv()()
+
+	ts, hh, err := repotest.NewTempServer("testdata/testserver/*.*")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	hh := helmpath.Home(thome)
-	cleanup := resetEnv()
 	defer func() {
 		ts.Stop()
-		os.RemoveAll(thome.String())
-		cleanup()
+		os.RemoveAll(hh.String())
 	}()
-	if err := ensureTestHome(t, hh); err != nil {
-		t.Fatal(err)
-	}
-
-	settings.Home = thome
+	ensureTestHome(t, hh)
+	settings.Home = hh
 
 	r, err := repo.NewChartRepository(&repo.Entry{
 		Name:  "charts",
diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go
index fd78c4d0d..73624c31e 100644
--- a/cmd/helm/rollback_test.go
+++ b/cmd/helm/rollback_test.go
@@ -21,7 +21,7 @@ import (
 )
 
 func TestRollbackCmd(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "rollback a release",
 		cmd:    "rollback funny-honey 1",
 		golden: "output/rollback.txt",
@@ -39,5 +39,5 @@ func TestRollbackCmd(t *testing.T) {
 		golden:    "output/rollback-no-args.txt",
 		wantError: true,
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/root.go b/cmd/helm/root.go
new file mode 100644
index 000000000..4ee894588
--- /dev/null
+++ b/cmd/helm/root.go
@@ -0,0 +1,102 @@
+/*
+Copyright 2016 The Kubernetes Authors All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main // import "k8s.io/helm/cmd/helm"
+
+import (
+	"io"
+
+	"github.com/spf13/cobra"
+
+	"k8s.io/helm/pkg/helm"
+)
+
+var globalUsage = `The Kubernetes package manager
+
+To begin working with Helm, run the 'helm init' command:
+
+	$ helm init
+
+This will set up any necessary local configuration.
+
+Common actions from this point include:
+
+- helm search:    search for charts
+- helm fetch:     download a chart to your local directory to view
+- helm install:   upload the chart to Kubernetes
+- helm list:      list releases of charts
+
+Environment:
+  $HELM_HOME          set an alternative location for Helm files. By default, these are stored in ~/.helm
+  $HELM_NO_PLUGINS    disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
+  $KUBECONFIG         set an alternative Kubernetes configuration file (default "~/.kube/config")
+`
+
+func newRootCmd(c helm.Interface, out io.Writer, args []string) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:          "helm",
+		Short:        "The Helm package manager for Kubernetes.",
+		Long:         globalUsage,
+		SilenceUsage: true,
+	}
+	flags := cmd.PersistentFlags()
+
+	settings.AddFlags(flags)
+
+	cmd.AddCommand(
+		// chart commands
+		newCreateCmd(out),
+		newDependencyCmd(out),
+		newFetchCmd(out),
+		newInspectCmd(out),
+		newLintCmd(out),
+		newPackageCmd(out),
+		newRepoCmd(out),
+		newSearchCmd(out),
+		newVerifyCmd(out),
+
+		// release commands
+		newDeleteCmd(c, out),
+		newGetCmd(c, out),
+		newHistoryCmd(c, out),
+		newInstallCmd(c, out),
+		newListCmd(c, out),
+		newReleaseTestCmd(c, out),
+		newRollbackCmd(c, out),
+		newStatusCmd(c, out),
+		newUpgradeCmd(c, out),
+
+		newCompletionCmd(out),
+		newHomeCmd(out),
+		newInitCmd(out),
+		newPluginCmd(out),
+		newTemplateCmd(out),
+		newVersionCmd(out),
+
+		// Hidden documentation generator command: 'helm docs'
+		newDocsCmd(out),
+	)
+
+	flags.Parse(args)
+
+	// set defaults from environment
+	settings.Init(flags)
+
+	// Find and add plugins
+	loadPlugins(cmd, out)
+
+	return cmd
+}
diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go
new file mode 100644
index 000000000..4787a43b1
--- /dev/null
+++ b/cmd/helm/root_test.go
@@ -0,0 +1,93 @@
+/*
+Copyright 2016 The Kubernetes Authors All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+func TestRootCmd(t *testing.T) {
+	defer resetEnv()()
+
+	tests := []struct {
+		name, args, home string
+		envars           map[string]string
+	}{
+		{
+			name: "defaults",
+			args: "home",
+			home: filepath.Join(os.Getenv("HOME"), "/.helm"),
+		},
+		{
+			name: "with --home set",
+			args: "--home /foo",
+			home: "/foo",
+		},
+		{
+			name: "subcommands with --home set",
+			args: "home --home /foo",
+			home: "/foo",
+		},
+		{
+			name:   "with $HELM_HOME set",
+			args:   "home",
+			envars: map[string]string{"HELM_HOME": "/bar"},
+			home:   "/bar",
+		},
+		{
+			name:   "subcommands with $HELM_HOME set",
+			args:   "home",
+			envars: map[string]string{"HELM_HOME": "/bar"},
+			home:   "/bar",
+		},
+		{
+			name:   "with $HELM_HOME and --home set",
+			args:   "home --home /foo",
+			envars: map[string]string{"HELM_HOME": "/bar"},
+			home:   "/foo",
+		},
+	}
+
+	// ensure not set locally
+	os.Unsetenv("HELM_HOME")
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			defer os.Unsetenv("HELM_HOME")
+
+			for k, v := range tt.envars {
+				os.Setenv(k, v)
+			}
+
+			cmd, _, err := executeCommandC(nil, tt.args)
+			if err != nil {
+				t.Fatalf("unexpected error: %s", err)
+			}
+
+			if settings.Home.String() != tt.home {
+				t.Errorf("expected home %q, got %q", tt.home, settings.Home)
+			}
+			homeFlag := cmd.Flag("home").Value.String()
+			homeFlag = os.ExpandEnv(homeFlag)
+			if homeFlag != tt.home {
+				t.Errorf("expected home %q, got %q", tt.home, homeFlag)
+			}
+		})
+	}
+}
diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go
index 27d8c6c3e..9733942c0 100644
--- a/cmd/helm/search_test.go
+++ b/cmd/helm/search_test.go
@@ -21,51 +21,52 @@ import (
 )
 
 func TestSearchCmd(t *testing.T) {
-	tests := []releaseCase{{
+	defer resetEnv()()
+
+	setHome := func(cmd string) string {
+		return cmd + " --home=testdata/helmhome"
+	}
+
+	tests := []cmdTestCase{{
 		name:   "search for 'maria', expect one match",
-		cmd:    "search maria",
+		cmd:    setHome("search maria"),
 		golden: "output/search-single.txt",
 	}, {
 		name:   "search for 'alpine', expect two matches",
-		cmd:    "search alpine",
+		cmd:    setHome("search alpine"),
 		golden: "output/search-multiple.txt",
 	}, {
 		name:   "search for 'alpine' with versions, expect three matches",
-		cmd:    "search alpine --versions",
+		cmd:    setHome("search alpine --versions"),
 		golden: "output/search-multiple-versions.txt",
 	}, {
 		name:   "search for 'alpine' with version constraint, expect one match with version 0.1.0",
-		cmd:    "search alpine --version '>= 0.1, < 0.2'",
+		cmd:    setHome("search alpine --version '>= 0.1, < 0.2'"),
 		golden: "output/search-constraint.txt",
 	}, {
 		name:   "search for 'alpine' with version constraint, expect one match with version 0.1.0",
-		cmd:    "search alpine --versions --version '>= 0.1, < 0.2'",
+		cmd:    setHome("search alpine --versions --version '>= 0.1, < 0.2'"),
 		golden: "output/search-versions-constraint.txt",
 	}, {
 		name:   "search for 'alpine' with version constraint, expect one match with version 0.2.0",
-		cmd:    "search alpine --version '>= 0.1'",
+		cmd:    setHome("search alpine --version '>= 0.1'"),
 		golden: "output/search-constraint-single.txt",
 	}, {
 		name:   "search for 'alpine' with version constraint and --versions, expect two matches",
-		cmd:    "search alpine --versions --version '>= 0.1'",
+		cmd:    setHome("search alpine --versions --version '>= 0.1'"),
 		golden: "output/search-multiple-versions-constraints.txt",
 	}, {
 		name:   "search for 'syzygy', expect no matches",
-		cmd:    "search syzygy",
+		cmd:    setHome("search syzygy"),
 		golden: "output/search-not-found.txt",
 	}, {
 		name:   "search for 'alp[a-z]+', expect two matches",
-		cmd:    "search alp[a-z]+ --regexp",
+		cmd:    setHome("search alp[a-z]+ --regexp"),
 		golden: "output/search-regex.txt",
 	}, {
 		name:      "search for 'alp[', expect failure to compile regexp",
-		cmd:       "search alp[ --regexp",
+		cmd:       setHome("search alp[ --regexp"),
 		wantError: true,
 	}}
-
-	cleanup := resetEnv()
-	defer cleanup()
-
-	settings.Home = "testdata/helmhome"
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go
index c94d278a3..787b59e6f 100644
--- a/cmd/helm/status_test.go
+++ b/cmd/helm/status_test.go
@@ -32,7 +32,7 @@ func TestStatusCmd(t *testing.T) {
 		}}
 	}
 
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "get status of a deployed release",
 		cmd:    "status flummoxed-chickadee",
 		golden: "output/status.txt",
@@ -89,5 +89,5 @@ func TestStatusCmd(t *testing.T) {
 			},
 		}),
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/testdata/output/upgrade-with-install-timeout.txt b/cmd/helm/testdata/output/upgrade-with-install-timeout.txt
index 4a2233228..11d66d8d3 100644
--- a/cmd/helm/testdata/output/upgrade-with-install-timeout.txt
+++ b/cmd/helm/testdata/output/upgrade-with-install-timeout.txt
@@ -1,47 +1,3 @@
-REVISION: 1
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "crazy-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/testdata/output/upgrade-with-install.txt b/cmd/helm/testdata/output/upgrade-with-install.txt
index 90d69d65f..95cc1f625 100644
--- a/cmd/helm/testdata/output/upgrade-with-install.txt
+++ b/cmd/helm/testdata/output/upgrade-with-install.txt
@@ -1,47 +1,3 @@
-REVISION: 1
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "zany-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/testdata/output/upgrade-with-reset-values.txt b/cmd/helm/testdata/output/upgrade-with-reset-values.txt
index 4e5989e7f..53227b192 100644
--- a/cmd/helm/testdata/output/upgrade-with-reset-values.txt
+++ b/cmd/helm/testdata/output/upgrade-with-reset-values.txt
@@ -1,47 +1,3 @@
-REVISION: 4
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "funny-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/testdata/output/upgrade-with-reset-values2.txt b/cmd/helm/testdata/output/upgrade-with-reset-values2.txt
index 12c5d47a8..53227b192 100644
--- a/cmd/helm/testdata/output/upgrade-with-reset-values2.txt
+++ b/cmd/helm/testdata/output/upgrade-with-reset-values2.txt
@@ -1,47 +1,3 @@
-REVISION: 5
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "funny-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/testdata/output/upgrade-with-timeout.txt b/cmd/helm/testdata/output/upgrade-with-timeout.txt
index 156fa5951..53227b192 100644
--- a/cmd/helm/testdata/output/upgrade-with-timeout.txt
+++ b/cmd/helm/testdata/output/upgrade-with-timeout.txt
@@ -1,47 +1,3 @@
-REVISION: 3
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "funny-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/testdata/output/upgrade-with-wait.txt b/cmd/helm/testdata/output/upgrade-with-wait.txt
index 092c813a2..11d66d8d3 100644
--- a/cmd/helm/testdata/output/upgrade-with-wait.txt
+++ b/cmd/helm/testdata/output/upgrade-with-wait.txt
@@ -1,47 +1,3 @@
-REVISION: 2
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "crazy-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/testdata/output/upgrade.txt b/cmd/helm/testdata/output/upgrade.txt
index 2a31c7ea7..53227b192 100644
--- a/cmd/helm/testdata/output/upgrade.txt
+++ b/cmd/helm/testdata/output/upgrade.txt
@@ -1,47 +1,3 @@
-REVISION: 2
-RELEASED: Fri Sep  2 22:04:05 1977
-CHART: testUpgradeChart-0.1.0
-USER-SUPPLIED VALUES:
-name: "value"
-COMPUTED VALUES:
-affinity: {}
-fullnameOverride: ""
-image:
-  pullPolicy: IfNotPresent
-  repository: nginx
-  tag: stable
-ingress:
-  annotations: {}
-  enabled: false
-  hosts:
-  - chart-example.local
-  path: /
-  tls: []
-name: value
-nameOverride: ""
-nodeSelector: {}
-replicaCount: 1
-resources: {}
-service:
-  port: 80
-  type: ClusterIP
-tolerations: []
-
-HOOKS:
----
-# pre-install-hook
-apiVersion: v1
-kind: Job
-metadata:
-  annotations:
-    "helm.sh/hook": pre-install
-
-MANIFEST:
-apiVersion: v1
-kind: Secret
-metadata:
-  name: fixture
-
 Release "funny-bunny" has been upgraded. Happy Helming!
 LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC
 NAMESPACE: default
diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go
index 4b689297e..c75b50b1a 100644
--- a/cmd/helm/upgrade_test.go
+++ b/cmd/helm/upgrade_test.go
@@ -17,9 +17,6 @@ limitations under the License.
 package main
 
 import (
-	"io/ioutil"
-	"os"
-	"path/filepath"
 	"testing"
 
 	"k8s.io/helm/pkg/chartutil"
@@ -29,8 +26,7 @@ import (
 )
 
 func TestUpgradeCmd(t *testing.T) {
-	tmpChart, _ := ioutil.TempDir("testdata", "tmp")
-	defer os.RemoveAll(tmpChart)
+	tmpChart := testTempDir(t)
 	cfile := &chart.Metadata{
 		Name:        "testUpgradeChart",
 		Description: "A Helm chart for Kubernetes",
@@ -38,9 +34,12 @@ func TestUpgradeCmd(t *testing.T) {
 	}
 	chartPath, err := chartutil.Create(cfile, tmpChart)
 	if err != nil {
-		t.Errorf("Error creating chart for upgrade: %v", err)
+		t.Fatalf("Error creating chart for upgrade: %v", err)
+	}
+	ch, err := chartutil.Load(chartPath)
+	if err != nil {
+		t.Fatalf("Error loading chart: %v", err)
 	}
-	ch, _ := chartutil.Load(chartPath)
 	_ = helm.ReleaseMock(&helm.MockReleaseOptions{
 		Name:  "funny-bunny",
 		Chart: ch,
@@ -55,11 +54,11 @@ func TestUpgradeCmd(t *testing.T) {
 
 	chartPath, err = chartutil.Create(cfile, tmpChart)
 	if err != nil {
-		t.Errorf("Error creating chart: %v", err)
+		t.Fatalf("Error creating chart: %v", err)
 	}
 	ch, err = chartutil.Load(chartPath)
 	if err != nil {
-		t.Errorf("Error loading updated chart: %v", err)
+		t.Fatalf("Error loading updated chart: %v", err)
 	}
 
 	// update chart version again
@@ -71,22 +70,22 @@ func TestUpgradeCmd(t *testing.T) {
 
 	chartPath, err = chartutil.Create(cfile, tmpChart)
 	if err != nil {
-		t.Errorf("Error creating chart: %v", err)
+		t.Fatalf("Error creating chart: %v", err)
 	}
 	var ch2 *chart.Chart
 	ch2, err = chartutil.Load(chartPath)
 	if err != nil {
-		t.Errorf("Error loading updated chart: %v", err)
+		t.Fatalf("Error loading updated chart: %v", err)
 	}
 
-	missingDepsPath := filepath.Join("testdata/testcharts/chart-missing-deps")
-	badDepsPath := filepath.Join("testdata/testcharts/chart-bad-requirements")
+	missingDepsPath := "testdata/testcharts/chart-missing-deps"
+	badDepsPath := "testdata/testcharts/chart-bad-requirements"
 
 	relMock := func(n string, v int, ch *chart.Chart) *release.Release {
 		return helm.ReleaseMock(&helm.MockReleaseOptions{Name: n, Version: v, Chart: ch})
 	}
 
-	tests := []releaseCase{
+	tests := []cmdTestCase{
 		{
 			name:   "upgrade a release",
 			cmd:    "upgrade funny-bunny " + chartPath,
@@ -142,5 +141,5 @@ func TestUpgradeCmd(t *testing.T) {
 			wantError: true,
 		},
 	}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/version_test.go b/cmd/helm/version_test.go
index 166b78f93..0573dbf06 100644
--- a/cmd/helm/version_test.go
+++ b/cmd/helm/version_test.go
@@ -20,7 +20,7 @@ import (
 )
 
 func TestVersion(t *testing.T) {
-	tests := []releaseCase{{
+	tests := []cmdTestCase{{
 		name:   "default",
 		cmd:    "version",
 		golden: "output/version.txt",
@@ -29,5 +29,5 @@ func TestVersion(t *testing.T) {
 		cmd:    "version --template='Version: {{.Version}}'",
 		golden: "output/version-template.txt",
 	}}
-	testReleaseCmd(t, tests)
+	runTestCmd(t, tests)
 }
diff --git a/internal/test/test.go b/internal/test/test.go
index 6dbff7b90..a61c32a83 100644
--- a/internal/test/test.go
+++ b/internal/test/test.go
@@ -42,7 +42,7 @@ func AssertGoldenBytes(t TestingT, actual []byte, filename string) {
 	t.Helper()
 
 	if err := compare(actual, path(filename)); err != nil {
-		t.Fatalf("%+v", err)
+		t.Fatalf("%v", err)
 	}
 }
 
@@ -50,7 +50,7 @@ func AssertGoldenString(t TestingT, actual, filename string) {
 	t.Helper()
 
 	if err := compare([]byte(actual), path(filename)); err != nil {
-		t.Fatalf("%+v", err)
+		t.Fatalf("%v", err)
 	}
 }
 
diff --git a/pkg/helm/environment/environment_test.go b/pkg/helm/environment/environment_test.go
index ebc5822b4..3f506b099 100644
--- a/pkg/helm/environment/environment_test.go
+++ b/pkg/helm/environment/environment_test.go
@@ -71,11 +71,10 @@ func TestEnvSettings(t *testing.T) {
 		},
 	}
 
-	cleanup := resetEnv()
-	defer cleanup()
-
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			defer resetEnv()()
+
 			for k, v := range tt.envars {
 				os.Setenv(k, v)
 			}
@@ -103,8 +102,6 @@ func TestEnvSettings(t *testing.T) {
 			if settings.KubeContext != tt.kcontext {
 				t.Errorf("expected kube-context %q, got %q", tt.kcontext, settings.KubeContext)
 			}
-
-			cleanup()
 		})
 	}
 }
-- 
GitLab