Repository: helm/helm
Pull request #9326 (2021-02-04 20:48:47)

chore(go.mod): bump Masterminds/{spring,goutils} and deislabs/oras
Signed-off-by: Adam Reese <adam@reese.io>


Patch:
diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go
index 23dca194a..e845751c1 100644
--- a/cmd/helm/repo_update.go
+++ b/cmd/helm/repo_update.go
@@ -63,15 +63,9 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
 
 func (o *repoUpdateOptions) run(out io.Writer) error {
 	f, err := repo.LoadFile(o.repoFile)
-	switch {
-	case isNotExist(err):
-		return errNoRepositories
-	case err != nil:
-		return errors.Wrapf(err, "failed loading file: %s", o.repoFile)
-	case len(f.Repositories) == 0:
+	if isNotExist(err) || len(f.Repositories) == 0 {
 		return errNoRepositories
 	}
-
 	var repos []*repo.ChartRepository
 	for _, cfg := range f.Repositories {
 		r, err := repo.NewChartRepository(cfg, getter.All(settings))
diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go
index ba692a2e7..ba26ee5e9 100644
--- a/cmd/helm/search_repo.go
+++ b/cmd/helm/search_repo.go
@@ -143,7 +143,7 @@ func (o *searchRepoOptions) setupSearchedVersion() {
 }
 
 func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) {
-	if o.version == "" {
+	if len(o.version) == 0 {
 		return res, nil
 	}
 
@@ -154,19 +154,26 @@ func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Res
 
 	data := res[:0]
 	foundNames := map[string]bool{}
+	appendSearchResults := func(res *search.Result) {
+		data = append(data, res)
+		if !o.versions {
+			foundNames[res.Name] = true // If user hasn't requested all versions, only show the latest that matches
+		}
+	}
 	for _, r := range res {
-		// if not returning all versions and already have found a result,
-		// you're done!
-		if !o.versions && foundNames[r.Name] {
+		if _, found := foundNames[r.Name]; found {
 			continue
 		}
 		v, err := semver.NewVersion(r.Chart.Version)
+
 		if err != nil {
-			continue
-		}
-		if constraint.Check(v) {
-			data = append(data, r)
-			foundNames[r.Name] = true
+			// If the current version number check appears ErrSegmentStartsZero or ErrInvalidPrerelease error and not devel mode, ignore
+			if (err == semver.ErrSegmentStartsZero || err == semver.ErrInvalidPrerelease) && !o.devel {
+				continue
+			}
+			appendSearchResults(r)
+		} else if constraint.Check(v) {
+			appendSearchResults(r)
 		}
 	}
 
@@ -187,7 +194,6 @@ func (o *searchRepoOptions) buildIndex() (*search.Index, error) {
 		ind, err := repo.LoadIndexFile(f)
 		if err != nil {
 			warning("Repo %q is corrupt or missing. Try 'helm repo update'.", n)
-			warning("%s", err)
 			continue
 		}
 
diff --git a/go.mod b/go.mod
index 84b7a5437..c8d764459 100644
--- a/go.mod
+++ b/go.mod
@@ -5,15 +5,15 @@ go 1.15
 require (
 	github.com/BurntSushi/toml v0.3.1
 	github.com/DATA-DOG/go-sqlmock v1.5.0
-	github.com/Masterminds/goutils v1.1.0
+	github.com/Masterminds/goutils v1.1.1
 	github.com/Masterminds/semver/v3 v3.1.1
-	github.com/Masterminds/sprig/v3 v3.2.0
+	github.com/Masterminds/sprig/v3 v3.2.2
 	github.com/Masterminds/squirrel v1.5.0
 	github.com/Masterminds/vcs v1.13.1
 	github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
 	github.com/containerd/containerd v1.4.3
 	github.com/cyphar/filepath-securejoin v0.2.2
-	github.com/deislabs/oras v0.9.0
+	github.com/deislabs/oras v0.10.0
 	github.com/docker/distribution v2.7.1+incompatible
 	github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce
 	github.com/docker/go-units v0.4.0
diff --git a/go.sum b/go.sum
index a0fde6db5..f4cf5db0f 100644
--- a/go.sum
+++ b/go.sum
@@ -52,12 +52,12 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
 github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/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/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/sprig/v3 v3.2.0 h1:P1ekkbuU73Ui/wS0nK1HOM37hh4xdfZo485UPf8rc+Y=
-github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI=
+github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
+github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
 github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8=
 github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
 github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
@@ -170,16 +170,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
-github.com/deislabs/oras v0.9.0 h1:R6PRN3bTruUjHcGKgdteurzbpsCxwf3XJCLsxLFyBuU=
-github.com/deislabs/oras v0.9.0/go.mod h1:QXnMi3+eEm/rkgGT6L+Lt0TT2WLA7pOzuk7tZIsUhFM=
+github.com/deislabs/oras v0.10.0 h1:Eufbi8zVaULb7vYj5HKM9qv9qw6fJ7P75JSjn//gR0E=
+github.com/deislabs/oras v0.10.0/go.mod h1:N1UzE7rBa9qLyN4l8IlBTxc2PkrRcKgWQ3HTJvRnJRE=
 github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
 github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
-github.com/docker/cli v20.10.2+incompatible h1:CR/6BZX5w3TLgAHZTyRpVh3yi+Q8Sj5j1fCsb0J2rCk=
-github.com/docker/cli v20.10.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v20.10.3+incompatible h1:WVEgoV/GpsTK5hruhHdYi79blQ+nmcm+7Ru/ZuiF+7E=
+github.com/docker/cli v20.10.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v0.0.0-20191216044856-a8371794149d h1:jC8tT/S0OGx2cswpeUTn4gOIea8P08lD3VFQT0cOZ50=
 github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
 github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
@@ -836,6 +836,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/pkg/chart/dependency.go b/pkg/chart/dependency.go
index b2819f373..9ec4544c2 100644
--- a/pkg/chart/dependency.go
+++ b/pkg/chart/dependency.go
@@ -49,23 +49,6 @@ type Dependency struct {
 	Alias string `json:"alias,omitempty"`
 }
 
-// Validate checks for common problems with the dependency datastructure in
-// the chart. This check must be done at load time before the dependency's charts are
-// loaded.
-func (d *Dependency) Validate() error {
-	d.Name = sanitizeString(d.Name)
-	d.Version = sanitizeString(d.Version)
-	d.Repository = sanitizeString(d.Repository)
-	d.Condition = sanitizeString(d.Condition)
-	for i := range d.Tags {
-		d.Tags[i] = sanitizeString(d.Tags[i])
-	}
-	if d.Alias != "" && !aliasNameFormat.MatchString(d.Alias) {
-		return ValidationErrorf("dependency %q has disallowed characters in the alias", d.Name)
-	}
-	return nil
-}
-
 // Lock is a lock file for dependencies.
 //
 // It represents the state that the dependencies should be in.
diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go
index 1925e45ac..1848eb280 100644
--- a/pkg/chart/metadata.go
+++ b/pkg/chart/metadata.go
@@ -15,13 +15,6 @@ limitations under the License.
 
 package chart
 
-import (
-	"strings"
-	"unicode"
-
-	"github.com/Masterminds/semver/v3"
-)
-
 // Maintainer describes a Chart maintainer.
 type Maintainer struct {
 	// Name is a user name or organization name
@@ -32,23 +25,15 @@ type Maintainer struct {
 	URL string `json:"url,omitempty"`
 }
 
-// Validate checks valid data and sanitizes string characters.
-func (m *Maintainer) Validate() error {
-	m.Name = sanitizeString(m.Name)
-	m.Email = sanitizeString(m.Email)
-	m.URL = sanitizeString(m.URL)
-	return nil
-}
-
 // Metadata for a Chart file. This models the structure of a Chart.yaml file.
 type Metadata struct {
-	// The name of the chart. Required.
+	// The name of the chart
 	Name string `json:"name,omitempty"`
 	// The URL to a relevant project page, git repo, or contact person
 	Home string `json:"home,omitempty"`
 	// Source is the URL to the source code of this chart
 	Sources []string `json:"sources,omitempty"`
-	// A SemVer 2 conformant version string of the chart. Required.
+	// A SemVer 2 conformant version string of the chart
 	Version string `json:"version,omitempty"`
 	// A one-sentence description of the chart
 	Description string `json:"description,omitempty"`
@@ -58,7 +43,7 @@ type Metadata struct {
 	Maintainers []*Maintainer `json:"maintainers,omitempty"`
 	// The URL to an icon file.
 	Icon string `json:"icon,omitempty"`
-	// The API Version of this chart. Required.
+	// The API Version of this chart.
 	APIVersion string `json:"apiVersion,omitempty"`
 	// The condition to check to enable chart
 	Condition string `json:"condition,omitempty"`
@@ -79,28 +64,11 @@ type Metadata struct {
 	Type string `json:"type,omitempty"`
 }
 
-// Validate checks the metadata for known issues and sanitizes string
-// characters.
+// Validate checks the metadata for known issues, returning an error if metadata is not correct
 func (md *Metadata) Validate() error {
 	if md == nil {
 		return ValidationError("chart.metadata is required")
 	}
-
-	md.Name = sanitizeString(md.Name)
-	md.Description = sanitizeString(md.Description)
-	md.Home = sanitizeString(md.Home)
-	md.Icon = sanitizeString(md.Icon)
-	md.Condition = sanitizeString(md.Condition)
-	md.Tags = sanitizeString(md.Tags)
-	md.AppVersion = sanitizeString(md.AppVersion)
-	md.KubeVersion = sanitizeString(md.KubeVersion)
-	for i := range md.Sources {
-		md.Sources[i] = sanitizeString(md.Sources[i])
-	}
-	for i := range md.Keywords {
-		md.Keywords[i] = sanitizeString(md.Keywords[i])
-	}
-
 	if md.APIVersion == "" {
 		return ValidationError("chart.metadata.apiVersion is required")
 	}
@@ -110,26 +78,19 @@ func (md *Metadata) Validate() error {
 	if md.Version == "" {
 		return ValidationError("chart.metadata.version is required")
 	}
-	if !isValidSemver(md.Version) {
-		return ValidationErrorf("chart.metadata.version %q is invalid", md.Version)
-	}
 	if !isValidChartType(md.Type) {
 		return ValidationError("chart.metadata.type must be application or library")
 	}
 
-	for _, m := range md.Maintainers {
-		if err := m.Validate(); err != nil {
-			return err
-		}
-	}
-
 	// Aliases need to be validated here to make sure that the alias name does
 	// not contain any illegal characters.
 	for _, dependency := range md.Dependencies {
-		if err := dependency.Validate(); err != nil {
+		if err := validateDependency(dependency); err != nil {
 			return err
 		}
 	}
+
+	// TODO validate valid semver here?
 	return nil
 }
 
@@ -141,20 +102,12 @@ func isValidChartType(in string) bool {
 	return false
 }
 
-func isValidSemver(v string) bool {
-	_, err := semver.NewVersion(v)
-	return err == nil
-}
-
-// sanitizeString normalize spaces and removes non-printable characters.
-func sanitizeString(str string) string {
-	return strings.Map(func(r rune) rune {
-		if unicode.IsSpace(r) {
-			return ' '
-		}
-		if unicode.IsPrint(r) {
-			return r
-		}
-		return -1
-	}, str)
+// validateDependency checks for common problems with the dependency datastructure in
+// the chart. This check must be done at load time before the dependency's charts are
+// loaded.
+func validateDependency(dep *Dependency) error {
+	if len(dep.Alias) > 0 && !aliasNameFormat.MatchString(dep.Alias) {
+		return ValidationErrorf("dependency %q has disallowed characters in the alias", dep.Name)
+	}
+	return nil
 }
diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go
index 9ead561b2..93b5527a1 100644
--- a/pkg/plugin/plugin.go
+++ b/pkg/plugin/plugin.go
@@ -23,7 +23,6 @@ import (
 	"regexp"
 	"runtime"
 	"strings"
-	"unicode"
 
 	"github.com/pkg/errors"
 	"sigs.k8s.io/yaml"
@@ -176,25 +175,10 @@ func validatePluginData(plug *Plugin, filepath string) error {
 	if !validPluginName.MatchString(plug.Metadata.Name) {
 		return fmt.Errorf("invalid plugin name at %q", filepath)
 	}
-	plug.Metadata.Usage = sanitizeString(plug.Metadata.Usage)
-
 	// We could also validate SemVer, executable, and other fields should we so choose.
 	return nil
 }
 
-// sanitizeString normalize spaces and removes non-printable characters.
-func sanitizeString(str string) string {
-	return strings.Map(func(r rune) rune {
-		if unicode.IsSpace(r) {
-			return ' '
-		}
-		if unicode.IsPrint(r) {
-			return r
-		}
-		return -1
-	}, str)
-}
-
 func detectDuplicates(plugs []*Plugin) error {
 	names := map[string]string{}
 
diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go
index 09b94fd42..92892bb85 100644
--- a/pkg/repo/chartrepo.go
+++ b/pkg/repo/chartrepo.go
@@ -82,8 +82,6 @@ func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository,
 // Load loads a directory of charts as if it were a repository.
 //
 // It requires the presence of an index.yaml file in the directory.
-//
-// Deprecated: remove in Helm 4.
 func (r *ChartRepository) Load() error {
 	dirInfo, err := os.Stat(r.Config.Name)
 	if err != nil {
@@ -101,7 +99,7 @@ func (r *ChartRepository) Load() error {
 			if strings.Contains(f.Name(), "-index.yaml") {
 				i, err := LoadIndexFile(path)
 				if err != nil {
-					return err
+					return nil
 				}
 				r.IndexFile = i
 			} else if strings.HasSuffix(f.Name(), ".tgz") {
@@ -139,7 +137,7 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) {
 		return "", err
 	}
 
-	indexFile, err := loadIndex(index, r.Config.URL)
+	indexFile, err := loadIndex(index)
 	if err != nil {
 		return "", err
 	}
@@ -189,9 +187,7 @@ func (r *ChartRepository) generateIndex() error {
 		}
 
 		if !r.IndexFile.Has(ch.Name(), ch.Metadata.Version) {
-			if err := r.IndexFile.MustAdd(ch.Metadata, path, r.Config.URL, digest); err != nil {
-				return errors.Wrapf(err, "failed adding to %s to index", path)
-			}
+			r.IndexFile.Add(ch.Metadata, path, r.Config.URL, digest)
 		}
 		// TODO: If a chart exists, but has a different Digest, should we error?
 	}
diff --git a/pkg/repo/index.go b/pkg/repo/index.go
index 5adaf5530..43f1e1c87 100644
--- a/pkg/repo/index.go
+++ b/pkg/repo/index.go
@@ -19,7 +19,6 @@ package repo
 import (
 	"bytes"
 	"io/ioutil"
-	"log"
 	"os"
 	"path"
 	"path/filepath"
@@ -106,27 +105,16 @@ func LoadIndexFile(path string) (*IndexFile, error) {
 	if err != nil {
 		return nil, err
 	}
-	i, err := loadIndex(b, path)
-	if err != nil {
-		return nil, errors.Wrapf(err, "error loading %s", path)
-	}
-	return i, nil
+	return loadIndex(b)
 }
 
-// MustAdd adds a file to the index
+// Add adds a file to the index
 // This can leave the index in an unsorted state
-func (i IndexFile) MustAdd(md *chart.Metadata, filename, baseURL, digest string) error {
-	if md.APIVersion == "" {
-		md.APIVersion = chart.APIVersionV1
-	}
-	if err := md.Validate(); err != nil {
-		return errors.Wrapf(err, "validate failed for %s", filename)
-	}
-
+func (i IndexFile) Add(md *chart.Metadata, filename, baseURL, digest string) {
 	u := filename
 	if baseURL != "" {
-		_, file := filepath.Split(filename)
 		var err error
+		_, file := filepath.Split(filename)
 		u, err = urlutil.URLJoin(baseURL, file)
 		if err != nil {
 			u = path.Join(baseURL, file)
@@ -138,17 +126,10 @@ func (i IndexFile) MustAdd(md *chart.Metadata, filename, baseURL, digest string)
 		Digest:   digest,
 		Created:  time.Now(),
 	}
-	ee := i.Entries[md.Name]
-	i.Entries[md.Name] = append(ee, cr)
-	return nil
-}
-
-// Add adds a file to the index and logs an error.
-//
-// Deprecated: Use index.MustAdd instead.
-func (i IndexFile) Add(md *chart.Metadata, filename, baseURL, digest string) {
-	if err := i.MustAdd(md, filename, baseURL, digest); err != nil {
-		log.Printf("skipping loading invalid entry for chart %q %q from %s: %s", md.Name, md.Version, filename, err)
+	if ee, ok := i.Entries[md.Name]; !ok {
+		i.Entries[md.Name] = ChartVersions{cr}
+	} else {
+		i.Entries[md.Name] = append(ee, cr)
 	}
 }
 
@@ -313,34 +294,19 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
 		if err != nil {
 			return index, err
 		}
-		if err := index.MustAdd(c.Metadata, fname, parentURL, hash); err != nil {
-			return index, errors.Wrapf(err, "failed adding to %s to index", fname)
-		}
+		index.Add(c.Metadata, fname, parentURL, hash)
 	}
 	return index, nil
 }
 
 // loadIndex loads an index file and does minimal validity checking.
 //
-// The source parameter is only used for logging.
 // This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
-func loadIndex(data []byte, source string) (*IndexFile, error) {
+func loadIndex(data []byte) (*IndexFile, error) {
 	i := &IndexFile{}
 	if err := yaml.UnmarshalStrict(data, i); err != nil {
 		return i, err
 	}
-
-	for name, cvs := range i.Entries {
-		for idx := len(cvs) - 1; idx >= 0; idx-- {
-			if cvs[idx].APIVersion == "" {
-				cvs[idx].APIVersion = chart.APIVersionV1
-			}
-			if err := cvs[idx].Validate(); err != nil {
-				log.Printf("skipping loading invalid entry for chart %q %q from %s: %s", name, cvs[idx].Version, source, err)
-				cvs = append(cvs[:idx], cvs[idx+1:]...)
-			}
-		}
-	}
 	i.SortEntries()
 	if i.APIVersion == "" {
 		return i, ErrNoAPIVersion


Test patch:
diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go
index 83ef24349..f4936b367 100644
--- a/cmd/helm/repo_update_test.go
+++ b/cmd/helm/repo_update_test.go
@@ -19,7 +19,6 @@ import (
 	"bytes"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
@@ -54,27 +53,20 @@ func TestUpdateCmd(t *testing.T) {
 }
 
 func TestUpdateCustomCacheCmd(t *testing.T) {
+	var out bytes.Buffer
 	rootDir := ensure.TempDir(t)
 	cachePath := filepath.Join(rootDir, "updcustomcache")
-	os.Mkdir(cachePath, os.ModePerm)
+	_ = os.Mkdir(cachePath, os.ModePerm)
 	defer os.RemoveAll(cachePath)
-
-	ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer ts.Stop()
-
 	o := &repoUpdateOptions{
 		update:    updateCharts,
-		repoFile:  filepath.Join(ts.Root(), "repositories.yaml"),
+		repoFile:  "testdata/repositories.yaml",
 		repoCache: cachePath,
 	}
-	b := ioutil.Discard
-	if err := o.run(b); err != nil {
+	if err := o.run(&out); err != nil {
 		t.Fatal(err)
 	}
-	if _, err := os.Stat(filepath.Join(cachePath, "test-index.yaml")); err != nil {
+	if _, err := os.Stat(filepath.Join(cachePath, "charts-index.yaml")); err != nil {
 		t.Fatalf("error finding created index file in custom cache: %v", err)
 	}
 }
diff --git a/cmd/helm/search_repo_test.go b/cmd/helm/search_repo_test.go
index 39c9c53f5..86519cd42 100644
--- a/cmd/helm/search_repo_test.go
+++ b/cmd/helm/search_repo_test.go
@@ -68,6 +68,14 @@ func TestSearchRepositoriesCmd(t *testing.T) {
 		name:   "search for 'maria', expect valid json output",
 		cmd:    "search repo maria --output json",
 		golden: "output/search-output-json.txt",
+	}, {
+		name:   "search for 'maria', expect one match with semver begin with zero development version",
+		cmd:    "search repo maria --devel",
+		golden: "output/search-semver-pre-zero-devel-release.txt",
+	}, {
+		name:   "search for 'nginx-ingress', expect one match with invalid development pre version",
+		cmd:    "search repo nginx-ingress --devel",
+		golden: "output/search-semver-pre-invalid-release.txt",
 	}, {
 		name:   "search for 'alpine', expect valid yaml output",
 		cmd:    "search repo alpine --output yaml",
diff --git a/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml b/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml
index 889d7d87a..d76501e57 100644
--- a/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml
+++ b/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml
@@ -13,7 +13,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
     - name: alpine
       url: https://charts.helm.sh/stable/alpine-0.2.0.tgz
       checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
@@ -26,7 +25,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
     - name: alpine
       url: https://charts.helm.sh/stable/alpine-0.3.0-rc.1.tgz
       checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
@@ -39,7 +37,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
   mariadb:
     - name: mariadb
       url: https://charts.helm.sh/stable/mariadb-0.3.0.tgz
@@ -58,4 +55,33 @@ entries:
         - name: Bitnami
           email: containers@bitnami.com
       icon: ""
-      apiVersion: v2
+    - name: mariadb
+      url: https://charts.helm.sh/stable/mariadb-0.3.0-0565674.tgz
+      checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56
+      home: https://mariadb.org
+      sources:
+        - https://github.com/bitnami/bitnami-docker-mariadb
+      version: 0.3.0-0565674
+      description: Chart for MariaDB
+      keywords:
+        - mariadb
+        - mysql
+        - database
+        - sql
+      maintainers:
+        - name: Bitnami
+          email: containers@bitnami.com
+      icon: ""
+  nginx-ingress:
+    - name: nginx-ingress
+      url: https://github.com/kubernetes/ingress-nginx/ingress-a.b.c.sdfsdf.tgz
+      checksum: 25229f6de44a2be9f215d11dbff31167ddc8ba56
+      home: https://github.com/kubernetes/ingress-nginx
+      sources:
+        - https://github.com/kubernetes/ingress-nginx
+      version: a.b.c.sdfsdf
+      description: Chart for nginx-ingress
+      keywords:
+        - ingress
+        - nginx
+      icon: ""
diff --git a/cmd/helm/testdata/output/search-semver-pre-invalid-release.txt b/cmd/helm/testdata/output/search-semver-pre-invalid-release.txt
new file mode 100644
index 000000000..ea39e2978
--- /dev/null
+++ b/cmd/helm/testdata/output/search-semver-pre-invalid-release.txt
@@ -0,0 +1,2 @@
+NAME                 	CHART VERSION	APP VERSION	DESCRIPTION            
+testing/nginx-ingress	a.b.c.sdfsdf 	           	Chart for nginx-ingress
diff --git a/cmd/helm/testdata/output/search-semver-pre-zero-devel-release.txt b/cmd/helm/testdata/output/search-semver-pre-zero-devel-release.txt
new file mode 100644
index 000000000..971c6523e
--- /dev/null
+++ b/cmd/helm/testdata/output/search-semver-pre-zero-devel-release.txt
@@ -0,0 +1,2 @@
+NAME           	CHART VERSION	APP VERSION	DESCRIPTION      
+testing/mariadb	0.3.0-0565674	           	Chart for MariaDB
diff --git a/internal/resolver/testdata/repository/kubernetes-charts-index.yaml b/internal/resolver/testdata/repository/kubernetes-charts-index.yaml
index c6b7962a1..493a162f5 100644
--- a/internal/resolver/testdata/repository/kubernetes-charts-index.yaml
+++ b/internal/resolver/testdata/repository/kubernetes-charts-index.yaml
@@ -13,7 +13,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
     - name: alpine
       urls:
         - https://charts.helm.sh/stable/alpine-0.2.0.tgz
@@ -26,7 +25,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
   mariadb:
     - name: mariadb
       urls:
@@ -46,4 +44,3 @@ entries:
         - name: Bitnami
           email: containers@bitnami.com
       icon: ""
-      apiVersion: v2
diff --git a/pkg/chart/dependency_test.go b/pkg/chart/dependency_test.go
deleted file mode 100644
index 99c45b4b5..000000000
--- a/pkg/chart/dependency_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-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 chart
-
-import (
-	"testing"
-)
-
-func TestValidateDependency(t *testing.T) {
-	dep := &Dependency{
-		Name: "example",
-	}
-	for value, shouldFail := range map[string]bool{
-		"abcdefghijklmenopQRSTUVWXYZ-0123456780_": false,
-		"-okay":      false,
-		"_okay":      false,
-		"- bad":      true,
-		" bad":       true,
-		"bad\nvalue": true,
-		"bad ":       true,
-		"bad$":       true,
-	} {
-		dep.Alias = value
-		res := dep.Validate()
-		if res != nil && !shouldFail {
-			t.Errorf("Failed on case %q", dep.Alias)
-		} else if res == nil && shouldFail {
-			t.Errorf("Expected failure for %q", dep.Alias)
-		}
-	}
-}
diff --git a/pkg/chart/metadata_test.go b/pkg/chart/metadata_test.go
index 9f881a4e1..0c7b173dd 100644
--- a/pkg/chart/metadata_test.go
+++ b/pkg/chart/metadata_test.go
@@ -72,10 +72,6 @@ func TestValidate(t *testing.T) {
 			},
 			ValidationError("dependency \"bad\" has disallowed characters in the alias"),
 		},
-		{
-			&Metadata{APIVersion: "v2", Name: "test", Version: "1.2.3.4"},
-			ValidationError("chart.metadata.version \"1.2.3.4\" is invalid"),
-		},
 	}
 
 	for _, tt := range tests {
@@ -86,15 +82,26 @@ func TestValidate(t *testing.T) {
 	}
 }
 
-func TestValidate_sanitize(t *testing.T) {
-	md := &Metadata{APIVersion: "v2", Name: "test", Version: "1.0", Description: "\adescr\u0081iption\rtest", Maintainers: []*Maintainer{{Name: "\r"}}}
-	if err := md.Validate(); err != nil {
-		t.Fatalf("unexpected error: %s", err)
+func TestValidateDependency(t *testing.T) {
+	dep := &Dependency{
+		Name: "example",
 	}
-	if md.Description != "description test" {
-		t.Fatalf("description was not sanitized: %q", md.Description)
-	}
-	if md.Maintainers[0].Name != " " {
-		t.Fatal("maintainer name was not sanitized")
+	for value, shouldFail := range map[string]bool{
+		"abcdefghijklmenopQRSTUVWXYZ-0123456780_": false,
+		"-okay":      false,
+		"_okay":      false,
+		"- bad":      true,
+		" bad":       true,
+		"bad\nvalue": true,
+		"bad ":       true,
+		"bad$":       true,
+	} {
+		dep.Alias = value
+		res := validateDependency(dep)
+		if res != nil && !shouldFail {
+			t.Errorf("Failed on case %q", dep.Alias)
+		} else if res == nil && shouldFail {
+			t.Errorf("Expected failure for %q", dep.Alias)
+		}
 	}
 }
diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go
index 23c3f9467..3a45b2992 100644
--- a/pkg/chartutil/save_test.go
+++ b/pkg/chartutil/save_test.go
@@ -139,7 +139,7 @@ func TestSavePreservesTimestamps(t *testing.T) {
 		Metadata: &chart.Metadata{
 			APIVersion: chart.APIVersionV1,
 			Name:       "ahab",
-			Version:    "1.2.3",
+			Version:    "1.2.3.4",
 		},
 		Values: map[string]interface{}{
 			"imageName": "testimage",
diff --git a/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml b/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml
index 52dcf930b..b9e3dc69e 100644
--- a/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml
+++ b/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml
@@ -13,7 +13,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
     - name: alpine
       urls:
         - https://charts.helm.sh/stable/alpine-0.2.0.tgz
@@ -26,7 +25,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
   mariadb:
     - name: mariadb
       urls:
@@ -46,4 +44,3 @@ entries:
         - name: Bitnami
           email: containers@bitnami.com
       icon: ""
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/malformed-index.yaml b/pkg/downloader/testdata/repository/malformed-index.yaml
index fa319abdd..887e129e9 100644
--- a/pkg/downloader/testdata/repository/malformed-index.yaml
+++ b/pkg/downloader/testdata/repository/malformed-index.yaml
@@ -13,4 +13,3 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/testing-basicauth-index.yaml b/pkg/downloader/testdata/repository/testing-basicauth-index.yaml
index ed092ef41..da3ed5108 100644
--- a/pkg/downloader/testdata/repository/testing-basicauth-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-basicauth-index.yaml
@@ -12,4 +12,3 @@ entries:
         - http://username:password@example.com/foo-1.2.3.tgz
       version: 1.2.3
       checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
index 81901efc7..17cdde1c6 100644
--- a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml
@@ -12,4 +12,3 @@ entries:
         - https://example.com/foo-1.2.3.tgz
       version: 1.2.3
       checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/testing-https-index.yaml b/pkg/downloader/testdata/repository/testing-https-index.yaml
index 81901efc7..17cdde1c6 100644
--- a/pkg/downloader/testdata/repository/testing-https-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-https-index.yaml
@@ -12,4 +12,3 @@ entries:
         - https://example.com/foo-1.2.3.tgz
       version: 1.2.3
       checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/testing-index.yaml b/pkg/downloader/testdata/repository/testing-index.yaml
index f588bf1fb..c238b8f8d 100644
--- a/pkg/downloader/testdata/repository/testing-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-index.yaml
@@ -13,7 +13,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
     - name: alpine
       urls:
         - http://example.com/alpine-0.2.0.tgz
@@ -27,7 +26,6 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
   foo:
     - name: foo
       description: Foo Chart
@@ -40,4 +38,3 @@ entries:
         - http://example.com/foo-1.2.3.tgz
       version: 1.2.3
       checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/testing-querystring-index.yaml b/pkg/downloader/testdata/repository/testing-querystring-index.yaml
index fa319abdd..887e129e9 100644
--- a/pkg/downloader/testdata/repository/testing-querystring-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-querystring-index.yaml
@@ -13,4 +13,3 @@ entries:
       keywords: []
       maintainers: []
       icon: ""
-      apiVersion: v2
diff --git a/pkg/downloader/testdata/repository/testing-relative-index.yaml b/pkg/downloader/testdata/repository/testing-relative-index.yaml
index ba27ed257..62197b698 100644
--- a/pkg/downloader/testdata/repository/testing-relative-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-relative-index.yaml
@@ -1,28 +1,26 @@
-apiVersion: v1
-entries:
-  foo:
-    - name: foo
-      description: Foo Chart With Relative Path
-      home: https://helm.sh/helm
-      keywords: []
-      maintainers: []
-      sources:
-        - https://github.com/helm/charts
-      urls:
-        - charts/foo-1.2.3.tgz
-      version: 1.2.3
-      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
-  bar:
-    - name: bar
-      description: Bar Chart With Relative Path
-      home: https://helm.sh/helm
-      keywords: []
-      maintainers: []
-      sources:
-        - https://github.com/helm/charts
-      urls:
-        - bar-1.2.3.tgz
-      version: 1.2.3
-      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
+apiVersion: v1
+entries:
+  foo:
+    - name: foo
+      description: Foo Chart With Relative Path
+      home: https://helm.sh/helm
+      keywords: []
+      maintainers: []
+      sources:
+        - https://github.com/helm/charts
+      urls:
+        - charts/foo-1.2.3.tgz
+      version: 1.2.3
+      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
+  bar:
+    - name: bar
+      description: Bar Chart With Relative Path
+      home: https://helm.sh/helm
+      keywords: []
+      maintainers: []
+      sources:
+        - https://github.com/helm/charts
+      urls:
+        - bar-1.2.3.tgz
+      version: 1.2.3
+      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
diff --git a/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml b/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml
index ba27ed257..62197b698 100644
--- a/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml
+++ b/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml
@@ -1,28 +1,26 @@
-apiVersion: v1
-entries:
-  foo:
-    - name: foo
-      description: Foo Chart With Relative Path
-      home: https://helm.sh/helm
-      keywords: []
-      maintainers: []
-      sources:
-        - https://github.com/helm/charts
-      urls:
-        - charts/foo-1.2.3.tgz
-      version: 1.2.3
-      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
-  bar:
-    - name: bar
-      description: Bar Chart With Relative Path
-      home: https://helm.sh/helm
-      keywords: []
-      maintainers: []
-      sources:
-        - https://github.com/helm/charts
-      urls:
-        - bar-1.2.3.tgz
-      version: 1.2.3
-      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
-      apiVersion: v2
+apiVersion: v1
+entries:
+  foo:
+    - name: foo
+      description: Foo Chart With Relative Path
+      home: https://helm.sh/helm
+      keywords: []
+      maintainers: []
+      sources:
+        - https://github.com/helm/charts
+      urls:
+        - charts/foo-1.2.3.tgz
+      version: 1.2.3
+      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
+  bar:
+    - name: bar
+      description: Bar Chart With Relative Path
+      home: https://helm.sh/helm
+      keywords: []
+      maintainers: []
+      sources:
+        - https://github.com/helm/charts
+      urls:
+        - bar-1.2.3.tgz
+      version: 1.2.3
+      checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go
index 47f3c6b2e..b3d91402b 100644
--- a/pkg/repo/index_test.go
+++ b/pkg/repo/index_test.go
@@ -27,10 +27,11 @@ import (
 	"strings"
 	"testing"
 
-	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/cli"
 	"helm.sh/helm/v3/pkg/getter"
 	"helm.sh/helm/v3/pkg/helmpath"
+
+	"helm.sh/helm/v3/pkg/chart"
 )
 
 const (
@@ -64,23 +65,12 @@ entries:
 
 func TestIndexFile(t *testing.T) {
 	i := NewIndexFile()
-	for _, x := range []struct {
-		md       *chart.Metadata
-		filename string
-		baseURL  string
-		digest   string
-	}{
-		{&chart.Metadata{APIVersion: "v2", Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890"},
-		{&chart.Metadata{APIVersion: "v2", Name: "cutter", Version: "0.1.1"}, "cutter-0.1.1.tgz", "http://example.com/charts", "sha256:1234567890abc"},
-		{&chart.Metadata{APIVersion: "v2", Name: "cutter", Version: "0.1.0"}, "cutter-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890abc"},
-		{&chart.Metadata{APIVersion: "v2", Name: "cutter", Version: "0.2.0"}, "cutter-0.2.0.tgz", "http://example.com/charts", "sha256:1234567890abc"},
-		{&chart.Metadata{APIVersion: "v2", Name: "setter", Version: "0.1.9+alpha"}, "setter-0.1.9+alpha.tgz", "http://example.com/charts", "sha256:1234567890abc"},
-		{&chart.Metadata{APIVersion: "v2", Name: "setter", Version: "0.1.9+beta"}, "setter-0.1.9+beta.tgz", "http://example.com/charts", "sha256:1234567890abc"},
-	} {
-		if err := i.MustAdd(x.md, x.filename, x.baseURL, x.digest); err != nil {
-			t.Errorf("unexpected error adding to index: %s", err)
-		}
-	}
+	i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
+	i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.1"}, "cutter-0.1.1.tgz", "http://example.com/charts", "sha256:1234567890abc")
+	i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.0"}, "cutter-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890abc")
+	i.Add(&chart.Metadata{Name: "cutter", Version: "0.2.0"}, "cutter-0.2.0.tgz", "http://example.com/charts", "sha256:1234567890abc")
+	i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+alpha"}, "setter-0.1.9+alpha.tgz", "http://example.com/charts", "sha256:1234567890abc")
+	i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+beta"}, "setter-0.1.9+beta.tgz", "http://example.com/charts", "sha256:1234567890abc")
 
 	i.SortEntries()
 
@@ -136,7 +126,11 @@ func TestLoadIndex(t *testing.T) {
 		tc := tc
 		t.Run(tc.Name, func(t *testing.T) {
 			t.Parallel()
-			i, err := LoadIndexFile(tc.Filename)
+			b, err := ioutil.ReadFile(tc.Filename)
+			if err != nil {
+				t.Fatal(err)
+			}
+			i, err := loadIndex(b)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -147,11 +141,19 @@ func TestLoadIndex(t *testing.T) {
 
 // TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages.
 func TestLoadIndex_Duplicates(t *testing.T) {
-	if _, err := loadIndex([]byte(indexWithDuplicates), "indexWithDuplicates"); err == nil {
+	if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil {
 		t.Errorf("Expected an error when duplicate entries are present")
 	}
 }
 
+func TestLoadIndexFile(t *testing.T) {
+	i, err := LoadIndexFile(testfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+	verifyLocalIndex(t, i)
+}
+
 func TestLoadIndexFileAnnotations(t *testing.T) {
 	i, err := LoadIndexFile(annotationstestfile)
 	if err != nil {
@@ -168,7 +170,11 @@ func TestLoadIndexFileAnnotations(t *testing.T) {
 }
 
 func TestLoadUnorderedIndex(t *testing.T) {
-	i, err := LoadIndexFile(unorderedTestfile)
+	b, err := ioutil.ReadFile(unorderedTestfile)
+	if err != nil {
+		t.Fatal(err)
+	}
+	i, err := loadIndex(b)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -177,26 +183,20 @@ func TestLoadUnorderedIndex(t *testing.T) {
 
 func TestMerge(t *testing.T) {
 	ind1 := NewIndexFile()
-
-	if err := ind1.MustAdd(&chart.Metadata{APIVersion: "v2", Name: "dreadnought", Version: "0.1.0"}, "dreadnought-0.1.0.tgz", "http://example.com", "aaaa"); err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
+	ind1.Add(&chart.Metadata{
+		Name:    "dreadnought",
+		Version: "0.1.0",
+	}, "dreadnought-0.1.0.tgz", "http://example.com", "aaaa")
 
 	ind2 := NewIndexFile()
-
-	for _, x := range []struct {
-		md       *chart.Metadata
-		filename string
-		baseURL  string
-		digest   string
-	}{
-		{&chart.Metadata{APIVersion: "v2", Name: "dreadnought", Version: "0.2.0"}, "dreadnought-0.2.0.tgz", "http://example.com", "aaaabbbb"},
-		{&chart.Metadata{APIVersion: "v2", Name: "doughnut", Version: "0.2.0"}, "doughnut-0.2.0.tgz", "http://example.com", "ccccbbbb"},
-	} {
-		if err := ind2.MustAdd(x.md, x.filename, x.baseURL, x.digest); err != nil {
-			t.Errorf("unexpected error: %s", err)
-		}
-	}
+	ind2.Add(&chart.Metadata{
+		Name:    "dreadnought",
+		Version: "0.2.0",
+	}, "dreadnought-0.2.0.tgz", "http://example.com", "aaaabbbb")
+	ind2.Add(&chart.Metadata{
+		Name:    "doughnut",
+		Version: "0.2.0",
+	}, "doughnut-0.2.0.tgz", "http://example.com", "ccccbbbb")
 
 	ind1.Merge(ind2)
 
@@ -239,7 +239,12 @@ func TestDownloadIndexFile(t *testing.T) {
 			t.Fatalf("error finding created index file: %#v", err)
 		}
 
-		i, err := LoadIndexFile(idx)
+		b, err := ioutil.ReadFile(idx)
+		if err != nil {
+			t.Fatalf("error reading index file: %#v", err)
+		}
+
+		i, err := loadIndex(b)
 		if err != nil {
 			t.Fatalf("Index %q failed to parse: %s", testfile, err)
 		}
@@ -251,7 +256,7 @@ func TestDownloadIndexFile(t *testing.T) {
 			t.Fatalf("error finding created charts file: %#v", err)
 		}
 
-		b, err := ioutil.ReadFile(idx)
+		b, err = ioutil.ReadFile(idx)
 		if err != nil {
 			t.Fatalf("error reading charts file: %#v", err)
 		}
@@ -292,7 +297,12 @@ func TestDownloadIndexFile(t *testing.T) {
 			t.Fatalf("error finding created index file: %#v", err)
 		}
 
-		i, err := LoadIndexFile(idx)
+		b, err := ioutil.ReadFile(idx)
+		if err != nil {
+			t.Fatalf("error reading index file: %#v", err)
+		}
+
+		i, err := loadIndex(b)
 		if err != nil {
 			t.Fatalf("Index %q failed to parse: %s", testfile, err)
 		}
@@ -304,7 +314,7 @@ func TestDownloadIndexFile(t *testing.T) {
 			t.Fatalf("error finding created charts file: %#v", err)
 		}
 
-		b, err := ioutil.ReadFile(idx)
+		b, err = ioutil.ReadFile(idx)
 		if err != nil {
 			t.Fatalf("error reading charts file: %#v", err)
 		}
@@ -335,7 +345,6 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) {
 	expects := []*ChartVersion{
 		{
 			Metadata: &chart.Metadata{
-				APIVersion:  "v2",
 				Name:        "alpine",
 				Description: "string",
 				Version:     "1.0.0",
@@ -350,7 +359,6 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) {
 		},
 		{
 			Metadata: &chart.Metadata{
-				APIVersion:  "v2",
 				Name:        "nginx",
 				Description: "string",
 				Version:     "0.2.0",
@@ -364,7 +372,6 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) {
 		},
 		{
 			Metadata: &chart.Metadata{
-				APIVersion:  "v2",
 				Name:        "nginx",
 				Description: "string",
 				Version:     "0.1.0",
@@ -469,44 +476,28 @@ func TestIndexDirectory(t *testing.T) {
 
 func TestIndexAdd(t *testing.T) {
 	i := NewIndexFile()
-
-	for _, x := range []struct {
-		md       *chart.Metadata
-		filename string
-		baseURL  string
-		digest   string
-	}{
-
-		{&chart.Metadata{APIVersion: "v2", Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890"},
-		{&chart.Metadata{APIVersion: "v2", Name: "alpine", Version: "0.1.0"}, "/home/charts/alpine-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890"},
-		{&chart.Metadata{APIVersion: "v2", Name: "deis", Version: "0.1.0"}, "/home/charts/deis-0.1.0.tgz", "http://example.com/charts/", "sha256:1234567890"},
-	} {
-		if err := i.MustAdd(x.md, x.filename, x.baseURL, x.digest); err != nil {
-			t.Errorf("unexpected error adding to index: %s", err)
-		}
-	}
+	i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
 
 	if i.Entries["clipper"][0].URLs[0] != "http://example.com/charts/clipper-0.1.0.tgz" {
 		t.Errorf("Expected http://example.com/charts/clipper-0.1.0.tgz, got %s", i.Entries["clipper"][0].URLs[0])
 	}
+
+	i.Add(&chart.Metadata{Name: "alpine", Version: "0.1.0"}, "/home/charts/alpine-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
+
 	if i.Entries["alpine"][0].URLs[0] != "http://example.com/charts/alpine-0.1.0.tgz" {
 		t.Errorf("Expected http://example.com/charts/alpine-0.1.0.tgz, got %s", i.Entries["alpine"][0].URLs[0])
 	}
+
+	i.Add(&chart.Metadata{Name: "deis", Version: "0.1.0"}, "/home/charts/deis-0.1.0.tgz", "http://example.com/charts/", "sha256:1234567890")
+
 	if i.Entries["deis"][0].URLs[0] != "http://example.com/charts/deis-0.1.0.tgz" {
 		t.Errorf("Expected http://example.com/charts/deis-0.1.0.tgz, got %s", i.Entries["deis"][0].URLs[0])
 	}
-
-	// test error condition
-	if err := i.MustAdd(&chart.Metadata{}, "error-0.1.0.tgz", "", ""); err == nil {
-		t.Fatal("expected error adding to index")
-	}
 }
 
 func TestIndexWrite(t *testing.T) {
 	i := NewIndexFile()
-	if err := i.MustAdd(&chart.Metadata{APIVersion: "v2", Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890"); err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
+	i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890")
 	dir, err := ioutil.TempDir("", "helm-tmp")
 	if err != nil {
 		t.Fatal(err)
diff --git a/pkg/repo/testdata/chartmuseum-index.yaml b/pkg/repo/testdata/chartmuseum-index.yaml
index 349a529aa..364e42c54 100644
--- a/pkg/repo/testdata/chartmuseum-index.yaml
+++ b/pkg/repo/testdata/chartmuseum-index.yaml
@@ -14,7 +14,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
     - urls:
         - https://charts.helm.sh/stable/nginx-0.1.0.tgz
       name: nginx
@@ -26,7 +25,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
   alpine:
     - urls:
         - https://charts.helm.sh/stable/alpine-1.0.0.tgz
@@ -41,7 +39,6 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
   chartWithNoURL:
     - name: chartWithNoURL
       description: string
@@ -51,4 +48,3 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
diff --git a/pkg/repo/testdata/local-index-annotations.yaml b/pkg/repo/testdata/local-index-annotations.yaml
index 833ab854b..d429aa11c 100644
--- a/pkg/repo/testdata/local-index-annotations.yaml
+++ b/pkg/repo/testdata/local-index-annotations.yaml
@@ -12,7 +12,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
     - urls:
         - https://charts.helm.sh/stable/nginx-0.1.0.tgz
       name: nginx
@@ -24,7 +23,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
   alpine:
     - urls:
         - https://charts.helm.sh/stable/alpine-1.0.0.tgz
@@ -39,7 +37,6 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
   chartWithNoURL:
     - name: chartWithNoURL
       description: string
@@ -49,6 +46,5 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
 annotations:
   helm.sh/test: foo bar
diff --git a/pkg/repo/testdata/local-index-unordered.yaml b/pkg/repo/testdata/local-index-unordered.yaml
index cdfaa7f24..3af72dfd1 100644
--- a/pkg/repo/testdata/local-index-unordered.yaml
+++ b/pkg/repo/testdata/local-index-unordered.yaml
@@ -12,7 +12,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
     - urls:
         - https://charts.helm.sh/stable/nginx-0.2.0.tgz
       name: nginx
@@ -24,7 +23,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
   alpine:
     - urls:
         - https://charts.helm.sh/stable/alpine-1.0.0.tgz
@@ -39,7 +37,6 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
   chartWithNoURL:
     - name: chartWithNoURL
       description: string
@@ -49,4 +46,3 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
diff --git a/pkg/repo/testdata/local-index.yaml b/pkg/repo/testdata/local-index.yaml
index d61f40dda..f8fa32bb2 100644
--- a/pkg/repo/testdata/local-index.yaml
+++ b/pkg/repo/testdata/local-index.yaml
@@ -12,7 +12,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
     - urls:
         - https://charts.helm.sh/stable/nginx-0.1.0.tgz
       name: nginx
@@ -24,7 +23,6 @@ entries:
         - popular
         - web server
         - proxy
-      apiVersion: v2
   alpine:
     - urls:
         - https://charts.helm.sh/stable/alpine-1.0.0.tgz
@@ -39,7 +37,6 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2
   chartWithNoURL:
     - name: chartWithNoURL
       description: string
@@ -49,4 +46,3 @@ entries:
         - small
         - sumtin
       digest: "sha256:1234567890abcdef"
-      apiVersion: v2