From 7150fc3d9e06e1532b55399a328210637bce7865 Mon Sep 17 00:00:00 2001
From: Shane Starcher <shanestarcher@gmail.com>
Date: Tue, 18 Apr 2017 18:10:42 -0400
Subject: [PATCH] bug(helm) - install/upgrade/search semver constraint support

---
 cmd/helm/search.go          | 45 ++++++++++++++++++++++++++++---------
 docs/helm/helm_search.md    |  7 +++---
 docs/man/man1/helm_search.1 |  6 ++++-
 pkg/repo/index.go           | 19 +++++++++++++---
 scripts/completions.bash    |  3 +++
 5 files changed, 62 insertions(+), 18 deletions(-)

diff --git a/cmd/helm/search.go b/cmd/helm/search.go
index a6ad7bbe4..4e7bbb2b3 100644
--- a/cmd/helm/search.go
+++ b/cmd/helm/search.go
@@ -21,6 +21,7 @@ import (
 	"io"
 	"strings"
 
+	"github.com/Masterminds/semver"
 	"github.com/gosuri/uitable"
 	"github.com/spf13/cobra"
 
@@ -45,6 +46,7 @@ type searchCmd struct {
 
 	versions bool
 	regexp   bool
+	version  string
 }
 
 func newSearchCmd(out io.Writer) *cobra.Command {
@@ -62,6 +64,7 @@ func newSearchCmd(out io.Writer) *cobra.Command {
 	f := cmd.Flags()
 	f.BoolVarP(&sc.regexp, "regexp", "r", false, "use regular expressions for searching")
 	f.BoolVarP(&sc.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line")
+	f.StringVarP(&sc.version, "version", "v", "", "search using semantic versioning constraints")
 
 	return cmd
 }
@@ -72,27 +75,47 @@ func (s *searchCmd) run(args []string) error {
 		return err
 	}
 
+	var res []*search.Result
 	if len(args) == 0 {
-		s.showAllCharts(index)
-		return nil
+		res = index.All()
+	} else {
+		q := strings.Join(args, " ")
+		res, err = index.Search(q, searchMaxScore, s.regexp)
+		if err != nil {
+			return nil
+		}
 	}
 
-	q := strings.Join(args, " ")
-	res, err := index.Search(q, searchMaxScore, s.regexp)
+	search.SortScore(res)
+	data, err := s.applyConstraint(res)
 	if err != nil {
-		return nil
+		return err
 	}
-	search.SortScore(res)
 
-	fmt.Fprintln(s.out, s.formatSearchResults(res))
+	fmt.Fprintln(s.out, s.formatSearchResults(data))
 
 	return nil
 }
 
-func (s *searchCmd) showAllCharts(i *search.Index) {
-	res := i.All()
-	search.SortScore(res)
-	fmt.Fprintln(s.out, s.formatSearchResults(res))
+func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, error) {
+	if len(s.version) == 0 {
+		return res, nil
+	}
+
+	constraint, err := semver.NewConstraint(s.version)
+	if err != nil {
+		return res, fmt.Errorf("an invalid version/constraint format: %s", err)
+	}
+
+	data := res[:0]
+	for _, r := range res {
+		v, err := semver.NewVersion(r.Chart.Version)
+		if err != nil || constraint.Check(v) {
+			data = append(data, r)
+		}
+	}
+
+	return data, nil
 }
 
 func (s *searchCmd) formatSearchResults(res []*search.Result) string {
diff --git a/docs/helm/helm_search.md b/docs/helm/helm_search.md
index 238f92298..6f1f8a645 100644
--- a/docs/helm/helm_search.md
+++ b/docs/helm/helm_search.md
@@ -19,8 +19,9 @@ helm search [keyword]
 ### Options
 
 ```
-  -r, --regexp     use regular expressions for searching
-  -l, --versions   show the long listing, with each version of each chart on its own line
+  -r, --regexp           use regular expressions for searching
+  -v, --version string   search using semantic versioning constraints
+  -l, --versions         show the long listing, with each version of each chart on its own line
 ```
 
 ### Options inherited from parent commands
@@ -36,4 +37,4 @@ helm search [keyword]
 ### SEE ALSO
 * [helm](helm.md)	 - The Helm package manager for Kubernetes.
 
-###### Auto generated by spf13/cobra on 16-Apr-2017
+###### Auto generated by spf13/cobra on 18-Apr-2017
diff --git a/docs/man/man1/helm_search.1 b/docs/man/man1/helm_search.1
index 639f957eb..7afc38a12 100644
--- a/docs/man/man1/helm_search.1
+++ b/docs/man/man1/helm_search.1
@@ -27,6 +27,10 @@ Repositories are managed with 'helm repo' commands.
 \fB\-r\fP, \fB\-\-regexp\fP[=false]
     use regular expressions for searching
 
+.PP
+\fB\-v\fP, \fB\-\-version\fP=""
+    search using semantic versioning constraints
+
 .PP
 \fB\-l\fP, \fB\-\-versions\fP[=false]
     show the long listing, with each version of each chart on its own line
@@ -61,4 +65,4 @@ Repositories are managed with 'helm repo' commands.
 
 .SH HISTORY
 .PP
-16\-Apr\-2017 Auto generated by spf13/cobra
+18\-Apr\-2017 Auto generated by spf13/cobra
diff --git a/pkg/repo/index.go b/pkg/repo/index.go
index 1ba5500d6..4e59b8d58 100644
--- a/pkg/repo/index.go
+++ b/pkg/repo/index.go
@@ -155,12 +155,25 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) {
 	if len(vs) == 0 {
 		return nil, ErrNoChartVersion
 	}
+
+	var constraint *semver.Constraints
 	if len(version) == 0 {
-		return vs[0], nil
+		constraint, _ = semver.NewConstraint("*")
+	} else {
+		var err error
+		constraint, err = semver.NewConstraint(version)
+		if err != nil {
+			return nil, err
+		}
 	}
+
 	for _, ver := range vs {
-		// TODO: Do we need to normalize the version field with the SemVer lib?
-		if ver.Version == version {
+		test, err := semver.NewVersion(ver.Version)
+		if err != nil {
+			continue
+		}
+
+		if constraint.Check(test) {
 			return ver, nil
 		}
 	}
diff --git a/scripts/completions.bash b/scripts/completions.bash
index c7b4d7870..b1cd9fe43 100644
--- a/scripts/completions.bash
+++ b/scripts/completions.bash
@@ -1265,6 +1265,9 @@ _helm_search()
     flags+=("--regexp")
     flags+=("-r")
     local_nonpersistent_flags+=("--regexp")
+    flags+=("--version=")
+    two_word_flags+=("-v")
+    local_nonpersistent_flags+=("--version=")
     flags+=("--versions")
     flags+=("-l")
     local_nonpersistent_flags+=("--versions")
-- 
GitLab