diff --git a/pkg/cmd/dependency_update_test.go b/pkg/cmd/dependency_update_test.go index 3eaa51df1..eed251b32 100644 --- a/pkg/cmd/dependency_update_test.go +++ b/pkg/cmd/dependency_update_test.go @@ -310,3 +310,89 @@ func createTestingChart(t *testing.T, dest, name, baseURL string) { t.Fatal(err) } } + +func TestDependencyUpdateCmd_NestedLocalDependencies(t *testing.T) { + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"), + ) + defer srv.Stop() + + if err := srv.LinkIndices(); err != nil { + t.Fatal(err) + } + + dir := func(p ...string) string { + return filepath.Join(append([]string{srv.Root()}, p...)...) + } + + // Create leaf dependency (has remote dependency) + leafChart := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV2, + Name: "leaf-chart", + Version: "0.1.0", + Dependencies: []*chart.Dependency{ + {Name: "reqtest", Version: "0.1.0", Repository: srv.URL()}, + }, + }, + } + if err := chartutil.SaveDir(leafChart, dir()); err != nil { + t.Fatal(err) + } + + // Create middle dependency (has local dependency on leaf) + middleChart := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV2, + Name: "middle-chart", + Version: "0.1.0", + Dependencies: []*chart.Dependency{ + {Name: "leaf-chart", Version: "0.1.0", Repository: "file://../leaf-chart"}, + }, + }, + } + if err := chartutil.SaveDir(middleChart, dir()); err != nil { + t.Fatal(err) + } + + rootChart := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV2, + Name: "root-chart", + Version: "0.1.0", + Dependencies: []*chart.Dependency{ + {Name: "middle-chart", Version: "0.1.0", Repository: "file://../middle-chart"}, + }, + }, + } + if err := chartutil.SaveDir(rootChart, dir()); err != nil { + t.Fatal(err) + } + + contentCache := t.TempDir() + + _, out, err := executeActionCommand( + fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --content-cache %s --plain-http", + dir("root-chart"), dir("repositories.yaml"), dir(), contentCache), + ) + if err != nil { + t.Logf("Output: %s", out) + t.Fatal(err) + } + + expectMiddle := dir("root-chart", "charts/middle-chart-0.1.0.tgz") + if _, err := os.Stat(expectMiddle); err != nil { + t.Fatalf("Expected middle-chart dependency: %s", err) + } + + expectLeaf := dir("middle-chart", "charts/leaf-chart-0.1.0.tgz") + if _, err := os.Stat(expectLeaf); err != nil { + t.Fatalf("Expected leaf-chart dependency: %s", err) + } + + expectReqtest := dir("leaf-chart", "charts/reqtest-0.1.0.tgz") + if _, err := os.Stat(expectReqtest); err != nil { + t.Fatalf("Expected reqtest dependency: %s", err) + } +} diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 6043fbaaa..522c57053 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -192,6 +192,26 @@ func (m *Manager) Update() error { } } + // do resolution for each local dependency first + // it is required when local dependencies may have their own dependencies which must be resolved + for _, dep := range req { + if !isLocalDependency(dep.Repository) { + continue + } + man := *m + // no need to update repositories, it is already done in main chart + man.SkipUpdate = true + chartpath, err := resolver.GetLocalPath(dep.Repository, man.ChartPath) + if err != nil { + return err + } + man.ChartPath = chartpath + err = man.Update() + if err != nil { + return err + } + } + // Now we need to find out which version of a chart best satisfies the // dependencies in the Chart.yaml lock, err := m.resolve(req, repoNames) @@ -300,7 +320,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { } continue } - if strings.HasPrefix(dep.Repository, "file://") { + if isLocalDependency(dep.Repository) { if m.Debug { fmt.Fprintf(m.Out, "Archiving %s from repo %s\n", dep.Name, dep.Repository) } @@ -920,3 +940,7 @@ func key(name string) (string, error) { } return hex.EncodeToString(hash.Sum(nil)), nil } + +func isLocalDependency(repositoryPath string) bool { + return strings.HasPrefix(repositoryPath, "file://") +}