@@ -143,7 +143,7 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
143143 }
144144
145145 // Stop Services
146- try await stopOldStuff ( services. map ( { $0 . serviceName } ) , remove: true )
146+ try await stopOldStuff ( services, remove: true )
147147
148148 // Process top-level networks
149149 // This creates named networks defined in the docker-compose.yml
@@ -186,11 +186,7 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
186186 fatalError ( " unreachable " )
187187 }
188188
189- private func getIPForRunningService( _ serviceName: String ) async throws -> String ? {
190- guard let projectName else { return nil }
191-
192- let containerName = " \( projectName) - \( serviceName) "
193-
189+ private func getIPForContainer( _ containerName: String ) async throws -> String ? {
194190 let container = try await ClientContainer . get ( id: containerName)
195191 let ip = container. networks. first? . ipv4Address. address. description
196192
@@ -202,36 +198,47 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
202198 /// - containerName: The exact name of the container (e.g. "Assignment-Manager-API-db").
203199 /// - timeout: Max seconds to wait before failing.
204200 /// - interval: How often to poll (in seconds).
205- /// - Returns: `true` if the container reached "running" state within the timeout.
206- private func waitUntilServiceIsRunning( _ serviceName: String , timeout: TimeInterval = 30 , interval: TimeInterval = 0.5 ) async throws {
207- guard let projectName else { return }
208- let containerName = " \( projectName) - \( serviceName) "
209-
201+ private func waitUntilContainerIsRunning( _ containerName: String , timeout: TimeInterval = 30 , interval: TimeInterval = 0.5 ) async throws {
210202 let deadline = Date ( ) . addingTimeInterval ( timeout)
203+ var lastStatusDescription : String ?
211204
212205 while Date ( ) < deadline {
213- let container = try ? await ClientContainer . get ( id: containerName)
214- if container? . status == . running {
215- return
206+ do {
207+ let container = try await ClientContainer . get ( id: containerName)
208+ lastStatusDescription = " \( container. status) "
209+ if container. status == . running {
210+ print ( " Container ' \( containerName) ' is now running. " )
211+ return
212+ }
213+ } catch {
214+ // Container doesn't exist yet, keep polling
216215 }
217216
218217 try await Task . sleep ( nanoseconds: UInt64 ( interval * 1_000_000_000 ) )
219218 }
220219
220+ let statusMessage = lastStatusDescription. map { " Last status: \( $0) " } ?? " Container was never found "
221221 throw NSError (
222222 domain: " ContainerWait " , code: 1 ,
223223 userInfo: [
224- NSLocalizedDescriptionKey: " Timed out waiting for container ' \( containerName) ' to be running. "
224+ NSLocalizedDescriptionKey: " Timed out waiting for container ' \( containerName) ' to be running. \( statusMessage ) "
225225 ] )
226226 }
227227
228- private func stopOldStuff( _ services: [ String ] , remove: Bool ) async throws {
228+ private func stopOldStuff( _ services: [ ( serviceName : String , service : Service ) ] , remove: Bool ) async throws {
229229 guard let projectName else { return }
230- let containers = services. map { " \( projectName) - \( $0) " }
231230
232- for container in containers {
233- print ( " Stopping container: \( container) " )
234- guard let container = try ? await ClientContainer . get ( id: container) else { continue }
231+ for (serviceName, service) in services {
232+ // Respect explicit container_name, otherwise use default pattern
233+ let containerName : String
234+ if let explicitContainerName = service. container_name {
235+ containerName = explicitContainerName
236+ } else {
237+ containerName = " \( projectName) - \( serviceName) "
238+ }
239+
240+ print ( " Stopping container: \( containerName) " )
241+ guard let container = try ? await ClientContainer . get ( id: containerName) else { continue }
235242
236243 do {
237244 try await container. stop ( )
@@ -250,8 +257,8 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
250257
251258 // MARK: Compose Top Level Functions
252259
253- private mutating func updateEnvironmentWithServiceIP( _ serviceName: String ) async throws {
254- let ip = try await getIPForRunningService ( serviceName )
260+ private mutating func updateEnvironmentWithServiceIP( _ serviceName: String , containerName : String ) async throws {
261+ let ip = try await getIPForContainer ( containerName )
255262 self . containerIps [ serviceName] = ip
256263 for (key, value) in environmentVariables. map ( { ( $0, $1) } ) where value == serviceName {
257264 self . environmentVariables [ key] = ip ?? value
@@ -554,6 +561,18 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
554561
555562 self . containerConsoleColors [ serviceName] = serviceColor
556563
564+ // Check if container already exists
565+ if let existingContainer = try ? await ClientContainer . get ( id: containerName) {
566+ if existingContainer. status == . running {
567+ print ( " Container ' \( containerName) ' is already running. " )
568+ try await updateEnvironmentWithServiceIP ( serviceName, containerName: containerName)
569+ return
570+ } else {
571+ print ( " Error: Container ' \( containerName) ' already exists with status: \( existingContainer. status) . " )
572+ return
573+ }
574+ }
575+
557576 Task { [ self , serviceColor] in
558577 @Sendable
559578 func handleOutput( _ output: String ) {
@@ -568,8 +587,8 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
568587 }
569588
570589 do {
571- try await waitUntilServiceIsRunning ( serviceName )
572- try await updateEnvironmentWithServiceIP ( serviceName)
590+ try await waitUntilContainerIsRunning ( containerName )
591+ try await updateEnvironmentWithServiceIP ( serviceName, containerName : containerName )
573592 } catch {
574593 print ( error)
575594 }
0 commit comments