From 9fe940eae9e90114b3531b31582456ba9c01eb93 Mon Sep 17 00:00:00 2001 From: Basit Mustafa Date: Wed, 22 Apr 2026 06:38:23 -0700 Subject: [PATCH] fix(parsing): support map form of depends_on with condition syntax Docker Compose allows depends_on to be expressed as either a list: depends_on: - db or a map with condition metadata: depends_on: db: condition: service_healthy container-compose only handled the list (and single-string) forms, throwing a typeMismatch error on the map form. This caused failures on any real-world compose file that uses health-check gating. Fix: after failing to decode as String or [String], attempt to decode as [String: [String: String]] and extract the keys as the dependency list. Conditions are silently dropped since Apple Container has no equivalent concept at the orchestration layer. Adds a parsing test covering the two-service map form with mixed condition values (service_healthy, service_started). --- .../Codable Structs/Service.swift | 8 +++++- .../DockerComposeParsingTests.swift | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Sources/Container-Compose/Codable Structs/Service.swift b/Sources/Container-Compose/Codable Structs/Service.swift index 82bfa36..d2327e0 100644 --- a/Sources/Container-Compose/Codable Structs/Service.swift +++ b/Sources/Container-Compose/Codable Structs/Service.swift @@ -192,8 +192,14 @@ public struct Service: Codable, Hashable { if let dependsOnString = try? container.decodeIfPresent(String.self, forKey: .depends_on) { depends_on = [dependsOnString] + } else if let dependsOnArray = try? container.decodeIfPresent([String].self, forKey: .depends_on) { + depends_on = dependsOnArray + } else if let dependsOnMap = try? container.decodeIfPresent([String: [String: String]].self, forKey: .depends_on) { + // Map form: depends_on: { db: { condition: service_healthy } } + // Preserve dependency order; conditions are not applicable to Apple Container. + depends_on = dependsOnMap.keys.sorted() } else { - depends_on = try container.decodeIfPresent([String].self, forKey: .depends_on) + depends_on = nil } user = try container.decodeIfPresent(String.self, forKey: .user) diff --git a/Tests/Container-Compose-StaticTests/DockerComposeParsingTests.swift b/Tests/Container-Compose-StaticTests/DockerComposeParsingTests.swift index 2749fe7..1418a21 100644 --- a/Tests/Container-Compose-StaticTests/DockerComposeParsingTests.swift +++ b/Tests/Container-Compose-StaticTests/DockerComposeParsingTests.swift @@ -180,6 +180,32 @@ struct DockerComposeParsingTests { #expect(compose.services["web"]??.depends_on?.contains("db") == true) } + @Test("Parse compose with depends_on map form (condition syntax)") + func parseComposeWithDependenciesMapForm() throws { + let yaml = """ + version: '3.8' + services: + web: + image: nginx:latest + depends_on: + db: + condition: service_healthy + cache: + condition: service_started + db: + image: postgres:14 + cache: + image: redis:7 + """ + + let decoder = YAMLDecoder() + let compose = try decoder.decode(DockerCompose.self, from: yaml) + + #expect(compose.services["web"]??.depends_on?.contains("db") == true) + #expect(compose.services["web"]??.depends_on?.contains("cache") == true) + #expect(compose.services["web"]??.depends_on?.count == 2) + } + @Test("Parse compose with build context") func parseComposeWithBuild() throws { let yaml = """