diff --git a/Sources/Container-Compose/Commands/ComposeDown.swift b/Sources/Container-Compose/Commands/ComposeDown.swift index 9108900..9db241b 100644 --- a/Sources/Container-Compose/Commands/ComposeDown.swift +++ b/Sources/Container-Compose/Commands/ComposeDown.swift @@ -86,7 +86,7 @@ public struct ComposeDown: AsyncParsableCommand { "Note: The 'name' field currently only affects container naming (e.g., '\(name)-serviceName'). Full project-level isolation for other resources (networks, implicit volumes) is not implemented by this tool." ) } else { - projectName = URL(fileURLWithPath: cwd).lastPathComponent // Default to directory name + projectName = deriveProjectName(cwd: cwd) print("Info: No 'name' field found in docker-compose.yml. Using directory name as project name: \(projectName ?? "")") } diff --git a/Sources/Container-Compose/Commands/ComposeUp.swift b/Sources/Container-Compose/Commands/ComposeUp.swift index 4b6adc6..3446d81 100644 --- a/Sources/Container-Compose/Commands/ComposeUp.swift +++ b/Sources/Container-Compose/Commands/ComposeUp.swift @@ -119,7 +119,7 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable { "Note: The 'name' field currently only affects container naming (e.g., '\(name)-serviceName'). Full project-level isolation for other resources (networks, implicit volumes) is not implemented by this tool." ) } else { - projectName = URL(fileURLWithPath: cwd).lastPathComponent // Default to directory name + projectName = deriveProjectName(cwd: cwd) print("Info: No 'name' field found in docker-compose.yml. Using directory name as project name: \(projectName ?? "")") } diff --git a/Sources/Container-Compose/Helper Functions.swift b/Sources/Container-Compose/Helper Functions.swift index 9e38521..9a21a2b 100644 --- a/Sources/Container-Compose/Helper Functions.swift +++ b/Sources/Container-Compose/Helper Functions.swift @@ -93,6 +93,17 @@ public func resolveVariable(_ value: String, with envVars: [String: String]) -> return resolvedValue } +/// Derives a project name from the current working directory. It replaces any '.' characters with +/// '_' to ensure compatibility with container naming conventions. +/// +/// - Parameter cwd: The current working directory path. +/// - Returns: A sanitized project name suitable for container naming. +public func deriveProjectName(cwd: String) -> String { + // We need to replace '.' with _ because it is not supported in the container name + let projectName = URL(fileURLWithPath: cwd).lastPathComponent.replacingOccurrences(of: ".", with: "_") + return projectName +} + extension String: @retroactive Error {} /// A structure representing the result of a command-line process execution. diff --git a/Tests/Container-Compose-StaticTests/HelperFunctionsTests.swift b/Tests/Container-Compose-StaticTests/HelperFunctionsTests.swift new file mode 100644 index 0000000..158c126 --- /dev/null +++ b/Tests/Container-Compose-StaticTests/HelperFunctionsTests.swift @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// Copyright © 2025 Morris Richman and the Container-Compose project authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//===----------------------------------------------------------------------===// + +import Testing +import Foundation +@testable import ContainerComposeCore + +@Suite("Helper Functions Tests") +struct HelperFunctionsTests { + + @Test("Derive project name from current working directory - contains dot") + func testDeriveProjectName() throws { + var cwd = "/Users/user/Projects/My.Project" + var projectName = deriveProjectName(cwd: cwd) + #expect(projectName == "My_Project") + + cwd = ".devcontainers" + projectName = deriveProjectName(cwd: cwd) + #expect(projectName == "_devcontainers") + } + +} \ No newline at end of file