Skip to content

Commit 7eb0c7d

Browse files
committed
fix(oci): render arm64 platform description without redundant v8
`Platform.description` rendered the same arm64 platform two different ways depending on how the value was constructed: `linux/arm64` when the variant was `nil`, and `linux/arm64/v8` when the variant was set to `"v8"`. These are the same platform — `==`, `hash`, and Set membership already treat an arm64 `nil` variant as equivalent to `"v8"` — so two equal values produced different descriptions and drifted between `arm64` and `arm64/v8` across stages of a single build (apple/container#1542). Omit the redundant `v8` variant for arm64 so equal platforms always describe as `linux/arm64`, matching how Docker and containerd display it. Only the rendered description changes; the stored variant and Codable encoding are untouched, so OCI content digests remain stable.
1 parent 44bec8b commit 7eb0c7d

2 files changed

Lines changed: 50 additions & 2 deletions

File tree

Sources/ContainerizationOCI/Platform.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,27 @@ public struct Platform: Sendable, Equatable {
4747
return .init(arch: normalized.arch, os: "linux", variant: normalized.variant)
4848
}
4949

50-
/// The computed description, for example, `linux/arm64/v8`.
50+
/// The computed description, for example, `linux/amd64` or `linux/arm/v7`.
51+
///
52+
/// `arm64`'s only defined variant is `v8`, which `==` and `hash` already treat as
53+
/// equivalent to a `nil` variant. The redundant `v8` is therefore omitted so that
54+
/// two equal arm64 platforms (one with `variant == nil`, one with `"v8"`) describe
55+
/// identically as `linux/arm64`, rather than drifting between `arm64` and
56+
/// `arm64/v8`. See apple/container#1542.
5157
public var description: String {
5258
let architecture = architecture
53-
if let variant = variant {
59+
if let variant, !Self.isRedundantVariant(variant, for: architecture) {
5460
return "\(os)/\(architecture)/\(variant)"
5561
}
5662
return "\(os)/\(architecture)"
5763
}
5864

65+
/// Whether `variant` is the canonical default for `architecture` and can be omitted
66+
/// from the rendered description without losing information.
67+
private static func isRedundantVariant(_ variant: String, for architecture: String) -> Bool {
68+
architecture == "arm64" && variant == "v8"
69+
}
70+
5971
/// The CPU architecture, for example, `amd64` or `arm64`.
6072
public var architecture: String {
6173
Self.normalizeArch(_rawArch).arch

Tests/ContainerizationOCITests/OCIPlatformTests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,40 @@ struct OCIPlatformTests {
8181
set.insert(withoutVariant)
8282
#expect(set.contains(withV8), "arm64/v8 must be found in a Set that contains arm64 with nil variant")
8383
}
84+
85+
// MARK: - description consistency (issue apple/container#1542)
86+
87+
@Test func arm64_nilAndV8_sameDescription() {
88+
let withoutVariant = Platform(arch: "arm64", os: "linux", variant: nil)
89+
let withV8 = Platform(arch: "arm64", os: "linux", variant: "v8")
90+
#expect(
91+
withoutVariant.description == withV8.description,
92+
"equal arm64 platforms must produce the same description"
93+
)
94+
}
95+
96+
@Test func arm64_descriptionDropsRedundantV8() {
97+
let withV8 = Platform(arch: "arm64", os: "linux", variant: "v8")
98+
#expect(withV8.description == "linux/arm64", "arm64/v8 is canonical arm64, rendered without the redundant variant")
99+
}
100+
101+
@Test func arm64_nilVariantDescription() {
102+
let withoutVariant = Platform(arch: "arm64", os: "linux", variant: nil)
103+
#expect(withoutVariant.description == "linux/arm64")
104+
}
105+
106+
@Test func arm64_fromStringWithV8DescriptionIsCanonical() throws {
107+
let parsed = try Platform(from: "linux/arm64/v8")
108+
#expect(parsed.description == "linux/arm64", "parsing arm64/v8 then describing must yield the canonical short form")
109+
}
110+
111+
@Test func arm_v7_descriptionKeepsVariant() {
112+
let armv7 = Platform(arch: "arm", os: "linux", variant: "v7")
113+
#expect(armv7.description == "linux/arm/v7", "non-redundant variants such as arm/v7 must be preserved")
114+
}
115+
116+
@Test func amd64_descriptionUnaffected() {
117+
let amd64 = Platform(arch: "amd64", os: "linux")
118+
#expect(amd64.description == "linux/amd64")
119+
}
84120
}

0 commit comments

Comments
 (0)