From ceb05ac4789834a9f26223ce2564612eef1eeeda Mon Sep 17 00:00:00 2001 From: Yonatan Kahana Date: Thu, 21 May 2020 23:07:11 +0300 Subject: [PATCH] feature(cmd): `helm dep list` with transitive dependencies Signed-off-by: Yonatan Kahana --- cmd/helm/dependency.go | 7 ++-- pkg/action/dependency.go | 33 ++++++++++++++---- pkg/action/dependency_test.go | 12 +++++-- .../charts/transitive-dependencies/Chart.yaml | 9 +++++ .../charts/wordpress-5.0.2.tgz | Bin 0 -> 23488 bytes .../transitive-dependencies/requirements.lock | 6 ++++ .../output/transitive-dependencies.txt | 4 +++ 7 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 pkg/action/testdata/charts/transitive-dependencies/Chart.yaml create mode 100644 pkg/action/testdata/charts/transitive-dependencies/charts/wordpress-5.0.2.tgz create mode 100644 pkg/action/testdata/charts/transitive-dependencies/requirements.lock create mode 100644 pkg/action/testdata/output/transitive-dependencies.txt diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go index 2cc4c5045..afdc1afc4 100644 --- a/cmd/helm/dependency.go +++ b/cmd/helm/dependency.go @@ -100,7 +100,7 @@ func newDependencyCmd(out io.Writer) *cobra.Command { func newDependencyListCmd(out io.Writer) *cobra.Command { client := action.NewDependency() - + var transitive *bool cmd := &cobra.Command{ Use: "list CHART", Aliases: []string{"ls"}, @@ -112,8 +112,11 @@ func newDependencyListCmd(out io.Writer) *cobra.Command { if len(args) > 0 { chartpath = filepath.Clean(args[0]) } - return client.List(chartpath, out) + return client.List(chartpath, out, *transitive) }, } + + transitive = cmd.Flags().Bool("transitive", false, "show transitive dependencies (dependencies of dependencies)") + return cmd } diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go index 4a4b8ebad..d87a467d1 100644 --- a/pkg/action/dependency.go +++ b/pkg/action/dependency.go @@ -44,7 +44,7 @@ func NewDependency() *Dependency { } // List executes 'helm dependency list'. -func (d *Dependency) List(chartpath string, out io.Writer) error { +func (d *Dependency) List(chartpath string, out io.Writer, transitive bool) error { c, err := loader.Load(chartpath) if err != nil { return err @@ -55,7 +55,17 @@ func (d *Dependency) List(chartpath string, out io.Writer) error { return nil } - d.printDependencies(chartpath, out, c) + table := uitable.New() + table.MaxColWidth = 80 + table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") + + d.printDependencies(table, chartpath, c) + + if transitive { + d.printTransitiveDependencies(table, c) + } + + fmt.Fprintln(out, table) fmt.Fprintln(out) d.printMissing(chartpath, out, c.Metadata.Dependencies) return nil @@ -138,14 +148,23 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p } // printDependencies prints all of the dependencies in the yaml file. -func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) { - table := uitable.New() - table.MaxColWidth = 80 - table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") +func (d *Dependency) printDependencies(table *uitable.Table, chartpath string, c *chart.Chart) { for _, row := range c.Metadata.Dependencies { table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row, c)) } - fmt.Fprintln(out, table) +} + +// printTransitiveDependencies prints all the transitive dependencies in a given chart. +func (d *Dependency) printTransitiveDependencies(table *uitable.Table, c *chart.Chart) { + for _, sc := range c.Dependencies() { + if sc.Lock != nil { + for _, td := range sc.Lock.Dependencies { + table.AddRow(td.Name, td.Version, td.Repository, "transitive") + } + } + + d.printTransitiveDependencies(table, sc) + } } // printMissing prints warnings about charts that are present on disk, but are diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go index 158acbfb9..fe0e571d2 100644 --- a/pkg/action/dependency_test.go +++ b/pkg/action/dependency_test.go @@ -25,8 +25,9 @@ import ( func TestList(t *testing.T) { for _, tcase := range []struct { - chart string - golden string + chart string + golden string + transitive bool }{ { chart: "testdata/charts/chart-with-compressed-dependencies", @@ -48,9 +49,14 @@ func TestList(t *testing.T) { chart: "testdata/charts/chart-missing-deps", golden: "output/missing-deps.txt", }, + { + chart: "testdata/charts/transitive-dependencies", + golden: "output/transitive-dependencies.txt", + transitive: true, + }, } { buf := bytes.Buffer{} - if err := NewDependency().List(tcase.chart, &buf); err != nil { + if err := NewDependency().List(tcase.chart, &buf, tcase.transitive); err != nil { t.Fatal(err) } test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden) diff --git a/pkg/action/testdata/charts/transitive-dependencies/Chart.yaml b/pkg/action/testdata/charts/transitive-dependencies/Chart.yaml new file mode 100644 index 000000000..1ea0fe624 --- /dev/null +++ b/pkg/action/testdata/charts/transitive-dependencies/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart +name: my-chart +version: 0.1.0 +dependencies: +- name: wordpress + version: 5.0.2 + repository: https://kubernetes-charts.storage.googleapis.com diff --git a/pkg/action/testdata/charts/transitive-dependencies/charts/wordpress-5.0.2.tgz b/pkg/action/testdata/charts/transitive-dependencies/charts/wordpress-5.0.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..6ec987053b18b98ea69083260ccea2c37656103d GIT binary patch literal 23488 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