Skip to content

Commit 05af1ac

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 2ec221a commit 05af1ac

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
@@ -115,4 +115,40 @@ struct OCIPlatformTests {
115115
#expect(!matcher(Platform(arch: "arm64", os: "windows", variant: "v8")), "matcher must reject a differing OS")
116116
#expect(matcher(Platform(arch: "arm64", os: "linux", variant: "v8")), "matcher must accept the same OS with an implied v8 variant")
117117
}
118+
119+
// MARK: - description consistency (issue apple/container#1542)
120+
121+
@Test func arm64_nilAndV8_sameDescription() {
122+
let withoutVariant = Platform(arch: "arm64", os: "linux", variant: nil)
123+
let withV8 = Platform(arch: "arm64", os: "linux", variant: "v8")
124+
#expect(
125+
withoutVariant.description == withV8.description,
126+
"equal arm64 platforms must produce the same description"
127+
)
128+
}
129+
130+
@Test func arm64_descriptionDropsRedundantV8() {
131+
let withV8 = Platform(arch: "arm64", os: "linux", variant: "v8")
132+
#expect(withV8.description == "linux/arm64", "arm64/v8 is canonical arm64, rendered without the redundant variant")
133+
}
134+
135+
@Test func arm64_nilVariantDescription() {
136+
let withoutVariant = Platform(arch: "arm64", os: "linux", variant: nil)
137+
#expect(withoutVariant.description == "linux/arm64")
138+
}
139+
140+
@Test func arm64_fromStringWithV8DescriptionIsCanonical() throws {
141+
let parsed = try Platform(from: "linux/arm64/v8")
142+
#expect(parsed.description == "linux/arm64", "parsing arm64/v8 then describing must yield the canonical short form")
143+
}
144+
145+
@Test func arm_v7_descriptionKeepsVariant() {
146+
let armv7 = Platform(arch: "arm", os: "linux", variant: "v7")
147+
#expect(armv7.description == "linux/arm/v7", "non-redundant variants such as arm/v7 must be preserved")
148+
}
149+
150+
@Test func amd64_descriptionUnaffected() {
151+
let amd64 = Platform(arch: "amd64", os: "linux")
152+
#expect(amd64.description == "linux/amd64")
153+
}
118154
}

0 commit comments

Comments
 (0)