feat(image): add --tree flag to image list - #5092
Open
ekalinin wants to merge 1 commit into
Open
Conversation
AkihiroSuda
reviewed
Jul 22, 2026
AkihiroSuda
reviewed
Jul 22, 2026
AkihiroSuda
reviewed
Jul 22, 2026
ekalinin
force-pushed
the
feat/images-tree
branch
from
August 4, 2026 16:05
4e9d5cc to
7939e98
Compare
ekalinin
force-pushed
the
feat/images-tree
branch
from
August 5, 2026 10:40
7939e98 to
be5fd77
Compare
Expand the Docker v29 view added in containerd#5093 with `--tree`, matching `docker image ls --tree`: the same IMAGE, ID, DISK USAGE, CONTENT SIZE and EXTRA columns, plus one row per platform the image declares, prefixed with the branch glyphs docker/cli uses. Like docker, a platform that an index lists but that was never pulled is shown with zero sizes. The collapsed and the legacy views keep describing only what is in the store, and so does EnsureAllContent, which would otherwise reach out for platforms the user never asked for. A platform row carries its own manifest digest as the ID, its own sizes, and the "U" flag only when a container actually runs that platform. The image row keeps aggregating its platforms and reuses the existing target-digest lookup, so the default view is unchanged. Matching a container to a platform is done on the full platform form, not on the name the row displays: an index can carry several windows/amd64 manifests that differ only by OSVersion, and keying on the displayed name would let a container on one build flag all of them. The container label is normalized first, since platforms.DefaultString does not normalize: on arm64 it carries a variant while the manifest platform normalizes to a bare linux/arm64, and a raw comparison would never match. The rows are sorted on that same full form, so the ones that render identically keep a stable order. Unlike docker/cli, which computes its column widths itself and separates the images with a blank line, the rows go through a tabwriter shared with the header, where a blank line would terminate the column block and misalign every following group. The groups are therefore separated by the glyphs alone. The flag combinations docker rejects in shouldUseTree are rejected here too, with the same messages. They are validated in pkg/cmd/image, where the options are consumed, so that library callers are covered as well: the tree branch takes precedence over the formatter, so Tree together with Format used to print unaligned rows with no header. The CLI validates too, so that the error still surfaces before a containerd connection is attempted. Also read the platform of a multi-platform image from its index descriptor rather than from the image config. The config may be less specific: alpine ships linux/arm/v6 and linux/arm/v7 manifests whose configs both declare a bare "linux/arm", which normalizes to linux/arm/v7 and collapsed the two onto a single key, dropping one platform. That also left it out of the aggregated sizes of the default view. The expected per-platform content sizes are declared in testutil, and the integration test runs against docker as well, only diverging where docker does: `docker pull` has no --all-platforms, and its message for the digests conflict names its internal flag. DISK USAGE is only asserted to cover CONTENT SIZE, because which platforms are unpacked depends on what the rest of the suite did with the shared image store. Closes containerd#5005 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
ekalinin
force-pushed
the
feat/images-tree
branch
from
August 7, 2026 14:04
be5fd77 to
174afe3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
--treetonerdctl images, expanding the Docker v29 view from #5093 with a row perplatform, like
docker image ls --tree: same columns (IMAGE,ID,DISK USAGE,CONTENT SIZE,EXTRA) and the same branch glyphs docker/cli uses.docker does. This is confined to the tree - the collapsed and legacy views, and
EnsureAllContent, keep describing only what is in the store.Uwhen a containerruns that platform. Matching is on the full platform form rather than on the displayed name, so
several
windows/amd64manifests differing only by OSVersion stay distinct; the container labelis normalized first, since
platforms.DefaultStringdoes not (on arm64 it carries a variant).shouldUseTreedoes, extended to thenerdctl-only
--names. Validated inpkg/cmd/imageso library callers are covered too, and inthe CLI so the error precedes the containerd connection.
a blank line would end the column block and misalign every following group. docker/cli sizes its
columns itself and has no such constraint.
Also fixes the platform of a multi-platform image being read from the image config instead of the
index descriptor: alpine's
linux/arm/v6andlinux/arm/v7configs both declare a barelinux/arm, which collapsed them onto one key and dropped a platform. This affects the non-treeview too, whose aggregated sizes were missing it - happy to split it into its own PR if you
prefer.
Review feedback: the expected per-platform content sizes now live in
testutil(contentsizeinimages.yamlplus accessors) and the test asserts them for every pulled platform, whileDISK USAGEis only asserted to coverCONTENT SIZE, since what is unpacked depends on what therest of the suite did with the shared image store. The test is no longer skipped for Docker and
only diverges where docker does:
docker pullhas no--all-platforms, docker's--digestsconflict message names its internal flag, and
--namesdoes not exist there. It lives in a_linux_test.gobecause thetestutilimage registry is Linux-only.Known difference: with several tags on one target, nerdctl prints a tree per image record while
docker groups the names into a single node - the collapsed view from #5093 behaves the same way.
Closes #5005