From 8809f00fae4557e1924c51a38ba68fdf6f2b9ca4 Mon Sep 17 00:00:00 2001 From: ttyS3 Date: Fri, 5 Dec 2025 00:21:08 +0800 Subject: [PATCH] fix: normalize volume paths to absolute paths for container command - Convert relative paths (e.g., ./data/postgres) to normalized absolute paths - Use URL.standardizedFileURL to remove ./ and ../ components - Pass absolute paths to container run -v command instead of relative paths - Fixes issue where container tool incorrectly treats relative paths as named volume names The container tool requires absolute paths for bind mounts. Previously, relative paths like './data/postgres' were passed directly, causing the tool to validate them as named volume names (which must match ^[A-Za-z0-9][A-Za-z0-9_.-]*$), resulting in validation errors. --- .../Container-Compose/Commands/ComposeUp.swift | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Sources/Container-Compose/Commands/ComposeUp.swift b/Sources/Container-Compose/Commands/ComposeUp.swift index 126d637..fe52d00 100644 --- a/Sources/Container-Compose/Commands/ComposeUp.swift +++ b/Sources/Container-Compose/Commands/ComposeUp.swift @@ -664,13 +664,15 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable { var isDirectory: ObjCBool = false // Ensure the path is absolute or relative to the current directory for FileManager let fullHostPath = (source.starts(with: "/") || source.starts(with: "~")) ? source : (cwd + "/" + source) + // Normalize the path to remove ./ and ../ components, and convert to absolute path + let normalizedHostPath = URL(fileURLWithPath: fullHostPath).standardizedFileURL.path(percentEncoded: false) - if fileManager.fileExists(atPath: fullHostPath, isDirectory: &isDirectory) { + if fileManager.fileExists(atPath: normalizedHostPath, isDirectory: &isDirectory) { if isDirectory.boolValue { // Host path exists and is a directory, add the volume runCommandArgs.append("-v") - // Reconstruct the volume string without mode, ensuring it's source:destination - runCommandArgs.append("\(source):\(destination)") // Use original source for command argument + // Use normalized absolute path for container command (container tool requires absolute paths) + runCommandArgs.append("\(normalizedHostPath):\(destination)") } else { // Host path exists but is a file print("Warning: Volume mount source '\(source)' is a file. The 'container' tool does not support direct file mounts. Skipping this volume.") @@ -678,12 +680,13 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable { } else { // Host path does not exist, assume it's meant to be a directory and try to create it. do { - try fileManager.createDirectory(atPath: fullHostPath, withIntermediateDirectories: true, attributes: nil) - print("Info: Created missing host directory for volume: \(fullHostPath)") + try fileManager.createDirectory(atPath: normalizedHostPath, withIntermediateDirectories: true, attributes: nil) + print("Info: Created missing host directory for volume: \(normalizedHostPath)") runCommandArgs.append("-v") - runCommandArgs.append("\(source):\(destination)") // Use original source for command argument + // Use normalized absolute path for container command (container tool requires absolute paths) + runCommandArgs.append("\(normalizedHostPath):\(destination)") } catch { - print("Error: Could not create host directory '\(fullHostPath)' for volume '\(resolvedVolume)': \(error.localizedDescription). Skipping this volume.") + print("Error: Could not create host directory '\(normalizedHostPath)' for volume '\(resolvedVolume)': \(error.localizedDescription). Skipping this volume.") } } } else {