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 @@ -17,10 +17,11 @@
import ContainerAPIClient
import ContainerResource
import ContainerizationOCI
import Foundation

extension ImageResource: ListDisplayable {
public static var tableHeader: [String] {
["NAME", "TAG", "DIGEST"]
["NAME", "TAG", "DIGEST", "DISK USAGE"]
}

public var tableRow: [String] {
Expand All @@ -30,10 +31,23 @@ extension ImageResource: ListDisplayable {
reference?.name ?? displayReference,
reference?.tag ?? "<none>",
Utility.trimDigest(digest: configuration.descriptor.digest),
formattedDiskUsage,
]
}

public var quietValue: String {
name
}

private var diskUsage: Int64 {
variants
// Skip attestation manifests, which use the `unknown/unknown` platform.
.filter { !($0.platform.os == "unknown" && $0.platform.architecture == "unknown") }
.reduce(0) { $0 + $1.size }
}

private var formattedDiskUsage: String {
let formatter = ByteCountFormatter()
return formatter.string(fromByteCount: diskUsage)
}
}
53 changes: 53 additions & 0 deletions Tests/ContainerCommandsTests/ListFormattingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

import ContainerResource
import ContainerizationOCI
import Foundation
import Testing

Expand Down Expand Up @@ -273,6 +274,58 @@ struct NetworkResourceDisplayTests {
}
}

// MARK: - ImageResource ListDisplayable conformance tests

struct ImageResourceDisplayTests {
@Test
func tableHeaderIncludesDiskUsage() {
#expect(ImageResource.tableHeader == ["NAME", "TAG", "DIGEST", "DISK USAGE"])
}

@Test
func tableRowIncludesDiskUsage() {
let resource = makeImageResource(variants: [
.init(platform: try! Platform(from: "linux/arm64"), digest: "sha256:manifest1", size: 1024, config: makeImageConfig()),
.init(platform: try! Platform(from: "linux/amd64"), digest: "sha256:manifest2", size: 2048, config: makeImageConfig()),
])

#expect(resource.tableRow == ["alpine", "latest", "abcdef123456", formattedByteCount(3072)])
}

@Test
func diskUsageSkipsUnknownPlatformAttestations() {
let resource = makeImageResource(variants: [
.init(platform: try! Platform(from: "linux/arm64"), digest: "sha256:manifest1", size: 1024, config: makeImageConfig()),
.init(platform: Platform(arch: "unknown", os: "unknown"), digest: "sha256:attestation", size: 2048, config: makeImageConfig()),
])

#expect(resource.tableRow.last == formattedByteCount(1024))
}

private func makeImageResource(variants: [ImageResource.Variant]) -> ImageResource {
ImageResource(
configuration: .init(
description: .init(
reference: "docker.io/library/alpine:latest",
descriptor: .init(mediaType: "application/vnd.oci.image.index.v1+json", digest: "sha256:abcdef123456", size: 42)
),
creationDate: Date(timeIntervalSince1970: 0)
),
variants: variants,
displayReference: "alpine:latest"
)
}

private func makeImageConfig() -> ContainerizationOCI.Image {
.init(architecture: "arm64", os: "linux", rootfs: .init(type: "layers", diffIDs: []))
}

private func formattedByteCount(_ value: Int64) -> String {
let formatter = ByteCountFormatter()
return formatter.string(fromByteCount: value)
}
}

// MARK: - ListFormat tests

struct ListFormatTests {
Expand Down
2 changes: 1 addition & 1 deletion docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ No options.

### `container image list (ls)`

Lists local images. Verbose output provides additional details such as image ID, creation time and full size; formatted output provides the same data in machine-readable form.
Lists local images with name, tag, digest, and disk usage. Verbose output provides additional details such as image ID, creation time and full size; formatted output provides the same data in machine-readable form.

**Usage**

Expand Down