Repository: helm/helm
Pull request #7096 (2019-11-27 16:59:50)

Allow tests to run on s390x
In addition to https://github.com/helm/helm/pull/6988, this will allow tests to run on s390x.


Patch:
diff --git a/KEYS b/KEYS
index 929938710..95e52f71c 100644
--- a/KEYS
+++ b/KEYS
@@ -622,3 +622,233 @@ BlA4kJPTfla4LmKRg/T/xow/naen/aM9mQCs7k2UAoeqNZ6IfQ6G5BZ81H9JNvHC
 beriLZDBuRy1LJRjBmZEz+UDBgZoR9oz5DOLh8dGVpkt
 =HZO9
 -----END PGP PUBLIC KEY BLOCK-----
+pub   rsa4096 2014-05-13 [SCEA]
+      ABA2529598F6626C420D335B62F49E747D911B60
+uid           [ unknown] Matt Butcher <matt.butcher@microsoft.com>
+sig 3        62F49E747D911B60 2017-08-11  Matt Butcher <matt.butcher@microsoft.com>
+sig          461449C25E36B98E 2018-12-12  Matthew Farina <matt@mattfarina.com>
+sig          1EF612347F8A9958 2018-12-12  Adam Reese <adam@reese.io>
+sig          2CDBBFBB37AE822A 2018-12-12  Adnan Abdulhussein <prydonius@gmail.com>
+uid           [ unknown] technosophos (keybase.io/technosophos) <technosophos@gmail.com>
+sig 3        62F49E747D911B60 2016-10-24  Matt Butcher <matt.butcher@microsoft.com>
+sig          461449C25E36B98E 2018-12-12  Matthew Farina <matt@mattfarina.com>
+sig          1EF612347F8A9958 2018-12-12  Adam Reese <adam@reese.io>
+sig          2CDBBFBB37AE822A 2018-12-12  Adnan Abdulhussein <prydonius@gmail.com>
+uid           [ unknown] keybase.io/technosophos <technosophos@keybase.io>
+sig 3        62F49E747D911B60 2014-05-13  Matt Butcher <matt.butcher@microsoft.com>
+sig          461449C25E36B98E 2018-12-12  Matthew Farina <matt@mattfarina.com>
+sig          1EF612347F8A9958 2018-12-12  Adam Reese <adam@reese.io>
+sig          2CDBBFBB37AE822A 2018-12-12  Adnan Abdulhussein <prydonius@gmail.com>
+sub   rsa2048 2014-05-13 [S] [expires: 2022-05-11]
+sig          62F49E747D911B60 2014-05-13  Matt Butcher <matt.butcher@microsoft.com>
+sub   rsa2048 2014-05-13 [E] [expires: 2022-05-11]
+sig          62F49E747D911B60 2014-05-13  Matt Butcher <matt.butcher@microsoft.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Comment: GPGTools - https://gpgtools.org
+
+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
+=nEJu
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/Makefile b/Makefile
index e48a20fd5..7d254e919 100644
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,16 @@
-BINDIR     := $(CURDIR)/bin
-DIST_DIRS  := find * -type d -exec
-TARGETS    := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64
-BINNAME    ?= helm
+BINDIR      := $(CURDIR)/bin
+DIST_DIRS   := find * -type d -exec
+TARGETS     := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
+TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-s390x.tar.gz linux-s390x.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256
+BINNAME     ?= helm
 
 GOPATH        = $(shell go env GOPATH)
 DEP           = $(GOPATH)/bin/dep
 GOX           = $(GOPATH)/bin/gox
 GOIMPORTS     = $(GOPATH)/bin/goimports
+ARCH          = $(shell uname -p)
 
-ACCEPTANCE_DIR:=$(GOPATH)/src/helm.sh/acceptance-testing
+ACCEPTANCE_DIR:=../acceptance-testing
 # To specify the subset of acceptance tests to run. '.' means all tests
 ACCEPTANCE_RUN_TESTS=.
 
@@ -39,10 +41,13 @@ ifneq ($(BINARY_VERSION),)
 	LDFLAGS += -X helm.sh/helm/v3/internal/version.version=${BINARY_VERSION}
 endif
 
+VERSION_METADATA = unreleased
 # Clear the "unreleased" string in BuildMetadata
 ifneq ($(GIT_TAG),)
-	LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=
+	VERSION_METADATA =
 endif
+
+LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=${VERSION_METADATA}
 LDFLAGS += -X helm.sh/helm/v3/internal/version.gitCommit=${GIT_COMMIT}
 LDFLAGS += -X helm.sh/helm/v3/internal/version.gitTreeState=${GIT_DIRTY}
 
@@ -63,7 +68,11 @@ $(BINDIR)/$(BINNAME): $(SRC)
 
 .PHONY: test
 test: build
+ifeq ($(ARCH),s390x)
+test: TESTFLAGS += -v
+else
 test: TESTFLAGS += -race -v
+endif
 test: test-style
 test: test-unit
 
@@ -138,6 +147,20 @@ dist:
 		$(DIST_DIRS) zip -r helm-${VERSION}-{}.zip {} \; \
 	)
 
