Skip to content
Merged
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
2 changes: 1 addition & 1 deletion cli/command/image/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func shouldUseTree(options imagesOptions) (bool, error) {

// isDangling is a copy of [formatter.isDangling].
func isDangling(img image.Summary) bool {
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 {
if len(img.RepoTags) == 0 {
return true
}
return len(img.RepoTags) == 1 && img.RepoTags[0] == "<none>:<none>" && len(img.RepoDigests) == 1 && img.RepoDigests[0] == "<none>@<none>"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
multiplatform:latest aaaaaaaaaaaa 25.5 MB 20.2 MB U
├─ linux/amd64 bbbbbbbbbbbb 12.1 MB 10.0 MB
└─ linux/arm64 cccccccccccc 13.4 MB 10.2 MB U

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
app:v1
app:latest 101010101010 30.5 MB 25.2 MB U
└─ linux/amd64 202020202020 15.2 MB 12.6 MB U

<untagged> 303030303030 12.3 MB 10.1 MB
└─ linux/arm/v7 404040404040 6.1 MB 5.0 MB

base:alpine 505050505050 5.5 MB 5.5 MB

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
<untagged> dddddddddddd 18.5 MB 15.2 MB
├─ linux/amd64 eeeeeeeeeeee 9.2 MB 7.6 MB
└─ linux/arm64 ffffffffffff 9.3 MB 7.6 MB

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
a:1 111111111111 5.5 MB 2.5 MB
<untagged> 222222222222 3.2 MB 1.6 MB
short:v1 333333333333 7.1 MB 3.5 MB U
12 changes: 9 additions & 3 deletions cli/command/image/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/opencontainers/go-digest"
)

const untaggedName = "<untagged>"

type treeOptions struct {
images []imagetypes.Summary
all bool
Expand Down Expand Up @@ -111,7 +113,7 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) (int,
continue
}

if opts.all && len(sortedTags) == 0 {
if len(sortedTags) == 0 {
view.images = append(view.images, topImage{
Details: topDetails,
Children: children,
Expand Down Expand Up @@ -433,7 +435,7 @@ func printChildren(out tui.Output, headers []imgColumn, img topImage, normalColo

func printNames(out tui.Output, headers []imgColumn, img topImage, color, untaggedColor aec.ANSI) {
if len(img.Names) == 0 {
_, _ = fmt.Fprint(out, headers[0].Print(untaggedColor, "<untagged>"))
_, _ = fmt.Fprint(out, headers[0].Print(untaggedColor, untaggedName))
}

for nameIdx, name := range img.Names {
Expand Down Expand Up @@ -545,7 +547,11 @@ func (h imgColumn) PrintR(clr aec.ANSI, s string) string {
func widestFirstColumnValue(headers []imgColumn, images []topImage) int {
width := len(headers[0].Title)
for _, img := range images {
for _, name := range img.Names {
names := img.Names
if len(names) == 0 {
names = []string{untaggedName}
}
for _, name := range names {
if len(name) > width {
width = len(name)
}
Expand Down
199 changes: 199 additions & 0 deletions cli/command/image/tree_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package image

import (
"fmt"
"strings"
"testing"

"github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)

func TestPrintImageTreeAnsiTty(t *testing.T) {
Expand Down Expand Up @@ -154,3 +156,200 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
})
}
}

func TestPrintImageTreeGolden(t *testing.T) {
testCases := []struct {
name string
view treeView
expanded bool
}{
{
name: "width-calculation-untagged",
expanded: false,
view: treeView{
images: []topImage{
{
Names: []string{"a:1"},
Details: imageDetails{
ID: "sha256:1111111111111111111111111111111111111111111111111111111111111111",
DiskUsage: "5.5 MB",
InUse: false,
ContentSize: "2.5 MB",
},
},
{
// Untagged image name is longer than "a:1"
Names: []string{},
Details: imageDetails{
ID: "sha256:2222222222222222222222222222222222222222222222222222222222222222",
DiskUsage: "3.2 MB",
InUse: false,
ContentSize: "1.6 MB",
},
},
{
Names: []string{"short:v1"},
Details: imageDetails{
ID: "sha256:3333333333333333333333333333333333333333333333333333333333333333",
DiskUsage: "7.1 MB",
InUse: true,
ContentSize: "3.5 MB",
},
},
},
imageSpacing: false,
},
},
{
name: "expanded-view-with-platforms",
expanded: false,
view: treeView{
images: []topImage{
{
Names: []string{"multiplatform:latest"},
Details: imageDetails{
ID: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
DiskUsage: "25.5 MB",
InUse: true,
ContentSize: "20.2 MB",
},
Children: []subImage{
{
Platform: "linux/amd64",
Available: true,
Details: imageDetails{
ID: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
DiskUsage: "12.1 MB",
InUse: false,
ContentSize: "10.0 MB",
},
},
{
Platform: "linux/arm64",
Available: true,
Details: imageDetails{
ID: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
DiskUsage: "13.4 MB",
InUse: true,
ContentSize: "10.2 MB",
},
},
},
},
},
imageSpacing: true,
},
},
{
name: "untagged-with-platforms",
expanded: false,
view: treeView{
images: []topImage{
{
Names: []string{},
Details: imageDetails{
ID: "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
DiskUsage: "18.5 MB",
InUse: false,
ContentSize: "15.2 MB",
},
Children: []subImage{
{
Platform: "linux/amd64",
Available: true,
Details: imageDetails{
ID: "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
DiskUsage: "9.2 MB",
InUse: false,
ContentSize: "7.6 MB",
},
},
{
Platform: "linux/arm64",
Available: false,
Details: imageDetails{
ID: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
DiskUsage: "9.3 MB",
InUse: false,
ContentSize: "7.6 MB",
},
},
},
},
},
imageSpacing: true,
},
},
{
name: "mixed-tagged-untagged-with-children",
expanded: false,
view: treeView{
images: []topImage{
{
Names: []string{"app:v1", "app:latest"},
Details: imageDetails{
ID: "sha256:1010101010101010101010101010101010101010101010101010101010101010",
DiskUsage: "30.5 MB",
InUse: true,
ContentSize: "25.2 MB",
},
Children: []subImage{
{
Platform: "linux/amd64",
Available: true,
Details: imageDetails{
ID: "sha256:2020202020202020202020202020202020202020202020202020202020202020",
DiskUsage: "15.2 MB",
InUse: true,
ContentSize: "12.6 MB",
},
},
},
},
{
Names: []string{},
Details: imageDetails{
ID: "sha256:3030303030303030303030303030303030303030303030303030303030303030",
DiskUsage: "12.3 MB",
InUse: false,
ContentSize: "10.1 MB",
},
Children: []subImage{
{
Platform: "linux/arm/v7",
Available: true,
Details: imageDetails{
ID: "sha256:4040404040404040404040404040404040404040404040404040404040404040",
DiskUsage: "6.1 MB",
InUse: false,
ContentSize: "5.0 MB",
},
},
},
},
{
Names: []string{"base:alpine"},
Details: imageDetails{
ID: "sha256:5050505050505050505050505050505050505050505050505050505050505050",
DiskUsage: "5.5 MB",
InUse: false,
ContentSize: "5.5 MB",
},
},
},
imageSpacing: true,
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(nil)
cli.Out().SetIsTerminal(false)

printImageTree(cli, tc.view)

golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("tree-command-success.%s.golden", tc.name))
})
}
}
Loading