From c8dda1787a1d0439f6e4b844e954d1e9f3e58343 Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:49:19 -0700 Subject: [PATCH 1/9] fix healthcheck struct so tests can be an decoded from an array or single string --- .../xcschemes/Container-Compose.xcscheme | 2 +- .../Healthchecked Redis/docker-compose.yaml | 12 ++++++++++++ .../Codable Structs/Healthcheck.swift | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 Sample Compose Files/Healthchecked Redis/docker-compose.yaml diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme index c9d1c50..127ff5d 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme @@ -106,7 +106,7 @@ isEnabled = "YES"> diff --git a/Sample Compose Files/Healthchecked Redis/docker-compose.yaml b/Sample Compose Files/Healthchecked Redis/docker-compose.yaml new file mode 100644 index 0000000..d8c5ce6 --- /dev/null +++ b/Sample Compose Files/Healthchecked Redis/docker-compose.yaml @@ -0,0 +1,12 @@ +services: + redis: + restart: always + image: redis:7-alpine + volumes: + - redis-data:/data + healthcheck: + test: "redis-cli ping" + interval: 5s + retries: 20 + ports: + - "6379:6379" diff --git a/Sources/Container-Compose/Codable Structs/Healthcheck.swift b/Sources/Container-Compose/Codable Structs/Healthcheck.swift index c26e23a..3c3d94e 100644 --- a/Sources/Container-Compose/Codable Structs/Healthcheck.swift +++ b/Sources/Container-Compose/Codable Structs/Healthcheck.swift @@ -48,4 +48,22 @@ public struct Healthcheck: Codable, Hashable { self.retries = retries self.timeout = timeout } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + // Handle if `test` is a single string instead of an array + if let test = try? container.decodeIfPresent([String].self, forKey: .test) { + self.test = test + } else if let testString = try? container.decodeIfPresent(String.self, forKey: .test) { + self.test = [testString] + } else { + self.test = nil + } + + self.start_period = try container.decodeIfPresent(String.self, forKey: .start_period) + self.interval = try container.decodeIfPresent(String.self, forKey: .interval) + self.retries = try container.decodeIfPresent(Int.self, forKey: .retries) + self.timeout = try container.decodeIfPresent(String.self, forKey: .timeout) + } } From c2be73403746ced5949b7e5ec5aa693c3a1ec5aa Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:51:23 -0700 Subject: [PATCH 2/9] add test for parsing single test command --- .../HealthcheckConfigurationTests.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift index 72ecf8b..06daabd 100644 --- a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift +++ b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift @@ -35,6 +35,19 @@ struct HealthcheckConfigurationTests { #expect(healthcheck.test?.first == "CMD") } + @Test("Parse healthcheck with test command") + func parseHealthcheckWithTestAsSignleString() throws { + let yaml = """ + test: "redis-cli ping" + """ + + let decoder = YAMLDecoder() + let healthcheck = try decoder.decode(Healthcheck.self, from: yaml) + + #expect(healthcheck.test?.count == 1) + #expect(healthcheck.test?.first == "redis-cli ping") + } + @Test("Parse healthcheck with interval") func parseHealthcheckWithInterval() throws { let yaml = """ From 8a963ae730ddd984178ca79c3af8f34e0e759630 Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:55:55 -0700 Subject: [PATCH 3/9] fix test command separation for single string to be split by space This aligns with the compose spec https://docs.docker.com/reference/compose-file/services/ --- Sources/Container-Compose/Codable Structs/Healthcheck.swift | 2 +- .../HealthcheckConfigurationTests.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Container-Compose/Codable Structs/Healthcheck.swift b/Sources/Container-Compose/Codable Structs/Healthcheck.swift index 3c3d94e..153d14c 100644 --- a/Sources/Container-Compose/Codable Structs/Healthcheck.swift +++ b/Sources/Container-Compose/Codable Structs/Healthcheck.swift @@ -56,7 +56,7 @@ public struct Healthcheck: Codable, Hashable { if let test = try? container.decodeIfPresent([String].self, forKey: .test) { self.test = test } else if let testString = try? container.decodeIfPresent(String.self, forKey: .test) { - self.test = [testString] + self.test = ["CMD-SHELL"] + testString.components(separatedBy: " ") } else { self.test = nil } diff --git a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift index 06daabd..6cb2b5c 100644 --- a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift +++ b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift @@ -44,8 +44,8 @@ struct HealthcheckConfigurationTests { let decoder = YAMLDecoder() let healthcheck = try decoder.decode(Healthcheck.self, from: yaml) - #expect(healthcheck.test?.count == 1) - #expect(healthcheck.test?.first == "redis-cli ping") + #expect(healthcheck.test?.count == 2) + #expect(healthcheck.test?.first == "redis-cli") } @Test("Parse healthcheck with interval") From 068bb9194dc0f18eb04d23fc4addaff4c570cb3c Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:56:09 -0700 Subject: [PATCH 4/9] Update HealthcheckConfigurationTests.swift --- .../HealthcheckConfigurationTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift index 6cb2b5c..fe98e77 100644 --- a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift +++ b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift @@ -44,8 +44,8 @@ struct HealthcheckConfigurationTests { let decoder = YAMLDecoder() let healthcheck = try decoder.decode(Healthcheck.self, from: yaml) - #expect(healthcheck.test?.count == 2) - #expect(healthcheck.test?.first == "redis-cli") + #expect(healthcheck.test?.count == 3) + #expect(healthcheck.test?.first == "CMD-SHELL") } @Test("Parse healthcheck with interval") From c3fa7de53756103ab16b880596f1ddeabf3629c6 Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:59:51 -0700 Subject: [PATCH 5/9] Update .swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../xcode/xcshareddata/xcschemes/Container-Compose.xcscheme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme index 127ff5d..1af42bf 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Container-Compose.xcscheme @@ -106,7 +106,7 @@ isEnabled = "YES"> From 18a473a77f9f8a6554faea18bd22bb2d5f8c96db Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:00:58 -0700 Subject: [PATCH 6/9] Update Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../HealthcheckConfigurationTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift index fe98e77..bc45069 100644 --- a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift +++ b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift @@ -35,7 +35,7 @@ struct HealthcheckConfigurationTests { #expect(healthcheck.test?.first == "CMD") } - @Test("Parse healthcheck with test command") + @Test("Parse healthcheck with test command (string)") func parseHealthcheckWithTestAsSignleString() throws { let yaml = """ test: "redis-cli ping" From ea473d1304ff108c13366916f5506469bec204a2 Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:01:16 -0700 Subject: [PATCH 7/9] Update Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../HealthcheckConfigurationTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift index bc45069..fa25ada 100644 --- a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift +++ b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift @@ -36,7 +36,7 @@ struct HealthcheckConfigurationTests { } @Test("Parse healthcheck with test command (string)") - func parseHealthcheckWithTestAsSignleString() throws { + func parseHealthcheckWithTestAsSingleString() throws { let yaml = """ test: "redis-cli ping" """ From 80b4e827adc0e95ca977a67573d5769b44d84ed1 Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:01:39 -0700 Subject: [PATCH 8/9] Update Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../HealthcheckConfigurationTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift index fa25ada..eb2c6c9 100644 --- a/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift +++ b/Tests/Container-Compose-StaticTests/HealthcheckConfigurationTests.swift @@ -44,8 +44,9 @@ struct HealthcheckConfigurationTests { let decoder = YAMLDecoder() let healthcheck = try decoder.decode(Healthcheck.self, from: yaml) - #expect(healthcheck.test?.count == 3) + #expect(healthcheck.test?.count == 2) #expect(healthcheck.test?.first == "CMD-SHELL") + #expect(healthcheck.test?[1] == "redis-cli ping") } @Test("Parse healthcheck with interval") From 3528a5b84c261e0982188e4972e9074f241712d6 Mon Sep 17 00:00:00 2001 From: Morris Richman <81453549+Mcrich23@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:01:57 -0700 Subject: [PATCH 9/9] fix test command parsing to align with docker spec --- Sources/Container-Compose/Codable Structs/Healthcheck.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Container-Compose/Codable Structs/Healthcheck.swift b/Sources/Container-Compose/Codable Structs/Healthcheck.swift index 153d14c..b7e8fc5 100644 --- a/Sources/Container-Compose/Codable Structs/Healthcheck.swift +++ b/Sources/Container-Compose/Codable Structs/Healthcheck.swift @@ -56,7 +56,7 @@ public struct Healthcheck: Codable, Hashable { if let test = try? container.decodeIfPresent([String].self, forKey: .test) { self.test = test } else if let testString = try? container.decodeIfPresent(String.self, forKey: .test) { - self.test = ["CMD-SHELL"] + testString.components(separatedBy: " ") + self.test = ["CMD-SHELL", testString] } else { self.test = nil }