Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4f6df44

Browse files
authored
fix(Platform): make arm64 nil-variant and v8 hash identically (#764)
## Summary `Platform.==` treats `arm64` with `nil` variant as equal to `arm64/v8`, but `hash(into:)` used `description` which serializes them differently (`linux/arm64` vs `linux/arm64/v8`). This violates the `Hashable` contract — equal values must produce the same hash. ### Root cause ```swift // == returns true for these two let a = Platform(arch: "arm64", os: "linux", variant: nil) let b = Platform(arch: "arm64", os: "linux", variant: "v8") a == b // true ✓ // but hash was different — broken a.hashValue == b.hashValue // false ✗ (before this fix) ``` This mismatch caused `Set<Platform>` and `Dictionary<Platform, ...>` lookups to silently miss entries when one platform was decoded from JSON (no `variant` field in the manifest) and another was created via `Platform(from:)` or `Platform.current` (which both set `variant = "v8"`). ### Practical consequence In `apple/container`, this manifests as inconsistent platform-string normalization across stages of a single `container build` — some stages log `linux/arm64`, others `linux/arm64/v8` — which can cause `COPY --from=<stage>` to fail to resolve the source stage under concurrent builds. See apple/container#1542. ### Fix `hash(into:)` now normalizes `arm64` with `nil` variant to `"v8"` before hashing, matching the existing `==` behavior.
1 parent 1437c67 commit 4f6df44

2 files changed

Lines changed: 23 additions & 1 deletion

File tree

Sources/ContainerizationOCI/Platform.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,14 @@ extension Platform: Hashable {
272272
}
273273

274274
public func hash(into hasher: inout Swift.Hasher) {
275-
hasher.combine(description)
275+
hasher.combine(os)
276+
hasher.combine(architecture)
277+
// arm64 with no variant is equivalent to arm64/v8 per the == implementation
278+
if architecture == "arm64" {
279+
hasher.combine(variant ?? "v8")
280+
} else {
281+
hasher.combine(variant)
282+
}
276283
}
277284
}
278285

Tests/ContainerizationOCITests/OCIPlatformTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,19 @@ struct OCIPlatformTests {
6666
let rhs = Platform(arch: "arm64", os: "linux", variant: nil)
6767
#expect(lhs == rhs, "Both nil variants => variantEqual is true => overall equal")
6868
}
69+
70+
@Test func arm64_nilAndV8_sameHashValue() {
71+
let withoutVariant = Platform(arch: "arm64", os: "linux", variant: nil)
72+
let withV8 = Platform(arch: "arm64", os: "linux", variant: "v8")
73+
// Equal platforms must produce the same hash — violating this breaks Set/Dictionary lookups
74+
#expect(withoutVariant.hashValue == withV8.hashValue, "arm64 nil variant and v8 must hash identically")
75+
}
76+
77+
@Test func arm64_nilAndV8_setLookup() {
78+
let withoutVariant = Platform(arch: "arm64", os: "linux", variant: nil)
79+
let withV8 = Platform(arch: "arm64", os: "linux", variant: "v8")
80+
var set = Set<Platform>()
81+
set.insert(withoutVariant)
82+
#expect(set.contains(withV8), "arm64/v8 must be found in a Set that contains arm64 with nil variant")
83+
}
6984
}

0 commit comments

Comments
 (0)