diff --git a/pkg/repo/v1/index.go b/pkg/repo/v1/index.go index 4a9a879b7..b91dc5570 100644 --- a/pkg/repo/v1/index.go +++ b/pkg/repo/v1/index.go @@ -104,6 +104,21 @@ func NewIndexFile() *IndexFile { // LoadIndexFile takes a file at the given path and returns an IndexFile object func LoadIndexFile(path string) (*IndexFile, error) { + return LoadIndexFileForEntries(path, nil) +} + +// LoadIndexFileForEntries loads an index file but only validates and retains +// entries matching the provided chart names. Unmatched entries are discarded +// after unmarshal without validation, reducing memory and CPU overhead. +// If names is nil, all entries are loaded (equivalent to LoadIndexFile). +func LoadIndexFileForEntries(path string, names []string) (*IndexFile, error) { + var entries map[string]struct{} + if names != nil { + entries = make(map[string]struct{}, len(names)) + for _, n := range names { + entries[n] = struct{}{} + } + } b, err := os.ReadFile(path) if err != nil { return nil, err @@ -112,6 +127,14 @@ func LoadIndexFile(path string) (*IndexFile, error) { if err != nil { return nil, fmt.Errorf("error loading %s: %w", path, err) } + if entries != nil { + for name := range i.Entries { + if _, ok := entries[name]; !ok { + delete(i.Entries, name) + } + } + } + i.SortEntries() return i, nil } diff --git a/pkg/repo/v1/index_test.go b/pkg/repo/v1/index_test.go index 550c8e82c..7b918e3ad 100644 --- a/pkg/repo/v1/index_test.go +++ b/pkg/repo/v1/index_test.go @@ -718,3 +718,59 @@ func TestLoadIndex_DuplicateChartDeps(t *testing.T) { }) } } + + +func TestLoadIndexFileForEntries(t *testing.T) { + // nil names loads all entries + t.Run("nil names loads all", func(t *testing.T) { + i, err := LoadIndexFileForEntries("testdata/local-index.yaml", nil) + if err != nil { + t.Fatal(err) + } + if len(i.Entries) == 0 { + t.Fatal("expected entries, got none") + } + }) + + // filter to a specific chart + t.Run("filter to one entry", func(t *testing.T) { + i, err := LoadIndexFileForEntries("testdata/local-index.yaml", []string{"alpine"}) + if err != nil { + t.Fatal(err) + } + if len(i.Entries) != 1 { + t.Fatalf("expected 1 entry, got %d", len(i.Entries)) + } + if _, ok := i.Entries["alpine"]; !ok { + t.Fatal("expected 'alpine' entry") + } + }) + + // no matches returns empty entries + t.Run("no matches returns empty", func(t *testing.T) { + i, err := LoadIndexFileForEntries("testdata/local-index.yaml", []string{"nonexistent-chart"}) + if err != nil { + t.Fatal(err) + } + if len(i.Entries) != 0 { + t.Fatalf("expected 0 entries, got %d", len(i.Entries)) + } + }) + + // partial match retains only matched entries + t.Run("partial match", func(t *testing.T) { + full, _ := LoadIndexFile("testdata/local-index.yaml") + var firstName string + for k := range full.Entries { + firstName = k + break + } + i, err := LoadIndexFileForEntries("testdata/local-index.yaml", []string{firstName, "nonexistent-chart"}) + if err != nil { + t.Fatal(err) + } + if len(i.Entries) != 1 { + t.Fatalf("expected 1 entry, got %d", len(i.Entries)) + } + }) +} \ No newline at end of file