Skip to content

Commit 7893728

Browse files
committed
image/list: Show collapsed tree by default
Use the new tree view by default and only fallback if format or old view-related options are used. The expanded view is shown when `--tree` is passed. Signed-off-by: Paweł Gronowski <[email protected]>
1 parent cdaae14 commit 7893728

File tree

2 files changed

+72
-35
lines changed

2 files changed

+72
-35
lines changed

cli/command/image/list.go

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,15 @@ func newListCommand(dockerCLI command.Cli) *cobra.Command {
8282
return &cmd
8383
}
8484

85-
//nolint:gocyclo
8685
func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions) error {
8786
filters := options.filter.Value()
8887
if options.matchName != "" {
8988
filters.Add("reference", options.matchName)
9089
}
9190

92-
if options.tree {
93-
if options.quiet {
94-
return errors.New("--quiet is not yet supported with --tree")
95-
}
96-
if options.noTrunc {
97-
return errors.New("--no-trunc is not yet supported with --tree")
98-
}
99-
if options.showDigests {
100-
return errors.New("--show-digest is not yet supported with --tree")
101-
}
102-
if options.format != "" {
103-
return errors.New("--format is not yet supported with --tree")
104-
}
91+
useTree, err := shouldUseTree(options)
92+
if err != nil {
93+
return err
10594
}
10695

10796
listOpts := client.ImageListOptions{
@@ -114,18 +103,20 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
114103
if err != nil {
115104
return err
116105
}
106+
117107
images := res.Items
118108
if !options.all {
119109
if _, ok := filters["dangling"]; !ok {
120110
images = slices.DeleteFunc(images, isDangling)
121111
}
122112
}
123113

124-
if options.tree {
114+
if useTree {
125115
return runTree(ctx, dockerCLI, treeOptions{
126-
images: images,
127-
all: options.all,
128-
filters: filters,
116+
images: images,
117+
all: options.all,
118+
filters: filters,
119+
expanded: options.tree,
129120
})
130121
}
131122

@@ -155,6 +146,34 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
155146
return nil
156147
}
157148

149+
func shouldUseTree(options imagesOptions) (bool, error) {
150+
if options.quiet {
151+
if options.tree {
152+
return false, errors.New("--quiet is not yet supported with --tree")
153+
}
154+
return false, nil
155+
}
156+
if options.noTrunc {
157+
if options.tree {
158+
return false, errors.New("--no-trunc is not yet supported with --tree")
159+
}
160+
return false, nil
161+
}
162+
if options.showDigests {
163+
if options.tree {
164+
return false, errors.New("--show-digest is not yet supported with --tree")
165+
}
166+
return false, nil
167+
}
168+
if options.format != "" {
169+
if options.tree {
170+
return false, errors.New("--format is not yet supported with --tree")
171+
}
172+
return false, nil
173+
}
174+
return true, nil
175+
}
176+
158177
// isDangling is a copy of [formatter.isDangling].
159178
func isDangling(img image.Summary) bool {
160179
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 {

cli/command/image/tree.go

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ import (
2424
)
2525

2626
type treeOptions struct {
27-
images []imagetypes.Summary
28-
all bool
29-
filters client.Filters
27+
images []imagetypes.Summary
28+
all bool
29+
filters client.Filters
30+
expanded bool
3031
}
3132

3233
type treeView struct {
@@ -48,7 +49,7 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) error
4849
if ctx.Err() != nil {
4950
return ctx.Err()
5051
}
51-
details := imageDetails{
52+
topDetails := imageDetails{
5253
ID: img.ID,
5354
DiskUsage: units.HumanSizeWithPrecision(float64(img.Size), 3),
5455
InUse: img.Containers > 0,
@@ -67,41 +68,58 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) error
6768
continue
6869
}
6970

71+
inUse := len(im.ImageData.Containers) > 0
72+
if inUse {
73+
// Mark top-level parent image as used if any of its subimages are used.
74+
topDetails.InUse = true
75+
}
76+
77+
if !opts.expanded {
78+
continue
79+
}
80+
7081
sub := subImage{
7182
Platform: platforms.Format(im.ImageData.Platform),
7283
Available: im.Available,
7384
Details: imageDetails{
7485
ID: im.ID,
7586
DiskUsage: units.HumanSizeWithPrecision(float64(im.Size.Total), 3),
76-
InUse: len(im.ImageData.Containers) > 0,
87+
InUse: inUse,
7788
ContentSize: units.HumanSizeWithPrecision(float64(im.Size.Content), 3),
7889
},
7990
}
8091

81-
if sub.Details.InUse {
82-
// Mark top-level parent image as used if any of its subimages are used.
83-
details.InUse = true
84-
}
85-
8692
children = append(children, sub)
8793

8894
// Add extra spacing between images if there's at least one entry with children.
8995
view.imageSpacing = true
9096
}
9197

92-
details.ContentSize = units.HumanSizeWithPrecision(float64(totalContent), 3)
98+
topDetails.ContentSize = units.HumanSizeWithPrecision(float64(totalContent), 3)
9399

94100
// Sort tags for this image
95101
sortedTags := make([]string, len(img.RepoTags))
96102
copy(sortedTags, img.RepoTags)
97103
slices.Sort(sortedTags)
98104

99-
view.images = append(view.images, topImage{
100-
Names: sortedTags,
101-
Details: details,
102-
Children: children,
103-
created: img.Created,
104-
})
105+
if opts.expanded {
106+
view.images = append(view.images, topImage{
107+
Names: sortedTags,
108+
Details: topDetails,
109+
Children: children,
110+
created: img.Created,
111+
})
112+
continue
113+
}
114+
115+
for _, tag := range sortedTags {
116+
view.images = append(view.images, topImage{
117+
Names: []string{tag},
118+
Details: topDetails,
119+
Children: children,
120+
created: img.Created,
121+
})
122+
}
105123
}
106124

107125
slices.SortFunc(view.images, func(a, b topImage) int {

0 commit comments

Comments
 (0)