@@ -47,39 +47,47 @@ struct ComposeOptions: ParsableArguments {
4747    } 
4848
4949    func  getComposeFileURLs( )  ->  [ URL ]  { 
50-         // If no files specified, use default
51-         let  files  =  file. isEmpty ?  [ " docker-compose.yaml " ,  " docker-compose.yml " ,  " compose.yaml " ,  " compose.yml " ]  :  file
52-         
53-         var  urls :  [ URL ]  =  [ ] 
5450        let  currentPath  =  FileManager . default. currentDirectoryPath
5551
56-         for  fileName  in  files { 
57-             let  url :  URL 
58-             if  fileName. hasPrefix ( " / " )  { 
59-                 url =  URL ( fileURLWithPath:  fileName) 
60-             }  else  { 
61-                 url =  URL ( fileURLWithPath:  currentPath) . appendingPathComponent ( fileName) 
62-             } 
63-             
64-             // For default files, only add if they exist
65-             if  file. isEmpty { 
66-                 if  FileManager . default. fileExists ( atPath:  url. path)  { 
67-                     urls. append ( url) 
68-                     break  // Use first found default file
69-                 } 
70-             }  else  { 
71-                 // For explicitly specified files, add them all (parser will check existence)
72-                 urls. append ( url) 
52+         // If files were explicitly specified, return all of them (relative to cwd)
53+         if  !file. isEmpty { 
54+             return  file. map  {  name in 
55+                 if  name. hasPrefix ( " / " )  {  return  URL ( fileURLWithPath:  name)  } 
56+                 return  URL ( fileURLWithPath:  currentPath) . appendingPathComponent ( name) 
7357            } 
7458        } 
7559
76-         // If no files found from defaults, return the first default for error message
77-         if  urls. isEmpty && file. isEmpty { 
78-             let  defaultFile  =  URL ( fileURLWithPath:  currentPath) . appendingPathComponent ( " docker-compose.yaml " ) 
79-             urls. append ( defaultFile) 
60+         // Default behavior: detect base compose file and include matching override
61+         let  candidates  =  [ 
62+             " docker-compose.yml " , 
63+             " docker-compose.yaml " , 
64+             " compose.yml " , 
65+             " compose.yaml " , 
66+         ] 
67+         
68+         for  base  in  candidates { 
69+             let  baseURL  =  URL ( fileURLWithPath:  currentPath) . appendingPathComponent ( base) 
70+             if  FileManager . default. fileExists ( atPath:  baseURL. path)  { 
71+                 var  urls  =  [ baseURL] 
72+                 // Include override for the chosen base
73+                 let  overrideCandidates :  [ String ] 
74+                 if  base. hasPrefix ( " docker-compose " )  { 
75+                     overrideCandidates =  [ " docker-compose.override.yml " ,  " docker-compose.override.yaml " ] 
76+                 }  else  { 
77+                     overrideCandidates =  [ " compose.override.yml " ,  " compose.override.yaml " ] 
78+                 } 
79+                 for  o  in  overrideCandidates { 
80+                     let  oURL  =  URL ( fileURLWithPath:  currentPath) . appendingPathComponent ( o) 
81+                     if  FileManager . default. fileExists ( atPath:  oURL. path)  { 
82+                         urls. append ( oURL) 
83+                     } 
84+                 } 
85+                 return  urls
86+             } 
8087        } 
8188
82-         return  urls
89+         // Nothing found: return a sensible default path for better error message downstream
90+         return  [ URL ( fileURLWithPath:  currentPath) . appendingPathComponent ( " docker-compose.yml " ) ] 
8391    } 
8492
8593    func  setEnvironmentVariables( )  { 
@@ -90,4 +98,28 @@ struct ComposeOptions: ParsableArguments {
9098            } 
9199        } 
92100    } 
101+ 
102+     /// Load .env from current working directory and export vars into process env
103+     /// Compose uses .env for interpolation; we approximate by exporting to env before parsing
104+     func  loadDotEnvIfPresent( )  { 
105+         let  cwd  =  FileManager . default. currentDirectoryPath
106+         let  dotEnvURL  =  URL ( fileURLWithPath:  cwd) . appendingPathComponent ( " .env " ) 
107+         guard  FileManager . default. fileExists ( atPath:  dotEnvURL. path)  else  {  return  } 
108+         if  let  contents =  try ?   String ( contentsOf:  dotEnvURL,  encoding:  . utf8)  { 
109+             for  line  in  contents. split ( whereSeparator:  {  $0. isNewline } )  { 
110+                 var  s  =  String ( line) . trimmingCharacters ( in:  . whitespaces) 
111+                 if  s. isEmpty || s. hasPrefix ( " # " )  {  continue  } 
112+                 if  s. hasPrefix ( " export  " )  {  s. removeFirst ( " export  " . count)  } 
113+                 let  parts  =  s. split ( separator:  " = " ,  maxSplits:  1 ) 
114+                 if  parts. count ==  2  { 
115+                     let  key  =  String ( parts [ 0 ] ) . trimmingCharacters ( in:  . whitespaces) 
116+                     var  val  =  String ( parts [ 1 ] ) . trimmingCharacters ( in:  . whitespaces) 
117+                     if  ( val. hasPrefix ( " \" " )  && val. hasSuffix ( " \" " ) )  || ( val. hasPrefix ( " ' " )  && val. hasSuffix ( " ' " ) )  { 
118+                         val =  String ( val. dropFirst ( ) . dropLast ( ) ) 
119+                     } 
120+                     setenv ( key,  val,  1 )  // override to ensure deterministic interpolation in this process
121+                 } 
122+             } 
123+         } 
124+     } 
93125} 
0 commit comments