diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index a5b0af080..d0f101dab 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -659,9 +659,27 @@ func (m *Manager) UpdateRepositories() error { return nil } +// Filter out duplicate repos by URL, including those with trailing slashes. +func dedupeRepos(repos []*repo.Entry) []*repo.Entry { + seen := make(map[string]*repo.Entry) + for _, r := range repos { + // Normalize URL by removing trailing slashes. + r.URL = strings.TrimSuffix(r.URL, "/") + seen[r.URL] = r + } + var unique []*repo.Entry + for _, r := range seen { + unique = append(unique, r) + } + return unique +} + func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { var wg sync.WaitGroup + + repos = dedupeRepos(repos) + for _, c := range repos { r, err := repo.NewChartRepository(c, m.Getters) if err != nil { diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index f7ab1a568..13c94e116 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -26,6 +26,7 @@ import ( "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/repo/repotest" ) @@ -572,3 +573,71 @@ func TestKey(t *testing.T) { } } } + +// Test dedupeRepos tests that the dedupeRepos function correctly deduplicates +func TestDedupeRepos(t *testing.T) { + tests := []struct { + name string + repos []*repo.Entry + want []*repo.Entry + }{ + { + name: "no duplicates", + repos: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts2", + }, + }, + want: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts2", + }, + }, + }, + { + name: "duplicates", + repos: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts", + }, + }, + want: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + }, + }, + { + name: "duplicates with trailing slash", + repos: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts/", + }, + }, + want: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := dedupeRepos(tt.repos); !reflect.DeepEqual(got, tt.want) { + t.Errorf("received:\n%v\nwant:\n%v", got, tt.want) + } + }) + } +}