+.PHONY: fetch-dist
+fetch-dist:
+	mkdir -p _dist
+	cd _dist && \
+	for obj in ${TARGET_OBJS} ; do \
+		curl -sSL -o helm-${VERSION}-$${obj} https://get.helm.sh/helm-${VERSION}-$${obj} ; \
+	done
+
+.PHONY: sign
+sign:
+	for f in _dist/*.{gz,zip,sha256} ; do \
+		gpg --armor --detach-sign $${f} ; \
+	done
+
 .PHONY: checksum
 checksum:
 	for f in _dist/*.{gz,zip} ; do \
diff --git a/OWNERS b/OWNERS
index 715c8993d..d7dac5514 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,6 +4,7 @@ maintainers:
   - fibonacci1729
   - hickeyma
   - jdolitsky
+  - marckhouzam
   - mattfarina
   - michelleN
   - prydonius
diff --git a/README.md b/README.md
index bb1e40ca6..745a60c2b 100644
--- a/README.md
+++ b/README.md
@@ -2,14 +2,14 @@
 
 [![CircleCI](https://circleci.com/gh/helm/helm.svg?style=shield)](https://circleci.com/gh/helm/helm)
 [![Go Report Card](https://goreportcard.com/badge/github.com/helm/helm)](https://goreportcard.com/report/github.com/helm/helm)
-[![GoDoc](https://godoc.org/helm.sh/helm?status.svg)](https://godoc.org/helm.sh/helm)
+[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/helm.sh/helm/v3)
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3131/badge)](https://bestpractices.coreinfrastructure.org/projects/3131)
 
 Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources.
 
 Use Helm to:
 
-- Find and use [popular software packaged as Helm Charts](https://github.com/helm/charts) to run in Kubernetes
+- Find and use [popular software packaged as Helm Charts](https://hub.helm.sh) to run in Kubernetes
 - Share your own applications as Helm Charts
 - Create reproducible builds of your Kubernetes applications
 - Intelligently manage your Kubernetes manifest files
diff --git a/cmd/helm/env.go b/cmd/helm/env.go
index 1b9cb4012..2687272ba 100644
--- a/cmd/helm/env.go
+++ b/cmd/helm/env.go
@@ -19,6 +19,7 @@ package main
 import (
 	"fmt"
 	"io"
+	"sort"
 
 	"helm.sh/helm/v3/pkg/cli"
 
@@ -55,8 +56,18 @@ type envOptions struct {
 }
 
 func (o *envOptions) run(out io.Writer) error {
-	for k, v := range o.settings.EnvVars() {
-		fmt.Printf("%s=\"%s\"\n", k, v)
+	envVars := o.settings.EnvVars()
+
+	// Sort the variables by alphabetical order.
+	// This allows for a constant output across calls to 'helm env'.
+	var keys []string
+	for k := range envVars {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	for _, k := range keys {
+		fmt.Printf("%s=\"%s\"\n", k, envVars[k])
 	}
 	return nil
 }
diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go
index 467abbd6e..aa22603f4 100644
--- a/cmd/helm/flags.go
+++ b/cmd/helm/flags.go
@@ -31,7 +31,7 @@ import (
 const outputFlag = "output"
 
 func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
-	f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL(can specify multiple)")
+	f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)")
 	f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
 	f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
 	f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go
index 2c3c9e401..112d5123f 100644
--- a/cmd/helm/helm.go
+++ b/cmd/helm/helm.go
@@ -68,8 +68,7 @@ func main() {
 	cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:])
 
 	if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), debug); err != nil {
-		debug("%+v", err)
-		os.Exit(1)
+		log.Fatal(err)
 	}
 
 	if err := cmd.Execute(); err != nil {
diff --git a/cmd/helm/install.go b/cmd/helm/install.go
index 40935db17..b75dfce74 100644
--- a/cmd/helm/install.go
+++ b/cmd/helm/install.go
@@ -17,6 +17,7 @@ limitations under the License.
 package main
 
 import (
+	"fmt"
 	"io"
 	"time"
 
@@ -130,11 +131,12 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
 	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
 	f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
-	f.BoolVar(&client.Replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
+	f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
 	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
 	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.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
 	f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
+	f.StringVar(&client.Description, "description", "", "add a custom description")
 	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
 	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart")
 	f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used")
@@ -181,6 +183,10 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
 		return nil, err
 	}
 
+	if chartRequested.Metadata.Deprecated {
+		fmt.Fprintln(out, "WARNING: This chart is deprecated")
+	}
+
 	if req := chartRequested.Metadata.Dependencies; req != nil {
 		// If CheckDependencies returns an error, we have unfulfilled dependencies.
 		// As of Helm 2.4.0, this is treated as a stopping condition:
diff --git a/cmd/helm/list.go b/cmd/helm/list.go
index 83edfe156..57fc4be3c 100644
--- a/cmd/helm/list.go
+++ b/cmd/helm/list.go
@@ -32,7 +32,7 @@ import (
 )
 
 var listHelp = `
-This command lists all of the releases.
+This command lists all of the releases for a specified namespace (uses current namespace context if namespace not specified).
 
 By default, it lists only releases that are deployed or failed. Flags like
 '--uninstalled' and '--all' will alter this behavior. Such flags can be combined:
@@ -96,8 +96,8 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format")
 	f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date")
 	f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order")
-	f.BoolVarP(&client.All, "all", "a", false, "show all releases, not just the ones marked deployed or failed")
-	f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases")
+	f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied")
+	f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)")
 	f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases")
 	f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled")
 	f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go
index ed99a5164..f2fb5c01d 100644
--- a/cmd/helm/load_plugins.go
+++ b/cmd/helm/load_plugins.go
@@ -151,8 +151,11 @@ func manuallyProcessArgs(args []string) ([]string, []string) {
 		case "--debug":
 			known = append(known, a)
 		case isKnown(a):
-			known = append(known, a, args[i+1])
+			known = append(known, a)
 			i++
+			if i < len(args) {
+				known = append(known, args[i])
+			}
 		default:
 			if knownArg(a) {
 				known = append(known, a)
diff --git a/cmd/helm/package.go b/cmd/helm/package.go
index dd4bb2d17..9f7961f95 100644
--- a/cmd/helm/package.go
+++ b/cmd/helm/package.go
@@ -36,10 +36,15 @@ This command packages a chart into a versioned chart archive file. If a path
 is given, this will look at that path for a chart (which must contain a
 Chart.yaml file) and then package that directory.
 
-If no path is given, this will look in the present working directory for a
-Chart.yaml file, and (if found) build the current directory into a chart.
-
 Versioned chart archives are used by Helm package repositories.
+
+To sign a chart, use the '--sign' flag. In most cases, you should also
+provide '--keyring path/to/secret/keys' and '--key keyname'.
+
+  $ helm package --sign ./mychart --key mykey --keyring ~/.gnupg/secring.gpg
+
+If '--keyring' is not specified, Helm usually defaults to the public keyring
+unless your environment is otherwise configured.
 `
 
 func newPackageCmd(out io.Writer) *cobra.Command {
diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go
index 1c84ad8d6..e6afce3d5 100644
--- a/cmd/helm/repo_add.go
+++ b/cmd/helm/repo_add.go
@@ -29,7 +29,7 @@ import (
 	"github.com/gofrs/flock"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
-	"gopkg.in/yaml.v2"
+	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v3/cmd/helm/require"
 	"helm.sh/helm/v3/pkg/getter"
diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go
index c1ecb1829..ea7a5db24 100644
--- a/cmd/helm/repo_remove.go
+++ b/cmd/helm/repo_remove.go
@@ -76,7 +76,12 @@ func (o *repoRemoveOptions) run(out io.Writer) error {
 }
 
 func removeRepoCache(root, name string) error {
-	idx := filepath.Join(root, helmpath.CacheIndexFile(name))
+	idx := filepath.Join(root, helmpath.CacheChartsFile(name))
+	if _, err := os.Stat(idx); err == nil {
+		os.Remove(idx)
+	}
+
+	idx = filepath.Join(root, helmpath.CacheIndexFile(name))
 	if _, err := os.Stat(idx); os.IsNotExist(err) {
 		return nil
 	} else if err != nil {
diff --git a/cmd/helm/root.go b/cmd/helm/root.go
index f0a034d45..bba3ff643 100644
--- a/cmd/helm/root.go
+++ b/cmd/helm/root.go
@@ -64,12 +64,12 @@ __helm_override_flags_to_kubectl_flags()
     # --kubeconfig, -n, --namespace stay the same for kubectl
     # --kube-context becomes --context for kubectl
     __helm_debug "${FUNCNAME[0]}: flags to convert: $1"
-    echo "$1" | sed s/kube-context/context/
+    echo "$1" | \sed s/kube-context/context/
 }
 
 __helm_get_repos()
 {
-    eval $(__helm_binary_name) repo list 2>/dev/null | tail +2 | cut -f1
+    eval $(__helm_binary_name) repo list 2>/dev/null | \tail -n +2 | \cut -f1
 }
 
 __helm_get_contexts()
@@ -125,7 +125,7 @@ __helm_zsh_comp_nospace() {
     # We only do this if there is a single choice left for completion
     # to reduce the times the user could be presented with the fake completion choice.
 
-    out=($(echo ${in[*]} | tr " " "\n" | \grep "^${cur}"))
+    out=($(echo ${in[*]} | \tr " " "\n" | \grep "^${cur}"))
     __helm_debug "${FUNCNAME[0]}: out is ${out[*]}"
 
     [ ${#out[*]} -eq 1 ] && out+=("${out}.")
@@ -139,13 +139,25 @@ __helm_zsh_comp_nospace() {
 __helm_list_charts()
 {
     __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
-    local repo url file out=() nospace=0 wantFiles=$1
+    local prefix chart repo url file out=() nospace=0 wantFiles=$1
 
     # Handle completions for repos
     for repo in $(__helm_get_repos); do
         if [[ "${cur}" =~ ^${repo}/.* ]]; then
             # We are doing completion from within a repo
-            out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | cut -f1 | \grep ^${cur})
+            local cacheFile=$(eval $(__helm_binary_name) env 2>/dev/null | \grep HELM_REPOSITORY_CACHE | \cut -d= -f2 | \sed s/\"//g)/${repo}-charts.txt
+            if [ -f "$cacheFile" ]; then
+                # Get the list of charts from the cached file
+                prefix=${cur#${repo}/}
+                for chart in $(\grep ^$prefix $cacheFile); do
+                    out+=(${repo}/${chart})
+                done
+            else
+                # If there is no cached list file, fallback to helm search, which is much slower
+                # This will happen after the caching feature is first installed but before the user
+                # does a 'helm repo update' to generate the cached list file.
+				out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | \cut -f1 | \grep ^${cur})
+            fi
             nospace=0
         elif [[ ${repo} =~ ^${cur}.* ]]; then
             # We are completing a repo name
@@ -236,7 +248,7 @@ __helm_list_plugins()
     __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
     local out
     # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3)
-    if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | tail +2 | cut -f1); then
+    if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | \tail -n +2 | \cut -f1); then
         COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
     fi
 }
@@ -418,7 +430,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
 		registry.ClientOptWriter(out),
 	)
 	if err != nil {
-		// TODO: dont panic here, refactor newRootCmd to return error
+		// TODO: don't panic here, refactor newRootCmd to return error
 		panic(err)
 	}
 	actionConfig.RegistryClient = registryClient
diff --git a/cmd/helm/status.go b/cmd/helm/status.go
index 92e12dbe0..92a947c26 100644
--- a/cmd/helm/status.go
+++ b/cmd/helm/status.go
@@ -31,12 +31,13 @@ import (
 	"helm.sh/helm/v3/pkg/release"
 )
 
+// NOTE: Keep the list of statuses up-to-date with pkg/release/status.go.
 var statusHelp = `
 This command shows the status of a named release.
 The status consists of:
 - last deployment time
 - k8s namespace in which the release lives
-- state of the release (can be: unknown, deployed, deleted, superseded, failed or deleting)
+- state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback)
 - list of resources that this release consists of, sorted by kind
 - details on last test suite run, if applicable
 - additional notes provided by the chart
diff --git a/cmd/helm/template.go b/cmd/helm/template.go
index 5312d798f..a47631c0d 100644
--- a/cmd/helm/template.go
+++ b/cmd/helm/template.go
@@ -44,6 +44,7 @@ faked locally. Additionally, none of the server-side testing of chart validity
 
 func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	var validate bool
+	var includeCrds bool
 	client := action.NewInstall(cfg)
 	valueOpts := &values.Options{}
 	var extraAPIs []string
@@ -67,7 +68,14 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 
 			var manifests bytes.Buffer
 
+			if includeCrds {
+				for _, f := range rel.Chart.CRDs() {
+					fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", f.Name, f.Data)
+				}
+			}
+
 			fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest))
+
 			if !client.DisableHooks {
 				for _, m := range rel.Hooks {
 					fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest)
@@ -120,6 +128,8 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates")
 	f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
 	f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install")
+	f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output")
+	f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall")
 	f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions")
 
 	return cmd
diff --git a/cmd/helm/testdata/testcharts/object-order/values.yaml b/cmd/helm/testdata/testcharts/object-order/values.yaml
new file mode 100644
index 000000000..e69de29bb
diff --git a/cmd/helm/uninstall.go b/cmd/helm/uninstall.go
index 27b5a8426..7096d7873 100644
--- a/cmd/helm/uninstall.go
+++ b/cmd/helm/uninstall.go
@@ -69,6 +69,7 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation")
 	f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history")
 	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
+	f.StringVar(&client.Description, "description", "", "add a custom description")
 
 	return cmd
 }
diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go
index eadc3b63d..acfc23198 100644
--- a/cmd/helm/upgrade.go
+++ b/cmd/helm/upgrade.go
@@ -127,6 +127,10 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 				}
 			}
 
+			if ch.Metadata.Deprecated {
+				fmt.Fprintln(out, "WARNING: This chart is deprecated")
+			}
+
 			rel, err := client.Run(args[0], ch, vals)
 			if err != nil {
 				return errors.Wrap(err, "UPGRADE FAILED")
@@ -156,6 +160,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.IntVar(&client.MaxHistory, "history-max", 10, "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")
 	addChartPathOptionsFlags(f, &client.ChartPathOptions)
 	addValueOptionsFlags(f, valueOpts)
 	bindOutputFlag(cmd, &outfmt)
diff --git a/cmd/helm/verify.go b/cmd/helm/verify.go
index 1cdd26290..d3ae517c9 100644
--- a/cmd/helm/verify.go
+++ b/cmd/helm/verify.go
@@ -27,7 +27,7 @@ import (
 const verifyDesc = `
 Verify that the given chart has a valid provenance file.
 
-Provenance files provide crytographic verification that a chart has not been
+Provenance files provide cryptographic verification that a chart has not been
 tampered with, and was packaged by a trusted provider.
 
 This command can be used to verify a local chart. Several other commands provide
diff --git a/code-of-conduct.md b/code-of-conduct.md
index 0d15c00cf..91ccaf035 100644
--- a/code-of-conduct.md
+++ b/code-of-conduct.md
@@ -1,3 +1,3 @@
-# Kubernetes Community Code of Conduct
+# Community Code of Conduct
 
-Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
+Helm follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
diff --git a/go.mod b/go.mod
index 57b342ed2..ed074074d 100644
--- a/go.mod
+++ b/go.mod
@@ -5,8 +5,8 @@ go 1.13
 require (
 	github.com/BurntSushi/toml v0.3.1
 	github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
-	github.com/Masterminds/semver/v3 v3.0.1
-	github.com/Masterminds/sprig/v3 v3.0.0
+	github.com/Masterminds/semver/v3 v3.0.3
+	github.com/Masterminds/sprig/v3 v3.0.2
 	github.com/Masterminds/vcs v1.13.0
 	github.com/Microsoft/go-winio v0.4.12 // indirect
 	github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
@@ -59,7 +59,6 @@ require (
 	google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
 	google.golang.org/grpc v1.24.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
-	gopkg.in/yaml.v2 v2.2.4
 	k8s.io/api v0.0.0-20191016110408-35e52d86657a
 	k8s.io/apiextensions-apiserver v0.0.0-20191016113550-5357c4baaf65
 	k8s.io/apimachinery v0.0.0-20191004115801-a2eda9f80ab8
diff --git a/go.sum b/go.sum
index 5659b06f8..629d466c2 100644
--- a/go.sum
+++ b/go.sum
@@ -25,10 +25,10 @@ github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm
 github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
 github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
-github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g=
-github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
+github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
+github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
+github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
 github.com/Masterminds/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA=
 github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
diff --git a/internal/experimental/registry/util.go b/internal/experimental/registry/util.go
index 3a4589d01..697a890e3 100644
--- a/internal/experimental/registry/util.go
+++ b/internal/experimental/registry/util.go
@@ -49,7 +49,7 @@ func shortDigest(digest string) string {
 	return digest
 }
 
-// timeAgo returns a human-readable timestamp respresenting time that has passed
+// timeAgo returns a human-readable timestamp representing time that has passed
 func timeAgo(t time.Time) string {
 	return units.HumanDuration(time.Now().UTC().Sub(t))
 }
diff --git a/internal/ignore/rules.go b/internal/ignore/rules.go
index c9aaeacca..9049aff0d 100644
--- a/internal/ignore/rules.go
+++ b/internal/ignore/rules.go
@@ -73,7 +73,7 @@ func Parse(file io.Reader) (*Rules, error) {
 	return r, s.Err()
 }
 
-// Ignore evalutes the file at the given path, and returns true if it should be ignored.
+// Ignore evaluates the file at the given path, and returns true if it should be ignored.
 //
 // Ignore evaluates path against the rules in order. Evaluation stops when a match
 // is found. Matching a negative rule will stop evaluation.
diff --git a/internal/monocular/search.go b/internal/monocular/search.go
index 61695f554..10e1f2136 100644
--- a/internal/monocular/search.go
+++ b/internal/monocular/search.go
@@ -58,7 +58,7 @@ type Chart struct {
 	Icon        string             `json:"icon"`
 }
 
-// Repo contains the name in monocular the the url for the repository
+// Repo contains the name in monocular the url for the repository
 type Repo struct {
 	Name string `json:"name"`
 	URL  string `json:"url"`
diff --git a/internal/version/version.go b/internal/version/version.go
index 131a2b130..22439d11b 100644
--- a/internal/version/version.go
+++ b/internal/version/version.go
@@ -33,7 +33,7 @@ var (
 	version = "v3.0"
 
 	// metadata is extra build time data
-	metadata = "unreleased"
+	metadata = ""
 	// gitCommit is the git sha1
 	gitCommit = ""
 	// gitTreeState is the state of the git tree
diff --git a/pkg/action/action.go b/pkg/action/action.go
index f74a25e41..9405cc401 100644
--- a/pkg/action/action.go
+++ b/pkg/action/action.go
@@ -66,7 +66,7 @@ var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+
 
 // Configuration injects the dependencies that all actions share.
 type Configuration struct {
-	// RESTClientGetter is an interface that loads Kuberbetes clients.
+	// RESTClientGetter is an interface that loads Kubernetes clients.
 	RESTClientGetter RESTClientGetter
 
 	// Releases stores records of releases.
@@ -167,8 +167,8 @@ func (c *Configuration) releaseContent(name string, version int) (*release.Relea
 // GetVersionSet retrieves a set of available k8s API versions
 func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.VersionSet, error) {
 	groups, resources, err := client.ServerGroupsAndResources()
-	if err != nil {
-		return chartutil.DefaultVersionSet, err
+	if err != nil && !discovery.IsGroupDiscoveryFailedError(err) {
+		return chartutil.DefaultVersionSet, errors.Wrap(err, "could not get apiVersions from Kubernetes")
 	}
 
 	// FIXME: The Kubernetes test fixture for cli appears to always return nil
diff --git a/pkg/action/get_values.go b/pkg/action/get_values.go
index 5bc3a7005..9c32db213 100644
--- a/pkg/action/get_values.go
+++ b/pkg/action/get_values.go
@@ -39,6 +39,10 @@ func NewGetValues(cfg *Configuration) *GetValues {
 
 // Run executes 'helm get values' against the given release.
 func (g *GetValues) Run(name string) (map[string]interface{}, error) {
+	if err := g.cfg.KubeClient.IsReachable(); err != nil {
+		return nil, err
+	}
+
 	rel, err := g.cfg.releaseContent(name, g.Version)
 	if err != nil {
 		return nil, err
diff --git a/pkg/action/install.go b/pkg/action/install.go
index 8cd270693..292a7ec27 100644
--- a/pkg/action/install.go
+++ b/pkg/action/install.go
@@ -79,6 +79,7 @@ type Install struct {
 	ReleaseName      string
 	GenerateName     bool
 	NameTemplate     string
+	Description      string
 	OutputDir        string
 	Atomic           bool
 	SkipCRDs         bool
@@ -86,6 +87,8 @@ type Install struct {
 	// APIVersions allows a manual set of supported API Versions to be passed
 	// (for things like templating). These are ignored if ClientOnly is false
 	APIVersions chartutil.VersionSet
+	// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
+	IsUpgrade bool
 }
 
 // ChartPathOptions captures common options used for controlling chart paths
@@ -197,11 +200,14 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
 		return nil, err
 	}
 
+	//special case for helm template --is-upgrade
+	isUpgrade := i.IsUpgrade && i.DryRun
 	options := chartutil.ReleaseOptions{
 		Name:      i.ReleaseName,
 		Namespace: i.Namespace,
 		Revision:  1,
-		IsInstall: true,
+		IsInstall: !isUpgrade,
+		IsUpgrade: isUpgrade,
 	}
 	valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
 	if err != nil {
@@ -237,7 +243,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
 	// we'll end up in a state where we will delete those resources upon
 	// deleting the release because the manifest will be pointing at that
 	// resource
-	if !i.ClientOnly {
+	if !i.ClientOnly && !isUpgrade {
 		if err := existingResourceConflict(resources); err != nil {
 			return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install")
 		}
@@ -292,7 +298,11 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
 		}
 	}
 
-	rel.SetStatus(release.StatusDeployed, "Install complete")
+	if len(i.Description) > 0 {
+		rel.SetStatus(release.StatusDeployed, i.Description)
+	} else {
+		rel.SetStatus(release.StatusDeployed, "Install complete")
+	}
 
 	// This is a tricky case. The release has been created, but the result
 	// cannot be recorded. The truest thing to tell the user is that the
@@ -426,8 +436,20 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values
 		}
 	}
 
-	files, err := engine.Render(ch, values)
-	if err != nil {
+	var files map[string]string
+	var err2 error
+
+	if c.RESTClientGetter != nil {
+		rest, err := c.RESTClientGetter.ToRESTConfig()
+		if err != nil {
+			return hs, b, "", err
+		}
+		files, err2 = engine.RenderWithClient(ch, values, rest)
+	} else {
+		files, err2 = engine.Render(ch, values)
+	}
+
+	if err2 != nil {
 		return hs, b, "", err
 	}
 
@@ -545,6 +567,10 @@ func (i *Install) NameAndChart(args []string) (string, string, error) {
 		return nil
 	}
 
+	if len(args) > 2 {
+		return args[0], args[1], errors.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", "))
+	}
+
 	if len(args) == 2 {
 		return args[0], args[1], flagsNotSet()
 	}
diff --git a/pkg/action/list.go b/pkg/action/list.go
index 4af0ef8e5..5d3417203 100644
--- a/pkg/action/list.go
+++ b/pkg/action/list.go
@@ -41,7 +41,7 @@ const (
 	ListPendingInstall
 	// ListPendingUpgrade filters on status "pending_upgrade" (upgrade in progress)
 	ListPendingUpgrade
-	// ListPendingRollback filters on status "pending_rollback" (rollback in progres)
+	// ListPendingRollback filters on status "pending_rollback" (rollback in progress)
 	ListPendingRollback
 	// ListSuperseded filters on status "superseded" (historical release version that is no longer deployed)
 	ListSuperseded
diff --git a/pkg/action/pull.go b/pkg/action/pull.go
index aaf63861e..4ff5f5c3e 100644
--- a/pkg/action/pull.go
+++ b/pkg/action/pull.go
@@ -63,6 +63,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
 		Getters: getter.All(p.Settings),
 		Options: []getter.Option{
 			getter.WithBasicAuth(p.Username, p.Password),
+			getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
 		},
 		RepositoryConfig: p.Settings.RepositoryConfig,
 		RepositoryCache:  p.Settings.RepositoryCache,
@@ -109,13 +110,21 @@ func (p *Pull) Run(chartRef string) (string, error) {
 		if !filepath.IsAbs(ud) {
 			ud = filepath.Join(p.DestDir, ud)
 		}
-		if fi, err := os.Stat(ud); err != nil {
-			if err := os.MkdirAll(ud, 0755); err != nil {
+		// Let udCheck to check conflict file/dir without replacing ud when untarDir is the current directory(.).
+		udCheck := ud
+		if udCheck == "." {
+			_, udCheck = filepath.Split(chartRef)
+		} else {
+			_, chartName := filepath.Split(chartRef)
+			udCheck = filepath.Join(udCheck, chartName)
+		}
+		if _, err := os.Stat(udCheck); err != nil {
+			if err := os.MkdirAll(udCheck, 0755); err != nil {
 				return out.String(), errors.Wrap(err, "failed to untar (mkdir)")
 			}
 
-		} else if !fi.IsDir() {
-			return out.String(), errors.Errorf("failed to untar: %s is not a directory", ud)
+		} else {
+			return out.String(), errors.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck)
 		}
 
 		return out.String(), chartutil.ExpandFile(ud, saved)
diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go
index fb72a845b..dfaa98472 100644
--- a/pkg/action/uninstall.go
+++ b/pkg/action/uninstall.go
@@ -37,6 +37,7 @@ type Uninstall struct {
 	DryRun       bool
 	KeepHistory  bool
 	Timeout      time.Duration
+	Description  string
 }
 
 // NewUninstall creates a new Uninstall object with the given configuration.
@@ -118,7 +119,11 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
 	}
 
 	rel.Info.Status = release.StatusUninstalled
-	rel.Info.Description = "Uninstallation complete"
+	if len(u.Description) > 0 {
+		rel.Info.Description = u.Description
+	} else {
+		rel.Info.Description = "Uninstallation complete"
+	}
 
 	if !u.KeepHistory {
 		u.cfg.Log("purge requested for %s", name)
diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go
index d0495a864..cdc40eaaa 100644
--- a/pkg/action/upgrade.go
+++ b/pkg/action/upgrade.go
@@ -58,6 +58,7 @@ type Upgrade struct {
 	Atomic        bool
 	CleanupOnFail bool
 	SubNotes      bool
+	Description   string
 }
 
 // NewUpgrade creates a new Upgrade object with the given configuration.
@@ -218,7 +219,11 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
 
 	if u.DryRun {
 		u.cfg.Log("dry run for %s", upgradedRelease.Name)
-		upgradedRelease.Info.Description = "Dry run complete"
+		if len(u.Description) > 0 {
+			upgradedRelease.Info.Description = u.Description
+		} else {
+			upgradedRelease.Info.Description = "Dry run complete"
+		}
 		return upgradedRelease, nil
 	}
 
@@ -270,7 +275,11 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
 	u.cfg.recordRelease(originalRelease)
 
 	upgradedRelease.Info.Status = release.StatusDeployed
-	upgradedRelease.Info.Description = "Upgrade complete"
+	if len(u.Description) > 0 {
+		upgradedRelease.Info.Description = u.Description
+	} else {
+		upgradedRelease.Info.Description = "Upgrade complete"
+	}
 
 	return upgradedRelease, nil
 }
diff --git a/pkg/action/validate.go b/pkg/action/validate.go
index 526a4c7fc..6bbfc5e8d 100644
--- a/pkg/action/validate.go
+++ b/pkg/action/validate.go
@@ -33,7 +33,8 @@ func existingResourceConflict(resources kube.ResourceList) error {
 		}
 
 		helper := resource.NewHelper(info.Client, info.Mapping)
-		if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil {
+		existing, err := helper.Get(info.Namespace, info.Name, info.Export)
+		if err != nil {
 			if apierrors.IsNotFound(err) {
 				return nil
 			}
@@ -41,7 +42,7 @@ func existingResourceConflict(resources kube.ResourceList) error {
 			return errors.Wrap(err, "could not get information about the resource")
 		}
 
-		return fmt.Errorf("existing resource conflict: kind: %s, namespace: %s, name: %s", info.Mapping.GroupVersionKind.Kind, info.Namespace, info.Name)
+		return fmt.Errorf("existing resource conflict: namespace: %s, name: %s, existing_kind: %s, new_kind: %s", info.Namespace, info.Name, existing.GetObjectKind().GroupVersionKind(), info.Mapping.GroupVersionKind)
 	})
 	return err
 }
diff --git a/pkg/chart/dependency.go b/pkg/chart/dependency.go
index 0837f8d19..9ec4544c2 100644
--- a/pkg/chart/dependency.go
+++ b/pkg/chart/dependency.go
@@ -53,7 +53,7 @@ type Dependency struct {
 //
 // It represents the state that the dependencies should be in.
 type Lock struct {
-	// Genderated is the date the lock file was last generated.
+	// Generated is the date the lock file was last generated.
 	Generated time.Time `json:"generated"`
 	// Digest is a hash of the dependencies in Chart.yaml.
 	Digest string `json:"digest"`
diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go
index 3c50fe379..7e187a170 100644
--- a/pkg/chart/loader/archive.go
+++ b/pkg/chart/loader/archive.go
@@ -126,6 +126,12 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
 			continue
 		}
 
+		switch hd.Typeflag {
+		// We don't want to process these extension header files.
+		case tar.TypeXGlobalHeader, tar.TypeXHeader:
+			continue
+		}
+
 		// Archive could contain \ if generated on Windows
 		delimiter := "/"
 		if strings.ContainsRune(hd.Name, '\\') {
diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go
index 3c38519bc..dd4fd2dff 100644
--- a/pkg/chart/loader/load.go
+++ b/pkg/chart/loader/load.go
@@ -114,12 +114,18 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
 			if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil {
 				return c, errors.Wrap(err, "cannot load requirements.yaml")
 			}
+			if c.Metadata.APIVersion == chart.APIVersionV1 {
+				c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
+			}
 		// Deprecated: requirements.lock is deprecated use Chart.lock.
 		case f.Name == "requirements.lock":
 			c.Lock = new(chart.Lock)
 			if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil {
 				return c, errors.Wrap(err, "cannot load requirements.lock")
 			}
+			if c.Metadata.APIVersion == chart.APIVersionV1 {
+				c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
+			}
 
 		case strings.HasPrefix(f.Name, "templates/"):
 			c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data})
diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go
index 68176ed5d..808a902b1 100644
--- a/pkg/chartutil/chartfile.go
+++ b/pkg/chartutil/chartfile.go
@@ -42,7 +42,16 @@ func LoadChartfile(filename string) (*chart.Metadata, error) {
 //
 // 'filename' should be the complete path and filename ('foo/Chart.yaml')
 func SaveChartfile(filename string, cf *chart.Metadata) error {
+	// Pull out the dependencies of a v1 Chart, since there's no way
+	// to tell the serializer to skip a field for just this use case
+	savedDependencies := cf.Dependencies
+	if cf.APIVersion == chart.APIVersionV1 {
+		cf.Dependencies = nil
+	}
 	out, err := yaml.Marshal(cf)
+	if cf.APIVersion == chart.APIVersionV1 {
+		cf.Dependencies = savedDependencies
+	}
 	if err != nil {
 		return err
 	}
diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go
index c67cde04f..390f12f4c 100644
--- a/pkg/chartutil/create.go
+++ b/pkg/chartutil/create.go
@@ -103,6 +103,8 @@ fullnameOverride: ""
 serviceAccount:
   # Specifies whether a service account should be created
   create: true
+  # Annotations to add to the service account
+  annotations: {}
   # The name of the service account to use.
   # If not set and create is true, a name is generated using the fullname template
   name:
@@ -170,6 +172,7 @@ const defaultIgnore = `# Patterns to ignore when building packages.
 *.swp
 *.bak
 *.tmp
+*.orig
 *~
 # Various IDEs
 .project
@@ -302,6 +305,10 @@ metadata:
   name: {{ include "<CHARTNAME>.serviceAccountName" . }}
   labels:
 {{ include "<CHARTNAME>.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end -}}
 `
 
diff --git a/pkg/chartutil/doc.go b/pkg/chartutil/doc.go
index 5dd9c7038..efcda2cfa 100644
--- a/pkg/chartutil/doc.go
+++ b/pkg/chartutil/doc.go
@@ -17,7 +17,7 @@ limitations under the License.
 /*Package chartutil contains tools for working with charts.
 
 Charts are described in the protocol buffer definition (pkg/proto/charts).
-This packe provides utilities for serializing and deserializing charts.
+This package provides utilities for serializing and deserializing charts.
 
 A chart can be represented on the file system in one of two ways:
 
@@ -25,7 +25,7 @@ A chart can be represented on the file system in one of two ways:
 	- As a tarred gzipped file containing a directory that then contains a
 	Chart.yaml file.
 
-This package provides utilitites for working with those file formats.
+This package provides utilities for working with those file formats.
 
 The preferred way of loading a chart is using 'loader.Load`:
 
diff --git a/pkg/chartutil/jsonschema.go b/pkg/chartutil/jsonschema.go
index 0848faaf2..753dc98c1 100644
--- a/pkg/chartutil/jsonschema.go
+++ b/pkg/chartutil/jsonschema.go
@@ -39,10 +39,10 @@ func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) err
 		}
 	}
 
-	// For each dependency, recurively call this function with the coalesced values
-	for _, subchrt := range chrt.Dependencies() {
-		subchrtValues := values[subchrt.Name()].(map[string]interface{})
-		if err := ValidateAgainstSchema(subchrt, subchrtValues); err != nil {
+	// For each dependency, recursively call this function with the coalesced values
+	for _, subchart := range chrt.Dependencies() {
+		subchartValues := values[subchart.Name()].(map[string]interface{})
+		if err := ValidateAgainstSchema(subchart, subchartValues); err != nil {
 			sb.WriteString(err.Error())
 		}
 	}
diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go
index bc8f5bdd9..fb985bb59 100644
--- a/pkg/chartutil/save.go
+++ b/pkg/chartutil/save.go
@@ -23,6 +23,7 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"time"
 
 	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
@@ -141,8 +142,17 @@ func Save(c *chart.Chart, outDir string) (string, error) {
 func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
 	base := filepath.Join(prefix, c.Name())
 
+	// Pull out the dependencies of a v1 Chart, since there's no way
+	// to tell the serializer to skip a field for just this use case
+	savedDependencies := c.Metadata.Dependencies
+	if c.Metadata.APIVersion == chart.APIVersionV1 {
+		c.Metadata.Dependencies = nil
+	}
 	// Save Chart.yaml
 	cdata, err := yaml.Marshal(c.Metadata)
+	if c.Metadata.APIVersion == chart.APIVersionV1 {
+		c.Metadata.Dependencies = savedDependencies
+	}
 	if err != nil {
 		return err
 	}
@@ -198,9 +208,10 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
 func writeToTar(out *tar.Writer, name string, body []byte) error {
 	// TODO: Do we need to create dummy parent directory names if none exist?
 	h := &tar.Header{
-		Name: name,
-		Mode: 0644,
-		Size: int64(len(body)),
+		Name:    name,
+		Mode:    0644,
+		Size:    int64(len(body)),
+		ModTime: time.Now(),
 	}
 	if err := out.WriteHeader(h); err != nil {
 		return err
diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go
index 28e7873be..5f947aec7 100644
--- a/pkg/cli/environment.go
+++ b/pkg/cli/environment.go
@@ -52,7 +52,7 @@ type EnvSettings struct {
 	RegistryConfig string
 	// RepositoryConfig is the path to the repositories file.
 	RepositoryConfig string
-	// Repositoryache is the path to the repository cache directory.
+	// RepositoryCache is the path to the repository cache directory.
 	RepositoryCache string
 	// PluginsDirectory is the path to the plugins directory.
 	PluginsDirectory string
diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go
index 8e251bc89..f3d4321c5 100644
--- a/pkg/downloader/chart_downloader.go
+++ b/pkg/downloader/chart_downloader.go
@@ -214,6 +214,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 		c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password))
 	}
 
+	if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" {
+		c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile))
+	}
+
 	// Next, we need to load the index, and actually look up the chart.
 	idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
 	i, err := repo.LoadIndexFile(idxFile)
diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go
index 4e9d691d8..e46af6944 100644
--- a/pkg/downloader/manager.go
+++ b/pkg/downloader/manager.go
@@ -149,6 +149,13 @@ func (m *Manager) Update() error {
 		return err
 	}
 
+	// downloadAll might overwrite dependency version, recalculate lock digest
+	newDigest, err := resolver.HashReq(req, lock.Dependencies)
+	if err != nil {
+		return err
+	}
+	lock.Digest = newDigest
+
 	// If the lock file hasn't changed, don't write a new one.
 	oldLock := c.Lock
 	if oldLock != nil && oldLock.Digest == lock.Digest {
@@ -156,7 +163,7 @@ func (m *Manager) Update() error {
 	}
 
 	// Finally, we need to write the lockfile.
-	return writeLock(m.ChartPath, lock)
+	return writeLock(m.ChartPath, lock, c.Metadata.APIVersion == chart.APIVersionV1)
 }
 
 func (m *Manager) loadChartDir() (*chart.Chart, error) {
@@ -627,12 +634,16 @@ func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, err
 }
 
 // writeLock writes a lockfile to disk
-func writeLock(chartpath string, lock *chart.Lock) error {
+func writeLock(chartpath string, lock *chart.Lock, legacyLockfile bool) error {
 	data, err := yaml.Marshal(lock)
 	if err != nil {
 		return err
 	}
-	dest := filepath.Join(chartpath, "Chart.lock")
+	lockfileName := "Chart.lock"
+	if legacyLockfile {
+		lockfileName = "requirements.lock"
+	}
+	dest := filepath.Join(chartpath, lockfileName)
 	return ioutil.WriteFile(dest, data, 0644)
 }
 
diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go
index dae0b6be7..1d98cd763 100644
--- a/pkg/engine/engine.go
+++ b/pkg/engine/engine.go
@@ -27,6 +27,7 @@ import (
 	"text/template"
 
 	"github.com/pkg/errors"
+	"k8s.io/client-go/rest"
 
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -39,6 +40,8 @@ type Engine struct {
 	Strict bool
 	// In LintMode, some 'required' template values may be missing, so don't fail
 	LintMode bool
+	// the rest config to connect to te kubernetes api
+	config *rest.Config
 }
 
 // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
@@ -71,6 +74,15 @@ func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, erro
 	return new(Engine).Render(chrt, values)
 }
 
+// RenderWithClient takes a chart, optional values, and value overrides, and attempts to
+// render the Go templates using the default options. This engine is client aware and so can have template
+// functions that interact with the client
+func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) {
+	return Engine{
+		config: config,
+	}.Render(chrt, values)
+}
+
 // renderable is an object that can be rendered.
 type renderable struct {
 	// tpl is the current template.
@@ -93,11 +105,19 @@ func warnWrap(warn string) string {
 // initFunMap creates the Engine's FuncMap and adds context-specific functions.
 func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
 	funcMap := funcMap()
+	includedNames := make([]string, 0)
 
 	// Add the 'include' function here so we can close over t.
 	funcMap["include"] = func(name string, data interface{}) (string, error) {
 		var buf strings.Builder
+		for _, n := range includedNames {
+			if n == name {
+				return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name)
+			}
+		}
+		includedNames = append(includedNames, name)
 		err := t.ExecuteTemplate(&buf, name, data)
+		includedNames = includedNames[:len(includedNames)-1]
 		return buf.String(), err
 	}
 
@@ -149,6 +169,9 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
 		}
 		return val, nil
 	}
+	if e.config != nil {
+		funcMap["lookup"] = NewLookupFunction(e.config)
+	}
 
 	t.Funcs(funcMap)
 }
diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go
new file mode 100644
index 000000000..14f2351b4
--- /dev/null
+++ b/pkg/engine/lookup_func.go
@@ -0,0 +1,106 @@
+/*
+Copyright The Helm Authors.
+
+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 engine
+
+import (
+	"log"
+	"strings"
+
+	"github.com/pkg/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/discovery"
+	"k8s.io/client-go/dynamic"
+	"k8s.io/client-go/rest"
+)
+
+type lookupFunc = func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error)
+
+func NewLookupFunction(config *rest.Config) lookupFunc {
+	return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) {
+		var client dynamic.ResourceInterface
+		c, namespaced, err := getDynamicClientOnKind(apiversion, resource, config)
+		if err != nil {
+			return map[string]interface{}{}, err
+		}
+		if namespaced && namespace != "" {
+			client = c.Namespace(namespace)
+		} else {
+			client = c
+		}
+		if name != "" {
+			//this will return a single object
+			obj, err := client.Get(name, metav1.GetOptions{})
+			if err != nil {
+				return map[string]interface{}{}, err
+			}
+			return obj.UnstructuredContent(), nil
+		}
+		//this will return a list
+		obj, err := client.List(metav1.ListOptions{})
+		if err != nil {
+			return map[string]interface{}{}, err
+		}
+		return obj.UnstructuredContent(), nil
+	}
+}
+
+// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced.
+func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) {
+	gvk := schema.FromAPIVersionAndKind(apiversion, kind)
+	apiRes, err := getAPIReourceForGVK(gvk, config)
+	if err != nil {
+		log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err)
+		return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String())
+	}
+	gvr := schema.GroupVersionResource{
+		Group:    apiRes.Group,
+		Version:  apiRes.Version,
+		Resource: apiRes.Name,
+	}
+	intf, err := dynamic.NewForConfig(config)
+	if err != nil {
+		log.Printf("[ERROR] unable to get dynamic client %s", err)
+		return nil, false, err
+	}
+	res := intf.Resource(gvr)
+	return res, apiRes.Namespaced, nil
+}
+
+func getAPIReourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) {
+	res := metav1.APIResource{}
+	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
+	if err != nil {
+		log.Printf("[ERROR] unable to create discovery client %s", err)
+		return res, err
+	}
+	resList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String())
+	if err != nil {
+		log.Printf("[ERROR] unable to retrieve resource list for: %s , error: %s", gvk.GroupVersion().String(), err)
+		return res, err
+	}
+	for _, resource := range resList.APIResources {
+		//if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now.
+		if resource.Kind == gvk.Kind && !strings.Contains(resource.Name, "/") {
+			res = resource
+			res.Group = gvk.Group
+			res.Version = gvk.Version
+			break
+		}
+	}
+	return res, nil
+}
diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go
index 89abfb1cf..5b476ff2d 100644
--- a/pkg/getter/httpgetter.go
+++ b/pkg/getter/httpgetter.go
@@ -27,7 +27,7 @@ import (
 	"helm.sh/helm/v3/internal/version"
 )
 
-// HTTPGetter is the efault HTTP(/S) backend handler
+// HTTPGetter is the default HTTP(/S) backend handler
 type HTTPGetter struct {
 	opts options
 }
diff --git a/pkg/helmpath/home.go b/pkg/helmpath/home.go
index 0b0f110a5..b3f14c558 100644
--- a/pkg/helmpath/home.go
+++ b/pkg/helmpath/home.go
@@ -25,10 +25,19 @@ func CachePath(elem ...string) string { return lp.cachePath(elem...) }
 // DataPath returns the path where Helm stores data.
 func DataPath(elem ...string) string { return lp.dataPath(elem...) }
 
-// CacheIndex returns the path to an index for the given named repository.
+// CacheIndexFile returns the path to an index for the given named repository.
 func CacheIndexFile(name string) string {
 	if name != "" {
 		name += "-"
 	}
 	return name + "index.yaml"
 }
+
+// CacheChartsFile returns the path to a text file listing all the charts
+// within the given named repository.
+func CacheChartsFile(name string) string {
+	if name != "" {
+		name += "-"
+	}
+	return name + "charts.txt"
+}
diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index ab1f600ff..cdd996925 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -30,10 +30,12 @@ import (
 	"github.com/pkg/errors"
 	batch "k8s.io/api/batch/v1"
 	v1 "k8s.io/api/core/v1"
+	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/fields"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/strategicpatch"
@@ -41,6 +43,7 @@ import (
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/cli-runtime/pkg/resource"
 	"k8s.io/client-go/kubernetes/scheme"
+	cachetools "k8s.io/client-go/tools/cache"
 	watchtools "k8s.io/client-go/tools/watch"
 	cmdutil "k8s.io/kubectl/pkg/cmd/util"
 )
@@ -60,6 +63,10 @@ func New(getter genericclioptions.RESTClientGetter) *Client {
 		getter = genericclioptions.NewConfigFlags(true)
 	}
 	// Add CRDs to the scheme. They are missing by default.
+	if err := apiextv1.AddToScheme(scheme.Scheme); err != nil {
+		// This should never happen.
+		panic(err)
+	}
 	if err := apiextv1beta1.AddToScheme(scheme.Scheme); err != nil {
 		// This should never happen.
 		panic(err)
@@ -195,6 +202,11 @@ func (c *Client) Update(original, target ResourceList, force bool) (*Result, err
 	}
 
 	for _, info := range original.Difference(target) {
+		if info.Mapping.GroupVersionKind.Kind == "CustomResourceDefinition" {
+			c.Log("Skipping the deletion of CustomResourceDefinition %q", info.Name)
+			continue
+		}
+
 		c.Log("Deleting %q in %s...", info.Name, info.Namespace)
 		res.Deleted = append(res.Deleted, info)
 		if err := deleteResource(info); err != nil {
@@ -217,6 +229,11 @@ func (c *Client) Delete(resources ResourceList) (*Result, []error) {
 	var errs []error
 	res := &Result{}
 	err := perform(resources, func(info *resource.Info) error {
+		if info.Mapping.GroupVersionKind.Kind == "CustomResourceDefinition" {
+			c.Log("Skipping the deletion of CustomResourceDefinition %q", info.Name)
+			return nil
+		}
+
 		c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind)
 		if err := c.skipIfNotFound(deleteResource(info)); err != nil {
 			// Collect the error and continue on
@@ -353,7 +370,12 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
 	// returned from ConvertToVersion. Anything that's unstructured should
 	// use the jsonpatch.CreateMergePatch. Strategic Merge Patch is not supported
 	// on objects like CRDs.
-	if _, ok := versionedObject.(runtime.Unstructured); ok {
+	_, isUnstructured := versionedObject.(runtime.Unstructured)
+
+	// On newer K8s versions, CRDs aren't unstructured but has this dedicated type
+	_, isCRD := versionedObject.(*apiextv1beta1.CustomResourceDefinition)
+
+	if isUnstructured || isCRD {
 		// fall back to generic JSON merge patch
 		patch, err := jsonpatch.CreateMergePatch(oldData, newData)
 		return patch, types.MergePatchType, err
@@ -419,10 +441,13 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err
 
 	c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
 
-	w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
+	// Use a selector on the name of the resource. This should be unique for the
+	// given version and kind
+	selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name))
 	if err != nil {
 		return err
 	}
+	lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector)
 
 	// What we watch for depends on the Kind.
 	// - For a Job, we watch for completion.
@@ -432,7 +457,7 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err
 
 	ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout)
 	defer cancel()
-	_, err = watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
+	_, err = watchtools.ListWatchUntil(ctx, lw, func(e watch.Event) (bool, error) {
 		// Make sure the incoming object is versioned as we use unstructured
 		// objects when we build manifests
 		obj := convertWithMapper(e.Object, info.Mapping)
diff --git a/pkg/kube/fake/printer.go b/pkg/kube/fake/printer.go
index ec75aa790..58b389ab5 100644
--- a/pkg/kube/fake/printer.go
+++ b/pkg/kube/fake/printer.go
@@ -33,7 +33,7 @@ type PrintingKubeClient struct {
 	Out io.Writer
 }
 
-// isReachable checks if the cluster is reachable
+// IsReachable checks if the cluster is reachable
 func (p *PrintingKubeClient) IsReachable() error {
 	return nil
 }
diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go
index 35bb81571..91a64fe13 100644
--- a/pkg/lint/rules/chartfile.go
+++ b/pkg/lint/rules/chartfile.go
@@ -40,13 +40,12 @@ func Chartfile(linter *support.Linter) {
 	chartFile, err := chartutil.LoadChartfile(chartPath)
 	validChartFile := linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlFormat(err))
 
-	// Guard clause. Following linter rules require a parseable ChartFile
+	// Guard clause. Following linter rules require a parsable ChartFile
 	if !validChartFile {
 		return
 	}
 
 	linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile))
-	linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile))
 
 	// Chart metadata
 	linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile))
@@ -82,13 +81,6 @@ func validateChartName(cf *chart.Metadata) error {
 	return nil
 }
 
-func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error {
-	if cf.Name != filepath.Base(chartDir) {
-		return errors.Errorf("directory name (%s) and chart name (%s) must be the same", filepath.Base(chartDir), cf.Name)
-	}
-	return nil
-}
-
 func validateChartAPIVersion(cf *chart.Metadata) error {
 	if cf.APIVersion == "" {
 		return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"")
diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go
index 21a65cd13..1a5d74cca 100644
--- a/pkg/plugin/installer/vcs_installer.go
+++ b/pkg/plugin/installer/vcs_installer.go
@@ -135,7 +135,7 @@ func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) {
 	sort.Sort(sort.Reverse(semver.Collection(semvers)))
 	for _, v := range semvers {
 		if constraint.Check(v) {
-			// If the constrint passes get the original reference
+			// If the constraint passes get the original reference
 			ver := v.Original()
 			debug("setting to %s", ver)
 			return ver, nil
diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go
index 6032eb063..5d16779f1 100644
--- a/pkg/provenance/sign.go
+++ b/pkg/provenance/sign.go
@@ -169,7 +169,7 @@ func (s *Signatory) DecryptKey(fn PassphraseFetcher) error {
 	if s.Entity == nil {
 		return errors.New("private key not found")
 	} else if s.Entity.PrivateKey == nil {
-		return errors.New("provided key is not a private key")
+		return errors.New("provided key is not a private key. Try providing a keyring with secret keys")
 	}
 
 	// Nothing else to do if key is not encrypted.
@@ -203,7 +203,7 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
 	if s.Entity == nil {
 		return "", errors.New("private key not found")
 	} else if s.Entity.PrivateKey == nil {
-		return "", errors.New("provided key is not a private key")
+		return "", errors.New("provided key is not a private key. Try providing a keyring with secret keys")
 	}
 
 	if fi, err := os.Stat(chartpath); err != nil {
diff --git a/pkg/release/status.go b/pkg/release/status.go
index cd025e279..49b0f1544 100644
--- a/pkg/release/status.go
+++ b/pkg/release/status.go
@@ -19,12 +19,13 @@ package release
 type Status string
 
 // Describe the status of a release
+// NOTE: Make sure to update cmd/helm/status.go when adding or modifying any of these statuses.
 const (
 	// StatusUnknown indicates that a release is in an uncertain state.
 	StatusUnknown Status = "unknown"
 	// StatusDeployed indicates that the release has been pushed to Kubernetes.
 	StatusDeployed Status = "deployed"
-	// StatusUninstalled indicates that a release has been uninstalled from Kubermetes.
+	// StatusUninstalled indicates that a release has been uninstalled from Kubernetes.
 	StatusUninstalled Status = "uninstalled"
 	// StatusSuperseded indicates that this release object is outdated and a newer one exists.
 	StatusSuperseded Status = "superseded"
diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go
index a5110a100..92ffa03f2 100644
--- a/pkg/releaseutil/kind_sorter.go
+++ b/pkg/releaseutil/kind_sorter.go
@@ -31,12 +31,12 @@ var InstallOrder KindSortOrder = []string{
 	"LimitRange",
 	"PodSecurityPolicy",
 	"PodDisruptionBudget",
+	"ServiceAccount",
 	"Secret",
 	"ConfigMap",
 	"StorageClass",
 	"PersistentVolume",
 	"PersistentVolumeClaim",
-	"ServiceAccount",
 	"CustomResourceDefinition",
 	"ClusterRole",
 	"ClusterRoleList",
@@ -85,12 +85,12 @@ var UninstallOrder KindSortOrder = []string{
 	"ClusterRoleList",
 	"ClusterRole",
 	"CustomResourceDefinition",
-	"ServiceAccount",
 	"PersistentVolumeClaim",
 	"PersistentVolume",
 	"StorageClass",
 	"ConfigMap",
 	"Secret",
+	"ServiceAccount",
 	"PodDisruptionBudget",
 	"PodSecurityPolicy",
 	"LimitRange",
@@ -101,10 +101,10 @@ var UninstallOrder KindSortOrder = []string{
 
 // sortByKind does an in-place sort of manifests by Kind.
 //
-// Results are sorted by 'ordering'
+// Results are sorted by 'ordering', keeping order of items with equal kind/priority
 func sortByKind(manifests []Manifest, ordering KindSortOrder) []Manifest {
 	ks := newKindSorter(manifests, ordering)
-	sort.Sort(ks)
+	sort.Stable(ks)
 	return ks.manifests
 }
 
@@ -134,13 +134,13 @@ func (k *kindSorter) Less(i, j int) bool {
 	b := k.manifests[j]
 	first, aok := k.ordering[a.Head.Kind]
 	second, bok := k.ordering[b.Head.Kind]
-	// if same kind (including unknown) sub sort alphanumeric
-	if first == second {
-		// if both are unknown and of different kind sort by kind alphabetically
-		if !aok && !bok && a.Head.Kind != b.Head.Kind {
+
+	if !aok && !bok {
+		// if both are unknown then sort alphabetically by kind, keep original order if same kind
+		if a.Head.Kind != b.Head.Kind {
 			return a.Head.Kind < b.Head.Kind
 		}
-		return a.Name < b.Name
+		return first < second
 	}
 	// unknown kind is last
 	if !aok {
@@ -149,6 +149,6 @@ func (k *kindSorter) Less(i, j int) bool {
 	if !bok {
 		return true
 	}
-	// sort different kinds
+	// sort different kinds, keep original order if same priority
 	return first < second
 }
diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go
index 37503b390..0b04a4599 100644
--- a/pkg/releaseutil/manifest.go
+++ b/pkg/releaseutil/manifest.go
@@ -19,6 +19,7 @@ package releaseutil
 import (
 	"fmt"
 	"regexp"
+	"strconv"
 	"strings"
 )
 
@@ -37,8 +38,9 @@ var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*")
 // SplitManifests takes a string of manifest and returns a map contains individual manifests
 func SplitManifests(bigFile string) map[string]string {
 	// Basically, we're quickly splitting a stream of YAML documents into an
-	// array of YAML docs. In the current implementation, the file name is just
-	// a place holder, and doesn't have any further meaning.
+	// array of YAML docs. The file name is just a place holder, but should be
+	// integer-sortable so that manifests get output in the same order as the
+	// input (see `BySplitManifestsOrder`).
 	tpl := "manifest-%d"
 	res := map[string]string{}
 	// Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly.
@@ -56,3 +58,15 @@ func SplitManifests(bigFile string) map[string]string {
 	}
 	return res
 }
+
+// BySplitManifestsOrder sorts by in-file manifest order, as provided in function `SplitManifests`
+type BySplitManifestsOrder []string
+
+func (a BySplitManifestsOrder) Len() int { return len(a) }
+func (a BySplitManifestsOrder) Less(i, j int) bool {
+	// Split `manifest-%d`
+	anum, _ := strconv.ParseInt(a[i][len("manifest-"):], 10, 0)
+	bnum, _ := strconv.ParseInt(a[j][len("manifest-"):], 10, 0)
+	return anum < bnum
+}
+func (a BySplitManifestsOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go
index 5f4b4e8d9..24b0c3c95 100644
--- a/pkg/releaseutil/manifest_sorter.go
+++ b/pkg/releaseutil/manifest_sorter.go
@@ -19,6 +19,7 @@ package releaseutil
 import (
 	"log"
 	"path"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -74,10 +75,17 @@ var events = map[string]release.HookEvent{
 //
 // Files that do not parse into the expected format are simply placed into a map and
 // returned.
-func SortManifests(files map[string]string, apis chartutil.VersionSet, sort KindSortOrder) ([]*release.Hook, []Manifest, error) {
+func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering KindSortOrder) ([]*release.Hook, []Manifest, error) {
 	result := &result{}
 
-	for filePath, c := range files {
+	var sortedFilePaths []string
+	for filePath := range files {
+		sortedFilePaths = append(sortedFilePaths, filePath)
+	}
+	sort.Strings(sortedFilePaths)
+
+	for _, filePath := range sortedFilePaths {
+		content := files[filePath]
 
 		// Skip partials. We could return these as a separate map, but there doesn't
 		// seem to be any need for that at this time.
@@ -85,12 +93,12 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, sort Kind
 			continue
 		}
 		// Skip empty files and log this.
-		if strings.TrimSpace(c) == "" {
+		if strings.TrimSpace(content) == "" {
 			continue
 		}
 
 		manifestFile := &manifestFile{
-			entries: SplitManifests(c),
+			entries: SplitManifests(content),
 			path:    filePath,
 			apis:    apis,
 		}
@@ -100,7 +108,7 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, sort Kind
 		}
 	}
 
-	return result.hooks, sortByKind(result.generic, sort), nil
+	return result.hooks, sortByKind(result.generic, ordering), nil
 }
 
 // sort takes a manifestFile object which may contain multiple resource definition
@@ -123,7 +131,16 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, sort Kind
 // 		annotations:
 // 			helm.sh/hook-delete-policy: hook-succeeded
 func (file *manifestFile) sort(result *result) error {
-	for _, m := range file.entries {
+	// Go through manifests in order found in file (function `SplitManifests` creates integer-sortable keys)
+	var sortedEntryKeys []string
+	for entryKey := range file.entries {
+		sortedEntryKeys = append(sortedEntryKeys, entryKey)
+	}
+	sort.Sort(BySplitManifestsOrder(sortedEntryKeys))
+
+	for _, entryKey := range sortedEntryKeys {
+		m := file.entries[entryKey]
+
 		var entry SimpleHead
 		if err := yaml.Unmarshal([]byte(m), &entry); err != nil {
 			return errors.Wrapf(err, "YAML parse error on %s", file.path)
diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go
index c8d0d6a3d..38b6b8fb0 100644
--- a/pkg/repo/chartrepo.go
+++ b/pkg/repo/chartrepo.go
@@ -133,10 +133,21 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) {
 		return "", err
 	}
 
-	if _, err := loadIndex(index); err != nil {
+	indexFile, err := loadIndex(index)
+	if err != nil {
 		return "", err
 	}
 
+	// Create the chart list file in the cache directory
+	var charts strings.Builder
+	for name := range indexFile.Entries {
+		fmt.Fprintln(&charts, name)
+	}
+	chartsFile := filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))
+	os.MkdirAll(filepath.Dir(chartsFile), 0755)
+	ioutil.WriteFile(chartsFile, []byte(charts.String()), 0644)
+
+	// Create the index file in the cache directory
 	fname := filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name))
 	os.MkdirAll(filepath.Dir(fname), 0755)
 	return fname, ioutil.WriteFile(fname, index, 0644)
diff --git a/pkg/repo/doc.go b/pkg/repo/doc.go
index 19ccf267c..05650100b 100644
--- a/pkg/repo/doc.go
+++ b/pkg/repo/doc.go
@@ -27,7 +27,7 @@ The first is the 'index.yaml' format, which is expressed like this:
 	entries:
 	  frobnitz:
 	  - created: 2016-09-29T12:14:34.830161306-06:00
-		description: This is a frobniz.
+		description: This is a frobnitz.
 		digest: 587bd19a9bd9d2bc4a6d25ab91c8c8e7042c47b4ac246e37bf8e1e74386190f4
 		home: http://example.com
 		keywords:
diff --git a/pkg/repo/index.go b/pkg/repo/index.go
index a7fea673b..36386665e 100644
--- a/pkg/repo/index.go
+++ b/pkg/repo/index.go
@@ -17,8 +17,6 @@ limitations under the License.
 package repo
 
 import (
-	"encoding/json"
-	"fmt"
 	"io/ioutil"
 	"os"
 	"path"
@@ -290,48 +288,3 @@ func loadIndex(data []byte) (*IndexFile, error) {
 	}
 	return i, nil
 }
-
-// unversionedEntry represents a deprecated pre-Alpha.5 format.
-//
-// This will be removed prior to v2.0.0
-type unversionedEntry struct {
-	Checksum  string          `json:"checksum"`
-	URL       string          `json:"url"`
-	Chartfile *chart.Metadata `json:"chartfile"`
-}
-
-// loadUnversionedIndex loads a pre-Alpha.5 index.yaml file.
-//
-// This format is deprecated. This function will be removed prior to v2.0.0.
-func loadUnversionedIndex(data []byte) (*IndexFile, error) {
-	fmt.Fprintln(os.Stderr, "WARNING: Deprecated index file format. Try 'helm repo update'")
-	i := map[string]unversionedEntry{}
-
-	// This gets around an error in the YAML parser. Instead of parsing as YAML,
-	// we convert to JSON, and then decode again.
-	var err error
-	data, err = yaml.YAMLToJSON(data)
-	if err != nil {
-		return nil, err
-	}
-	if err := json.Unmarshal(data, &i); err != nil {
-		return nil, err
-	}
-
-	if len(i) == 0 {
-		return nil, ErrNoAPIVersion
-	}
-	ni := NewIndexFile()
-	for n, item := range i {
-		if item.Chartfile == nil || item.Chartfile.Name == "" {
-			parts := strings.Split(n, "-")
-			ver := ""
-			if len(parts) > 1 {
-				ver = strings.TrimSuffix(parts[1], ".tgz")
-			}
-			item.Chartfile = &chart.Metadata{Name: parts[0], Version: ver}
-		}
-		ni.Add(item.Chartfile, item.URL, "", item.Checksum)
-	}
-	return ni, nil
-}
diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go
index eea285be1..dc55cf458 100644
--- a/pkg/storage/driver/secrets.go
+++ b/pkg/storage/driver/secrets.go
@@ -44,7 +44,7 @@ type Secrets struct {
 	Log  func(string, ...interface{})
 }
 
-// NewSecrets initializes a new Secrets wrapping an implmenetation of
+// NewSecrets initializes a new Secrets wrapping an implementation of
 // the kubernetes SecretsInterface.
 func NewSecrets(impl corev1.SecretInterface) *Secrets {
 	return &Secrets{
diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go
index 58a7eb06f..56881493b 100644
--- a/pkg/storage/storage.go
+++ b/pkg/storage/storage.go
@@ -66,7 +66,7 @@ func (s *Storage) Create(rls *rspb.Release) error {
 	return s.Driver.Create(makeKey(rls.Name, rls.Version), rls)
 }
 
-// Update update the release in storage. An error is returned if the
+// Update updates the release in storage. An error is returned if the
 // storage backend fails to update the release or if the release
 // does not exist.
 func (s *Storage) Update(rls *rspb.Release) error {
@@ -112,9 +112,6 @@ func (s *Storage) ListDeployed() ([]*rspb.Release, error) {
 func (s *Storage) Deployed(name string) (*rspb.Release, error) {
 	ls, err := s.DeployedAll(name)
 	if err != nil {
-		if strings.Contains(err.Error(), "not found") {
-			return nil, errors.Errorf("%q has no deployed releases", name)
-		}
 		return nil, err
 	}
 
@@ -122,7 +119,7 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) {
 		return nil, errors.Errorf("%q has no deployed releases", name)
 	}
 
-	return ls[0], err
+	return ls[0], nil
 }
 
 // DeployedAll returns all deployed releases with the provided name, or
diff --git a/scripts/validate-license.sh b/scripts/validate-license.sh
index 00bd38ea2..dc247436f 100755
--- a/scripts/validate-license.sh
+++ b/scripts/validate-license.sh
@@ -27,14 +27,16 @@ find_files() {
   \( -name '*.go' -o -name '*.sh' \)
 }
 
-mapfile -t failed_license_header < <(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")')
+# Use "|| :" to ignore the error code when grep returns empty
+failed_license_header=($(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")' || :))
 if (( ${#failed_license_header[@]} > 0 )); then
   echo "Some source files are missing license headers."
   printf '%s\n' "${failed_license_header[@]}"
   exit 1
 fi
 
-mapfile -t failed_copyright_header < <(find_files | xargs grep -L 'Copyright The Helm Authors.')
+# Use "|| :" to ignore the error code when grep returns empty
+failed_copyright_header=($(find_files | xargs grep -L 'Copyright The Helm Authors.' || :))
 if (( ${#failed_copyright_header[@]} > 0 )); then
   echo "Some source files are missing the copyright header."
   printf '%s\n' "${failed_copyright_header[@]}"


Test patch:
diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go
index 53d67ef59..1f9d55867 100644
--- a/cmd/helm/dependency_update_test.go
+++ b/cmd/helm/dependency_update_test.go
@@ -111,9 +111,9 @@ func TestDependencyUpdateCmd(t *testing.T) {
 	if _, err := os.Stat(expect); err != nil {
 		t.Fatalf("Expected %q: %s", expect, err)
 	}
-	dontExpect := dir(chartname, "charts/compressedchart-0.1.0.tgz")
-	if _, err := os.Stat(dontExpect); err == nil {
-		t.Fatalf("Unexpected %q", dontExpect)
+	unexpected := dir(chartname, "charts/compressedchart-0.1.0.tgz")
+	if _, err := os.Stat(unexpected); err == nil {
+		t.Fatalf("Unexpected %q", unexpected)
 	}
 }
 
@@ -182,7 +182,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
 func createTestingMetadata(name, baseURL string) *chart.Chart {
 	return &chart.Chart{
 		Metadata: &chart.Metadata{
-			APIVersion: chart.APIVersionV1,
+			APIVersion: chart.APIVersionV2,
 			Name:       name,
 			Version:    "1.2.3",
 			Dependencies: []*chart.Dependency{
diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go
index 924e8e9d3..b7156daf3 100644
--- a/cmd/helm/helm_test.go
+++ b/cmd/helm/helm_test.go
@@ -31,6 +31,7 @@ import (
 	"helm.sh/helm/v3/internal/test"
 	"helm.sh/helm/v3/pkg/action"
 	"helm.sh/helm/v3/pkg/chartutil"
+	"helm.sh/helm/v3/pkg/cli"
 	kubefake "helm.sh/helm/v3/pkg/kube/fake"
 	"helm.sh/helm/v3/pkg/release"
 	"helm.sh/helm/v3/pkg/storage"
@@ -47,24 +48,26 @@ func init() {
 func runTestCmd(t *testing.T, tests []cmdTestCase) {
 	t.Helper()
 	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			defer resetEnv()()
-
-			storage := storageFixture()
-			for _, rel := range tt.rels {
-				if err := storage.Create(rel); err != nil {
-					t.Fatal(err)
+		for i := 0; i <= tt.repeat; i++ {
+			t.Run(tt.name, func(t *testing.T) {
+				defer resetEnv()()
+
+				storage := storageFixture()
+				for _, rel := range tt.rels {
+					if err := storage.Create(rel); err != nil {
+						t.Fatal(err)
+					}
 				}
-			}
-			t.Log("running cmd: ", tt.cmd)
-			_, out, err := executeActionCommandC(storage, tt.cmd)
-			if (err != nil) != tt.wantError {
-				t.Errorf("expected error, got '%v'", err)
-			}
-			if tt.golden != "" {
-				test.AssertGoldenString(t, out, tt.golden)
-			}
-		})
+				t.Logf("running cmd (attempt %d): %s", i+1, tt.cmd)
+				_, out, err := executeActionCommandC(storage, tt.cmd)
+				if (err != nil) != tt.wantError {
+					t.Errorf("expected error, got '%v'", err)
+				}
+				if tt.golden != "" {
+					test.AssertGoldenString(t, out, tt.golden)
+				}
+			})
+		}
 	}
 }
 
@@ -124,6 +127,9 @@ type cmdTestCase struct {
 	wantError bool
 	// Rels are the available releases at the start of the test.
 	rels []*release.Release
+	// Number of repeats (in case a feature was previously flaky and the test checks
+	// it's now stably producing identical results). 0 means test is run exactly once.
+	repeat int
 }
 
 func executeActionCommand(cmd string) (*cobra.Command, string, error) {
@@ -131,14 +137,14 @@ func executeActionCommand(cmd string) (*cobra.Command, string, error) {
 }
 
 func resetEnv() func() {
-	origSettings, origEnv := settings, os.Environ()
+	origEnv := os.Environ()
 	return func() {
 		os.Clearenv()
-		settings = origSettings
 		for _, pair := range origEnv {
 			kv := strings.SplitN(pair, "=", 2)
 			os.Setenv(kv[0], kv[1])
 		}
+		settings = cli.New()
 	}
 }
 
diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go
index 9ab25417b..e8b573dfc 100644
--- a/cmd/helm/install_test.go
+++ b/cmd/helm/install_test.go
@@ -183,6 +183,12 @@ func TestInstall(t *testing.T) {
 			wantError: true,
 			golden:    "output/subchart-schema-cli-negative.txt",
 		},
+		// Install deprecated chart
+		{
+			name:   "install with warning about deprecated chart",
+			cmd:    "install aeneas testdata/testcharts/deprecated --namespace default",
+			golden: "output/deprecated-chart.txt",
+		},
 	}
 
 	runTestActionCmd(t, tests)
diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go
index 248195774..1aca66100 100644
--- a/cmd/helm/pull_test.go
+++ b/cmd/helm/pull_test.go
@@ -41,7 +41,10 @@ func TestPullCmd(t *testing.T) {
 	tests := []struct {
 		name         string
 		args         string
+		existFile    string
+		existDir     string
 		wantError    bool
+		wantErrorMsg string
 		failExpect   string
 		expectFile   string
 		expectDir    bool
@@ -87,10 +90,24 @@ func TestPullCmd(t *testing.T) {
 			expectFile: "./signtest",
 			expectDir:  true,
 		},
+		{
+			name:         "Fetch untar when file with same name existed",
+			args:         "test/test1 --untar --untardir test1",
+			existFile:    "test1",
+			wantError:    true,
+			wantErrorMsg: fmt.Sprintf("failed to untar: a file or directory with the name %s already exists", filepath.Join(srv.Root(), "test1")),
+		},
+		{
+			name:         "Fetch untar when dir with same name existed",
+			args:         "test/test2 --untar --untardir test2",
+			existDir:     "test2",
+			wantError:    true,
+			wantErrorMsg: fmt.Sprintf("failed to untar: a file or directory with the name %s already exists", filepath.Join(srv.Root(), "test2")),
+		},
 		{
 			name:         "Fetch, verify, untar",
-			args:         "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest",
-			expectFile:   "./signtest",
+			args:         "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest2",
+			expectFile:   "./signtest2",
 			expectDir:    true,
 			expectVerify: true,
 		},
@@ -127,9 +144,27 @@ func TestPullCmd(t *testing.T) {
 				filepath.Join(outdir, "repositories.yaml"),
 				outdir,
 			)
+			// Create file or Dir before helm pull --untar, see: https://github.com/helm/helm/issues/7182
+			if tt.existFile != "" {
+				file := filepath.Join(outdir, tt.existFile)
+				_, err := os.Create(file)
+				if err != nil {
+					t.Fatal("err")
+				}
+			}
+			if tt.existDir != "" {
+				file := filepath.Join(outdir, tt.existDir)
+				err := os.Mkdir(file, 0755)
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
 			_, out, err := executeActionCommand(cmd)
 			if err != nil {
 				if tt.wantError {
+					if tt.wantErrorMsg != "" && tt.wantErrorMsg == err.Error() {
+						t.Fatalf("Actual error %s, not equal to expected error %s", err, tt.wantErrorMsg)
+					}
 					return
 				}
 				t.Fatalf("%q reported error: %s", tt.name, err)
diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go
index 1ac61fba8..d1b9bc0fc 100644
--- a/cmd/helm/repo_add_test.go
+++ b/cmd/helm/repo_add_test.go
@@ -19,13 +19,16 @@ package main
 import (
 	"fmt"
 	"io/ioutil"
+	"os"
 	"path/filepath"
 	"sync"
 	"testing"
 
-	"gopkg.in/yaml.v2"
+	"sigs.k8s.io/yaml"
 
 	"helm.sh/helm/v3/internal/test/ensure"
+	"helm.sh/helm/v3/pkg/helmpath"
+	"helm.sh/helm/v3/pkg/helmpath/xdg"
 	"helm.sh/helm/v3/pkg/repo"
 	"helm.sh/helm/v3/pkg/repo/repotest"
 )
@@ -55,7 +58,8 @@ func TestRepoAdd(t *testing.T) {
 	}
 	defer ts.Stop()
 
-	repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
+	rootDir := ensure.TempDir(t)
+	repoFile := filepath.Join(rootDir, "repositories.yaml")
 
 	const testRepoName = "test-name"
 
@@ -65,6 +69,7 @@ func TestRepoAdd(t *testing.T) {
 		noUpdate: true,
 		repoFile: repoFile,
 	}
+	os.Setenv(xdg.CacheHomeEnvVar, rootDir)
 
 	if err := o.run(ioutil.Discard); err != nil {
 		t.Error(err)
@@ -79,6 +84,15 @@ func TestRepoAdd(t *testing.T) {
 		t.Errorf("%s was not successfully inserted into %s", testRepoName, repoFile)
 	}
 
+	idx := filepath.Join(helmpath.CachePath("repository"), helmpath.CacheIndexFile(testRepoName))
+	if _, err := os.Stat(idx); os.IsNotExist(err) {
+		t.Errorf("Error cache index file was not created for repository %s", testRepoName)
+	}
+	idx = filepath.Join(helmpath.CachePath("repository"), helmpath.CacheChartsFile(testRepoName))
+	if _, err := os.Stat(idx); os.IsNotExist(err) {
+		t.Errorf("Error cache charts file was not created for repository %s", testRepoName)
+	}
+
 	o.noUpdate = false
 
 	if err := o.run(ioutil.Discard); err != nil {
diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go
index 3fdcbe9c3..85c76bb92 100644
--- a/cmd/helm/repo_remove_test.go
+++ b/cmd/helm/repo_remove_test.go
@@ -63,10 +63,13 @@ func TestRepoRemove(t *testing.T) {
 	}
 
 	idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName))
-
 	mf, _ := os.Create(idx)
 	mf.Close()
 
+	idx2 := filepath.Join(rootDir, helmpath.CacheChartsFile(testRepoName))
+	mf, _ = os.Create(idx2)
+	mf.Close()
+
 	b.Reset()
 
 	if err := rmOpts.run(b); err != nil {
@@ -77,7 +80,11 @@ func TestRepoRemove(t *testing.T) {
 	}
 
 	if _, err := os.Stat(idx); err == nil {
-		t.Errorf("Error cache file was not removed for repository %s", testRepoName)
+		t.Errorf("Error cache index file was not removed for repository %s", testRepoName)
+	}
+
+	if _, err := os.Stat(idx2); err == nil {
+		t.Errorf("Error cache chart file was not removed for repository %s", testRepoName)
 	}
 
 	f, err := repo.LoadFile(repoFile)
diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go
index 7d606b8a9..f3eef8b6d 100644
--- a/cmd/helm/root_test.go
+++ b/cmd/helm/root_test.go
@@ -31,7 +31,7 @@ func TestRootCmd(t *testing.T) {
 
 	tests := []struct {
 		name, args, cachePath, configPath, dataPath string
-		envars                                      map[string]string
+		envvars                                     map[string]string
 	}{
 		{
 			name: "defaults",
@@ -40,19 +40,19 @@ func TestRootCmd(t *testing.T) {
 		{
 			name:      "with $XDG_CACHE_HOME set",
 			args:      "home",
-			envars:    map[string]string{xdg.CacheHomeEnvVar: "/bar"},
+			envvars:   map[string]string{xdg.CacheHomeEnvVar: "/bar"},
 			cachePath: "/bar/helm",
 		},
 		{
 			name:       "with $XDG_CONFIG_HOME set",
 			args:       "home",
-			envars:     map[string]string{xdg.ConfigHomeEnvVar: "/bar"},
+			envvars:    map[string]string{xdg.ConfigHomeEnvVar: "/bar"},
 			configPath: "/bar/helm",
 		},
 		{
 			name:     "with $XDG_DATA_HOME set",
 			args:     "home",
-			envars:   map[string]string{xdg.DataHomeEnvVar: "/bar"},
+			envvars:  map[string]string{xdg.DataHomeEnvVar: "/bar"},
 			dataPath: "/bar/helm",
 		},
 	}
@@ -61,7 +61,7 @@ func TestRootCmd(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			defer ensure.HelmHome(t)()
 
-			for k, v := range tt.envars {
+			for k, v := range tt.envvars {
 				os.Setenv(k, v)
 			}
 
diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go
index 8aa2f6c06..35a8e996b 100644
--- a/cmd/helm/template_test.go
+++ b/cmd/helm/template_test.go
@@ -79,6 +79,19 @@ func TestTemplateCmd(t *testing.T) {
 			cmd:    fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath),
 			golden: "output/template-with-api-version.txt",
 		},
+		{
+			name:   "template with CRDs",
+			cmd:    fmt.Sprintf("template '%s' --include-crds", chartPath),
+			golden: "output/template-with-crds.txt",
+		},
+		{
+			name:   "sorted output of manifests (order of filenames, then order of objects within each YAML file)",
+			cmd:    fmt.Sprintf("template '%s'", "testdata/testcharts/object-order"),
+			golden: "output/object-order.txt",
+			// Helm previously used random file order. Repeat the test so we
+			// don't accidentally get the expected result.
+			repeat: 10,
+		},
 	}
 	runTestCmd(t, tests)
 }
diff --git a/cmd/helm/testdata/output/deprecated-chart.txt b/cmd/helm/testdata/output/deprecated-chart.txt
new file mode 100644
index 000000000..e5be2c3f1
--- /dev/null
+++ b/cmd/helm/testdata/output/deprecated-chart.txt
@@ -0,0 +1,7 @@
+WARNING: This chart is deprecated
+NAME: aeneas
+LAST DEPLOYED: Fri Sep  2 22:04:05 1977
+NAMESPACE: default
+STATUS: deployed
+REVISION: 1
+TEST SUITE: None
diff --git a/cmd/helm/testdata/output/object-order.txt b/cmd/helm/testdata/output/object-order.txt
new file mode 100644
index 000000000..307f928f2
--- /dev/null
+++ b/cmd/helm/testdata/output/object-order.txt
@@ -0,0 +1,191 @@
+---
+# Source: object-order/templates/01-a.yml
+# 1
+kind: NetworkPolicy
+apiVersion: networking.k8s.io/v1
+metadata:
+  name: first
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/01-a.yml
+# 2
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: second
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/01-a.yml
+# 3
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: third
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 5
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: fifth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 7
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: seventh
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 8
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: eighth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 9
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: ninth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 10
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: tenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 11
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: eleventh
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 12
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: twelfth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 13
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: thirteenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 14
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: fourteenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/02-b.yml
+# 15 (11th object within 02-b.yml, in order to test `SplitManifests` which assigns `manifest-10`
+# to this object which should then come *after* `manifest-9`)
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: fifteenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+---
+# Source: object-order/templates/01-a.yml
+# 4 (Deployment should come after all NetworkPolicy manifests, since 'helm template' outputs in install order)
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: fourth
+spec:
+  selector:
+    matchLabels:
+      pod: fourth
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        pod: fourth
+    spec:
+      containers:
+        - name: hello-world
+          image: gcr.io/google-samples/node-hello:1.0
+---
+# Source: object-order/templates/02-b.yml
+# 6 (implementation detail: currently, 'helm template' outputs hook manifests last; and yes, NetworkPolicy won't make a reasonable hook, this is just a dummy unit test manifest)
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  annotations:
+    "helm.sh/hook": pre-install
+  name: sixth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
diff --git a/cmd/helm/testdata/output/search-single.txt b/cmd/helm/testdata/output/search-single.txt
deleted file mode 100644
index 936605ae1..000000000
--- a/cmd/helm/testdata/output/search-single.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-NAME           	CHART VERSION	APP VERSION	DESCRIPTION      
-testing/mariadb	0.3.0        	           	Chart for MariaDB
diff --git a/cmd/helm/testdata/output/status-with-resource.txt b/cmd/helm/testdata/output/status-with-resource.txt
deleted file mode 100644
index 5f6f4e663..000000000
--- a/cmd/helm/testdata/output/status-with-resource.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-NAME: flummoxed-chickadee
-LAST DEPLOYED: 2016-01-16 00:00:00 +0000 UTC
-NAMESPACE: default
-STATUS: deployed
-
-RESOURCES:
-resource A
-resource B
-
diff --git a/cmd/helm/testdata/output/status.yaml b/cmd/helm/testdata/output/status.yaml
deleted file mode 100644
index a7bc12276..000000000
--- a/cmd/helm/testdata/output/status.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-info:
-  deleted: "0001-01-01T00:00:00Z"
-  first_deployed: "0001-01-01T00:00:00Z"
-  last_deployed: "2016-01-16T00:00:00Z"
-  resources: |
-    resource A
-    resource B
-  status: deployed
-name: flummoxed-chickadee
-namespace: default
diff --git a/cmd/helm/testdata/output/template-with-crds.txt b/cmd/helm/testdata/output/template-with-crds.txt
new file mode 100644
index 000000000..9fa1c7e6d
--- /dev/null
+++ b/cmd/helm/testdata/output/template-with-crds.txt
@@ -0,0 +1,72 @@
+---
+# Source: crds/crdA.yaml
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: testCRDs
+spec:
+  group: testCRDGroups
+  names:
+    kind: TestCRD
+    listKind: TestCRDList
+    plural: TestCRDs
+    shortNames:
+      - tc
+    singular: authconfig
+
+---
+# Source: subchart1/charts/subcharta/templates/service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+  name: subcharta
+  labels:
+    helm.sh/chart: "subcharta-0.1.0"
+spec:
+  type: ClusterIP
+  ports:
+  - port: 80
+    targetPort: 80
+    protocol: TCP
+    name: apache
+  selector:
+    app.kubernetes.io/name: subcharta
+---
+# Source: subchart1/charts/subchartb/templates/service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+  name: subchartb
+  labels:
+    helm.sh/chart: "subchartb-0.1.0"
+spec:
+  type: ClusterIP
+  ports:
+  - port: 80
+    targetPort: 80
+    protocol: TCP
+    name: nginx
+  selector:
+    app.kubernetes.io/name: subchartb
+---
+# Source: subchart1/templates/service.yaml
+apiVersion: v1
+kind: Service
+metadata:
+  name: subchart1
+  labels:
+    helm.sh/chart: "subchart1-0.1.0"
+    app.kubernetes.io/instance: "RELEASE-NAME"
+    kube-version/major: "1"
+    kube-version/minor: "16"
+    kube-version/version: "v1.16.0"
+    kube-api-version/test: v1
+spec:
+  type: ClusterIP
+  ports:
+  - port: 80
+    targetPort: 80
+    protocol: TCP
+    name: nginx
+  selector:
+    app.kubernetes.io/name: subchart1
diff --git a/cmd/helm/testdata/output/version-client-shorthand.txt b/cmd/helm/testdata/output/version-client-shorthand.txt
index 0d9b536e6..4b493d31c 100644
--- a/cmd/helm/testdata/output/version-client-shorthand.txt
+++ b/cmd/helm/testdata/output/version-client-shorthand.txt
@@ -1,1 +1,1 @@
-version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"", GitTreeState:"", GoVersion:""}
+version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""}
diff --git a/cmd/helm/testdata/output/version-client.txt b/cmd/helm/testdata/output/version-client.txt
index 0d9b536e6..4b493d31c 100644
--- a/cmd/helm/testdata/output/version-client.txt
+++ b/cmd/helm/testdata/output/version-client.txt
@@ -1,1 +1,1 @@
-version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"", GitTreeState:"", GoVersion:""}
+version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""}
diff --git a/cmd/helm/testdata/output/version-short.txt b/cmd/helm/testdata/output/version-short.txt
index 5e7749489..d9fb9c736 100644
--- a/cmd/helm/testdata/output/version-short.txt
+++ b/cmd/helm/testdata/output/version-short.txt
@@ -1,1 +1,1 @@
-v3.0+unreleased
+v3.0
diff --git a/cmd/helm/testdata/output/version-template.txt b/cmd/helm/testdata/output/version-template.txt
index c12e29c2d..776c1919b 100644
--- a/cmd/helm/testdata/output/version-template.txt
+++ b/cmd/helm/testdata/output/version-template.txt
@@ -1,1 +1,1 @@
-Version: v3.0+unreleased
\ No newline at end of file
+Version: v3.0
\ No newline at end of file
diff --git a/cmd/helm/testdata/output/version.txt b/cmd/helm/testdata/output/version.txt
index 0d9b536e6..4b493d31c 100644
--- a/cmd/helm/testdata/output/version.txt
+++ b/cmd/helm/testdata/output/version.txt
@@ -1,1 +1,1 @@
-version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"", GitTreeState:"", GoVersion:""}
+version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""}
diff --git a/cmd/helm/testdata/testcharts/deprecated/Chart.yaml b/cmd/helm/testdata/testcharts/deprecated/Chart.yaml
new file mode 100644
index 000000000..10185beeb
--- /dev/null
+++ b/cmd/helm/testdata/testcharts/deprecated/Chart.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+description: Deprecated testing chart
+home: https://helm.sh/helm
+name: deprecated
+sources:
+  - https://github.com/helm/helm
+version: 0.1.0
+deprecated: true
diff --git a/cmd/helm/testdata/testcharts/deprecated/README.md b/cmd/helm/testdata/testcharts/deprecated/README.md
new file mode 100644
index 000000000..0df9a8bbc
--- /dev/null
+++ b/cmd/helm/testdata/testcharts/deprecated/README.md
@@ -0,0 +1,3 @@
+#Deprecated
+
+This space intentionally left blank.
diff --git a/cmd/helm/testdata/testcharts/object-order/Chart.yaml b/cmd/helm/testdata/testcharts/object-order/Chart.yaml
new file mode 100644
index 000000000..d2eb42fd7
--- /dev/null
+++ b/cmd/helm/testdata/testcharts/object-order/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v2
+name: object-order
+description: Test ordering of manifests in output
+type: application
+version: 0.1.0
diff --git a/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml b/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml
new file mode 100644
index 000000000..32aa4a475
--- /dev/null
+++ b/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml
@@ -0,0 +1,57 @@
+# 1
+kind: NetworkPolicy
+apiVersion: networking.k8s.io/v1
+metadata:
+  name: first
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 2
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: second
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 3
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: third
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 4 (Deployment should come after all NetworkPolicy manifests, since 'helm template' outputs in install order)
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: fourth
+spec:
+  selector:
+    matchLabels:
+      pod: fourth
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        pod: fourth
+    spec:
+      containers:
+        - name: hello-world
+          image: gcr.io/google-samples/node-hello:1.0
diff --git a/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml b/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml
new file mode 100644
index 000000000..895db8cf7
--- /dev/null
+++ b/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml
@@ -0,0 +1,143 @@
+# 5
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: fifth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 6 (implementation detail: currently, 'helm template' outputs hook manifests last; and yes, NetworkPolicy won't make a reasonable hook, this is just a dummy unit test manifest)
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  annotations:
+    "helm.sh/hook": pre-install
+  name: sixth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 7
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: seventh
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 8
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: eighth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 9
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: ninth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 10
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: tenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 11
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: eleventh
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 12
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: twelfth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 13
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: thirteenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 14
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: fourteenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
+
+---
+
+# 15 (11th object within 02-b.yml, in order to test `SplitManifests` which assigns `manifest-10`
+# to this object which should then come *after* `manifest-9`)
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: fifteenth
+spec:
+  podSelector: {}
+  policyTypes:
+    - Egress
+    - Ingress
diff --git a/internal/third_party/dep/fs/fs_test.go b/internal/third_party/dep/fs/fs_test.go
index bf4b803f8..a9678d8c1 100644
--- a/internal/third_party/dep/fs/fs_test.go
+++ b/internal/third_party/dep/fs/fs_test.go
@@ -170,7 +170,7 @@ func TestCopyDir(t *testing.T) {
 func TestCopyDirFail_SrcInaccessible(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		// XXX: setting permissions works differently in
-		// Microsoft Windows. Skipping this this until a
+		// Microsoft Windows. Skipping this until a
 		// compatible implementation is provided.
 		t.Skip("skipping on windows")
 	}
@@ -209,7 +209,7 @@ func TestCopyDirFail_SrcInaccessible(t *testing.T) {
 func TestCopyDirFail_DstInaccessible(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		// XXX: setting permissions works differently in
-		// Microsoft Windows. Skipping this this until a
+		// Microsoft Windows. Skipping this until a
 		// compatible implementation is provided.
 		t.Skip("skipping on windows")
 	}
@@ -309,7 +309,7 @@ func TestCopyDirFailOpen(t *testing.T) {
 		// Microsoft Windows. os.Chmod(..., 0222) below is not
 		// enough for the file to be readonly, and os.Chmod(...,
 		// 0000) returns an invalid argument error. Skipping
-		// this this until a compatible implementation is
+		// this until a compatible implementation is
 		// provided.
 		t.Skip("skipping on windows")
 	}
@@ -478,7 +478,7 @@ func TestCopyFileSymlink(t *testing.T) {
 func TestCopyFileFail(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		// XXX: setting permissions works differently in
-		// Microsoft Windows. Skipping this this until a
+		// Microsoft Windows. Skipping this until a
 		// compatible implementation is provided.
 		t.Skip("skipping on windows")
 	}
@@ -521,7 +521,7 @@ func TestCopyFileFail(t *testing.T) {
 }
 
 // setupInaccessibleDir creates a temporary location with a single
-// directory in it, in such a way that that directory is not accessible
+// directory in it, in such a way that directory is not accessible
 // after this function returns.
 //
 // op is called with the directory as argument, so that it can create
diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go
index 4637a4b10..d6f1c88cd 100644
--- a/pkg/action/install_test.go
+++ b/pkg/action/install_test.go
@@ -505,6 +505,14 @@ func TestNameAndChart(t *testing.T) {
 		t.Fatal("expected an error")
 	}
 	is.Equal("must either provide a name or specify --generate-name", err.Error())
+
+	instAction.NameTemplate = ""
+	instAction.ReleaseName = ""
+	_, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"})
+	if err == nil {
+		t.Fatal("expected an error")
+	}
+	is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error())
 }
 
 func TestNameAndChartGenerateName(t *testing.T) {
diff --git a/pkg/action/list_test.go b/pkg/action/list_test.go
index cd86aee68..378a747b0 100644
--- a/pkg/action/list_test.go
+++ b/pkg/action/list_test.go
@@ -237,27 +237,36 @@ func makeMeSomeReleases(store *storage.Storage, t *testing.T) {
 }
 
 func TestFilterList(t *testing.T) {
-	one := releaseStub()
-	one.Name = "one"
-	one.Namespace = "default"
-	one.Version = 1
-	two := releaseStub()
-	two.Name = "two"
-	two.Namespace = "default"
-	two.Version = 1
-	anotherOldOne := releaseStub()
-	anotherOldOne.Name = "one"
-	anotherOldOne.Namespace = "testing"
-	anotherOldOne.Version = 1
-	anotherOne := releaseStub()
-	anotherOne.Name = "one"
-	anotherOne.Namespace = "testing"
-	anotherOne.Version = 2
-
-	list := []*release.Release{one, two, anotherOne}
-	expectedFilteredList := []*release.Release{one, two, anotherOne}
-
-	filteredList := filterList(list)
-
-	assert.ElementsMatch(t, expectedFilteredList, filteredList)
+	t.Run("should filter old versions of the same release", func(t *testing.T) {
+		r1 := releaseStub()
+		r1.Name = "r"
+		r1.Version = 1
+		r2 := releaseStub()
+		r2.Name = "r"
+		r2.Version = 2
+		another := releaseStub()
+		another.Name = "another"
+		another.Version = 1
+
+		filteredList := filterList([]*release.Release{r1, r2, another})
+		expectedFilteredList := []*release.Release{r2, another}
+
+		assert.ElementsMatch(t, expectedFilteredList, filteredList)
+	})
+
+	t.Run("should not filter out any version across namespaces", func(t *testing.T) {
+		r1 := releaseStub()
+		r1.Name = "r"
+		r1.Namespace = "default"
+		r1.Version = 1
+		r2 := releaseStub()
+		r2.Name = "r"
+		r2.Namespace = "testing"
+		r2.Version = 2
+
+		filteredList := filterList([]*release.Release{r1, r2})
+		expectedFilteredList := []*release.Release{r1, r2}
+
+		assert.ElementsMatch(t, expectedFilteredList, filteredList)
+	})
 }
diff --git a/pkg/chart/loader/archive_test.go b/pkg/chart/loader/archive_test.go
new file mode 100644
index 000000000..7d8c8b51e
--- /dev/null
+++ b/pkg/chart/loader/archive_test.go
@@ -0,0 +1,110 @@
+/*
+Copyright The Helm Authors.
+
+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 loader
+
+import (
+	"archive/tar"
+	"bytes"
+	"compress/gzip"
+	"testing"
+)
+
+func TestLoadArchiveFiles(t *testing.T) {
+	tcs := []struct {
+		name     string
+		generate func(w *tar.Writer)
+		check    func(t *testing.T, files []*BufferedFile, err error)
+	}{
+		{
+			name:     "empty input should return no files",
+			generate: func(w *tar.Writer) {},
+			check: func(t *testing.T, files []*BufferedFile, err error) {
+				if err.Error() != "no files in chart archive" {
+					t.Fatalf(`expected "no files in chart archive", got [%#v]`, err)
+				}
+			},
+		},
+		{
+			name: "should ignore files with XGlobalHeader type",
+			generate: func(w *tar.Writer) {
+				// simulate the presence of a `pax_global_header` file like you would get when
+				// processing a GitHub release archive.
+				_ = w.WriteHeader(&tar.Header{
+					Typeflag: tar.TypeXGlobalHeader,
+					Name:     "pax_global_header",
+				})
+
+				// we need to have at least one file, otherwise we'll get the "no files in chart archive" error
+				_ = w.WriteHeader(&tar.Header{
+					Typeflag: tar.TypeReg,
+					Name:     "dir/empty",
+				})
+			},
+			check: func(t *testing.T, files []*BufferedFile, err error) {
+				if err != nil {
+					t.Fatalf(`got unwanted error [%#v] for tar file with pax_global_header content`, err)
+				}
+
+				if len(files) != 1 {
+					t.Fatalf(`expected to get one file but got [%v]`, files)
+				}
+			},
+		},
+		{
+			name: "should ignore files with TypeXHeader type",
+			generate: func(w *tar.Writer) {
+				// simulate the presence of a `pax_header` file like you might get when
+				// processing a GitHub release archive.
+				_ = w.WriteHeader(&tar.Header{
+					Typeflag: tar.TypeXHeader,
+					Name:     "pax_header",
+				})
+
+				// we need to have at least one file, otherwise we'll get the "no files in chart archive" error
+				_ = w.WriteHeader(&tar.Header{
+					Typeflag: tar.TypeReg,
+					Name:     "dir/empty",
+				})
+			},
+			check: func(t *testing.T, files []*BufferedFile, err error) {
+				if err != nil {
+					t.Fatalf(`got unwanted error [%#v] for tar file with pax_header content`, err)
+				}
+
+				if len(files) != 1 {
+					t.Fatalf(`expected to get one file but got [%v]`, files)
+				}
+			},
+		},
+	}
+
+	for _, tc := range tcs {
+		t.Run(tc.name, func(t *testing.T) {
+			buf := &bytes.Buffer{}
+			gzw := gzip.NewWriter(buf)
+			tw := tar.NewWriter(gzw)
+
+			tc.generate(tw)
+
+			_ = tw.Close()
+			_ = gzw.Close()
+
+			files, err := LoadArchiveFiles(buf)
+			tc.check(t, files, err)
+		})
+	}
+}
diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go
index 6576002eb..26513d359 100644
--- a/pkg/chart/loader/load_test.go
+++ b/pkg/chart/loader/load_test.go
@@ -461,7 +461,7 @@ func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
 				t.Fatalf("Expected 2 Dependency, got %d", len(dep.Dependencies()))
 			}
 		default:
-			t.Errorf("Unexpected dependeny %s", dep.Name())
+			t.Errorf("Unexpected dependency %s", dep.Name())
 		}
 	}
 }
diff --git a/pkg/chartutil/errors_test.go b/pkg/chartutil/errors_test.go
index 3f9d04a71..3f63e3733 100644
--- a/pkg/chartutil/errors_test.go
+++ b/pkg/chartutil/errors_test.go
@@ -20,7 +20,7 @@ import (
 	"testing"
 )
 
-func TestErrorNoTableDoesntPanic(t *testing.T) {
+func TestErrorNoTableDoesNotPanic(t *testing.T) {
 	x := "empty"
 
 	y := ErrNoTable{x}
@@ -28,7 +28,7 @@ func TestErrorNoTableDoesntPanic(t *testing.T) {
 	t.Logf("error is: %s", y)
 }
 
-func TestErrorNoValueDoesntPanic(t *testing.T) {
+func TestErrorNoValueDoesNotPanic(t *testing.T) {
 	x := "empty"
 
 	y := ErrNoValue{x}
diff --git a/pkg/chartutil/jsonschema_test.go b/pkg/chartutil/jsonschema_test.go
index 2677ee0ea..33f009259 100644
--- a/pkg/chartutil/jsonschema_test.go
+++ b/pkg/chartutil/jsonschema_test.go
@@ -63,7 +63,7 @@ func TestValidateAgainstSingleSchemaNegative(t *testing.T) {
 	}
 }
 
-const subchrtSchema = `{
+const subchartSchema = `{
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "Values",
   "type": "object",
@@ -81,23 +81,23 @@ const subchrtSchema = `{
 `
 
 func TestValidateAgainstSchema(t *testing.T) {
-	subchrtJSON := []byte(subchrtSchema)
-	subchrt := &chart.Chart{
+	subchartJSON := []byte(subchartSchema)
+	subchart := &chart.Chart{
 		Metadata: &chart.Metadata{
-			Name: "subchrt",
+			Name: "subchart",
 		},
-		Schema: subchrtJSON,
+		Schema: subchartJSON,
 	}
 	chrt := &chart.Chart{
 		Metadata: &chart.Metadata{
 			Name: "chrt",
 		},
 	}
-	chrt.AddDependency(subchrt)
+	chrt.AddDependency(subchart)
 
 	vals := map[string]interface{}{
 		"name": "John",
-		"subchrt": map[string]interface{}{
+		"subchart": map[string]interface{}{
 			"age": 25,
 		},
 	}
@@ -108,23 +108,23 @@ func TestValidateAgainstSchema(t *testing.T) {
 }
 
 func TestValidateAgainstSchemaNegative(t *testing.T) {
-	subchrtJSON := []byte(subchrtSchema)
-	subchrt := &chart.Chart{
+	subchartJSON := []byte(subchartSchema)
+	subchart := &chart.Chart{
 		Metadata: &chart.Metadata{
-			Name: "subchrt",
+			Name: "subchart",
 		},
-		Schema: subchrtJSON,
+		Schema: subchartJSON,
 	}
 	chrt := &chart.Chart{
 		Metadata: &chart.Metadata{
 			Name: "chrt",
 		},
 	}
-	chrt.AddDependency(subchrt)
+	chrt.AddDependency(subchart)
 
 	vals := map[string]interface{}{
-		"name":    "John",
-		"subchrt": map[string]interface{}{},
+		"name":     "John",
+		"subchart": map[string]interface{}{},
 	}
 
 	var errString string
@@ -134,7 +134,7 @@ func TestValidateAgainstSchemaNegative(t *testing.T) {
 		errString = err.Error()
 	}
 
-	expectedErrString := `subchrt:
+	expectedErrString := `subchart:
 - (root): age is required
 `
 	if errString != expectedErrString {
diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go
index 8941d0368..f367d42eb 100644
--- a/pkg/chartutil/save_test.go
+++ b/pkg/chartutil/save_test.go
@@ -17,7 +17,10 @@ limitations under the License.
 package chartutil
 
 import (
+	"archive/tar"
 	"bytes"
+	"compress/gzip"
+	"io"
 	"io/ioutil"
 	"os"
 	"path"
@@ -25,6 +28,7 @@ import (
 	"regexp"
 	"strings"
 	"testing"
+	"time"
 
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/chart/loader"
@@ -99,6 +103,85 @@ func Indent(n int, text string) string {
 	return startOfLine.ReplaceAllLiteralString(text, indentation)
 }
 
+func TestSavePreservesTimestamps(t *testing.T) {
+	// Test executes so quickly that if we don't subtract a second, the
+	// check will fail because `initialCreateTime` will be identical to the
+	// written timestamp for the files.
+	initialCreateTime := time.Now().Add(-1 * time.Second)
+
+	tmp, err := ioutil.TempDir("", "helm-")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmp)
+
+	c := &chart.Chart{
+		Metadata: &chart.Metadata{
+			APIVersion: chart.APIVersionV1,
+			Name:       "ahab",
+			Version:    "1.2.3.4",
+		},
+		Values: map[string]interface{}{
+			"imageName": "testimage",
+			"imageId":   42,
+		},
+		Files: []*chart.File{
+			{Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")},
+		},
+		Schema: []byte("{\n  \"title\": \"Values\"\n}"),
+	}
+
+	where, err := Save(c, tmp)
+	if err != nil {
+		t.Fatalf("Failed to save: %s", err)
+	}
+
+	allHeaders, err := retrieveAllHeadersFromTar(where)
+	if err != nil {
+		t.Fatalf("Failed to parse tar: %v", err)
+	}
+
+	for _, header := range allHeaders {
+		if header.ModTime.Before(initialCreateTime) {
+			t.Fatalf("File timestamp not preserved: %v", header.ModTime)
+		}
+	}
+}
+
+// We could refactor `load.go` to use this `retrieveAllHeadersFromTar` function
+// as well, so we are not duplicating components of the code which iterate
+// through the tar.
+func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) {
+	raw, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer raw.Close()
+
+	unzipped, err := gzip.NewReader(raw)
+	if err != nil {
+		return nil, err
+	}
+	defer unzipped.Close()
+
+	tr := tar.NewReader(unzipped)
+	headers := []*tar.Header{}
+	for {
+		hd, err := tr.Next()
+		if err == io.EOF {
+			break
+		}
+
+		if err != nil {
+			return nil, err
+		}
+
+		headers = append(headers, hd)
+	}
+
+	return headers, nil
+}
+
 func TestSaveDir(t *testing.T) {
 	tmp, err := ioutil.TempDir("", "helm-")
 	if err != nil {
diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml
new file mode 100644
index 000000000..fca77fd4b
--- /dev/null
+++ b/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml
@@ -0,0 +1,13 @@
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: testCRDs
+spec:
+  group: testCRDGroups
+  names:
+    kind: TestCRD
+    listKind: TestCRDList
+    plural: TestCRDs
+    shortNames:
+      - tc
+    singular: authconfig
diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml
index 72d3fa5c8..a974e316a 100644
--- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml
+++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml
@@ -23,7 +23,7 @@ overridden-chartA:
   SCAbool: true
   SCAfloat: 3.14
   SCAint: 100
-  SCAstring: "jabathehut"
+  SCAstring: "jabbathehut"
   SC1extra3: true
 
 imported-chartA-B:
diff --git a/pkg/chartutil/values_test.go b/pkg/chartutil/values_test.go
index a3fe7aeac..c95fa503a 100644
--- a/pkg/chartutil/values_test.go
+++ b/pkg/chartutil/values_test.go
@@ -76,7 +76,7 @@ func TestToRenderValues(t *testing.T) {
 		},
 	}
 
-	overideValues := map[string]interface{}{
+	overrideValues := map[string]interface{}{
 		"name": "Haroun",
 		"where": map[string]interface{}{
 			"city": "Baghdad",
@@ -103,7 +103,7 @@ func TestToRenderValues(t *testing.T) {
 		IsInstall: true,
 	}
 
-	res, err := ToRenderValues(c, overideValues, o, nil)
+	res, err := ToRenderValues(c, overrideValues, o, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -275,10 +275,10 @@ chapter:
 	} else if v != "Loomings" {
 		t.Errorf("No error but got wrong value for title: %s\n%v", err, d)
 	}
-	if _, err := d.PathValue("chapter.one.doesntexist"); err == nil {
+	if _, err := d.PathValue("chapter.one.doesnotexist"); err == nil {
 		t.Errorf("Non-existent key should return error: %s\n%v", err, d)
 	}
-	if _, err := d.PathValue("chapter.doesntexist.one"); err == nil {
+	if _, err := d.PathValue("chapter.doesnotexist.one"); err == nil {
 		t.Errorf("Non-existent key in middle of path should return error: %s\n%v", err, d)
 	}
 	if _, err := d.PathValue(""); err == nil {
diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go
index d6856dd01..fadc2981e 100644
--- a/pkg/cli/environment_test.go
+++ b/pkg/cli/environment_test.go
@@ -29,8 +29,8 @@ func TestEnvSettings(t *testing.T) {
 		name string
 
 		// input
-		args   string
-		envars map[string]string
+		args    string
+		envvars map[string]string
 
 		// expected values
 		ns, kcontext string
@@ -47,17 +47,17 @@ func TestEnvSettings(t *testing.T) {
 			debug: true,
 		},
 		{
-			name:   "with envvars set",
-			envars: 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"},
+			ns:      "yourns",
+			debug:   true,
 		},
 		{
-			name:   "with flags and envvars set",
-			args:   "--debug --namespace=myns",
-			envars: 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,
 		},
 	}
 
@@ -65,7 +65,7 @@ func TestEnvSettings(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			defer resetEnv()()
 
-			for k, v := range tt.envars {
+			for k, v := range tt.envvars {
 				os.Setenv(k, v)
 			}
 
diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go
index 80249e240..e0692c8c8 100644
--- a/pkg/downloader/chart_downloader_test.go
+++ b/pkg/downloader/chart_downloader_test.go
@@ -80,6 +80,67 @@ func TestResolveChartRef(t *testing.T) {
 	}
 }
 
+func TestResolveChartOpts(t *testing.T) {
+	tests := []struct {
+		name, ref, version string
+		expect             []getter.Option
+	}{
+		{
+			name: "repo with CA-file",
+			ref:  "testing-ca-file/foo",
+			expect: []getter.Option{
+				getter.WithURL("https://example.com/foo-1.2.3.tgz"),
+				getter.WithTLSClientConfig("cert", "key", "ca"),
+			},
+		},
+	}
+
+	c := ChartDownloader{
+		Out:              os.Stderr,
+		RepositoryConfig: repoConfig,
+		RepositoryCache:  repoCache,
+		Getters: getter.All(&cli.EnvSettings{
+			RepositoryConfig: repoConfig,
+			RepositoryCache:  repoCache,
+		}),
+	}
+
+	// snapshot options
+	snapshotOpts := c.Options
+
+	for _, tt := range tests {
+		// reset chart downloader options for each test case
+		c.Options = snapshotOpts
+
+		expect, err := getter.NewHTTPGetter(tt.expect...)
+		if err != nil {
+			t.Errorf("%s: failed to setup http client: %s", tt.name, err)
+			continue
+		}
+
+		u, err := c.ResolveChartVersion(tt.ref, tt.version)
+		if err != nil {
+			t.Errorf("%s: failed with error %s", tt.name, err)
+			continue
+		}
+
+		got, err := getter.NewHTTPGetter(
+			append(
+				c.Options,
+				getter.WithURL(u.String()),
+			)...,
+		)
+		if err != nil {
+			t.Errorf("%s: failed to create http client: %s", tt.name, err)
+			continue
+		}
+
+		if *(got.(*getter.HTTPGetter)) != *(expect.(*getter.HTTPGetter)) {
+			t.Errorf("%s: expected %s, got %s", tt.name, expect, got)
+		}
+	}
+}
+
 func TestVerifyChart(t *testing.T) {
 	v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub")
 	if err != nil {
diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go
index 6e32b3e85..ea235c13f 100644
--- a/pkg/downloader/manager_test.go
+++ b/pkg/downloader/manager_test.go
@@ -181,6 +181,74 @@ func TestGetRepoNames(t *testing.T) {
 	}
 }
 
+func TestUpdateBeforeBuild(t *testing.T) {
+	// Set up a fake repo
+	srv, err := repotest.NewTempServer("testdata/*.tgz*")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer srv.Stop()
+	if err := srv.LinkIndices(); err != nil {
+		t.Fatal(err)
+	}
+	dir := func(p ...string) string {
+		return filepath.Join(append([]string{srv.Root()}, p...)...)
+	}
+
+	// Save dep
+	d := &chart.Chart{
+		Metadata: &chart.Metadata{
+			Name:       "dep-chart",
+			Version:    "0.1.0",
+			APIVersion: "v1",
+		},
+	}
+	if err := chartutil.SaveDir(d, dir()); err != nil {
+		t.Fatal(err)
+	}
+	// Save a chart
+	c := &chart.Chart{
+		Metadata: &chart.Metadata{
+			Name:       "with-dependency",
+			Version:    "0.1.0",
+			APIVersion: "v2",
+			Dependencies: []*chart.Dependency{{
+				Name:       d.Metadata.Name,
+				Version:    ">=0.1.0",
+				Repository: "file://../dep-chart",
+			}},
+		},
+	}
+	if err := chartutil.SaveDir(c, dir()); err != nil {
+		t.Fatal(err)
+	}
+
+	// Set-up a manager
+	b := bytes.NewBuffer(nil)
+	g := getter.Providers{getter.Provider{
+		Schemes: []string{"http", "https"},
+		New:     getter.NewHTTPGetter,
+	}}
+	m := &Manager{
+		ChartPath:        dir(c.Metadata.Name),
+		Out:              b,
+		Getters:          g,
+		RepositoryConfig: dir("repositories.yaml"),
+		RepositoryCache:  dir(),
+	}
+
+	// Update before Build. see issue: https://github.com/helm/helm/issues/7101
+	err = m.Update()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = m.Build()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
 // This function is the skeleton test code of failing tests for #6416 and #6871 and bugs due to #5874.
 //
 // This function is used by below tests that ensures success of build operation
@@ -217,7 +285,7 @@ func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Depe
 		Metadata: &chart.Metadata{
 			Name:         chartName,
 			Version:      "0.1.0",
-			APIVersion:   "v1",
+			APIVersion:   "v2",
 			Dependencies: []*chart.Dependency{&dep},
 		},
 	}
diff --git a/pkg/downloader/testdata/repositories.yaml b/pkg/downloader/testdata/repositories.yaml
index 374d95c8a..430865269 100644
--- a/pkg/downloader/testdata/repositories.yaml
+++ b/pkg/downloader/testdata/repositories.yaml
@@ -15,4 +15,9 @@ repositories:
   - name: testing-relative
     url: "http://example.com/helm"
   - name: testing-relative-trailing-slash
-    url: "http://example.com/helm/"
\ No newline at end of file
+    url: "http://example.com/helm/"
+  - name: testing-ca-file
+    url: "https://example.com"
+    certFile: "cert"
+    keyFile: "key"
+    caFile: "ca"
diff --git a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
new file mode 100644
index 000000000..17cdde1c6
--- /dev/null
+++ b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+entries:
+  foo:
+    - name: foo
+      description: Foo Chart
+      home: https://helm.sh/helm
+      keywords: []
+      maintainers: []
+      sources:
+        - https://github.com/helm/charts
+      urls:
+        - https://example.com/foo-1.2.3.tgz
+      version: 1.2.3
+      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go
index 031527dd0..f3609fcbd 100644
--- a/pkg/engine/engine_test.go
+++ b/pkg/engine/engine_test.go
@@ -460,6 +460,15 @@ func TestAlterFuncMap_include(t *testing.T) {
 		},
 	}
 
+	// Check nested reference in include FuncMap
+	d := &chart.Chart{
+		Metadata: &chart.Metadata{Name: "nested"},
+		Templates: []*chart.File{
+			{Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)},
+			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
+		},
+	}
+
 	v := chartutil.Values{
 		"Values": "",
 		"Chart":  c.Metadata,
@@ -477,6 +486,12 @@ func TestAlterFuncMap_include(t *testing.T) {
 	if got := out["conrad/templates/quote"]; got != expect {
 		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
 	}
+
+	_, err = Render(d, v)
+	expectErrName := "nested/templates/quote"
+	if err == nil {
+		t.Errorf("Expected err of nested reference name: %v", expectErrName)
+	}
 }
 
 func TestAlterFuncMap_require(t *testing.T) {
diff --git a/pkg/kube/resource_test.go b/pkg/kube/resource_test.go
index 2e13c3253..3c906ceca 100644
--- a/pkg/kube/resource_test.go
+++ b/pkg/kube/resource_test.go
@@ -37,6 +37,10 @@ func TestResourceList(t *testing.T) {
 	r1 = []*resource.Info{info("foo"), info("bar")}
 	r2 = []*resource.Info{info("bar")}
 
+	if r1.Get(info("bar")).Mapping.Resource.Resource != "pod" {
+		t.Error("expected get pod")
+	}
+
 	diff := r1.Difference(r2)
 	if len(diff) != 1 {
 		t.Error("expected 1 result")
diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go
index b770704c6..2a982d088 100644
--- a/pkg/lint/lint_test.go
+++ b/pkg/lint/lint_test.go
@@ -35,12 +35,12 @@ const goodChartDir = "rules/testdata/goodone"
 
 func TestBadChart(t *testing.T) {
 	m := All(badChartDir, values, namespace, strict).Messages
-	if len(m) != 8 {
+	if len(m) != 7 {
 		t.Errorf("Number of errors %v", len(m))
 		t.Errorf("All didn't fail with expected errors, got %#v", m)
 	}
 	// There should be one INFO, 2 WARNINGs and one ERROR messages, check for them
-	var i, w, e, e2, e3, e4, e5, e6 bool
+	var i, w, e, e2, e3, e4, e5 bool
 	for _, msg := range m {
 		if msg.Severity == support.InfoSev {
 			if strings.Contains(msg.Err.Error(), "icon is recommended") {
@@ -59,24 +59,21 @@ func TestBadChart(t *testing.T) {
 			if strings.Contains(msg.Err.Error(), "name is required") {
 				e2 = true
 			}
-			if strings.Contains(msg.Err.Error(), "directory name (badchartfile) and chart name () must be the same") {
-				e3 = true
-			}
 
 			if strings.Contains(msg.Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") {
-				e4 = true
+				e3 = true
 			}
 
 			if strings.Contains(msg.Err.Error(), "chart type is not valid in apiVersion") {
-				e5 = true
+				e4 = true
 			}
 
 			if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
-				e6 = true
+				e5 = true
 			}
 		}
 	}
-	if !e || !e2 || !e3 || !e4 || !e5 || !e6 || !w || !i {
+	if !e || !e2 || !e3 || !e4 || !e5 || !w || !i {
 		t.Errorf("Didn't find all the expected errors, got %#v", m)
 	}
 }
diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go
index ff2a63583..d032dd12f 100644
--- a/pkg/lint/rules/chartfile_test.go
+++ b/pkg/lint/rules/chartfile_test.go
@@ -30,18 +30,15 @@ import (
 )
 
 const (
-	badChartDir  = "testdata/badchartfile"
-	goodChartDir = "testdata/goodone"
+	badChartDir = "testdata/badchartfile"
 )
 
 var (
 	badChartFilePath         = filepath.Join(badChartDir, "Chart.yaml")
-	goodChartFilePath        = filepath.Join(goodChartDir, "Chart.yaml")
 	nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml")
 )
 
 var badChart, _ = chartutil.LoadChartfile(badChartFilePath)
-var goodChart, _ = chartutil.LoadChartfile(goodChartFilePath)
 
 // Validation functions Test
 func TestValidateChartYamlNotDirectory(t *testing.T) {
@@ -73,24 +70,6 @@ func TestValidateChartName(t *testing.T) {
 	}
 }
 
-func TestValidateChartNameDirMatch(t *testing.T) {
-	err := validateChartNameDirMatch(goodChartDir, goodChart)
-	if err != nil {
-		t.Errorf("validateChartNameDirMatch to return no error, gor a linter error")
-	}
-	// It has not name
-	err = validateChartNameDirMatch(badChartDir, badChart)
-	if err == nil {
-		t.Errorf("validatechartnamedirmatch to return a linter error, got no error")
-	}
-
-	// Wrong path
-	err = validateChartNameDirMatch(badChartDir, goodChart)
-	if err == nil {
-		t.Errorf("validatechartnamedirmatch to return a linter error, got no error")
-	}
-}
-
 func TestValidateChartVersion(t *testing.T) {
 	var failTest = []struct {
 		Version  string
@@ -209,36 +188,32 @@ func TestChartfile(t *testing.T) {
 	Chartfile(&linter)
 	msgs := linter.Messages
 
-	if len(msgs) != 7 {
-		t.Errorf("Expected 7 errors, got %d", len(msgs))
+	if len(msgs) != 6 {
+		t.Errorf("Expected 6 errors, got %d", len(msgs))
 	}
 
 	if !strings.Contains(msgs[0].Err.Error(), "name is required") {
 		t.Errorf("Unexpected message 0: %s", msgs[0].Err)
 	}
 
-	if !strings.Contains(msgs[1].Err.Error(), "directory name (badchartfile) and chart name () must be the same") {
+	if !strings.Contains(msgs[1].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") {
 		t.Errorf("Unexpected message 1: %s", msgs[1].Err)
 	}
 
-	if !strings.Contains(msgs[2].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") {
+	if !strings.Contains(msgs[2].Err.Error(), "version '0.0.0.0' is not a valid SemVer") {
 		t.Errorf("Unexpected message 2: %s", msgs[2].Err)
 	}
 
-	if !strings.Contains(msgs[3].Err.Error(), "version '0.0.0.0' is not a valid SemVer") {
+	if !strings.Contains(msgs[3].Err.Error(), "icon is recommended") {
 		t.Errorf("Unexpected message 3: %s", msgs[3].Err)
 	}
 
-	if !strings.Contains(msgs[4].Err.Error(), "icon is recommended") {
+	if !strings.Contains(msgs[4].Err.Error(), "chart type is not valid in apiVersion") {
 		t.Errorf("Unexpected message 4: %s", msgs[4].Err)
 	}
 
-	if !strings.Contains(msgs[5].Err.Error(), "chart type is not valid in apiVersion") {
+	if !strings.Contains(msgs[5].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
 		t.Errorf("Unexpected message 5: %s", msgs[5].Err)
 	}
 
-	if !strings.Contains(msgs[6].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
-		t.Errorf("Unexpected message 6: %s", msgs[6].Err)
-	}
-
 }
diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go
index 89884f107..cfa2e4cbe 100644
--- a/pkg/plugin/installer/http_installer_test.go
+++ b/pkg/plugin/installer/http_installer_test.go
@@ -234,12 +234,12 @@ func TestExtract(t *testing.T) {
 	gz.Close()
 	// END tarball creation
 
-	extr, err := NewExtractor(source)
+	extractor, err := NewExtractor(source)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	if err = extr.Extract(&buf, tempDir); err != nil {
+	if err = extractor.Extract(&buf, tempDir); err != nil {
 		t.Errorf("Did not expect error but got error: %v", err)
 	}
 
diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go
index f637eca28..076ae1187 100644
--- a/pkg/plugin/plugin_test.go
+++ b/pkg/plugin/plugin_test.go
@@ -87,6 +87,7 @@ func TestPlatformPrepareCommand(t *testing.T) {
 			PlatformCommand: []PlatformCommand{
 				{OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"},
 				{OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"},
+				{OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"},
 				{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
 			},
 		},
@@ -98,6 +99,8 @@ func TestPlatformPrepareCommand(t *testing.T) {
 		osStrCmp = "linux-i386"
 	} else if os == "linux" && arch == "amd64" {
 		osStrCmp = "linux-amd64"
+	} else if os == "linux" && arch == "s390x" {
+		osStrCmp = "linux-s390x"
 	} else if os == "windows" && arch == "amd64" {
 		osStrCmp = "win-64"
 	} else {
diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go
index 93d8ae782..4747e8252 100644
--- a/pkg/releaseutil/kind_sorter_test.go
+++ b/pkg/releaseutil/kind_sorter_test.go
@@ -40,7 +40,7 @@ func TestKindSorter(t *testing.T) {
 			Head: &SimpleHead{Kind: "ClusterRoleBindingList"},
 		},
 		{
-			Name: "e",
+			Name: "f",
 			Head: &SimpleHead{Kind: "ConfigMap"},
 		},
 		{
@@ -84,11 +84,11 @@ func TestKindSorter(t *testing.T) {
 			Head: &SimpleHead{Kind: "NetworkPolicy"},
 		},
 		{
-			Name: "f",
+			Name: "g",
 			Head: &SimpleHead{Kind: "PersistentVolume"},
 		},
 		{
-			Name: "g",
+			Name: "h",
 			Head: &SimpleHead{Kind: "PersistentVolumeClaim"},
 		},
 		{
@@ -132,7 +132,7 @@ func TestKindSorter(t *testing.T) {
 			Head: &SimpleHead{Kind: "RoleBindingList"},
 		},
 		{
-			Name: "d",
+			Name: "e",
 			Head: &SimpleHead{Kind: "Secret"},
 		},
 		{
@@ -140,7 +140,7 @@ func TestKindSorter(t *testing.T) {
 			Head: &SimpleHead{Kind: "Service"},
 		},
 		{
-			Name: "h",
+			Name: "d",
 			Head: &SimpleHead{Kind: "ServiceAccount"},
 		},
 		{
@@ -166,8 +166,8 @@ func TestKindSorter(t *testing.T) {
 		order       KindSortOrder
 		expected    string
 	}{
-		{"install", InstallOrder, "aAbcC3de1fgh2iIjJkKlLmnopqrxstuvw!"},
-		{"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hgf1ed3CcbAa!"},
+		{"install", InstallOrder, "aAbcC3def1gh2iIjJkKlLmnopqrxstuvw!"},
+		{"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hg1fed3CcbAa!"},
 	} {
 		var buf bytes.Buffer
 		t.Run(test.description, func(t *testing.T) {
@@ -185,8 +185,8 @@ func TestKindSorter(t *testing.T) {
 	}
 }
 
-// TestKindSorterSubSort verifies manifests of same kind are also sorted alphanumeric
-func TestKindSorterSubSort(t *testing.T) {
+// TestKindSorterKeepOriginalOrder verifies manifests of same kind are kept in original order
+func TestKindSorterKeepOriginalOrder(t *testing.T) {
 	manifests := []Manifest{
 		{
 			Name: "a",
@@ -230,8 +230,8 @@ func TestKindSorterSubSort(t *testing.T) {
 		order       KindSortOrder
 		expected    string
 	}{
-		// expectation is sorted by kind (unknown is last) and then sub sorted alphabetically within each group
-		{"cm,clusterRole,clusterRoleBinding,Unknown,Unknown2", InstallOrder, "01Aa!zu1u2t3"},
+		// expectation is sorted by kind (unknown is last) and within each group of same kind, the order is kept
+		{"cm,clusterRole,clusterRoleBinding,Unknown,Unknown2", InstallOrder, "01aAz!u2u1t3"},
 	} {
 		var buf bytes.Buffer
 		t.Run(test.description, func(t *testing.T) {
@@ -245,3 +245,24 @@ func TestKindSorterSubSort(t *testing.T) {
 		})
 	}
 }
+
+func TestKindSorterNamespaceAgainstUnknown(t *testing.T) {
+	unknown := Manifest{
+		Name: "a",
+		Head: &SimpleHead{Kind: "Unknown"},
+	}
+	namespace := Manifest{
+		Name: "b",
+		Head: &SimpleHead{Kind: "Namespace"},
+	}
+
+	manifests := []Manifest{unknown, namespace}
+	sortByKind(manifests, InstallOrder)
+
+	expectedOrder := []Manifest{namespace, unknown}
+	for i, manifest := range manifests {
+		if expectedOrder[i].Name != manifest.Name {
+			t.Errorf("Expected %s, got %s", expectedOrder[i].Name, manifest.Name)
+		}
+	}
+}
diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go
index 9f4273682..c6b227acf 100644
--- a/pkg/repo/chartrepo_test.go
+++ b/pkg/repo/chartrepo_test.go
@@ -237,7 +237,7 @@ func verifyIndex(t *testing.T, actual *IndexFile) {
 				t.Errorf("Expected %q, got %q", e.Version, g.Version)
 			}
 			if len(g.Keywords) != 3 {
-				t.Error("Expected 3 keyrwords.")
+				t.Error("Expected 3 keywords.")
 			}
 			if len(g.Maintainers) != 2 {
 				t.Error("Expected 2 maintainers.")
diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go
index bc0b45e2c..5dbd5e551 100644
--- a/pkg/repo/index_test.go
+++ b/pkg/repo/index_test.go
@@ -17,14 +17,19 @@ limitations under the License.
 package repo
 
 import (
+	"bufio"
+	"bytes"
 	"io/ioutil"
 	"net/http"
 	"os"
+	"path/filepath"
+	"sort"
 	"strings"
 	"testing"
 
 	"helm.sh/helm/v3/pkg/cli"
 	"helm.sh/helm/v3/pkg/getter"
+	"helm.sh/helm/v3/pkg/helmpath"
 
 	"helm.sh/helm/v3/pkg/chart"
 )
@@ -131,7 +136,7 @@ func TestMerge(t *testing.T) {
 
 	if len(ind1.Entries) != 2 {
 		t.Errorf("Expected 2 entries, got %d", len(ind1.Entries))
-		vs := ind1.Entries["dreadnaught"]
+		vs := ind1.Entries["dreadnought"]
 		if len(vs) != 2 {
 			t.Errorf("Expected 2 versions, got %d", len(vs))
 		}
@@ -178,6 +183,18 @@ func TestDownloadIndexFile(t *testing.T) {
 			t.Fatalf("Index %q failed to parse: %s", testfile, err)
 		}
 		verifyLocalIndex(t, i)
+
+		// Check that charts file is also created
+		idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))
+		if _, err := os.Stat(idx); err != nil {
+			t.Fatalf("error finding created charts file: %#v", err)
+		}
+
+		b, err = ioutil.ReadFile(idx)
+		if err != nil {
+			t.Fatalf("error reading charts file: %#v", err)
+		}
+		verifyLocalChartsFile(t, b, i)
 	})
 
 	t.Run("should not decode the path in the repo url while downloading index", func(t *testing.T) {
@@ -224,6 +241,18 @@ func TestDownloadIndexFile(t *testing.T) {
 			t.Fatalf("Index %q failed to parse: %s", testfile, err)
 		}
 		verifyLocalIndex(t, i)
+
+		// Check that charts file is also created
+		idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))
+		if _, err := os.Stat(idx); err != nil {
+			t.Fatalf("error finding created charts file: %#v", err)
+		}
+
+		b, err = ioutil.ReadFile(idx)
+		if err != nil {
+			t.Fatalf("error reading charts file: %#v", err)
+		}
+		verifyLocalChartsFile(t, b, i)
 	})
 }
 
@@ -322,6 +351,24 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) {
 	}
 }
 
+func verifyLocalChartsFile(t *testing.T, chartsContent []byte, indexContent *IndexFile) {
+	var expected, real []string
+	for chart := range indexContent.Entries {
+		expected = append(expected, chart)
+	}
+	sort.Strings(expected)
+
+	scanner := bufio.NewScanner(bytes.NewReader(chartsContent))
+	for scanner.Scan() {
+		real = append(real, scanner.Text())
+	}
+	sort.Strings(real)
+
+	if strings.Join(expected, " ") != strings.Join(real, " ") {
+		t.Errorf("Cached charts file content unexpected. Expected:\n%s\ngot:\n%s", expected, real)
+	}
+}
+
 func TestIndexDirectory(t *testing.T) {
 	dir := "testdata/repository"
 	index, err := IndexDirectory(dir, "http://localhost:8080")
@@ -361,26 +408,6 @@ func TestIndexDirectory(t *testing.T) {
 	}
 }
 
-func TestLoadUnversionedIndex(t *testing.T) {
-	data, err := ioutil.ReadFile("testdata/unversioned-index.yaml")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	ind, err := loadUnversionedIndex(data)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if l := len(ind.Entries); l != 2 {
-		t.Fatalf("Expected 2 entries, got %d", l)
-	}
-
-	if l := len(ind.Entries["mysql"]); l != 3 {
-		t.Fatalf("Expected 3 mysql versions, got %d", l)
-	}
-}
-
 func TestIndexAdd(t *testing.T) {
 	i := NewIndexFile()
 	i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
diff --git a/pkg/repo/testdata/unversioned-index.yaml b/pkg/repo/testdata/unversioned-index.yaml
deleted file mode 100644
index 1d814c7ae..000000000
--- a/pkg/repo/testdata/unversioned-index.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-memcached-0.1.0:
-  name: memcached
-  url: https://mumoshu.github.io/charts/memcached-0.1.0.tgz
-  created: 2016-08-04 02:05:02.259205055 +0000 UTC
-  checksum: ce9b76576c4b4eb74286fa30a978c56d69e7a522
-  chartfile:
-    name: memcached
-    home: http://https://hub.docker.com/_/memcached/
-    sources: []
-    version: 0.1.0
-    description: A simple Memcached cluster
-    keywords: []
-    maintainers:
-      - name: Matt Butcher
-        email: mbutcher@deis.com
-mysql-0.2.0:
-  name: mysql
-  url: https://mumoshu.github.io/charts/mysql-0.2.0.tgz
-  created: 2016-08-04 00:42:47.517342022 +0000 UTC
-  checksum: aa5edd2904d639b0b6295f1c7cf4c0a8e4f77dd3
-  chartfile:
-    name: mysql
-    home: https://www.mysql.com/
-    sources: []
-    version: 0.2.0
-    description: Chart running MySQL.
-    keywords: []
-    maintainers:
-      - name: Matt Fisher
-        email: mfisher@deis.com
-mysql-0.2.1:
-  name: mysql
-  url: https://mumoshu.github.io/charts/mysql-0.2.1.tgz
-  created: 2016-08-04 02:40:29.717829534 +0000 UTC
-  checksum: 9d9f056171beefaaa04db75680319ca4edb6336a
-  chartfile:
-    name: mysql
-    home: https://www.mysql.com/
-    sources: []
-    version: 0.2.1
-    description: Chart running MySQL.
-    keywords: []
-    maintainers:
-      - name: Matt Fisher
-        email: mfisher@deis.com
-mysql-0.2.2:
-  name: mysql
-  url: https://mumoshu.github.io/charts/mysql-0.2.2.tgz
-  created: 2016-08-04 02:40:29.71841952 +0000 UTC
-  checksum: 6d6810e76a5987943faf0040ec22990d9fb141c7
-  chartfile:
-    name: mysql
-    home: https://www.mysql.com/
-    sources: []
-    version: 0.2.2
-    description: Chart running MySQL.
-    keywords: []
-    maintainers:
-      - name: Matt Fisher
-        email: mfisher@deis.com