From 1e259062fce6afa10afe74549b7370cd63a505b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Fri, 12 Dec 2025 15:25:43 +0100 Subject: [PATCH 1/2] cli/tree: Remove unused `all` field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- cli/command/image/list.go | 1 - cli/command/image/tree.go | 1 - 2 files changed, 2 deletions(-) diff --git a/cli/command/image/list.go b/cli/command/image/list.go index 7efaf8426d20..a34ddc5f3bf8 100644 --- a/cli/command/image/list.go +++ b/cli/command/image/list.go @@ -127,7 +127,6 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions if useTree { return runTree(ctx, dockerCLI, treeOptions{ images: images, - all: options.all, filters: filters, expanded: options.tree, }) diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go index 48b7d409fff1..70b74828679b 100644 --- a/cli/command/image/tree.go +++ b/cli/command/image/tree.go @@ -26,7 +26,6 @@ const untaggedName = "" type treeOptions struct { images []imagetypes.Summary - all bool filters client.Filters expanded bool } From 0f03c31ab2193e1b39c210d829e847fb0c38a390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Fri, 12 Dec 2025 15:34:36 +0100 Subject: [PATCH 2/2] image/list: Fix `dangling=false` handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- cli/command/image/list.go | 2 +- cli/command/image/list_test.go | 82 +++++++++++++++++++ ...st-command-filter-dangling.all-flag.golden | 4 + ...mand-filter-dangling.dangling-false.golden | 2 + ...mmand-filter-dangling.dangling-true.golden | 3 + ...-filter-dangling.no-dangling-filter.golden | 2 + 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 cli/command/image/testdata/list-command-filter-dangling.all-flag.golden create mode 100644 cli/command/image/testdata/list-command-filter-dangling.dangling-false.golden create mode 100644 cli/command/image/testdata/list-command-filter-dangling.dangling-true.golden create mode 100644 cli/command/image/testdata/list-command-filter-dangling.no-dangling-filter.golden diff --git a/cli/command/image/list.go b/cli/command/image/list.go index a34ddc5f3bf8..b06e5c1779cd 100644 --- a/cli/command/image/list.go +++ b/cli/command/image/list.go @@ -109,7 +109,7 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions images := res.Items if !options.all { - if _, ok := filters["dangling"]; !ok { + if dangling, ok := filters["dangling"]; !ok || dangling["false"] { images = slices.DeleteFunc(images, isDangling) } } diff --git a/cli/command/image/list_test.go b/cli/command/image/list_test.go index b11b9d106021..8ceafa12c29f 100644 --- a/cli/command/image/list_test.go +++ b/cli/command/image/list_test.go @@ -4,10 +4,12 @@ import ( "errors" "fmt" "io" + "slices" "testing" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" + "github.com/moby/moby/api/types/image" "github.com/moby/moby/client" "gotest.tools/v3/assert" "gotest.tools/v3/golden" @@ -116,6 +118,86 @@ func TestNewListCommandAmbiguous(t *testing.T) { golden.Assert(t, cli.ErrBuffer().String(), "list-command-ambiguous.golden") } +func TestImagesFilterDangling(t *testing.T) { + // Create test images with different states + items := []image.Summary{ + { + ID: "sha256:87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7", + RepoTags: []string{"myimage:latest"}, + RepoDigests: []string{"myimage@sha256:abc123"}, + }, + { + ID: "sha256:0263829989b6fd954f72baaf2fc64bc2e2f01d692d4de72986ea808f6e99813f", + RepoTags: []string{}, + RepoDigests: []string{}, + }, + { + ID: "sha256:a3a5e715f0cc574a73c3f9bebb6bc24f32ffd5b67b387244c2c909da779a1478", + RepoTags: []string{}, + RepoDigests: []string{"image@sha256:a3a5e715f0cc574a73c3f9bebb6bc24f32ffd5b67b387244c2c909da779a1478"}, + }, + } + + testCases := []struct { + name string + args []string + imageListFunc func(options client.ImageListOptions) (client.ImageListResult, error) + }{ + { + name: "dangling-true", + args: []string{"-f", "dangling=true"}, + imageListFunc: func(options client.ImageListOptions) (client.ImageListResult, error) { + // Verify the filter is passed to the API + assert.Check(t, options.Filters["dangling"]["true"]) + // dangling=true is handled on the server side and returns only dangling images + return client.ImageListResult{Items: []image.Summary{items[1], items[2]}}, nil + }, + }, + { + name: "dangling-false", + args: []string{"-f", "dangling=false"}, + imageListFunc: func(options client.ImageListOptions) (client.ImageListResult, error) { + // Verify the filter is passed to the API + assert.Check(t, options.Filters["dangling"]["false"]) + // Return all images including dangling + return client.ImageListResult{Items: slices.Clone(items)}, nil + }, + }, + { + name: "no-dangling-filter", + args: []string{}, + imageListFunc: func(options client.ImageListOptions) (client.ImageListResult, error) { + // Verify no dangling filter is passed to the API + _, exists := options.Filters["dangling"] + assert.Check(t, !exists) + // Return all images including dangling + return client.ImageListResult{Items: slices.Clone(items)}, nil + }, + }, + { + name: "all-flag", + args: []string{"--all"}, + imageListFunc: func(options client.ImageListOptions) (client.ImageListResult, error) { + // Verify the All flag is set + assert.Check(t, options.All) + // Return all images including dangling + return client.ImageListResult{Items: slices.Clone(items)}, nil + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}) + cmd := newImagesCommand(cli) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NilError(t, err) + golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("list-command-filter-dangling.%s.golden", tc.name)) + }) + } +} + func nilToEmptySlice[T any](s []T) []T { if s == nil { return []T{} diff --git a/cli/command/image/testdata/list-command-filter-dangling.all-flag.golden b/cli/command/image/testdata/list-command-filter-dangling.all-flag.golden new file mode 100644 index 000000000000..79a821391845 --- /dev/null +++ b/cli/command/image/testdata/list-command-filter-dangling.all-flag.golden @@ -0,0 +1,4 @@ +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +myimage:latest 87428fc52280 0B 0B + 0263829989b6 0B 0B + a3a5e715f0cc 0B 0B diff --git a/cli/command/image/testdata/list-command-filter-dangling.dangling-false.golden b/cli/command/image/testdata/list-command-filter-dangling.dangling-false.golden new file mode 100644 index 000000000000..c51f6c1b1878 --- /dev/null +++ b/cli/command/image/testdata/list-command-filter-dangling.dangling-false.golden @@ -0,0 +1,2 @@ +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +myimage:latest 87428fc52280 0B 0B diff --git a/cli/command/image/testdata/list-command-filter-dangling.dangling-true.golden b/cli/command/image/testdata/list-command-filter-dangling.dangling-true.golden new file mode 100644 index 000000000000..a9ab1b8cc34a --- /dev/null +++ b/cli/command/image/testdata/list-command-filter-dangling.dangling-true.golden @@ -0,0 +1,3 @@ +IMAGE ID DISK USAGE CONTENT SIZE EXTRA + 0263829989b6 0B 0B + a3a5e715f0cc 0B 0B diff --git a/cli/command/image/testdata/list-command-filter-dangling.no-dangling-filter.golden b/cli/command/image/testdata/list-command-filter-dangling.no-dangling-filter.golden new file mode 100644 index 000000000000..c51f6c1b1878 --- /dev/null +++ b/cli/command/image/testdata/list-command-filter-dangling.no-dangling-filter.golden @@ -0,0 +1,2 @@ +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +myimage:latest 87428fc52280 0B 0B