Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Next, use the following command to view the content of the package:

```shell
kpt pkg tree
Package "nginx"
Package "nginx" (independent)
├── [Kptfile] Kptfile nginx
├── [deployment.yaml] Deployment my-nginx
└── [svc.yaml] Service my-nginx-svc
Expand Down
6 changes: 3 additions & 3 deletions documentation/content/en/book/02-concepts/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,17 @@ View the package hierarchy using the `tree` command:

```shell
kpt pkg tree wordpress/
Package "wordpress"
Package "wordpress" (independent)
├── [Kptfile] Kptfile wordpress
├── [service.yaml] Service wordpress
├── deployment
│   ├── [deployment.yaml] Deployment wordpress
│   └── [volume.yaml] PersistentVolumeClaim wp-pv-claim
└── Package "mysql"
└── Package "mysql" (dependent)
├── [Kptfile] Kptfile mysql
├── [deployment.yaml] PersistentVolumeClaim mysql-pv-claim
├── [deployment.yaml] Deployment wordpress-mysql
└── [deployment.yaml] Service wordpress-mysql
└── [service.yaml] Service wordpress-mysql
```

This _package hierarchy_ contains two packages:
Expand Down
12 changes: 6 additions & 6 deletions documentation/content/en/book/03-packages/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,17 @@ kpt also provides the `tree` command which is handy for quickly viewing the pack

```shell
kpt pkg tree wordpress/
Package "wordpress"
Package "wordpress" (independent)
├── [Kptfile] Kptfile wordpress
├── [service.yaml] Service wordpress
├── deployment
│   ├── [deployment.yaml] Deployment wordpress
│   └── [volume.yaml] PersistentVolumeClaim wp-pv-claim
└── Package "mysql"
└── Package "mysql" (dependent)
├── [Kptfile] Kptfile mysql
├── [deployment.yaml] PersistentVolumeClaim mysql-pv-claim
├── [deployment.yaml] Deployment wordpress-mysql
└── [deployment.yaml] Service wordpress-mysql
└── [service.yaml] Service wordpress-mysql
```

See the [tree command reference](../../reference/cli/pkg/tree/) for usage.
Expand Down Expand Up @@ -336,17 +336,17 @@ Let us revisit the `wordpress` package and see how it was composed in the first

```shell
kpt pkg tree wordpress/
Package "wordpress"
Package "wordpress" (independent)
├── [Kptfile] Kptfile wordpress
├── [service.yaml] Service wordpress
├── deployment
│   ├── [deployment.yaml] Deployment wordpress
│   └── [volume.yaml] PersistentVolumeClaim wp-pv-claim
└── Package "mysql"
└── Package "mysql" (dependent)
├── [Kptfile] Kptfile mysql
├── [deployment.yaml] PersistentVolumeClaim mysql-pv-claim
├── [deployment.yaml] Deployment wordpress-mysql
└── [deployment.yaml] Service wordpress-mysql
└── [service.yaml] Service wordpress-mysql
```

First, delete the `mysql` subpackage. Deleting a subpackage is done by deleting the subdirectory, as follows:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ Successfully executed 2 function(s) in 1 package(s).

# examine the output of backend package
$ kpt pkg tree backend
Package "backend"
Package "backend" (independent)
├── [Kptfile]  Kptfile backend
├── [namespace.yaml]  Namespace backend
├── [package-context.yaml]  ConfigMap kptfile.kpt.dev
Expand Down
25 changes: 25 additions & 0 deletions documentation/content/en/reference/cli/pkg/tree/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ description: |

`tree` displays resources, files and packages in a tree structure.

Each package (directory containing a `Kptfile`) is annotated with its type:

- **(independent)**: The package has an `upstream` section in its `Kptfile`,
meaning it tracks its own upstream source and can be updated independently.
- **(dependent)**: A subpackage that does not have an `upstream` section in its
`Kptfile`. Its upstream is implicitly inherited from its parent package.

A root package created locally (e.g. via `kpt pkg init`) has no annotation since
it is neither independent nor dependent — it simply has no upstream source.

### Synopsis

<!--mdtogo:Long-->
Expand Down Expand Up @@ -41,4 +51,19 @@ DIR:
$ kpt pkg tree
```

```shell
# Example output showing independent and dependent packages:
$ kpt pkg tree wordpress/
Package "wordpress" (independent)
├── [Kptfile] Kptfile wordpress
├── [service.yaml] Service wordpress
├── deployment
│ ├── [deployment.yaml] Deployment wordpress
│ └── [volume.yaml] PersistentVolumeClaim wp-pv-claim
└── Package "mysql" (dependent)
├── [Kptfile] Kptfile mysql
├── [deployment.yaml] Deployment wordpress-mysql
└── [service.yaml] Service wordpress-mysql
```

<!--mdtogo-->
13 changes: 13 additions & 0 deletions internal/docs/generated/pkgdocs/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

