diff --git a/cmd/helm/search.go b/cmd/helm/search.go index 541dabc98..020b6fe5f 100644 --- a/cmd/helm/search.go +++ b/cmd/helm/search.go @@ -43,7 +43,8 @@ type searchCmd struct { out io.Writer helmhome helmpath.Home - regexp bool + versions bool + regexp bool } func newSearchCmd(out io.Writer) *cobra.Command { @@ -59,7 +60,9 @@ func newSearchCmd(out io.Writer) *cobra.Command { PreRunE: requireInit, } - cmd.Flags().BoolVarP(&sc.regexp, "regexp", "r", false, "use regular expressions for searching") + 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.") return cmd } @@ -88,16 +91,7 @@ func (s *searchCmd) run(args []string) error { } func (s *searchCmd) showAllCharts(i *search.Index) { - e := i.Entries() - res := make([]*search.Result, len(e)) - j := 0 - for name, ch := range e { - res[j] = &search.Result{ - Name: name, - Chart: ch, - } - j++ - } + res := i.All() search.SortScore(res) fmt.Fprintln(s.out, s.formatSearchResults(res)) } @@ -129,7 +123,7 @@ func (s *searchCmd) buildIndex() (*search.Index, error) { continue } - i.AddRepo(n, ind) + i.AddRepo(n, ind, s.versions) } return i, nil } diff --git a/cmd/helm/search/search.go b/cmd/helm/search/search.go index aa17fd7ee..e96de2bc1 100644 --- a/cmd/helm/search/search.go +++ b/cmd/helm/search/search.go @@ -29,6 +29,8 @@ import ( "sort" "strings" + "github.com/Masterminds/semver" + "k8s.io/helm/pkg/repo" ) @@ -55,25 +57,51 @@ func NewIndex() *Index { return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}} } +// verSep is a separator for version fields in map keys. +const verSep = "$$" + // AddRepo adds a repository index to the search index. -func (i *Index) AddRepo(rname string, ind *repo.IndexFile) { +func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) { for name, ref := range ind.Entries { if len(ref) == 0 { - // Skip chart names that havae zero releases. + // Skip chart names that have zero releases. continue } // By convention, an index file is supposed to have the newest at the // 0 slot, so our best bet is to grab the 0 entry and build the index // entry off of that. fname := filepath.Join(rname, name) - i.lines[fname] = indstr(rname, ref[0]) - i.charts[fname] = ref[0] + if !all { + i.lines[fname] = indstr(rname, ref[0]) + i.charts[fname] = ref[0] + continue + } + + // If 'all' is set, then we go through all of the refs, and add them all + // to the index. This will generate a lot of near-duplicate entries. + for _, rr := range ref { + versionedName := fname + verSep + rr.Version + i.lines[versionedName] = indstr(rname, rr) + i.charts[versionedName] = rr + } } } -// Entries returns the entries in an index. -func (i *Index) Entries() map[string]*repo.ChartVersion { - return i.charts +// All returns all charts in the index as if they were search results. +// +// Each will be given a score of 0. +func (i *Index) All() []*Result { + res := make([]*Result, len(i.charts)) + j := 0 + for name, ch := range i.charts { + parts := strings.Split(name, verSep) + res[j] = &Result{ + Name: parts[0], + Chart: ch, + } + j++ + } + return res } // Search searches an index for the given term. @@ -118,7 +146,8 @@ func (i *Index) SearchLiteral(term string, threshold int) []*Result { for k, v := range i.lines { res := strings.Index(v, term) if score := i.calcScore(res, v); res != -1 && score < threshold { - buf = append(buf, &Result{Name: k, Score: score, Chart: i.charts[k]}) + parts := strings.Split(k, verSep) // Remove version, if it is there. + buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]}) } } return buf @@ -137,7 +166,8 @@ func (i *Index) SearchRegexp(re string, threshold int) ([]*Result, error) { continue } if score := i.calcScore(ind[0], v); ind[0] >= 0 && score < threshold { - buf = append(buf, &Result{Name: k, Score: score, Chart: i.charts[k]}) + parts := strings.Split(k, verSep) // Remove version, if it is there. + buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]}) } } return buf, nil @@ -179,6 +209,17 @@ func (s scoreSorter) Less(a, b int) bool { if first.Score < second.Score { return true } + if first.Name == second.Name { + v1, err := semver.NewVersion(first.Chart.Version) + if err != nil { + return true + } + v2, err := semver.NewVersion(second.Chart.Version) + if err != nil { + return true + } + return v1.GreaterThan(v2) + } return first.Name < second.Name } diff --git a/cmd/helm/search/search_test.go b/cmd/helm/search/search_test.go index 9cfa1bc3e..7f5d29409 100644 --- a/cmd/helm/search/search_test.go +++ b/cmd/helm/search/search_test.go @@ -93,9 +93,9 @@ var indexfileEntries = map[string]repo.ChartVersions{ }, } -func loadTestIndex(t *testing.T) *Index { +func loadTestIndex(t *testing.T, all bool) *Index { i := NewIndex() - i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}) + i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}, all) i.AddRepo("ztesting", &repo.IndexFile{Entries: map[string]repo.ChartVersions{ "pinta": { { @@ -107,10 +107,24 @@ func loadTestIndex(t *testing.T) *Index { }, }, }, - }}) + }}, all) return i } +func TestAll(t *testing.T) { + i := loadTestIndex(t, false) + all := i.All() + if len(all) != 4 { + t.Errorf("Expected 4 entries, got %d", len(all)) + } + + i = loadTestIndex(t, true) + all = i.All() + if len(all) != 5 { + t.Errorf("Expected 5 entries, got %d", len(all)) + } +} + func TestSearchByName(t *testing.T) { tests := []struct { @@ -188,7 +202,7 @@ func TestSearchByName(t *testing.T) { }, } - i := loadTestIndex(t) + i := loadTestIndex(t, false) for _, tt := range tests { @@ -224,6 +238,18 @@ func TestSearchByName(t *testing.T) { } } +func TestSearchByNameAll(t *testing.T) { + // Test with the All bit turned on. + i := loadTestIndex(t, true) + cs, err := i.Search("santa-maria", 100, false) + if err != nil { + t.Fatal(err) + } + if len(cs) != 2 { + t.Errorf("expected 2 charts, got %d", len(cs)) + } +} + func TestCalcScore(t *testing.T) { i := NewIndex() diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go index 40eb935d3..999e79414 100644 --- a/cmd/helm/search_test.go +++ b/cmd/helm/search_test.go @@ -41,6 +41,12 @@ func TestSearchCmd(t *testing.T) { args: []string{"alpine"}, expect: "NAME \tVERSION\tDESCRIPTION \ntesting/alpine\t0.1.0 \tDeploy a basic Alpine Linux pod", }, + { + name: "search for 'alpine' with versions, expect three matches", + args: []string{"alpine"}, + flags: []string{"--versions"}, + expect: "NAME \tVERSION\tDESCRIPTION \ntesting/alpine\t0.2.0 \tDeploy a basic Alpine Linux pod\ntesting/alpine\t0.1.0 \tDeploy a basic Alpine Linux pod", + }, { name: "search for 'syzygy', expect no matches", args: []string{"syzygy"},