185 changes: 183 additions & 2 deletions thirdparty/cmdconfig/commands/cmdtree/cmdtree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ openAPI:
├── [f1.yaml] Abstraction foo
├── [f1.yaml] Deployment foo
├── [f1.yaml] Service foo
└── Package "subpkg"
└── Package "subpkg" (dependent)
├── [Kptfile] Kptfile subpkg
└── [f2.yaml] Deployment bar
`, filepath.Base(d)), b.String()) {
Expand Down Expand Up @@ -405,14 +405,195 @@ metadata:
if !assert.Equal(t, `Package "Mainpkg"
├── [Kptfile] Kptfile Mainpkg
├── [f1.yaml] Deployment foo
└── Package "Subpkg"
└── Package "Subpkg" (dependent)
├── [Kptfile] Kptfile Subpkg
└── [f2.yaml] Deployment bar
`, b.String()) {
return
}
}

func TestTreeCommand_independentAndDependent(t *testing.T) {
d, err := os.MkdirTemp("", "tree-test")
defer os.RemoveAll(d)
if !assert.NoError(t, err) {
t.FailNow()
}

err = os.MkdirAll(filepath.Join(d, "subpkg"), 0700)
if !assert.NoError(t, err) {
t.FailNow()
}

err = os.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`kind: Deployment
metadata:
name: foo
spec:
replicas: 1
`), 0600)
if !assert.NoError(t, err) {
return
}
err = os.WriteFile(filepath.Join(d, "subpkg", "f2.yaml"), []byte(`kind: Deployment
metadata:
name: bar
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}

// Root package is independent (has upstream)
err = os.WriteFile(filepath.Join(d, "Kptfile"), []byte(`apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: mainpkg
upstream:
type: git
git:
repo: https://github.com/example/repo
directory: /
ref: main
updateStrategy: resource-merge
`), 0600)
if !assert.NoError(t, err) {
return
}
// Subpackage is dependent (no upstream)
err = os.WriteFile(filepath.Join(d, "subpkg", "Kptfile"), []byte(`apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: subpkg
`), 0600)
if !assert.NoError(t, err) {
return
}

b := &bytes.Buffer{}
r := GetTreeRunner(fake.CtxWithPrinter(b, nil), "")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}

if !assert.Equal(t, fmt.Sprintf(`Package %q (independent)
├── [Kptfile] Kptfile mainpkg
├── [f1.yaml] Deployment foo
└── Package "subpkg" (dependent)
├── [Kptfile] Kptfile subpkg
└── [f2.yaml] Deployment bar
`, filepath.Base(d)), b.String()) {
return
}
}

func TestTreeCommand_nestedSubpackages(t *testing.T) {
d, err := os.MkdirTemp("", "tree-test")
defer os.RemoveAll(d)
if !assert.NoError(t, err) {
t.FailNow()
}

err = os.MkdirAll(filepath.Join(d, "subpkg-a", "subpkg-b"), 0700)
if !assert.NoError(t, err) {
t.FailNow()
}

err = os.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`kind: Deployment
metadata:
name: root-deploy
spec:
replicas: 1
`), 0600)
if !assert.NoError(t, err) {
return
}
err = os.WriteFile(filepath.Join(d, "subpkg-a", "f2.yaml"), []byte(`kind: Service
metadata:
name: my-service
spec:
selector:
app: test
`), 0600)
if !assert.NoError(t, err) {
return
}
err = os.WriteFile(filepath.Join(d, "subpkg-a", "subpkg-b", "f3.yaml"), []byte(`kind: ConfigMap
metadata:
name: config
data:
key: value
`), 0600)
if !assert.NoError(t, err) {
return
}

// Root package is independent
err = os.WriteFile(filepath.Join(d, "Kptfile"), []byte(`apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: root
upstream:
type: git
git:
repo: https://github.com/example/repo
directory: /
ref: main
updateStrategy: resource-merge
`), 0600)
if !assert.NoError(t, err) {
return
}
// subpkg-a is also independent (different upstream)
err = os.WriteFile(filepath.Join(d, "subpkg-a", "Kptfile"), []byte(`apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: subpkg-a
upstream:
type: git
git:
repo: https://github.com/example/other-repo
directory: /networking
ref: v2.0
updateStrategy: resource-merge
`), 0600)
if !assert.NoError(t, err) {
return
}
// subpkg-b is dependent (no upstream)
err = os.WriteFile(filepath.Join(d, "subpkg-a", "subpkg-b", "Kptfile"), []byte(`apiVersion: kpt.dev/v1
kind: Kptfile
metadata:
name: subpkg-b
`), 0600)
if !assert.NoError(t, err) {
return
}

b := &bytes.Buffer{}
r := GetTreeRunner(fake.CtxWithPrinter(b, nil), "")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}

if !assert.Equal(t, fmt.Sprintf(`Package %q (independent)
├── [Kptfile] Kptfile root
├── [f1.yaml] Deployment root-deploy
└── Package "subpkg-a" (independent)
├── [Kptfile] Kptfile subpkg-a
├── [f2.yaml] Service my-service
└── Package "subpkg-b" (dependent)
├── [Kptfile] Kptfile subpkg-b
└── [f3.yaml] ConfigMap config
`, filepath.Base(d)), b.String()) {
return
}
}

func TestTreeCommand_symlink(t *testing.T) {
d, err := os.MkdirTemp("", "tree-test")
if !assert.NoError(t, err) {
Expand Down
Loading